18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Linux NET3: Internet Group Management Protocol [IGMP] 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * This code implements the IGMP protocol as defined in RFC1112. There has 68c2ecf20Sopenharmony_ci * been a further revision of this protocol since which is now supported. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * If you have trouble with this module be careful what gcc you have used, 98c2ecf20Sopenharmony_ci * the older version didn't come out right using gcc 2.5.8, the newer one 108c2ecf20Sopenharmony_ci * seems to fall out with gcc 2.6.2. 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * Authors: 138c2ecf20Sopenharmony_ci * Alan Cox <alan@lxorguk.ukuu.org.uk> 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci * Fixes: 168c2ecf20Sopenharmony_ci * 178c2ecf20Sopenharmony_ci * Alan Cox : Added lots of __inline__ to optimise 188c2ecf20Sopenharmony_ci * the memory usage of all the tiny little 198c2ecf20Sopenharmony_ci * functions. 208c2ecf20Sopenharmony_ci * Alan Cox : Dumped the header building experiment. 218c2ecf20Sopenharmony_ci * Alan Cox : Minor tweaks ready for multicast routing 228c2ecf20Sopenharmony_ci * and extended IGMP protocol. 238c2ecf20Sopenharmony_ci * Alan Cox : Removed a load of inline directives. Gcc 2.5.8 248c2ecf20Sopenharmony_ci * writes utterly bogus code otherwise (sigh) 258c2ecf20Sopenharmony_ci * fixed IGMP loopback to behave in the manner 268c2ecf20Sopenharmony_ci * desired by mrouted, fixed the fact it has been 278c2ecf20Sopenharmony_ci * broken since 1.3.6 and cleaned up a few minor 288c2ecf20Sopenharmony_ci * points. 298c2ecf20Sopenharmony_ci * 308c2ecf20Sopenharmony_ci * Chih-Jen Chang : Tried to revise IGMP to Version 2 318c2ecf20Sopenharmony_ci * Tsu-Sheng Tsao E-mail: chihjenc@scf.usc.edu and tsusheng@scf.usc.edu 328c2ecf20Sopenharmony_ci * The enhancements are mainly based on Steve Deering's 338c2ecf20Sopenharmony_ci * ipmulti-3.5 source code. 348c2ecf20Sopenharmony_ci * Chih-Jen Chang : Added the igmp_get_mrouter_info and 358c2ecf20Sopenharmony_ci * Tsu-Sheng Tsao igmp_set_mrouter_info to keep track of 368c2ecf20Sopenharmony_ci * the mrouted version on that device. 378c2ecf20Sopenharmony_ci * Chih-Jen Chang : Added the max_resp_time parameter to 388c2ecf20Sopenharmony_ci * Tsu-Sheng Tsao igmp_heard_query(). Using this parameter 398c2ecf20Sopenharmony_ci * to identify the multicast router version 408c2ecf20Sopenharmony_ci * and do what the IGMP version 2 specified. 418c2ecf20Sopenharmony_ci * Chih-Jen Chang : Added a timer to revert to IGMP V2 router 428c2ecf20Sopenharmony_ci * Tsu-Sheng Tsao if the specified time expired. 438c2ecf20Sopenharmony_ci * Alan Cox : Stop IGMP from 0.0.0.0 being accepted. 448c2ecf20Sopenharmony_ci * Alan Cox : Use GFP_ATOMIC in the right places. 458c2ecf20Sopenharmony_ci * Christian Daudt : igmp timer wasn't set for local group 468c2ecf20Sopenharmony_ci * memberships but was being deleted, 478c2ecf20Sopenharmony_ci * which caused a "del_timer() called 488c2ecf20Sopenharmony_ci * from %p with timer not initialized\n" 498c2ecf20Sopenharmony_ci * message (960131). 508c2ecf20Sopenharmony_ci * Christian Daudt : removed del_timer from 518c2ecf20Sopenharmony_ci * igmp_timer_expire function (960205). 528c2ecf20Sopenharmony_ci * Christian Daudt : igmp_heard_report now only calls 538c2ecf20Sopenharmony_ci * igmp_timer_expire if tm->running is 548c2ecf20Sopenharmony_ci * true (960216). 558c2ecf20Sopenharmony_ci * Malcolm Beattie : ttl comparison wrong in igmp_rcv made 568c2ecf20Sopenharmony_ci * igmp_heard_query never trigger. Expiry 578c2ecf20Sopenharmony_ci * miscalculation fixed in igmp_heard_query 588c2ecf20Sopenharmony_ci * and random() made to return unsigned to 598c2ecf20Sopenharmony_ci * prevent negative expiry times. 608c2ecf20Sopenharmony_ci * Alexey Kuznetsov: Wrong group leaving behaviour, backport 618c2ecf20Sopenharmony_ci * fix from pending 2.1.x patches. 628c2ecf20Sopenharmony_ci * Alan Cox: Forget to enable FDDI support earlier. 638c2ecf20Sopenharmony_ci * Alexey Kuznetsov: Fixed leaving groups on device down. 648c2ecf20Sopenharmony_ci * Alexey Kuznetsov: Accordance to igmp-v2-06 draft. 658c2ecf20Sopenharmony_ci * David L Stevens: IGMPv3 support, with help from 668c2ecf20Sopenharmony_ci * Vinay Kulkarni 678c2ecf20Sopenharmony_ci */ 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci#include <linux/module.h> 708c2ecf20Sopenharmony_ci#include <linux/slab.h> 718c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 728c2ecf20Sopenharmony_ci#include <linux/types.h> 738c2ecf20Sopenharmony_ci#include <linux/kernel.h> 748c2ecf20Sopenharmony_ci#include <linux/jiffies.h> 758c2ecf20Sopenharmony_ci#include <linux/string.h> 768c2ecf20Sopenharmony_ci#include <linux/socket.h> 778c2ecf20Sopenharmony_ci#include <linux/sockios.h> 788c2ecf20Sopenharmony_ci#include <linux/in.h> 798c2ecf20Sopenharmony_ci#include <linux/inet.h> 808c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 818c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 828c2ecf20Sopenharmony_ci#include <linux/inetdevice.h> 838c2ecf20Sopenharmony_ci#include <linux/igmp.h> 848c2ecf20Sopenharmony_ci#include <linux/if_arp.h> 858c2ecf20Sopenharmony_ci#include <linux/rtnetlink.h> 868c2ecf20Sopenharmony_ci#include <linux/times.h> 878c2ecf20Sopenharmony_ci#include <linux/pkt_sched.h> 888c2ecf20Sopenharmony_ci#include <linux/byteorder/generic.h> 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci#include <net/net_namespace.h> 918c2ecf20Sopenharmony_ci#include <net/arp.h> 928c2ecf20Sopenharmony_ci#include <net/ip.h> 938c2ecf20Sopenharmony_ci#include <net/protocol.h> 948c2ecf20Sopenharmony_ci#include <net/route.h> 958c2ecf20Sopenharmony_ci#include <net/sock.h> 968c2ecf20Sopenharmony_ci#include <net/checksum.h> 978c2ecf20Sopenharmony_ci#include <net/inet_common.h> 988c2ecf20Sopenharmony_ci#include <linux/netfilter_ipv4.h> 998c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_MROUTE 1008c2ecf20Sopenharmony_ci#include <linux/mroute.h> 1018c2ecf20Sopenharmony_ci#endif 1028c2ecf20Sopenharmony_ci#ifdef CONFIG_PROC_FS 1038c2ecf20Sopenharmony_ci#include <linux/proc_fs.h> 1048c2ecf20Sopenharmony_ci#include <linux/seq_file.h> 1058c2ecf20Sopenharmony_ci#endif 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_MULTICAST 1088c2ecf20Sopenharmony_ci/* Parameter names and values are taken from igmp-v2-06 draft */ 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci#define IGMP_QUERY_INTERVAL (125*HZ) 1118c2ecf20Sopenharmony_ci#define IGMP_QUERY_RESPONSE_INTERVAL (10*HZ) 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci#define IGMP_INITIAL_REPORT_DELAY (1) 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci/* IGMP_INITIAL_REPORT_DELAY is not from IGMP specs! 1168c2ecf20Sopenharmony_ci * IGMP specs require to report membership immediately after 1178c2ecf20Sopenharmony_ci * joining a group, but we delay the first report by a 1188c2ecf20Sopenharmony_ci * small interval. It seems more natural and still does not 1198c2ecf20Sopenharmony_ci * contradict to specs provided this delay is small enough. 1208c2ecf20Sopenharmony_ci */ 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci#define IGMP_V1_SEEN(in_dev) \ 1238c2ecf20Sopenharmony_ci (IPV4_DEVCONF_ALL(dev_net(in_dev->dev), FORCE_IGMP_VERSION) == 1 || \ 1248c2ecf20Sopenharmony_ci IN_DEV_CONF_GET((in_dev), FORCE_IGMP_VERSION) == 1 || \ 1258c2ecf20Sopenharmony_ci ((in_dev)->mr_v1_seen && \ 1268c2ecf20Sopenharmony_ci time_before(jiffies, (in_dev)->mr_v1_seen))) 1278c2ecf20Sopenharmony_ci#define IGMP_V2_SEEN(in_dev) \ 1288c2ecf20Sopenharmony_ci (IPV4_DEVCONF_ALL(dev_net(in_dev->dev), FORCE_IGMP_VERSION) == 2 || \ 1298c2ecf20Sopenharmony_ci IN_DEV_CONF_GET((in_dev), FORCE_IGMP_VERSION) == 2 || \ 1308c2ecf20Sopenharmony_ci ((in_dev)->mr_v2_seen && \ 1318c2ecf20Sopenharmony_ci time_before(jiffies, (in_dev)->mr_v2_seen))) 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_cistatic int unsolicited_report_interval(struct in_device *in_dev) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci int interval_ms, interval_jiffies; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci if (IGMP_V1_SEEN(in_dev) || IGMP_V2_SEEN(in_dev)) 1388c2ecf20Sopenharmony_ci interval_ms = IN_DEV_CONF_GET( 1398c2ecf20Sopenharmony_ci in_dev, 1408c2ecf20Sopenharmony_ci IGMPV2_UNSOLICITED_REPORT_INTERVAL); 1418c2ecf20Sopenharmony_ci else /* v3 */ 1428c2ecf20Sopenharmony_ci interval_ms = IN_DEV_CONF_GET( 1438c2ecf20Sopenharmony_ci in_dev, 1448c2ecf20Sopenharmony_ci IGMPV3_UNSOLICITED_REPORT_INTERVAL); 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci interval_jiffies = msecs_to_jiffies(interval_ms); 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci /* _timer functions can't handle a delay of 0 jiffies so ensure 1498c2ecf20Sopenharmony_ci * we always return a positive value. 1508c2ecf20Sopenharmony_ci */ 1518c2ecf20Sopenharmony_ci if (interval_jiffies <= 0) 1528c2ecf20Sopenharmony_ci interval_jiffies = 1; 1538c2ecf20Sopenharmony_ci return interval_jiffies; 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_cistatic void igmpv3_add_delrec(struct in_device *in_dev, struct ip_mc_list *im, 1578c2ecf20Sopenharmony_ci gfp_t gfp); 1588c2ecf20Sopenharmony_cistatic void igmpv3_del_delrec(struct in_device *in_dev, struct ip_mc_list *im); 1598c2ecf20Sopenharmony_cistatic void igmpv3_clear_delrec(struct in_device *in_dev); 1608c2ecf20Sopenharmony_cistatic int sf_setstate(struct ip_mc_list *pmc); 1618c2ecf20Sopenharmony_cistatic void sf_markstate(struct ip_mc_list *pmc); 1628c2ecf20Sopenharmony_ci#endif 1638c2ecf20Sopenharmony_cistatic void ip_mc_clear_src(struct ip_mc_list *pmc); 1648c2ecf20Sopenharmony_cistatic int ip_mc_add_src(struct in_device *in_dev, __be32 *pmca, int sfmode, 1658c2ecf20Sopenharmony_ci int sfcount, __be32 *psfsrc, int delta); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_cistatic void ip_ma_put(struct ip_mc_list *im) 1688c2ecf20Sopenharmony_ci{ 1698c2ecf20Sopenharmony_ci if (refcount_dec_and_test(&im->refcnt)) { 1708c2ecf20Sopenharmony_ci in_dev_put(im->interface); 1718c2ecf20Sopenharmony_ci kfree_rcu(im, rcu); 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci} 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci#define for_each_pmc_rcu(in_dev, pmc) \ 1768c2ecf20Sopenharmony_ci for (pmc = rcu_dereference(in_dev->mc_list); \ 1778c2ecf20Sopenharmony_ci pmc != NULL; \ 1788c2ecf20Sopenharmony_ci pmc = rcu_dereference(pmc->next_rcu)) 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci#define for_each_pmc_rtnl(in_dev, pmc) \ 1818c2ecf20Sopenharmony_ci for (pmc = rtnl_dereference(in_dev->mc_list); \ 1828c2ecf20Sopenharmony_ci pmc != NULL; \ 1838c2ecf20Sopenharmony_ci pmc = rtnl_dereference(pmc->next_rcu)) 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cistatic void ip_sf_list_clear_all(struct ip_sf_list *psf) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci struct ip_sf_list *next; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci while (psf) { 1908c2ecf20Sopenharmony_ci next = psf->sf_next; 1918c2ecf20Sopenharmony_ci kfree(psf); 1928c2ecf20Sopenharmony_ci psf = next; 1938c2ecf20Sopenharmony_ci } 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_MULTICAST 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci/* 1998c2ecf20Sopenharmony_ci * Timer management 2008c2ecf20Sopenharmony_ci */ 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_cistatic void igmp_stop_timer(struct ip_mc_list *im) 2038c2ecf20Sopenharmony_ci{ 2048c2ecf20Sopenharmony_ci spin_lock_bh(&im->lock); 2058c2ecf20Sopenharmony_ci if (del_timer(&im->timer)) 2068c2ecf20Sopenharmony_ci refcount_dec(&im->refcnt); 2078c2ecf20Sopenharmony_ci im->tm_running = 0; 2088c2ecf20Sopenharmony_ci im->reporter = 0; 2098c2ecf20Sopenharmony_ci im->unsolicit_count = 0; 2108c2ecf20Sopenharmony_ci spin_unlock_bh(&im->lock); 2118c2ecf20Sopenharmony_ci} 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci/* It must be called with locked im->lock */ 2148c2ecf20Sopenharmony_cistatic void igmp_start_timer(struct ip_mc_list *im, int max_delay) 2158c2ecf20Sopenharmony_ci{ 2168c2ecf20Sopenharmony_ci int tv = prandom_u32() % max_delay; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci im->tm_running = 1; 2198c2ecf20Sopenharmony_ci if (refcount_inc_not_zero(&im->refcnt)) { 2208c2ecf20Sopenharmony_ci if (mod_timer(&im->timer, jiffies + tv + 2)) 2218c2ecf20Sopenharmony_ci ip_ma_put(im); 2228c2ecf20Sopenharmony_ci } 2238c2ecf20Sopenharmony_ci} 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_cistatic void igmp_gq_start_timer(struct in_device *in_dev) 2268c2ecf20Sopenharmony_ci{ 2278c2ecf20Sopenharmony_ci int tv = prandom_u32() % in_dev->mr_maxdelay; 2288c2ecf20Sopenharmony_ci unsigned long exp = jiffies + tv + 2; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci if (in_dev->mr_gq_running && 2318c2ecf20Sopenharmony_ci time_after_eq(exp, (in_dev->mr_gq_timer).expires)) 2328c2ecf20Sopenharmony_ci return; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci in_dev->mr_gq_running = 1; 2358c2ecf20Sopenharmony_ci if (!mod_timer(&in_dev->mr_gq_timer, exp)) 2368c2ecf20Sopenharmony_ci in_dev_hold(in_dev); 2378c2ecf20Sopenharmony_ci} 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_cistatic void igmp_ifc_start_timer(struct in_device *in_dev, int delay) 2408c2ecf20Sopenharmony_ci{ 2418c2ecf20Sopenharmony_ci int tv = prandom_u32() % delay; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci if (!mod_timer(&in_dev->mr_ifc_timer, jiffies+tv+2)) 2448c2ecf20Sopenharmony_ci in_dev_hold(in_dev); 2458c2ecf20Sopenharmony_ci} 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_cistatic void igmp_mod_timer(struct ip_mc_list *im, int max_delay) 2488c2ecf20Sopenharmony_ci{ 2498c2ecf20Sopenharmony_ci spin_lock_bh(&im->lock); 2508c2ecf20Sopenharmony_ci im->unsolicit_count = 0; 2518c2ecf20Sopenharmony_ci if (del_timer(&im->timer)) { 2528c2ecf20Sopenharmony_ci if ((long)(im->timer.expires-jiffies) < max_delay) { 2538c2ecf20Sopenharmony_ci add_timer(&im->timer); 2548c2ecf20Sopenharmony_ci im->tm_running = 1; 2558c2ecf20Sopenharmony_ci spin_unlock_bh(&im->lock); 2568c2ecf20Sopenharmony_ci return; 2578c2ecf20Sopenharmony_ci } 2588c2ecf20Sopenharmony_ci refcount_dec(&im->refcnt); 2598c2ecf20Sopenharmony_ci } 2608c2ecf20Sopenharmony_ci igmp_start_timer(im, max_delay); 2618c2ecf20Sopenharmony_ci spin_unlock_bh(&im->lock); 2628c2ecf20Sopenharmony_ci} 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci/* 2668c2ecf20Sopenharmony_ci * Send an IGMP report. 2678c2ecf20Sopenharmony_ci */ 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci#define IGMP_SIZE (sizeof(struct igmphdr)+sizeof(struct iphdr)+4) 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_cistatic int is_in(struct ip_mc_list *pmc, struct ip_sf_list *psf, int type, 2738c2ecf20Sopenharmony_ci int gdeleted, int sdeleted) 2748c2ecf20Sopenharmony_ci{ 2758c2ecf20Sopenharmony_ci switch (type) { 2768c2ecf20Sopenharmony_ci case IGMPV3_MODE_IS_INCLUDE: 2778c2ecf20Sopenharmony_ci case IGMPV3_MODE_IS_EXCLUDE: 2788c2ecf20Sopenharmony_ci if (gdeleted || sdeleted) 2798c2ecf20Sopenharmony_ci return 0; 2808c2ecf20Sopenharmony_ci if (!(pmc->gsquery && !psf->sf_gsresp)) { 2818c2ecf20Sopenharmony_ci if (pmc->sfmode == MCAST_INCLUDE) 2828c2ecf20Sopenharmony_ci return 1; 2838c2ecf20Sopenharmony_ci /* don't include if this source is excluded 2848c2ecf20Sopenharmony_ci * in all filters 2858c2ecf20Sopenharmony_ci */ 2868c2ecf20Sopenharmony_ci if (psf->sf_count[MCAST_INCLUDE]) 2878c2ecf20Sopenharmony_ci return type == IGMPV3_MODE_IS_INCLUDE; 2888c2ecf20Sopenharmony_ci return pmc->sfcount[MCAST_EXCLUDE] == 2898c2ecf20Sopenharmony_ci psf->sf_count[MCAST_EXCLUDE]; 2908c2ecf20Sopenharmony_ci } 2918c2ecf20Sopenharmony_ci return 0; 2928c2ecf20Sopenharmony_ci case IGMPV3_CHANGE_TO_INCLUDE: 2938c2ecf20Sopenharmony_ci if (gdeleted || sdeleted) 2948c2ecf20Sopenharmony_ci return 0; 2958c2ecf20Sopenharmony_ci return psf->sf_count[MCAST_INCLUDE] != 0; 2968c2ecf20Sopenharmony_ci case IGMPV3_CHANGE_TO_EXCLUDE: 2978c2ecf20Sopenharmony_ci if (gdeleted || sdeleted) 2988c2ecf20Sopenharmony_ci return 0; 2998c2ecf20Sopenharmony_ci if (pmc->sfcount[MCAST_EXCLUDE] == 0 || 3008c2ecf20Sopenharmony_ci psf->sf_count[MCAST_INCLUDE]) 3018c2ecf20Sopenharmony_ci return 0; 3028c2ecf20Sopenharmony_ci return pmc->sfcount[MCAST_EXCLUDE] == 3038c2ecf20Sopenharmony_ci psf->sf_count[MCAST_EXCLUDE]; 3048c2ecf20Sopenharmony_ci case IGMPV3_ALLOW_NEW_SOURCES: 3058c2ecf20Sopenharmony_ci if (gdeleted || !psf->sf_crcount) 3068c2ecf20Sopenharmony_ci return 0; 3078c2ecf20Sopenharmony_ci return (pmc->sfmode == MCAST_INCLUDE) ^ sdeleted; 3088c2ecf20Sopenharmony_ci case IGMPV3_BLOCK_OLD_SOURCES: 3098c2ecf20Sopenharmony_ci if (pmc->sfmode == MCAST_INCLUDE) 3108c2ecf20Sopenharmony_ci return gdeleted || (psf->sf_crcount && sdeleted); 3118c2ecf20Sopenharmony_ci return psf->sf_crcount && !gdeleted && !sdeleted; 3128c2ecf20Sopenharmony_ci } 3138c2ecf20Sopenharmony_ci return 0; 3148c2ecf20Sopenharmony_ci} 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_cistatic int 3178c2ecf20Sopenharmony_ciigmp_scount(struct ip_mc_list *pmc, int type, int gdeleted, int sdeleted) 3188c2ecf20Sopenharmony_ci{ 3198c2ecf20Sopenharmony_ci struct ip_sf_list *psf; 3208c2ecf20Sopenharmony_ci int scount = 0; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci for (psf = pmc->sources; psf; psf = psf->sf_next) { 3238c2ecf20Sopenharmony_ci if (!is_in(pmc, psf, type, gdeleted, sdeleted)) 3248c2ecf20Sopenharmony_ci continue; 3258c2ecf20Sopenharmony_ci scount++; 3268c2ecf20Sopenharmony_ci } 3278c2ecf20Sopenharmony_ci return scount; 3288c2ecf20Sopenharmony_ci} 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci/* source address selection per RFC 3376 section 4.2.13 */ 3318c2ecf20Sopenharmony_cistatic __be32 igmpv3_get_srcaddr(struct net_device *dev, 3328c2ecf20Sopenharmony_ci const struct flowi4 *fl4) 3338c2ecf20Sopenharmony_ci{ 3348c2ecf20Sopenharmony_ci struct in_device *in_dev = __in_dev_get_rcu(dev); 3358c2ecf20Sopenharmony_ci const struct in_ifaddr *ifa; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci if (!in_dev) 3388c2ecf20Sopenharmony_ci return htonl(INADDR_ANY); 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci in_dev_for_each_ifa_rcu(ifa, in_dev) { 3418c2ecf20Sopenharmony_ci if (fl4->saddr == ifa->ifa_local) 3428c2ecf20Sopenharmony_ci return fl4->saddr; 3438c2ecf20Sopenharmony_ci } 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci return htonl(INADDR_ANY); 3468c2ecf20Sopenharmony_ci} 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_cistatic struct sk_buff *igmpv3_newpack(struct net_device *dev, unsigned int mtu) 3498c2ecf20Sopenharmony_ci{ 3508c2ecf20Sopenharmony_ci struct sk_buff *skb; 3518c2ecf20Sopenharmony_ci struct rtable *rt; 3528c2ecf20Sopenharmony_ci struct iphdr *pip; 3538c2ecf20Sopenharmony_ci struct igmpv3_report *pig; 3548c2ecf20Sopenharmony_ci struct net *net = dev_net(dev); 3558c2ecf20Sopenharmony_ci struct flowi4 fl4; 3568c2ecf20Sopenharmony_ci int hlen = LL_RESERVED_SPACE(dev); 3578c2ecf20Sopenharmony_ci int tlen = dev->needed_tailroom; 3588c2ecf20Sopenharmony_ci unsigned int size; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci size = min(mtu, IP_MAX_MTU); 3618c2ecf20Sopenharmony_ci while (1) { 3628c2ecf20Sopenharmony_ci skb = alloc_skb(size + hlen + tlen, 3638c2ecf20Sopenharmony_ci GFP_ATOMIC | __GFP_NOWARN); 3648c2ecf20Sopenharmony_ci if (skb) 3658c2ecf20Sopenharmony_ci break; 3668c2ecf20Sopenharmony_ci size >>= 1; 3678c2ecf20Sopenharmony_ci if (size < 256) 3688c2ecf20Sopenharmony_ci return NULL; 3698c2ecf20Sopenharmony_ci } 3708c2ecf20Sopenharmony_ci skb->priority = TC_PRIO_CONTROL; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci rt = ip_route_output_ports(net, &fl4, NULL, IGMPV3_ALL_MCR, 0, 3738c2ecf20Sopenharmony_ci 0, 0, 3748c2ecf20Sopenharmony_ci IPPROTO_IGMP, 0, dev->ifindex); 3758c2ecf20Sopenharmony_ci if (IS_ERR(rt)) { 3768c2ecf20Sopenharmony_ci kfree_skb(skb); 3778c2ecf20Sopenharmony_ci return NULL; 3788c2ecf20Sopenharmony_ci } 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci skb_dst_set(skb, &rt->dst); 3818c2ecf20Sopenharmony_ci skb->dev = dev; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci skb_reserve(skb, hlen); 3848c2ecf20Sopenharmony_ci skb_tailroom_reserve(skb, mtu, tlen); 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci skb_reset_network_header(skb); 3878c2ecf20Sopenharmony_ci pip = ip_hdr(skb); 3888c2ecf20Sopenharmony_ci skb_put(skb, sizeof(struct iphdr) + 4); 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci pip->version = 4; 3918c2ecf20Sopenharmony_ci pip->ihl = (sizeof(struct iphdr)+4)>>2; 3928c2ecf20Sopenharmony_ci pip->tos = 0xc0; 3938c2ecf20Sopenharmony_ci pip->frag_off = htons(IP_DF); 3948c2ecf20Sopenharmony_ci pip->ttl = 1; 3958c2ecf20Sopenharmony_ci pip->daddr = fl4.daddr; 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci rcu_read_lock(); 3988c2ecf20Sopenharmony_ci pip->saddr = igmpv3_get_srcaddr(dev, &fl4); 3998c2ecf20Sopenharmony_ci rcu_read_unlock(); 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci pip->protocol = IPPROTO_IGMP; 4028c2ecf20Sopenharmony_ci pip->tot_len = 0; /* filled in later */ 4038c2ecf20Sopenharmony_ci ip_select_ident(net, skb, NULL); 4048c2ecf20Sopenharmony_ci ((u8 *)&pip[1])[0] = IPOPT_RA; 4058c2ecf20Sopenharmony_ci ((u8 *)&pip[1])[1] = 4; 4068c2ecf20Sopenharmony_ci ((u8 *)&pip[1])[2] = 0; 4078c2ecf20Sopenharmony_ci ((u8 *)&pip[1])[3] = 0; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci skb->transport_header = skb->network_header + sizeof(struct iphdr) + 4; 4108c2ecf20Sopenharmony_ci skb_put(skb, sizeof(*pig)); 4118c2ecf20Sopenharmony_ci pig = igmpv3_report_hdr(skb); 4128c2ecf20Sopenharmony_ci pig->type = IGMPV3_HOST_MEMBERSHIP_REPORT; 4138c2ecf20Sopenharmony_ci pig->resv1 = 0; 4148c2ecf20Sopenharmony_ci pig->csum = 0; 4158c2ecf20Sopenharmony_ci pig->resv2 = 0; 4168c2ecf20Sopenharmony_ci pig->ngrec = 0; 4178c2ecf20Sopenharmony_ci return skb; 4188c2ecf20Sopenharmony_ci} 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_cistatic int igmpv3_sendpack(struct sk_buff *skb) 4218c2ecf20Sopenharmony_ci{ 4228c2ecf20Sopenharmony_ci struct igmphdr *pig = igmp_hdr(skb); 4238c2ecf20Sopenharmony_ci const int igmplen = skb_tail_pointer(skb) - skb_transport_header(skb); 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci pig->csum = ip_compute_csum(igmp_hdr(skb), igmplen); 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci return ip_local_out(dev_net(skb_dst(skb)->dev), skb->sk, skb); 4288c2ecf20Sopenharmony_ci} 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_cistatic int grec_size(struct ip_mc_list *pmc, int type, int gdel, int sdel) 4318c2ecf20Sopenharmony_ci{ 4328c2ecf20Sopenharmony_ci return sizeof(struct igmpv3_grec) + 4*igmp_scount(pmc, type, gdel, sdel); 4338c2ecf20Sopenharmony_ci} 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_cistatic struct sk_buff *add_grhead(struct sk_buff *skb, struct ip_mc_list *pmc, 4368c2ecf20Sopenharmony_ci int type, struct igmpv3_grec **ppgr, unsigned int mtu) 4378c2ecf20Sopenharmony_ci{ 4388c2ecf20Sopenharmony_ci struct net_device *dev = pmc->interface->dev; 4398c2ecf20Sopenharmony_ci struct igmpv3_report *pih; 4408c2ecf20Sopenharmony_ci struct igmpv3_grec *pgr; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci if (!skb) { 4438c2ecf20Sopenharmony_ci skb = igmpv3_newpack(dev, mtu); 4448c2ecf20Sopenharmony_ci if (!skb) 4458c2ecf20Sopenharmony_ci return NULL; 4468c2ecf20Sopenharmony_ci } 4478c2ecf20Sopenharmony_ci pgr = skb_put(skb, sizeof(struct igmpv3_grec)); 4488c2ecf20Sopenharmony_ci pgr->grec_type = type; 4498c2ecf20Sopenharmony_ci pgr->grec_auxwords = 0; 4508c2ecf20Sopenharmony_ci pgr->grec_nsrcs = 0; 4518c2ecf20Sopenharmony_ci pgr->grec_mca = pmc->multiaddr; 4528c2ecf20Sopenharmony_ci pih = igmpv3_report_hdr(skb); 4538c2ecf20Sopenharmony_ci pih->ngrec = htons(ntohs(pih->ngrec)+1); 4548c2ecf20Sopenharmony_ci *ppgr = pgr; 4558c2ecf20Sopenharmony_ci return skb; 4568c2ecf20Sopenharmony_ci} 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci#define AVAILABLE(skb) ((skb) ? skb_availroom(skb) : 0) 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_cistatic struct sk_buff *add_grec(struct sk_buff *skb, struct ip_mc_list *pmc, 4618c2ecf20Sopenharmony_ci int type, int gdeleted, int sdeleted) 4628c2ecf20Sopenharmony_ci{ 4638c2ecf20Sopenharmony_ci struct net_device *dev = pmc->interface->dev; 4648c2ecf20Sopenharmony_ci struct net *net = dev_net(dev); 4658c2ecf20Sopenharmony_ci struct igmpv3_report *pih; 4668c2ecf20Sopenharmony_ci struct igmpv3_grec *pgr = NULL; 4678c2ecf20Sopenharmony_ci struct ip_sf_list *psf, *psf_next, *psf_prev, **psf_list; 4688c2ecf20Sopenharmony_ci int scount, stotal, first, isquery, truncate; 4698c2ecf20Sopenharmony_ci unsigned int mtu; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci if (pmc->multiaddr == IGMP_ALL_HOSTS) 4728c2ecf20Sopenharmony_ci return skb; 4738c2ecf20Sopenharmony_ci if (ipv4_is_local_multicast(pmc->multiaddr) && 4748c2ecf20Sopenharmony_ci !READ_ONCE(net->ipv4.sysctl_igmp_llm_reports)) 4758c2ecf20Sopenharmony_ci return skb; 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci mtu = READ_ONCE(dev->mtu); 4788c2ecf20Sopenharmony_ci if (mtu < IPV4_MIN_MTU) 4798c2ecf20Sopenharmony_ci return skb; 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci isquery = type == IGMPV3_MODE_IS_INCLUDE || 4828c2ecf20Sopenharmony_ci type == IGMPV3_MODE_IS_EXCLUDE; 4838c2ecf20Sopenharmony_ci truncate = type == IGMPV3_MODE_IS_EXCLUDE || 4848c2ecf20Sopenharmony_ci type == IGMPV3_CHANGE_TO_EXCLUDE; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci stotal = scount = 0; 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci psf_list = sdeleted ? &pmc->tomb : &pmc->sources; 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci if (!*psf_list) 4918c2ecf20Sopenharmony_ci goto empty_source; 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci pih = skb ? igmpv3_report_hdr(skb) : NULL; 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci /* EX and TO_EX get a fresh packet, if needed */ 4968c2ecf20Sopenharmony_ci if (truncate) { 4978c2ecf20Sopenharmony_ci if (pih && pih->ngrec && 4988c2ecf20Sopenharmony_ci AVAILABLE(skb) < grec_size(pmc, type, gdeleted, sdeleted)) { 4998c2ecf20Sopenharmony_ci if (skb) 5008c2ecf20Sopenharmony_ci igmpv3_sendpack(skb); 5018c2ecf20Sopenharmony_ci skb = igmpv3_newpack(dev, mtu); 5028c2ecf20Sopenharmony_ci } 5038c2ecf20Sopenharmony_ci } 5048c2ecf20Sopenharmony_ci first = 1; 5058c2ecf20Sopenharmony_ci psf_prev = NULL; 5068c2ecf20Sopenharmony_ci for (psf = *psf_list; psf; psf = psf_next) { 5078c2ecf20Sopenharmony_ci __be32 *psrc; 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci psf_next = psf->sf_next; 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci if (!is_in(pmc, psf, type, gdeleted, sdeleted)) { 5128c2ecf20Sopenharmony_ci psf_prev = psf; 5138c2ecf20Sopenharmony_ci continue; 5148c2ecf20Sopenharmony_ci } 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci /* Based on RFC3376 5.1. Should not send source-list change 5178c2ecf20Sopenharmony_ci * records when there is a filter mode change. 5188c2ecf20Sopenharmony_ci */ 5198c2ecf20Sopenharmony_ci if (((gdeleted && pmc->sfmode == MCAST_EXCLUDE) || 5208c2ecf20Sopenharmony_ci (!gdeleted && pmc->crcount)) && 5218c2ecf20Sopenharmony_ci (type == IGMPV3_ALLOW_NEW_SOURCES || 5228c2ecf20Sopenharmony_ci type == IGMPV3_BLOCK_OLD_SOURCES) && psf->sf_crcount) 5238c2ecf20Sopenharmony_ci goto decrease_sf_crcount; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci /* clear marks on query responses */ 5268c2ecf20Sopenharmony_ci if (isquery) 5278c2ecf20Sopenharmony_ci psf->sf_gsresp = 0; 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci if (AVAILABLE(skb) < sizeof(__be32) + 5308c2ecf20Sopenharmony_ci first*sizeof(struct igmpv3_grec)) { 5318c2ecf20Sopenharmony_ci if (truncate && !first) 5328c2ecf20Sopenharmony_ci break; /* truncate these */ 5338c2ecf20Sopenharmony_ci if (pgr) 5348c2ecf20Sopenharmony_ci pgr->grec_nsrcs = htons(scount); 5358c2ecf20Sopenharmony_ci if (skb) 5368c2ecf20Sopenharmony_ci igmpv3_sendpack(skb); 5378c2ecf20Sopenharmony_ci skb = igmpv3_newpack(dev, mtu); 5388c2ecf20Sopenharmony_ci first = 1; 5398c2ecf20Sopenharmony_ci scount = 0; 5408c2ecf20Sopenharmony_ci } 5418c2ecf20Sopenharmony_ci if (first) { 5428c2ecf20Sopenharmony_ci skb = add_grhead(skb, pmc, type, &pgr, mtu); 5438c2ecf20Sopenharmony_ci first = 0; 5448c2ecf20Sopenharmony_ci } 5458c2ecf20Sopenharmony_ci if (!skb) 5468c2ecf20Sopenharmony_ci return NULL; 5478c2ecf20Sopenharmony_ci psrc = skb_put(skb, sizeof(__be32)); 5488c2ecf20Sopenharmony_ci *psrc = psf->sf_inaddr; 5498c2ecf20Sopenharmony_ci scount++; stotal++; 5508c2ecf20Sopenharmony_ci if ((type == IGMPV3_ALLOW_NEW_SOURCES || 5518c2ecf20Sopenharmony_ci type == IGMPV3_BLOCK_OLD_SOURCES) && psf->sf_crcount) { 5528c2ecf20Sopenharmony_cidecrease_sf_crcount: 5538c2ecf20Sopenharmony_ci psf->sf_crcount--; 5548c2ecf20Sopenharmony_ci if ((sdeleted || gdeleted) && psf->sf_crcount == 0) { 5558c2ecf20Sopenharmony_ci if (psf_prev) 5568c2ecf20Sopenharmony_ci psf_prev->sf_next = psf->sf_next; 5578c2ecf20Sopenharmony_ci else 5588c2ecf20Sopenharmony_ci *psf_list = psf->sf_next; 5598c2ecf20Sopenharmony_ci kfree(psf); 5608c2ecf20Sopenharmony_ci continue; 5618c2ecf20Sopenharmony_ci } 5628c2ecf20Sopenharmony_ci } 5638c2ecf20Sopenharmony_ci psf_prev = psf; 5648c2ecf20Sopenharmony_ci } 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ciempty_source: 5678c2ecf20Sopenharmony_ci if (!stotal) { 5688c2ecf20Sopenharmony_ci if (type == IGMPV3_ALLOW_NEW_SOURCES || 5698c2ecf20Sopenharmony_ci type == IGMPV3_BLOCK_OLD_SOURCES) 5708c2ecf20Sopenharmony_ci return skb; 5718c2ecf20Sopenharmony_ci if (pmc->crcount || isquery) { 5728c2ecf20Sopenharmony_ci /* make sure we have room for group header */ 5738c2ecf20Sopenharmony_ci if (skb && AVAILABLE(skb) < sizeof(struct igmpv3_grec)) { 5748c2ecf20Sopenharmony_ci igmpv3_sendpack(skb); 5758c2ecf20Sopenharmony_ci skb = NULL; /* add_grhead will get a new one */ 5768c2ecf20Sopenharmony_ci } 5778c2ecf20Sopenharmony_ci skb = add_grhead(skb, pmc, type, &pgr, mtu); 5788c2ecf20Sopenharmony_ci } 5798c2ecf20Sopenharmony_ci } 5808c2ecf20Sopenharmony_ci if (pgr) 5818c2ecf20Sopenharmony_ci pgr->grec_nsrcs = htons(scount); 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci if (isquery) 5848c2ecf20Sopenharmony_ci pmc->gsquery = 0; /* clear query state on report */ 5858c2ecf20Sopenharmony_ci return skb; 5868c2ecf20Sopenharmony_ci} 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_cistatic int igmpv3_send_report(struct in_device *in_dev, struct ip_mc_list *pmc) 5898c2ecf20Sopenharmony_ci{ 5908c2ecf20Sopenharmony_ci struct sk_buff *skb = NULL; 5918c2ecf20Sopenharmony_ci struct net *net = dev_net(in_dev->dev); 5928c2ecf20Sopenharmony_ci int type; 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci if (!pmc) { 5958c2ecf20Sopenharmony_ci rcu_read_lock(); 5968c2ecf20Sopenharmony_ci for_each_pmc_rcu(in_dev, pmc) { 5978c2ecf20Sopenharmony_ci if (pmc->multiaddr == IGMP_ALL_HOSTS) 5988c2ecf20Sopenharmony_ci continue; 5998c2ecf20Sopenharmony_ci if (ipv4_is_local_multicast(pmc->multiaddr) && 6008c2ecf20Sopenharmony_ci !READ_ONCE(net->ipv4.sysctl_igmp_llm_reports)) 6018c2ecf20Sopenharmony_ci continue; 6028c2ecf20Sopenharmony_ci spin_lock_bh(&pmc->lock); 6038c2ecf20Sopenharmony_ci if (pmc->sfcount[MCAST_EXCLUDE]) 6048c2ecf20Sopenharmony_ci type = IGMPV3_MODE_IS_EXCLUDE; 6058c2ecf20Sopenharmony_ci else 6068c2ecf20Sopenharmony_ci type = IGMPV3_MODE_IS_INCLUDE; 6078c2ecf20Sopenharmony_ci skb = add_grec(skb, pmc, type, 0, 0); 6088c2ecf20Sopenharmony_ci spin_unlock_bh(&pmc->lock); 6098c2ecf20Sopenharmony_ci } 6108c2ecf20Sopenharmony_ci rcu_read_unlock(); 6118c2ecf20Sopenharmony_ci } else { 6128c2ecf20Sopenharmony_ci spin_lock_bh(&pmc->lock); 6138c2ecf20Sopenharmony_ci if (pmc->sfcount[MCAST_EXCLUDE]) 6148c2ecf20Sopenharmony_ci type = IGMPV3_MODE_IS_EXCLUDE; 6158c2ecf20Sopenharmony_ci else 6168c2ecf20Sopenharmony_ci type = IGMPV3_MODE_IS_INCLUDE; 6178c2ecf20Sopenharmony_ci skb = add_grec(skb, pmc, type, 0, 0); 6188c2ecf20Sopenharmony_ci spin_unlock_bh(&pmc->lock); 6198c2ecf20Sopenharmony_ci } 6208c2ecf20Sopenharmony_ci if (!skb) 6218c2ecf20Sopenharmony_ci return 0; 6228c2ecf20Sopenharmony_ci return igmpv3_sendpack(skb); 6238c2ecf20Sopenharmony_ci} 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci/* 6268c2ecf20Sopenharmony_ci * remove zero-count source records from a source filter list 6278c2ecf20Sopenharmony_ci */ 6288c2ecf20Sopenharmony_cistatic void igmpv3_clear_zeros(struct ip_sf_list **ppsf) 6298c2ecf20Sopenharmony_ci{ 6308c2ecf20Sopenharmony_ci struct ip_sf_list *psf_prev, *psf_next, *psf; 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci psf_prev = NULL; 6338c2ecf20Sopenharmony_ci for (psf = *ppsf; psf; psf = psf_next) { 6348c2ecf20Sopenharmony_ci psf_next = psf->sf_next; 6358c2ecf20Sopenharmony_ci if (psf->sf_crcount == 0) { 6368c2ecf20Sopenharmony_ci if (psf_prev) 6378c2ecf20Sopenharmony_ci psf_prev->sf_next = psf->sf_next; 6388c2ecf20Sopenharmony_ci else 6398c2ecf20Sopenharmony_ci *ppsf = psf->sf_next; 6408c2ecf20Sopenharmony_ci kfree(psf); 6418c2ecf20Sopenharmony_ci } else 6428c2ecf20Sopenharmony_ci psf_prev = psf; 6438c2ecf20Sopenharmony_ci } 6448c2ecf20Sopenharmony_ci} 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_cistatic void kfree_pmc(struct ip_mc_list *pmc) 6478c2ecf20Sopenharmony_ci{ 6488c2ecf20Sopenharmony_ci ip_sf_list_clear_all(pmc->sources); 6498c2ecf20Sopenharmony_ci ip_sf_list_clear_all(pmc->tomb); 6508c2ecf20Sopenharmony_ci kfree(pmc); 6518c2ecf20Sopenharmony_ci} 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_cistatic void igmpv3_send_cr(struct in_device *in_dev) 6548c2ecf20Sopenharmony_ci{ 6558c2ecf20Sopenharmony_ci struct ip_mc_list *pmc, *pmc_prev, *pmc_next; 6568c2ecf20Sopenharmony_ci struct sk_buff *skb = NULL; 6578c2ecf20Sopenharmony_ci int type, dtype; 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci rcu_read_lock(); 6608c2ecf20Sopenharmony_ci spin_lock_bh(&in_dev->mc_tomb_lock); 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci /* deleted MCA's */ 6638c2ecf20Sopenharmony_ci pmc_prev = NULL; 6648c2ecf20Sopenharmony_ci for (pmc = in_dev->mc_tomb; pmc; pmc = pmc_next) { 6658c2ecf20Sopenharmony_ci pmc_next = pmc->next; 6668c2ecf20Sopenharmony_ci if (pmc->sfmode == MCAST_INCLUDE) { 6678c2ecf20Sopenharmony_ci type = IGMPV3_BLOCK_OLD_SOURCES; 6688c2ecf20Sopenharmony_ci dtype = IGMPV3_BLOCK_OLD_SOURCES; 6698c2ecf20Sopenharmony_ci skb = add_grec(skb, pmc, type, 1, 0); 6708c2ecf20Sopenharmony_ci skb = add_grec(skb, pmc, dtype, 1, 1); 6718c2ecf20Sopenharmony_ci } 6728c2ecf20Sopenharmony_ci if (pmc->crcount) { 6738c2ecf20Sopenharmony_ci if (pmc->sfmode == MCAST_EXCLUDE) { 6748c2ecf20Sopenharmony_ci type = IGMPV3_CHANGE_TO_INCLUDE; 6758c2ecf20Sopenharmony_ci skb = add_grec(skb, pmc, type, 1, 0); 6768c2ecf20Sopenharmony_ci } 6778c2ecf20Sopenharmony_ci pmc->crcount--; 6788c2ecf20Sopenharmony_ci if (pmc->crcount == 0) { 6798c2ecf20Sopenharmony_ci igmpv3_clear_zeros(&pmc->tomb); 6808c2ecf20Sopenharmony_ci igmpv3_clear_zeros(&pmc->sources); 6818c2ecf20Sopenharmony_ci } 6828c2ecf20Sopenharmony_ci } 6838c2ecf20Sopenharmony_ci if (pmc->crcount == 0 && !pmc->tomb && !pmc->sources) { 6848c2ecf20Sopenharmony_ci if (pmc_prev) 6858c2ecf20Sopenharmony_ci pmc_prev->next = pmc_next; 6868c2ecf20Sopenharmony_ci else 6878c2ecf20Sopenharmony_ci in_dev->mc_tomb = pmc_next; 6888c2ecf20Sopenharmony_ci in_dev_put(pmc->interface); 6898c2ecf20Sopenharmony_ci kfree_pmc(pmc); 6908c2ecf20Sopenharmony_ci } else 6918c2ecf20Sopenharmony_ci pmc_prev = pmc; 6928c2ecf20Sopenharmony_ci } 6938c2ecf20Sopenharmony_ci spin_unlock_bh(&in_dev->mc_tomb_lock); 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ci /* change recs */ 6968c2ecf20Sopenharmony_ci for_each_pmc_rcu(in_dev, pmc) { 6978c2ecf20Sopenharmony_ci spin_lock_bh(&pmc->lock); 6988c2ecf20Sopenharmony_ci if (pmc->sfcount[MCAST_EXCLUDE]) { 6998c2ecf20Sopenharmony_ci type = IGMPV3_BLOCK_OLD_SOURCES; 7008c2ecf20Sopenharmony_ci dtype = IGMPV3_ALLOW_NEW_SOURCES; 7018c2ecf20Sopenharmony_ci } else { 7028c2ecf20Sopenharmony_ci type = IGMPV3_ALLOW_NEW_SOURCES; 7038c2ecf20Sopenharmony_ci dtype = IGMPV3_BLOCK_OLD_SOURCES; 7048c2ecf20Sopenharmony_ci } 7058c2ecf20Sopenharmony_ci skb = add_grec(skb, pmc, type, 0, 0); 7068c2ecf20Sopenharmony_ci skb = add_grec(skb, pmc, dtype, 0, 1); /* deleted sources */ 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci /* filter mode changes */ 7098c2ecf20Sopenharmony_ci if (pmc->crcount) { 7108c2ecf20Sopenharmony_ci if (pmc->sfmode == MCAST_EXCLUDE) 7118c2ecf20Sopenharmony_ci type = IGMPV3_CHANGE_TO_EXCLUDE; 7128c2ecf20Sopenharmony_ci else 7138c2ecf20Sopenharmony_ci type = IGMPV3_CHANGE_TO_INCLUDE; 7148c2ecf20Sopenharmony_ci skb = add_grec(skb, pmc, type, 0, 0); 7158c2ecf20Sopenharmony_ci pmc->crcount--; 7168c2ecf20Sopenharmony_ci } 7178c2ecf20Sopenharmony_ci spin_unlock_bh(&pmc->lock); 7188c2ecf20Sopenharmony_ci } 7198c2ecf20Sopenharmony_ci rcu_read_unlock(); 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci if (!skb) 7228c2ecf20Sopenharmony_ci return; 7238c2ecf20Sopenharmony_ci (void) igmpv3_sendpack(skb); 7248c2ecf20Sopenharmony_ci} 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_cistatic int igmp_send_report(struct in_device *in_dev, struct ip_mc_list *pmc, 7278c2ecf20Sopenharmony_ci int type) 7288c2ecf20Sopenharmony_ci{ 7298c2ecf20Sopenharmony_ci struct sk_buff *skb; 7308c2ecf20Sopenharmony_ci struct iphdr *iph; 7318c2ecf20Sopenharmony_ci struct igmphdr *ih; 7328c2ecf20Sopenharmony_ci struct rtable *rt; 7338c2ecf20Sopenharmony_ci struct net_device *dev = in_dev->dev; 7348c2ecf20Sopenharmony_ci struct net *net = dev_net(dev); 7358c2ecf20Sopenharmony_ci __be32 group = pmc ? pmc->multiaddr : 0; 7368c2ecf20Sopenharmony_ci struct flowi4 fl4; 7378c2ecf20Sopenharmony_ci __be32 dst; 7388c2ecf20Sopenharmony_ci int hlen, tlen; 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci if (type == IGMPV3_HOST_MEMBERSHIP_REPORT) 7418c2ecf20Sopenharmony_ci return igmpv3_send_report(in_dev, pmc); 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci if (ipv4_is_local_multicast(group) && 7448c2ecf20Sopenharmony_ci !READ_ONCE(net->ipv4.sysctl_igmp_llm_reports)) 7458c2ecf20Sopenharmony_ci return 0; 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci if (type == IGMP_HOST_LEAVE_MESSAGE) 7488c2ecf20Sopenharmony_ci dst = IGMP_ALL_ROUTER; 7498c2ecf20Sopenharmony_ci else 7508c2ecf20Sopenharmony_ci dst = group; 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci rt = ip_route_output_ports(net, &fl4, NULL, dst, 0, 7538c2ecf20Sopenharmony_ci 0, 0, 7548c2ecf20Sopenharmony_ci IPPROTO_IGMP, 0, dev->ifindex); 7558c2ecf20Sopenharmony_ci if (IS_ERR(rt)) 7568c2ecf20Sopenharmony_ci return -1; 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci hlen = LL_RESERVED_SPACE(dev); 7598c2ecf20Sopenharmony_ci tlen = dev->needed_tailroom; 7608c2ecf20Sopenharmony_ci skb = alloc_skb(IGMP_SIZE + hlen + tlen, GFP_ATOMIC); 7618c2ecf20Sopenharmony_ci if (!skb) { 7628c2ecf20Sopenharmony_ci ip_rt_put(rt); 7638c2ecf20Sopenharmony_ci return -1; 7648c2ecf20Sopenharmony_ci } 7658c2ecf20Sopenharmony_ci skb->priority = TC_PRIO_CONTROL; 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci skb_dst_set(skb, &rt->dst); 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci skb_reserve(skb, hlen); 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci skb_reset_network_header(skb); 7728c2ecf20Sopenharmony_ci iph = ip_hdr(skb); 7738c2ecf20Sopenharmony_ci skb_put(skb, sizeof(struct iphdr) + 4); 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci iph->version = 4; 7768c2ecf20Sopenharmony_ci iph->ihl = (sizeof(struct iphdr)+4)>>2; 7778c2ecf20Sopenharmony_ci iph->tos = 0xc0; 7788c2ecf20Sopenharmony_ci iph->frag_off = htons(IP_DF); 7798c2ecf20Sopenharmony_ci iph->ttl = 1; 7808c2ecf20Sopenharmony_ci iph->daddr = dst; 7818c2ecf20Sopenharmony_ci iph->saddr = fl4.saddr; 7828c2ecf20Sopenharmony_ci iph->protocol = IPPROTO_IGMP; 7838c2ecf20Sopenharmony_ci ip_select_ident(net, skb, NULL); 7848c2ecf20Sopenharmony_ci ((u8 *)&iph[1])[0] = IPOPT_RA; 7858c2ecf20Sopenharmony_ci ((u8 *)&iph[1])[1] = 4; 7868c2ecf20Sopenharmony_ci ((u8 *)&iph[1])[2] = 0; 7878c2ecf20Sopenharmony_ci ((u8 *)&iph[1])[3] = 0; 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci ih = skb_put(skb, sizeof(struct igmphdr)); 7908c2ecf20Sopenharmony_ci ih->type = type; 7918c2ecf20Sopenharmony_ci ih->code = 0; 7928c2ecf20Sopenharmony_ci ih->csum = 0; 7938c2ecf20Sopenharmony_ci ih->group = group; 7948c2ecf20Sopenharmony_ci ih->csum = ip_compute_csum((void *)ih, sizeof(struct igmphdr)); 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci return ip_local_out(net, skb->sk, skb); 7978c2ecf20Sopenharmony_ci} 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_cistatic void igmp_gq_timer_expire(struct timer_list *t) 8008c2ecf20Sopenharmony_ci{ 8018c2ecf20Sopenharmony_ci struct in_device *in_dev = from_timer(in_dev, t, mr_gq_timer); 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci in_dev->mr_gq_running = 0; 8048c2ecf20Sopenharmony_ci igmpv3_send_report(in_dev, NULL); 8058c2ecf20Sopenharmony_ci in_dev_put(in_dev); 8068c2ecf20Sopenharmony_ci} 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_cistatic void igmp_ifc_timer_expire(struct timer_list *t) 8098c2ecf20Sopenharmony_ci{ 8108c2ecf20Sopenharmony_ci struct in_device *in_dev = from_timer(in_dev, t, mr_ifc_timer); 8118c2ecf20Sopenharmony_ci u32 mr_ifc_count; 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci igmpv3_send_cr(in_dev); 8148c2ecf20Sopenharmony_cirestart: 8158c2ecf20Sopenharmony_ci mr_ifc_count = READ_ONCE(in_dev->mr_ifc_count); 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci if (mr_ifc_count) { 8188c2ecf20Sopenharmony_ci if (cmpxchg(&in_dev->mr_ifc_count, 8198c2ecf20Sopenharmony_ci mr_ifc_count, 8208c2ecf20Sopenharmony_ci mr_ifc_count - 1) != mr_ifc_count) 8218c2ecf20Sopenharmony_ci goto restart; 8228c2ecf20Sopenharmony_ci igmp_ifc_start_timer(in_dev, 8238c2ecf20Sopenharmony_ci unsolicited_report_interval(in_dev)); 8248c2ecf20Sopenharmony_ci } 8258c2ecf20Sopenharmony_ci in_dev_put(in_dev); 8268c2ecf20Sopenharmony_ci} 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_cistatic void igmp_ifc_event(struct in_device *in_dev) 8298c2ecf20Sopenharmony_ci{ 8308c2ecf20Sopenharmony_ci struct net *net = dev_net(in_dev->dev); 8318c2ecf20Sopenharmony_ci if (IGMP_V1_SEEN(in_dev) || IGMP_V2_SEEN(in_dev)) 8328c2ecf20Sopenharmony_ci return; 8338c2ecf20Sopenharmony_ci WRITE_ONCE(in_dev->mr_ifc_count, in_dev->mr_qrv ?: READ_ONCE(net->ipv4.sysctl_igmp_qrv)); 8348c2ecf20Sopenharmony_ci igmp_ifc_start_timer(in_dev, 1); 8358c2ecf20Sopenharmony_ci} 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_cistatic void igmp_timer_expire(struct timer_list *t) 8398c2ecf20Sopenharmony_ci{ 8408c2ecf20Sopenharmony_ci struct ip_mc_list *im = from_timer(im, t, timer); 8418c2ecf20Sopenharmony_ci struct in_device *in_dev = im->interface; 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_ci spin_lock(&im->lock); 8448c2ecf20Sopenharmony_ci im->tm_running = 0; 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci if (im->unsolicit_count && --im->unsolicit_count) 8478c2ecf20Sopenharmony_ci igmp_start_timer(im, unsolicited_report_interval(in_dev)); 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ci im->reporter = 1; 8508c2ecf20Sopenharmony_ci spin_unlock(&im->lock); 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_ci if (IGMP_V1_SEEN(in_dev)) 8538c2ecf20Sopenharmony_ci igmp_send_report(in_dev, im, IGMP_HOST_MEMBERSHIP_REPORT); 8548c2ecf20Sopenharmony_ci else if (IGMP_V2_SEEN(in_dev)) 8558c2ecf20Sopenharmony_ci igmp_send_report(in_dev, im, IGMPV2_HOST_MEMBERSHIP_REPORT); 8568c2ecf20Sopenharmony_ci else 8578c2ecf20Sopenharmony_ci igmp_send_report(in_dev, im, IGMPV3_HOST_MEMBERSHIP_REPORT); 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci ip_ma_put(im); 8608c2ecf20Sopenharmony_ci} 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_ci/* mark EXCLUDE-mode sources */ 8638c2ecf20Sopenharmony_cistatic int igmp_xmarksources(struct ip_mc_list *pmc, int nsrcs, __be32 *srcs) 8648c2ecf20Sopenharmony_ci{ 8658c2ecf20Sopenharmony_ci struct ip_sf_list *psf; 8668c2ecf20Sopenharmony_ci int i, scount; 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci scount = 0; 8698c2ecf20Sopenharmony_ci for (psf = pmc->sources; psf; psf = psf->sf_next) { 8708c2ecf20Sopenharmony_ci if (scount == nsrcs) 8718c2ecf20Sopenharmony_ci break; 8728c2ecf20Sopenharmony_ci for (i = 0; i < nsrcs; i++) { 8738c2ecf20Sopenharmony_ci /* skip inactive filters */ 8748c2ecf20Sopenharmony_ci if (psf->sf_count[MCAST_INCLUDE] || 8758c2ecf20Sopenharmony_ci pmc->sfcount[MCAST_EXCLUDE] != 8768c2ecf20Sopenharmony_ci psf->sf_count[MCAST_EXCLUDE]) 8778c2ecf20Sopenharmony_ci break; 8788c2ecf20Sopenharmony_ci if (srcs[i] == psf->sf_inaddr) { 8798c2ecf20Sopenharmony_ci scount++; 8808c2ecf20Sopenharmony_ci break; 8818c2ecf20Sopenharmony_ci } 8828c2ecf20Sopenharmony_ci } 8838c2ecf20Sopenharmony_ci } 8848c2ecf20Sopenharmony_ci pmc->gsquery = 0; 8858c2ecf20Sopenharmony_ci if (scount == nsrcs) /* all sources excluded */ 8868c2ecf20Sopenharmony_ci return 0; 8878c2ecf20Sopenharmony_ci return 1; 8888c2ecf20Sopenharmony_ci} 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_cistatic int igmp_marksources(struct ip_mc_list *pmc, int nsrcs, __be32 *srcs) 8918c2ecf20Sopenharmony_ci{ 8928c2ecf20Sopenharmony_ci struct ip_sf_list *psf; 8938c2ecf20Sopenharmony_ci int i, scount; 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_ci if (pmc->sfmode == MCAST_EXCLUDE) 8968c2ecf20Sopenharmony_ci return igmp_xmarksources(pmc, nsrcs, srcs); 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_ci /* mark INCLUDE-mode sources */ 8998c2ecf20Sopenharmony_ci scount = 0; 9008c2ecf20Sopenharmony_ci for (psf = pmc->sources; psf; psf = psf->sf_next) { 9018c2ecf20Sopenharmony_ci if (scount == nsrcs) 9028c2ecf20Sopenharmony_ci break; 9038c2ecf20Sopenharmony_ci for (i = 0; i < nsrcs; i++) 9048c2ecf20Sopenharmony_ci if (srcs[i] == psf->sf_inaddr) { 9058c2ecf20Sopenharmony_ci psf->sf_gsresp = 1; 9068c2ecf20Sopenharmony_ci scount++; 9078c2ecf20Sopenharmony_ci break; 9088c2ecf20Sopenharmony_ci } 9098c2ecf20Sopenharmony_ci } 9108c2ecf20Sopenharmony_ci if (!scount) { 9118c2ecf20Sopenharmony_ci pmc->gsquery = 0; 9128c2ecf20Sopenharmony_ci return 0; 9138c2ecf20Sopenharmony_ci } 9148c2ecf20Sopenharmony_ci pmc->gsquery = 1; 9158c2ecf20Sopenharmony_ci return 1; 9168c2ecf20Sopenharmony_ci} 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_ci/* return true if packet was dropped */ 9198c2ecf20Sopenharmony_cistatic bool igmp_heard_report(struct in_device *in_dev, __be32 group) 9208c2ecf20Sopenharmony_ci{ 9218c2ecf20Sopenharmony_ci struct ip_mc_list *im; 9228c2ecf20Sopenharmony_ci struct net *net = dev_net(in_dev->dev); 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci /* Timers are only set for non-local groups */ 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ci if (group == IGMP_ALL_HOSTS) 9278c2ecf20Sopenharmony_ci return false; 9288c2ecf20Sopenharmony_ci if (ipv4_is_local_multicast(group) && 9298c2ecf20Sopenharmony_ci !READ_ONCE(net->ipv4.sysctl_igmp_llm_reports)) 9308c2ecf20Sopenharmony_ci return false; 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_ci rcu_read_lock(); 9338c2ecf20Sopenharmony_ci for_each_pmc_rcu(in_dev, im) { 9348c2ecf20Sopenharmony_ci if (im->multiaddr == group) { 9358c2ecf20Sopenharmony_ci igmp_stop_timer(im); 9368c2ecf20Sopenharmony_ci break; 9378c2ecf20Sopenharmony_ci } 9388c2ecf20Sopenharmony_ci } 9398c2ecf20Sopenharmony_ci rcu_read_unlock(); 9408c2ecf20Sopenharmony_ci return false; 9418c2ecf20Sopenharmony_ci} 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci/* return true if packet was dropped */ 9448c2ecf20Sopenharmony_cistatic bool igmp_heard_query(struct in_device *in_dev, struct sk_buff *skb, 9458c2ecf20Sopenharmony_ci int len) 9468c2ecf20Sopenharmony_ci{ 9478c2ecf20Sopenharmony_ci struct igmphdr *ih = igmp_hdr(skb); 9488c2ecf20Sopenharmony_ci struct igmpv3_query *ih3 = igmpv3_query_hdr(skb); 9498c2ecf20Sopenharmony_ci struct ip_mc_list *im; 9508c2ecf20Sopenharmony_ci __be32 group = ih->group; 9518c2ecf20Sopenharmony_ci int max_delay; 9528c2ecf20Sopenharmony_ci int mark = 0; 9538c2ecf20Sopenharmony_ci struct net *net = dev_net(in_dev->dev); 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ci if (len == 8) { 9578c2ecf20Sopenharmony_ci if (ih->code == 0) { 9588c2ecf20Sopenharmony_ci /* Alas, old v1 router presents here. */ 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_ci max_delay = IGMP_QUERY_RESPONSE_INTERVAL; 9618c2ecf20Sopenharmony_ci in_dev->mr_v1_seen = jiffies + 9628c2ecf20Sopenharmony_ci (in_dev->mr_qrv * in_dev->mr_qi) + 9638c2ecf20Sopenharmony_ci in_dev->mr_qri; 9648c2ecf20Sopenharmony_ci group = 0; 9658c2ecf20Sopenharmony_ci } else { 9668c2ecf20Sopenharmony_ci /* v2 router present */ 9678c2ecf20Sopenharmony_ci max_delay = ih->code*(HZ/IGMP_TIMER_SCALE); 9688c2ecf20Sopenharmony_ci in_dev->mr_v2_seen = jiffies + 9698c2ecf20Sopenharmony_ci (in_dev->mr_qrv * in_dev->mr_qi) + 9708c2ecf20Sopenharmony_ci in_dev->mr_qri; 9718c2ecf20Sopenharmony_ci } 9728c2ecf20Sopenharmony_ci /* cancel the interface change timer */ 9738c2ecf20Sopenharmony_ci WRITE_ONCE(in_dev->mr_ifc_count, 0); 9748c2ecf20Sopenharmony_ci if (del_timer(&in_dev->mr_ifc_timer)) 9758c2ecf20Sopenharmony_ci __in_dev_put(in_dev); 9768c2ecf20Sopenharmony_ci /* clear deleted report items */ 9778c2ecf20Sopenharmony_ci igmpv3_clear_delrec(in_dev); 9788c2ecf20Sopenharmony_ci } else if (len < 12) { 9798c2ecf20Sopenharmony_ci return true; /* ignore bogus packet; freed by caller */ 9808c2ecf20Sopenharmony_ci } else if (IGMP_V1_SEEN(in_dev)) { 9818c2ecf20Sopenharmony_ci /* This is a v3 query with v1 queriers present */ 9828c2ecf20Sopenharmony_ci max_delay = IGMP_QUERY_RESPONSE_INTERVAL; 9838c2ecf20Sopenharmony_ci group = 0; 9848c2ecf20Sopenharmony_ci } else if (IGMP_V2_SEEN(in_dev)) { 9858c2ecf20Sopenharmony_ci /* this is a v3 query with v2 queriers present; 9868c2ecf20Sopenharmony_ci * Interpretation of the max_delay code is problematic here. 9878c2ecf20Sopenharmony_ci * A real v2 host would use ih_code directly, while v3 has a 9888c2ecf20Sopenharmony_ci * different encoding. We use the v3 encoding as more likely 9898c2ecf20Sopenharmony_ci * to be intended in a v3 query. 9908c2ecf20Sopenharmony_ci */ 9918c2ecf20Sopenharmony_ci max_delay = IGMPV3_MRC(ih3->code)*(HZ/IGMP_TIMER_SCALE); 9928c2ecf20Sopenharmony_ci if (!max_delay) 9938c2ecf20Sopenharmony_ci max_delay = 1; /* can't mod w/ 0 */ 9948c2ecf20Sopenharmony_ci } else { /* v3 */ 9958c2ecf20Sopenharmony_ci if (!pskb_may_pull(skb, sizeof(struct igmpv3_query))) 9968c2ecf20Sopenharmony_ci return true; 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_ci ih3 = igmpv3_query_hdr(skb); 9998c2ecf20Sopenharmony_ci if (ih3->nsrcs) { 10008c2ecf20Sopenharmony_ci if (!pskb_may_pull(skb, sizeof(struct igmpv3_query) 10018c2ecf20Sopenharmony_ci + ntohs(ih3->nsrcs)*sizeof(__be32))) 10028c2ecf20Sopenharmony_ci return true; 10038c2ecf20Sopenharmony_ci ih3 = igmpv3_query_hdr(skb); 10048c2ecf20Sopenharmony_ci } 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_ci max_delay = IGMPV3_MRC(ih3->code)*(HZ/IGMP_TIMER_SCALE); 10078c2ecf20Sopenharmony_ci if (!max_delay) 10088c2ecf20Sopenharmony_ci max_delay = 1; /* can't mod w/ 0 */ 10098c2ecf20Sopenharmony_ci in_dev->mr_maxdelay = max_delay; 10108c2ecf20Sopenharmony_ci 10118c2ecf20Sopenharmony_ci /* RFC3376, 4.1.6. QRV and 4.1.7. QQIC, when the most recently 10128c2ecf20Sopenharmony_ci * received value was zero, use the default or statically 10138c2ecf20Sopenharmony_ci * configured value. 10148c2ecf20Sopenharmony_ci */ 10158c2ecf20Sopenharmony_ci in_dev->mr_qrv = ih3->qrv ?: READ_ONCE(net->ipv4.sysctl_igmp_qrv); 10168c2ecf20Sopenharmony_ci in_dev->mr_qi = IGMPV3_QQIC(ih3->qqic)*HZ ?: IGMP_QUERY_INTERVAL; 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_ci /* RFC3376, 8.3. Query Response Interval: 10198c2ecf20Sopenharmony_ci * The number of seconds represented by the [Query Response 10208c2ecf20Sopenharmony_ci * Interval] must be less than the [Query Interval]. 10218c2ecf20Sopenharmony_ci */ 10228c2ecf20Sopenharmony_ci if (in_dev->mr_qri >= in_dev->mr_qi) 10238c2ecf20Sopenharmony_ci in_dev->mr_qri = (in_dev->mr_qi/HZ - 1)*HZ; 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_ci if (!group) { /* general query */ 10268c2ecf20Sopenharmony_ci if (ih3->nsrcs) 10278c2ecf20Sopenharmony_ci return true; /* no sources allowed */ 10288c2ecf20Sopenharmony_ci igmp_gq_start_timer(in_dev); 10298c2ecf20Sopenharmony_ci return false; 10308c2ecf20Sopenharmony_ci } 10318c2ecf20Sopenharmony_ci /* mark sources to include, if group & source-specific */ 10328c2ecf20Sopenharmony_ci mark = ih3->nsrcs != 0; 10338c2ecf20Sopenharmony_ci } 10348c2ecf20Sopenharmony_ci 10358c2ecf20Sopenharmony_ci /* 10368c2ecf20Sopenharmony_ci * - Start the timers in all of our membership records 10378c2ecf20Sopenharmony_ci * that the query applies to for the interface on 10388c2ecf20Sopenharmony_ci * which the query arrived excl. those that belong 10398c2ecf20Sopenharmony_ci * to a "local" group (224.0.0.X) 10408c2ecf20Sopenharmony_ci * - For timers already running check if they need to 10418c2ecf20Sopenharmony_ci * be reset. 10428c2ecf20Sopenharmony_ci * - Use the igmp->igmp_code field as the maximum 10438c2ecf20Sopenharmony_ci * delay possible 10448c2ecf20Sopenharmony_ci */ 10458c2ecf20Sopenharmony_ci rcu_read_lock(); 10468c2ecf20Sopenharmony_ci for_each_pmc_rcu(in_dev, im) { 10478c2ecf20Sopenharmony_ci int changed; 10488c2ecf20Sopenharmony_ci 10498c2ecf20Sopenharmony_ci if (group && group != im->multiaddr) 10508c2ecf20Sopenharmony_ci continue; 10518c2ecf20Sopenharmony_ci if (im->multiaddr == IGMP_ALL_HOSTS) 10528c2ecf20Sopenharmony_ci continue; 10538c2ecf20Sopenharmony_ci if (ipv4_is_local_multicast(im->multiaddr) && 10548c2ecf20Sopenharmony_ci !READ_ONCE(net->ipv4.sysctl_igmp_llm_reports)) 10558c2ecf20Sopenharmony_ci continue; 10568c2ecf20Sopenharmony_ci spin_lock_bh(&im->lock); 10578c2ecf20Sopenharmony_ci if (im->tm_running) 10588c2ecf20Sopenharmony_ci im->gsquery = im->gsquery && mark; 10598c2ecf20Sopenharmony_ci else 10608c2ecf20Sopenharmony_ci im->gsquery = mark; 10618c2ecf20Sopenharmony_ci changed = !im->gsquery || 10628c2ecf20Sopenharmony_ci igmp_marksources(im, ntohs(ih3->nsrcs), ih3->srcs); 10638c2ecf20Sopenharmony_ci spin_unlock_bh(&im->lock); 10648c2ecf20Sopenharmony_ci if (changed) 10658c2ecf20Sopenharmony_ci igmp_mod_timer(im, max_delay); 10668c2ecf20Sopenharmony_ci } 10678c2ecf20Sopenharmony_ci rcu_read_unlock(); 10688c2ecf20Sopenharmony_ci return false; 10698c2ecf20Sopenharmony_ci} 10708c2ecf20Sopenharmony_ci 10718c2ecf20Sopenharmony_ci/* called in rcu_read_lock() section */ 10728c2ecf20Sopenharmony_ciint igmp_rcv(struct sk_buff *skb) 10738c2ecf20Sopenharmony_ci{ 10748c2ecf20Sopenharmony_ci /* This basically follows the spec line by line -- see RFC1112 */ 10758c2ecf20Sopenharmony_ci struct igmphdr *ih; 10768c2ecf20Sopenharmony_ci struct net_device *dev = skb->dev; 10778c2ecf20Sopenharmony_ci struct in_device *in_dev; 10788c2ecf20Sopenharmony_ci int len = skb->len; 10798c2ecf20Sopenharmony_ci bool dropped = true; 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_ci if (netif_is_l3_master(dev)) { 10828c2ecf20Sopenharmony_ci dev = dev_get_by_index_rcu(dev_net(dev), IPCB(skb)->iif); 10838c2ecf20Sopenharmony_ci if (!dev) 10848c2ecf20Sopenharmony_ci goto drop; 10858c2ecf20Sopenharmony_ci } 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_ci in_dev = __in_dev_get_rcu(dev); 10888c2ecf20Sopenharmony_ci if (!in_dev) 10898c2ecf20Sopenharmony_ci goto drop; 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_ci if (!pskb_may_pull(skb, sizeof(struct igmphdr))) 10928c2ecf20Sopenharmony_ci goto drop; 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ci if (skb_checksum_simple_validate(skb)) 10958c2ecf20Sopenharmony_ci goto drop; 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_ci ih = igmp_hdr(skb); 10988c2ecf20Sopenharmony_ci switch (ih->type) { 10998c2ecf20Sopenharmony_ci case IGMP_HOST_MEMBERSHIP_QUERY: 11008c2ecf20Sopenharmony_ci dropped = igmp_heard_query(in_dev, skb, len); 11018c2ecf20Sopenharmony_ci break; 11028c2ecf20Sopenharmony_ci case IGMP_HOST_MEMBERSHIP_REPORT: 11038c2ecf20Sopenharmony_ci case IGMPV2_HOST_MEMBERSHIP_REPORT: 11048c2ecf20Sopenharmony_ci /* Is it our report looped back? */ 11058c2ecf20Sopenharmony_ci if (rt_is_output_route(skb_rtable(skb))) 11068c2ecf20Sopenharmony_ci break; 11078c2ecf20Sopenharmony_ci /* don't rely on MC router hearing unicast reports */ 11088c2ecf20Sopenharmony_ci if (skb->pkt_type == PACKET_MULTICAST || 11098c2ecf20Sopenharmony_ci skb->pkt_type == PACKET_BROADCAST) 11108c2ecf20Sopenharmony_ci dropped = igmp_heard_report(in_dev, ih->group); 11118c2ecf20Sopenharmony_ci break; 11128c2ecf20Sopenharmony_ci case IGMP_PIM: 11138c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_PIMSM_V1 11148c2ecf20Sopenharmony_ci return pim_rcv_v1(skb); 11158c2ecf20Sopenharmony_ci#endif 11168c2ecf20Sopenharmony_ci case IGMPV3_HOST_MEMBERSHIP_REPORT: 11178c2ecf20Sopenharmony_ci case IGMP_DVMRP: 11188c2ecf20Sopenharmony_ci case IGMP_TRACE: 11198c2ecf20Sopenharmony_ci case IGMP_HOST_LEAVE_MESSAGE: 11208c2ecf20Sopenharmony_ci case IGMP_MTRACE: 11218c2ecf20Sopenharmony_ci case IGMP_MTRACE_RESP: 11228c2ecf20Sopenharmony_ci break; 11238c2ecf20Sopenharmony_ci default: 11248c2ecf20Sopenharmony_ci break; 11258c2ecf20Sopenharmony_ci } 11268c2ecf20Sopenharmony_ci 11278c2ecf20Sopenharmony_cidrop: 11288c2ecf20Sopenharmony_ci if (dropped) 11298c2ecf20Sopenharmony_ci kfree_skb(skb); 11308c2ecf20Sopenharmony_ci else 11318c2ecf20Sopenharmony_ci consume_skb(skb); 11328c2ecf20Sopenharmony_ci return 0; 11338c2ecf20Sopenharmony_ci} 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_ci#endif 11368c2ecf20Sopenharmony_ci 11378c2ecf20Sopenharmony_ci 11388c2ecf20Sopenharmony_ci/* 11398c2ecf20Sopenharmony_ci * Add a filter to a device 11408c2ecf20Sopenharmony_ci */ 11418c2ecf20Sopenharmony_ci 11428c2ecf20Sopenharmony_cistatic void ip_mc_filter_add(struct in_device *in_dev, __be32 addr) 11438c2ecf20Sopenharmony_ci{ 11448c2ecf20Sopenharmony_ci char buf[MAX_ADDR_LEN]; 11458c2ecf20Sopenharmony_ci struct net_device *dev = in_dev->dev; 11468c2ecf20Sopenharmony_ci 11478c2ecf20Sopenharmony_ci /* Checking for IFF_MULTICAST here is WRONG-WRONG-WRONG. 11488c2ecf20Sopenharmony_ci We will get multicast token leakage, when IFF_MULTICAST 11498c2ecf20Sopenharmony_ci is changed. This check should be done in ndo_set_rx_mode 11508c2ecf20Sopenharmony_ci routine. Something sort of: 11518c2ecf20Sopenharmony_ci if (dev->mc_list && dev->flags&IFF_MULTICAST) { do it; } 11528c2ecf20Sopenharmony_ci --ANK 11538c2ecf20Sopenharmony_ci */ 11548c2ecf20Sopenharmony_ci if (arp_mc_map(addr, buf, dev, 0) == 0) 11558c2ecf20Sopenharmony_ci dev_mc_add(dev, buf); 11568c2ecf20Sopenharmony_ci} 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_ci/* 11598c2ecf20Sopenharmony_ci * Remove a filter from a device 11608c2ecf20Sopenharmony_ci */ 11618c2ecf20Sopenharmony_ci 11628c2ecf20Sopenharmony_cistatic void ip_mc_filter_del(struct in_device *in_dev, __be32 addr) 11638c2ecf20Sopenharmony_ci{ 11648c2ecf20Sopenharmony_ci char buf[MAX_ADDR_LEN]; 11658c2ecf20Sopenharmony_ci struct net_device *dev = in_dev->dev; 11668c2ecf20Sopenharmony_ci 11678c2ecf20Sopenharmony_ci if (arp_mc_map(addr, buf, dev, 0) == 0) 11688c2ecf20Sopenharmony_ci dev_mc_del(dev, buf); 11698c2ecf20Sopenharmony_ci} 11708c2ecf20Sopenharmony_ci 11718c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_MULTICAST 11728c2ecf20Sopenharmony_ci/* 11738c2ecf20Sopenharmony_ci * deleted ip_mc_list manipulation 11748c2ecf20Sopenharmony_ci */ 11758c2ecf20Sopenharmony_cistatic void igmpv3_add_delrec(struct in_device *in_dev, struct ip_mc_list *im, 11768c2ecf20Sopenharmony_ci gfp_t gfp) 11778c2ecf20Sopenharmony_ci{ 11788c2ecf20Sopenharmony_ci struct ip_mc_list *pmc; 11798c2ecf20Sopenharmony_ci struct net *net = dev_net(in_dev->dev); 11808c2ecf20Sopenharmony_ci 11818c2ecf20Sopenharmony_ci /* this is an "ip_mc_list" for convenience; only the fields below 11828c2ecf20Sopenharmony_ci * are actually used. In particular, the refcnt and users are not 11838c2ecf20Sopenharmony_ci * used for management of the delete list. Using the same structure 11848c2ecf20Sopenharmony_ci * for deleted items allows change reports to use common code with 11858c2ecf20Sopenharmony_ci * non-deleted or query-response MCA's. 11868c2ecf20Sopenharmony_ci */ 11878c2ecf20Sopenharmony_ci pmc = kzalloc(sizeof(*pmc), gfp); 11888c2ecf20Sopenharmony_ci if (!pmc) 11898c2ecf20Sopenharmony_ci return; 11908c2ecf20Sopenharmony_ci spin_lock_init(&pmc->lock); 11918c2ecf20Sopenharmony_ci spin_lock_bh(&im->lock); 11928c2ecf20Sopenharmony_ci pmc->interface = im->interface; 11938c2ecf20Sopenharmony_ci in_dev_hold(in_dev); 11948c2ecf20Sopenharmony_ci pmc->multiaddr = im->multiaddr; 11958c2ecf20Sopenharmony_ci pmc->crcount = in_dev->mr_qrv ?: READ_ONCE(net->ipv4.sysctl_igmp_qrv); 11968c2ecf20Sopenharmony_ci pmc->sfmode = im->sfmode; 11978c2ecf20Sopenharmony_ci if (pmc->sfmode == MCAST_INCLUDE) { 11988c2ecf20Sopenharmony_ci struct ip_sf_list *psf; 11998c2ecf20Sopenharmony_ci 12008c2ecf20Sopenharmony_ci pmc->tomb = im->tomb; 12018c2ecf20Sopenharmony_ci pmc->sources = im->sources; 12028c2ecf20Sopenharmony_ci im->tomb = im->sources = NULL; 12038c2ecf20Sopenharmony_ci for (psf = pmc->sources; psf; psf = psf->sf_next) 12048c2ecf20Sopenharmony_ci psf->sf_crcount = pmc->crcount; 12058c2ecf20Sopenharmony_ci } 12068c2ecf20Sopenharmony_ci spin_unlock_bh(&im->lock); 12078c2ecf20Sopenharmony_ci 12088c2ecf20Sopenharmony_ci spin_lock_bh(&in_dev->mc_tomb_lock); 12098c2ecf20Sopenharmony_ci pmc->next = in_dev->mc_tomb; 12108c2ecf20Sopenharmony_ci in_dev->mc_tomb = pmc; 12118c2ecf20Sopenharmony_ci spin_unlock_bh(&in_dev->mc_tomb_lock); 12128c2ecf20Sopenharmony_ci} 12138c2ecf20Sopenharmony_ci 12148c2ecf20Sopenharmony_ci/* 12158c2ecf20Sopenharmony_ci * restore ip_mc_list deleted records 12168c2ecf20Sopenharmony_ci */ 12178c2ecf20Sopenharmony_cistatic void igmpv3_del_delrec(struct in_device *in_dev, struct ip_mc_list *im) 12188c2ecf20Sopenharmony_ci{ 12198c2ecf20Sopenharmony_ci struct ip_mc_list *pmc, *pmc_prev; 12208c2ecf20Sopenharmony_ci struct ip_sf_list *psf; 12218c2ecf20Sopenharmony_ci struct net *net = dev_net(in_dev->dev); 12228c2ecf20Sopenharmony_ci __be32 multiaddr = im->multiaddr; 12238c2ecf20Sopenharmony_ci 12248c2ecf20Sopenharmony_ci spin_lock_bh(&in_dev->mc_tomb_lock); 12258c2ecf20Sopenharmony_ci pmc_prev = NULL; 12268c2ecf20Sopenharmony_ci for (pmc = in_dev->mc_tomb; pmc; pmc = pmc->next) { 12278c2ecf20Sopenharmony_ci if (pmc->multiaddr == multiaddr) 12288c2ecf20Sopenharmony_ci break; 12298c2ecf20Sopenharmony_ci pmc_prev = pmc; 12308c2ecf20Sopenharmony_ci } 12318c2ecf20Sopenharmony_ci if (pmc) { 12328c2ecf20Sopenharmony_ci if (pmc_prev) 12338c2ecf20Sopenharmony_ci pmc_prev->next = pmc->next; 12348c2ecf20Sopenharmony_ci else 12358c2ecf20Sopenharmony_ci in_dev->mc_tomb = pmc->next; 12368c2ecf20Sopenharmony_ci } 12378c2ecf20Sopenharmony_ci spin_unlock_bh(&in_dev->mc_tomb_lock); 12388c2ecf20Sopenharmony_ci 12398c2ecf20Sopenharmony_ci spin_lock_bh(&im->lock); 12408c2ecf20Sopenharmony_ci if (pmc) { 12418c2ecf20Sopenharmony_ci im->interface = pmc->interface; 12428c2ecf20Sopenharmony_ci if (im->sfmode == MCAST_INCLUDE) { 12438c2ecf20Sopenharmony_ci swap(im->tomb, pmc->tomb); 12448c2ecf20Sopenharmony_ci swap(im->sources, pmc->sources); 12458c2ecf20Sopenharmony_ci for (psf = im->sources; psf; psf = psf->sf_next) 12468c2ecf20Sopenharmony_ci psf->sf_crcount = in_dev->mr_qrv ?: 12478c2ecf20Sopenharmony_ci READ_ONCE(net->ipv4.sysctl_igmp_qrv); 12488c2ecf20Sopenharmony_ci } else { 12498c2ecf20Sopenharmony_ci im->crcount = in_dev->mr_qrv ?: 12508c2ecf20Sopenharmony_ci READ_ONCE(net->ipv4.sysctl_igmp_qrv); 12518c2ecf20Sopenharmony_ci } 12528c2ecf20Sopenharmony_ci in_dev_put(pmc->interface); 12538c2ecf20Sopenharmony_ci kfree_pmc(pmc); 12548c2ecf20Sopenharmony_ci } 12558c2ecf20Sopenharmony_ci spin_unlock_bh(&im->lock); 12568c2ecf20Sopenharmony_ci} 12578c2ecf20Sopenharmony_ci 12588c2ecf20Sopenharmony_ci/* 12598c2ecf20Sopenharmony_ci * flush ip_mc_list deleted records 12608c2ecf20Sopenharmony_ci */ 12618c2ecf20Sopenharmony_cistatic void igmpv3_clear_delrec(struct in_device *in_dev) 12628c2ecf20Sopenharmony_ci{ 12638c2ecf20Sopenharmony_ci struct ip_mc_list *pmc, *nextpmc; 12648c2ecf20Sopenharmony_ci 12658c2ecf20Sopenharmony_ci spin_lock_bh(&in_dev->mc_tomb_lock); 12668c2ecf20Sopenharmony_ci pmc = in_dev->mc_tomb; 12678c2ecf20Sopenharmony_ci in_dev->mc_tomb = NULL; 12688c2ecf20Sopenharmony_ci spin_unlock_bh(&in_dev->mc_tomb_lock); 12698c2ecf20Sopenharmony_ci 12708c2ecf20Sopenharmony_ci for (; pmc; pmc = nextpmc) { 12718c2ecf20Sopenharmony_ci nextpmc = pmc->next; 12728c2ecf20Sopenharmony_ci ip_mc_clear_src(pmc); 12738c2ecf20Sopenharmony_ci in_dev_put(pmc->interface); 12748c2ecf20Sopenharmony_ci kfree_pmc(pmc); 12758c2ecf20Sopenharmony_ci } 12768c2ecf20Sopenharmony_ci /* clear dead sources, too */ 12778c2ecf20Sopenharmony_ci rcu_read_lock(); 12788c2ecf20Sopenharmony_ci for_each_pmc_rcu(in_dev, pmc) { 12798c2ecf20Sopenharmony_ci struct ip_sf_list *psf; 12808c2ecf20Sopenharmony_ci 12818c2ecf20Sopenharmony_ci spin_lock_bh(&pmc->lock); 12828c2ecf20Sopenharmony_ci psf = pmc->tomb; 12838c2ecf20Sopenharmony_ci pmc->tomb = NULL; 12848c2ecf20Sopenharmony_ci spin_unlock_bh(&pmc->lock); 12858c2ecf20Sopenharmony_ci ip_sf_list_clear_all(psf); 12868c2ecf20Sopenharmony_ci } 12878c2ecf20Sopenharmony_ci rcu_read_unlock(); 12888c2ecf20Sopenharmony_ci} 12898c2ecf20Sopenharmony_ci#endif 12908c2ecf20Sopenharmony_ci 12918c2ecf20Sopenharmony_cistatic void __igmp_group_dropped(struct ip_mc_list *im, gfp_t gfp) 12928c2ecf20Sopenharmony_ci{ 12938c2ecf20Sopenharmony_ci struct in_device *in_dev = im->interface; 12948c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_MULTICAST 12958c2ecf20Sopenharmony_ci struct net *net = dev_net(in_dev->dev); 12968c2ecf20Sopenharmony_ci int reporter; 12978c2ecf20Sopenharmony_ci#endif 12988c2ecf20Sopenharmony_ci 12998c2ecf20Sopenharmony_ci if (im->loaded) { 13008c2ecf20Sopenharmony_ci im->loaded = 0; 13018c2ecf20Sopenharmony_ci ip_mc_filter_del(in_dev, im->multiaddr); 13028c2ecf20Sopenharmony_ci } 13038c2ecf20Sopenharmony_ci 13048c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_MULTICAST 13058c2ecf20Sopenharmony_ci if (im->multiaddr == IGMP_ALL_HOSTS) 13068c2ecf20Sopenharmony_ci return; 13078c2ecf20Sopenharmony_ci if (ipv4_is_local_multicast(im->multiaddr) && 13088c2ecf20Sopenharmony_ci !READ_ONCE(net->ipv4.sysctl_igmp_llm_reports)) 13098c2ecf20Sopenharmony_ci return; 13108c2ecf20Sopenharmony_ci 13118c2ecf20Sopenharmony_ci reporter = im->reporter; 13128c2ecf20Sopenharmony_ci igmp_stop_timer(im); 13138c2ecf20Sopenharmony_ci 13148c2ecf20Sopenharmony_ci if (!in_dev->dead) { 13158c2ecf20Sopenharmony_ci if (IGMP_V1_SEEN(in_dev)) 13168c2ecf20Sopenharmony_ci return; 13178c2ecf20Sopenharmony_ci if (IGMP_V2_SEEN(in_dev)) { 13188c2ecf20Sopenharmony_ci if (reporter) 13198c2ecf20Sopenharmony_ci igmp_send_report(in_dev, im, IGMP_HOST_LEAVE_MESSAGE); 13208c2ecf20Sopenharmony_ci return; 13218c2ecf20Sopenharmony_ci } 13228c2ecf20Sopenharmony_ci /* IGMPv3 */ 13238c2ecf20Sopenharmony_ci igmpv3_add_delrec(in_dev, im, gfp); 13248c2ecf20Sopenharmony_ci 13258c2ecf20Sopenharmony_ci igmp_ifc_event(in_dev); 13268c2ecf20Sopenharmony_ci } 13278c2ecf20Sopenharmony_ci#endif 13288c2ecf20Sopenharmony_ci} 13298c2ecf20Sopenharmony_ci 13308c2ecf20Sopenharmony_cistatic void igmp_group_dropped(struct ip_mc_list *im) 13318c2ecf20Sopenharmony_ci{ 13328c2ecf20Sopenharmony_ci __igmp_group_dropped(im, GFP_KERNEL); 13338c2ecf20Sopenharmony_ci} 13348c2ecf20Sopenharmony_ci 13358c2ecf20Sopenharmony_cistatic void igmp_group_added(struct ip_mc_list *im) 13368c2ecf20Sopenharmony_ci{ 13378c2ecf20Sopenharmony_ci struct in_device *in_dev = im->interface; 13388c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_MULTICAST 13398c2ecf20Sopenharmony_ci struct net *net = dev_net(in_dev->dev); 13408c2ecf20Sopenharmony_ci#endif 13418c2ecf20Sopenharmony_ci 13428c2ecf20Sopenharmony_ci if (im->loaded == 0) { 13438c2ecf20Sopenharmony_ci im->loaded = 1; 13448c2ecf20Sopenharmony_ci ip_mc_filter_add(in_dev, im->multiaddr); 13458c2ecf20Sopenharmony_ci } 13468c2ecf20Sopenharmony_ci 13478c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_MULTICAST 13488c2ecf20Sopenharmony_ci if (im->multiaddr == IGMP_ALL_HOSTS) 13498c2ecf20Sopenharmony_ci return; 13508c2ecf20Sopenharmony_ci if (ipv4_is_local_multicast(im->multiaddr) && 13518c2ecf20Sopenharmony_ci !READ_ONCE(net->ipv4.sysctl_igmp_llm_reports)) 13528c2ecf20Sopenharmony_ci return; 13538c2ecf20Sopenharmony_ci 13548c2ecf20Sopenharmony_ci if (in_dev->dead) 13558c2ecf20Sopenharmony_ci return; 13568c2ecf20Sopenharmony_ci 13578c2ecf20Sopenharmony_ci im->unsolicit_count = READ_ONCE(net->ipv4.sysctl_igmp_qrv); 13588c2ecf20Sopenharmony_ci if (IGMP_V1_SEEN(in_dev) || IGMP_V2_SEEN(in_dev)) { 13598c2ecf20Sopenharmony_ci spin_lock_bh(&im->lock); 13608c2ecf20Sopenharmony_ci igmp_start_timer(im, IGMP_INITIAL_REPORT_DELAY); 13618c2ecf20Sopenharmony_ci spin_unlock_bh(&im->lock); 13628c2ecf20Sopenharmony_ci return; 13638c2ecf20Sopenharmony_ci } 13648c2ecf20Sopenharmony_ci /* else, v3 */ 13658c2ecf20Sopenharmony_ci 13668c2ecf20Sopenharmony_ci /* Based on RFC3376 5.1, for newly added INCLUDE SSM, we should 13678c2ecf20Sopenharmony_ci * not send filter-mode change record as the mode should be from 13688c2ecf20Sopenharmony_ci * IN() to IN(A). 13698c2ecf20Sopenharmony_ci */ 13708c2ecf20Sopenharmony_ci if (im->sfmode == MCAST_EXCLUDE) 13718c2ecf20Sopenharmony_ci im->crcount = in_dev->mr_qrv ?: READ_ONCE(net->ipv4.sysctl_igmp_qrv); 13728c2ecf20Sopenharmony_ci 13738c2ecf20Sopenharmony_ci igmp_ifc_event(in_dev); 13748c2ecf20Sopenharmony_ci#endif 13758c2ecf20Sopenharmony_ci} 13768c2ecf20Sopenharmony_ci 13778c2ecf20Sopenharmony_ci 13788c2ecf20Sopenharmony_ci/* 13798c2ecf20Sopenharmony_ci * Multicast list managers 13808c2ecf20Sopenharmony_ci */ 13818c2ecf20Sopenharmony_ci 13828c2ecf20Sopenharmony_cistatic u32 ip_mc_hash(const struct ip_mc_list *im) 13838c2ecf20Sopenharmony_ci{ 13848c2ecf20Sopenharmony_ci return hash_32((__force u32)im->multiaddr, MC_HASH_SZ_LOG); 13858c2ecf20Sopenharmony_ci} 13868c2ecf20Sopenharmony_ci 13878c2ecf20Sopenharmony_cistatic void ip_mc_hash_add(struct in_device *in_dev, 13888c2ecf20Sopenharmony_ci struct ip_mc_list *im) 13898c2ecf20Sopenharmony_ci{ 13908c2ecf20Sopenharmony_ci struct ip_mc_list __rcu **mc_hash; 13918c2ecf20Sopenharmony_ci u32 hash; 13928c2ecf20Sopenharmony_ci 13938c2ecf20Sopenharmony_ci mc_hash = rtnl_dereference(in_dev->mc_hash); 13948c2ecf20Sopenharmony_ci if (mc_hash) { 13958c2ecf20Sopenharmony_ci hash = ip_mc_hash(im); 13968c2ecf20Sopenharmony_ci im->next_hash = mc_hash[hash]; 13978c2ecf20Sopenharmony_ci rcu_assign_pointer(mc_hash[hash], im); 13988c2ecf20Sopenharmony_ci return; 13998c2ecf20Sopenharmony_ci } 14008c2ecf20Sopenharmony_ci 14018c2ecf20Sopenharmony_ci /* do not use a hash table for small number of items */ 14028c2ecf20Sopenharmony_ci if (in_dev->mc_count < 4) 14038c2ecf20Sopenharmony_ci return; 14048c2ecf20Sopenharmony_ci 14058c2ecf20Sopenharmony_ci mc_hash = kzalloc(sizeof(struct ip_mc_list *) << MC_HASH_SZ_LOG, 14068c2ecf20Sopenharmony_ci GFP_KERNEL); 14078c2ecf20Sopenharmony_ci if (!mc_hash) 14088c2ecf20Sopenharmony_ci return; 14098c2ecf20Sopenharmony_ci 14108c2ecf20Sopenharmony_ci for_each_pmc_rtnl(in_dev, im) { 14118c2ecf20Sopenharmony_ci hash = ip_mc_hash(im); 14128c2ecf20Sopenharmony_ci im->next_hash = mc_hash[hash]; 14138c2ecf20Sopenharmony_ci RCU_INIT_POINTER(mc_hash[hash], im); 14148c2ecf20Sopenharmony_ci } 14158c2ecf20Sopenharmony_ci 14168c2ecf20Sopenharmony_ci rcu_assign_pointer(in_dev->mc_hash, mc_hash); 14178c2ecf20Sopenharmony_ci} 14188c2ecf20Sopenharmony_ci 14198c2ecf20Sopenharmony_cistatic void ip_mc_hash_remove(struct in_device *in_dev, 14208c2ecf20Sopenharmony_ci struct ip_mc_list *im) 14218c2ecf20Sopenharmony_ci{ 14228c2ecf20Sopenharmony_ci struct ip_mc_list __rcu **mc_hash = rtnl_dereference(in_dev->mc_hash); 14238c2ecf20Sopenharmony_ci struct ip_mc_list *aux; 14248c2ecf20Sopenharmony_ci 14258c2ecf20Sopenharmony_ci if (!mc_hash) 14268c2ecf20Sopenharmony_ci return; 14278c2ecf20Sopenharmony_ci mc_hash += ip_mc_hash(im); 14288c2ecf20Sopenharmony_ci while ((aux = rtnl_dereference(*mc_hash)) != im) 14298c2ecf20Sopenharmony_ci mc_hash = &aux->next_hash; 14308c2ecf20Sopenharmony_ci *mc_hash = im->next_hash; 14318c2ecf20Sopenharmony_ci} 14328c2ecf20Sopenharmony_ci 14338c2ecf20Sopenharmony_ci 14348c2ecf20Sopenharmony_ci/* 14358c2ecf20Sopenharmony_ci * A socket has joined a multicast group on device dev. 14368c2ecf20Sopenharmony_ci */ 14378c2ecf20Sopenharmony_cistatic void ____ip_mc_inc_group(struct in_device *in_dev, __be32 addr, 14388c2ecf20Sopenharmony_ci unsigned int mode, gfp_t gfp) 14398c2ecf20Sopenharmony_ci{ 14408c2ecf20Sopenharmony_ci struct ip_mc_list *im; 14418c2ecf20Sopenharmony_ci 14428c2ecf20Sopenharmony_ci ASSERT_RTNL(); 14438c2ecf20Sopenharmony_ci 14448c2ecf20Sopenharmony_ci for_each_pmc_rtnl(in_dev, im) { 14458c2ecf20Sopenharmony_ci if (im->multiaddr == addr) { 14468c2ecf20Sopenharmony_ci im->users++; 14478c2ecf20Sopenharmony_ci ip_mc_add_src(in_dev, &addr, mode, 0, NULL, 0); 14488c2ecf20Sopenharmony_ci goto out; 14498c2ecf20Sopenharmony_ci } 14508c2ecf20Sopenharmony_ci } 14518c2ecf20Sopenharmony_ci 14528c2ecf20Sopenharmony_ci im = kzalloc(sizeof(*im), gfp); 14538c2ecf20Sopenharmony_ci if (!im) 14548c2ecf20Sopenharmony_ci goto out; 14558c2ecf20Sopenharmony_ci 14568c2ecf20Sopenharmony_ci im->users = 1; 14578c2ecf20Sopenharmony_ci im->interface = in_dev; 14588c2ecf20Sopenharmony_ci in_dev_hold(in_dev); 14598c2ecf20Sopenharmony_ci im->multiaddr = addr; 14608c2ecf20Sopenharmony_ci /* initial mode is (EX, empty) */ 14618c2ecf20Sopenharmony_ci im->sfmode = mode; 14628c2ecf20Sopenharmony_ci im->sfcount[mode] = 1; 14638c2ecf20Sopenharmony_ci refcount_set(&im->refcnt, 1); 14648c2ecf20Sopenharmony_ci spin_lock_init(&im->lock); 14658c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_MULTICAST 14668c2ecf20Sopenharmony_ci timer_setup(&im->timer, igmp_timer_expire, 0); 14678c2ecf20Sopenharmony_ci#endif 14688c2ecf20Sopenharmony_ci 14698c2ecf20Sopenharmony_ci im->next_rcu = in_dev->mc_list; 14708c2ecf20Sopenharmony_ci in_dev->mc_count++; 14718c2ecf20Sopenharmony_ci rcu_assign_pointer(in_dev->mc_list, im); 14728c2ecf20Sopenharmony_ci 14738c2ecf20Sopenharmony_ci ip_mc_hash_add(in_dev, im); 14748c2ecf20Sopenharmony_ci 14758c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_MULTICAST 14768c2ecf20Sopenharmony_ci igmpv3_del_delrec(in_dev, im); 14778c2ecf20Sopenharmony_ci#endif 14788c2ecf20Sopenharmony_ci igmp_group_added(im); 14798c2ecf20Sopenharmony_ci if (!in_dev->dead) 14808c2ecf20Sopenharmony_ci ip_rt_multicast_event(in_dev); 14818c2ecf20Sopenharmony_ciout: 14828c2ecf20Sopenharmony_ci return; 14838c2ecf20Sopenharmony_ci} 14848c2ecf20Sopenharmony_ci 14858c2ecf20Sopenharmony_civoid __ip_mc_inc_group(struct in_device *in_dev, __be32 addr, gfp_t gfp) 14868c2ecf20Sopenharmony_ci{ 14878c2ecf20Sopenharmony_ci ____ip_mc_inc_group(in_dev, addr, MCAST_EXCLUDE, gfp); 14888c2ecf20Sopenharmony_ci} 14898c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__ip_mc_inc_group); 14908c2ecf20Sopenharmony_ci 14918c2ecf20Sopenharmony_civoid ip_mc_inc_group(struct in_device *in_dev, __be32 addr) 14928c2ecf20Sopenharmony_ci{ 14938c2ecf20Sopenharmony_ci __ip_mc_inc_group(in_dev, addr, GFP_KERNEL); 14948c2ecf20Sopenharmony_ci} 14958c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ip_mc_inc_group); 14968c2ecf20Sopenharmony_ci 14978c2ecf20Sopenharmony_cistatic int ip_mc_check_iphdr(struct sk_buff *skb) 14988c2ecf20Sopenharmony_ci{ 14998c2ecf20Sopenharmony_ci const struct iphdr *iph; 15008c2ecf20Sopenharmony_ci unsigned int len; 15018c2ecf20Sopenharmony_ci unsigned int offset = skb_network_offset(skb) + sizeof(*iph); 15028c2ecf20Sopenharmony_ci 15038c2ecf20Sopenharmony_ci if (!pskb_may_pull(skb, offset)) 15048c2ecf20Sopenharmony_ci return -EINVAL; 15058c2ecf20Sopenharmony_ci 15068c2ecf20Sopenharmony_ci iph = ip_hdr(skb); 15078c2ecf20Sopenharmony_ci 15088c2ecf20Sopenharmony_ci if (iph->version != 4 || ip_hdrlen(skb) < sizeof(*iph)) 15098c2ecf20Sopenharmony_ci return -EINVAL; 15108c2ecf20Sopenharmony_ci 15118c2ecf20Sopenharmony_ci offset += ip_hdrlen(skb) - sizeof(*iph); 15128c2ecf20Sopenharmony_ci 15138c2ecf20Sopenharmony_ci if (!pskb_may_pull(skb, offset)) 15148c2ecf20Sopenharmony_ci return -EINVAL; 15158c2ecf20Sopenharmony_ci 15168c2ecf20Sopenharmony_ci iph = ip_hdr(skb); 15178c2ecf20Sopenharmony_ci 15188c2ecf20Sopenharmony_ci if (unlikely(ip_fast_csum((u8 *)iph, iph->ihl))) 15198c2ecf20Sopenharmony_ci return -EINVAL; 15208c2ecf20Sopenharmony_ci 15218c2ecf20Sopenharmony_ci len = skb_network_offset(skb) + ntohs(iph->tot_len); 15228c2ecf20Sopenharmony_ci if (skb->len < len || len < offset) 15238c2ecf20Sopenharmony_ci return -EINVAL; 15248c2ecf20Sopenharmony_ci 15258c2ecf20Sopenharmony_ci skb_set_transport_header(skb, offset); 15268c2ecf20Sopenharmony_ci 15278c2ecf20Sopenharmony_ci return 0; 15288c2ecf20Sopenharmony_ci} 15298c2ecf20Sopenharmony_ci 15308c2ecf20Sopenharmony_cistatic int ip_mc_check_igmp_reportv3(struct sk_buff *skb) 15318c2ecf20Sopenharmony_ci{ 15328c2ecf20Sopenharmony_ci unsigned int len = skb_transport_offset(skb); 15338c2ecf20Sopenharmony_ci 15348c2ecf20Sopenharmony_ci len += sizeof(struct igmpv3_report); 15358c2ecf20Sopenharmony_ci 15368c2ecf20Sopenharmony_ci return ip_mc_may_pull(skb, len) ? 0 : -EINVAL; 15378c2ecf20Sopenharmony_ci} 15388c2ecf20Sopenharmony_ci 15398c2ecf20Sopenharmony_cistatic int ip_mc_check_igmp_query(struct sk_buff *skb) 15408c2ecf20Sopenharmony_ci{ 15418c2ecf20Sopenharmony_ci unsigned int transport_len = ip_transport_len(skb); 15428c2ecf20Sopenharmony_ci unsigned int len; 15438c2ecf20Sopenharmony_ci 15448c2ecf20Sopenharmony_ci /* IGMPv{1,2}? */ 15458c2ecf20Sopenharmony_ci if (transport_len != sizeof(struct igmphdr)) { 15468c2ecf20Sopenharmony_ci /* or IGMPv3? */ 15478c2ecf20Sopenharmony_ci if (transport_len < sizeof(struct igmpv3_query)) 15488c2ecf20Sopenharmony_ci return -EINVAL; 15498c2ecf20Sopenharmony_ci 15508c2ecf20Sopenharmony_ci len = skb_transport_offset(skb) + sizeof(struct igmpv3_query); 15518c2ecf20Sopenharmony_ci if (!ip_mc_may_pull(skb, len)) 15528c2ecf20Sopenharmony_ci return -EINVAL; 15538c2ecf20Sopenharmony_ci } 15548c2ecf20Sopenharmony_ci 15558c2ecf20Sopenharmony_ci /* RFC2236+RFC3376 (IGMPv2+IGMPv3) require the multicast link layer 15568c2ecf20Sopenharmony_ci * all-systems destination addresses (224.0.0.1) for general queries 15578c2ecf20Sopenharmony_ci */ 15588c2ecf20Sopenharmony_ci if (!igmp_hdr(skb)->group && 15598c2ecf20Sopenharmony_ci ip_hdr(skb)->daddr != htonl(INADDR_ALLHOSTS_GROUP)) 15608c2ecf20Sopenharmony_ci return -EINVAL; 15618c2ecf20Sopenharmony_ci 15628c2ecf20Sopenharmony_ci return 0; 15638c2ecf20Sopenharmony_ci} 15648c2ecf20Sopenharmony_ci 15658c2ecf20Sopenharmony_cistatic int ip_mc_check_igmp_msg(struct sk_buff *skb) 15668c2ecf20Sopenharmony_ci{ 15678c2ecf20Sopenharmony_ci switch (igmp_hdr(skb)->type) { 15688c2ecf20Sopenharmony_ci case IGMP_HOST_LEAVE_MESSAGE: 15698c2ecf20Sopenharmony_ci case IGMP_HOST_MEMBERSHIP_REPORT: 15708c2ecf20Sopenharmony_ci case IGMPV2_HOST_MEMBERSHIP_REPORT: 15718c2ecf20Sopenharmony_ci return 0; 15728c2ecf20Sopenharmony_ci case IGMPV3_HOST_MEMBERSHIP_REPORT: 15738c2ecf20Sopenharmony_ci return ip_mc_check_igmp_reportv3(skb); 15748c2ecf20Sopenharmony_ci case IGMP_HOST_MEMBERSHIP_QUERY: 15758c2ecf20Sopenharmony_ci return ip_mc_check_igmp_query(skb); 15768c2ecf20Sopenharmony_ci default: 15778c2ecf20Sopenharmony_ci return -ENOMSG; 15788c2ecf20Sopenharmony_ci } 15798c2ecf20Sopenharmony_ci} 15808c2ecf20Sopenharmony_ci 15818c2ecf20Sopenharmony_cistatic __sum16 ip_mc_validate_checksum(struct sk_buff *skb) 15828c2ecf20Sopenharmony_ci{ 15838c2ecf20Sopenharmony_ci return skb_checksum_simple_validate(skb); 15848c2ecf20Sopenharmony_ci} 15858c2ecf20Sopenharmony_ci 15868c2ecf20Sopenharmony_cistatic int ip_mc_check_igmp_csum(struct sk_buff *skb) 15878c2ecf20Sopenharmony_ci{ 15888c2ecf20Sopenharmony_ci unsigned int len = skb_transport_offset(skb) + sizeof(struct igmphdr); 15898c2ecf20Sopenharmony_ci unsigned int transport_len = ip_transport_len(skb); 15908c2ecf20Sopenharmony_ci struct sk_buff *skb_chk; 15918c2ecf20Sopenharmony_ci 15928c2ecf20Sopenharmony_ci if (!ip_mc_may_pull(skb, len)) 15938c2ecf20Sopenharmony_ci return -EINVAL; 15948c2ecf20Sopenharmony_ci 15958c2ecf20Sopenharmony_ci skb_chk = skb_checksum_trimmed(skb, transport_len, 15968c2ecf20Sopenharmony_ci ip_mc_validate_checksum); 15978c2ecf20Sopenharmony_ci if (!skb_chk) 15988c2ecf20Sopenharmony_ci return -EINVAL; 15998c2ecf20Sopenharmony_ci 16008c2ecf20Sopenharmony_ci if (skb_chk != skb) 16018c2ecf20Sopenharmony_ci kfree_skb(skb_chk); 16028c2ecf20Sopenharmony_ci 16038c2ecf20Sopenharmony_ci return 0; 16048c2ecf20Sopenharmony_ci} 16058c2ecf20Sopenharmony_ci 16068c2ecf20Sopenharmony_ci/** 16078c2ecf20Sopenharmony_ci * ip_mc_check_igmp - checks whether this is a sane IGMP packet 16088c2ecf20Sopenharmony_ci * @skb: the skb to validate 16098c2ecf20Sopenharmony_ci * 16108c2ecf20Sopenharmony_ci * Checks whether an IPv4 packet is a valid IGMP packet. If so sets 16118c2ecf20Sopenharmony_ci * skb transport header accordingly and returns zero. 16128c2ecf20Sopenharmony_ci * 16138c2ecf20Sopenharmony_ci * -EINVAL: A broken packet was detected, i.e. it violates some internet 16148c2ecf20Sopenharmony_ci * standard 16158c2ecf20Sopenharmony_ci * -ENOMSG: IP header validation succeeded but it is not an IGMP packet. 16168c2ecf20Sopenharmony_ci * -ENOMEM: A memory allocation failure happened. 16178c2ecf20Sopenharmony_ci * 16188c2ecf20Sopenharmony_ci * Caller needs to set the skb network header and free any returned skb if it 16198c2ecf20Sopenharmony_ci * differs from the provided skb. 16208c2ecf20Sopenharmony_ci */ 16218c2ecf20Sopenharmony_ciint ip_mc_check_igmp(struct sk_buff *skb) 16228c2ecf20Sopenharmony_ci{ 16238c2ecf20Sopenharmony_ci int ret = ip_mc_check_iphdr(skb); 16248c2ecf20Sopenharmony_ci 16258c2ecf20Sopenharmony_ci if (ret < 0) 16268c2ecf20Sopenharmony_ci return ret; 16278c2ecf20Sopenharmony_ci 16288c2ecf20Sopenharmony_ci if (ip_hdr(skb)->protocol != IPPROTO_IGMP) 16298c2ecf20Sopenharmony_ci return -ENOMSG; 16308c2ecf20Sopenharmony_ci 16318c2ecf20Sopenharmony_ci ret = ip_mc_check_igmp_csum(skb); 16328c2ecf20Sopenharmony_ci if (ret < 0) 16338c2ecf20Sopenharmony_ci return ret; 16348c2ecf20Sopenharmony_ci 16358c2ecf20Sopenharmony_ci return ip_mc_check_igmp_msg(skb); 16368c2ecf20Sopenharmony_ci} 16378c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ip_mc_check_igmp); 16388c2ecf20Sopenharmony_ci 16398c2ecf20Sopenharmony_ci/* 16408c2ecf20Sopenharmony_ci * Resend IGMP JOIN report; used by netdev notifier. 16418c2ecf20Sopenharmony_ci */ 16428c2ecf20Sopenharmony_cistatic void ip_mc_rejoin_groups(struct in_device *in_dev) 16438c2ecf20Sopenharmony_ci{ 16448c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_MULTICAST 16458c2ecf20Sopenharmony_ci struct ip_mc_list *im; 16468c2ecf20Sopenharmony_ci int type; 16478c2ecf20Sopenharmony_ci struct net *net = dev_net(in_dev->dev); 16488c2ecf20Sopenharmony_ci 16498c2ecf20Sopenharmony_ci ASSERT_RTNL(); 16508c2ecf20Sopenharmony_ci 16518c2ecf20Sopenharmony_ci for_each_pmc_rtnl(in_dev, im) { 16528c2ecf20Sopenharmony_ci if (im->multiaddr == IGMP_ALL_HOSTS) 16538c2ecf20Sopenharmony_ci continue; 16548c2ecf20Sopenharmony_ci if (ipv4_is_local_multicast(im->multiaddr) && 16558c2ecf20Sopenharmony_ci !READ_ONCE(net->ipv4.sysctl_igmp_llm_reports)) 16568c2ecf20Sopenharmony_ci continue; 16578c2ecf20Sopenharmony_ci 16588c2ecf20Sopenharmony_ci /* a failover is happening and switches 16598c2ecf20Sopenharmony_ci * must be notified immediately 16608c2ecf20Sopenharmony_ci */ 16618c2ecf20Sopenharmony_ci if (IGMP_V1_SEEN(in_dev)) 16628c2ecf20Sopenharmony_ci type = IGMP_HOST_MEMBERSHIP_REPORT; 16638c2ecf20Sopenharmony_ci else if (IGMP_V2_SEEN(in_dev)) 16648c2ecf20Sopenharmony_ci type = IGMPV2_HOST_MEMBERSHIP_REPORT; 16658c2ecf20Sopenharmony_ci else 16668c2ecf20Sopenharmony_ci type = IGMPV3_HOST_MEMBERSHIP_REPORT; 16678c2ecf20Sopenharmony_ci igmp_send_report(in_dev, im, type); 16688c2ecf20Sopenharmony_ci } 16698c2ecf20Sopenharmony_ci#endif 16708c2ecf20Sopenharmony_ci} 16718c2ecf20Sopenharmony_ci 16728c2ecf20Sopenharmony_ci/* 16738c2ecf20Sopenharmony_ci * A socket has left a multicast group on device dev 16748c2ecf20Sopenharmony_ci */ 16758c2ecf20Sopenharmony_ci 16768c2ecf20Sopenharmony_civoid __ip_mc_dec_group(struct in_device *in_dev, __be32 addr, gfp_t gfp) 16778c2ecf20Sopenharmony_ci{ 16788c2ecf20Sopenharmony_ci struct ip_mc_list *i; 16798c2ecf20Sopenharmony_ci struct ip_mc_list __rcu **ip; 16808c2ecf20Sopenharmony_ci 16818c2ecf20Sopenharmony_ci ASSERT_RTNL(); 16828c2ecf20Sopenharmony_ci 16838c2ecf20Sopenharmony_ci for (ip = &in_dev->mc_list; 16848c2ecf20Sopenharmony_ci (i = rtnl_dereference(*ip)) != NULL; 16858c2ecf20Sopenharmony_ci ip = &i->next_rcu) { 16868c2ecf20Sopenharmony_ci if (i->multiaddr == addr) { 16878c2ecf20Sopenharmony_ci if (--i->users == 0) { 16888c2ecf20Sopenharmony_ci ip_mc_hash_remove(in_dev, i); 16898c2ecf20Sopenharmony_ci *ip = i->next_rcu; 16908c2ecf20Sopenharmony_ci in_dev->mc_count--; 16918c2ecf20Sopenharmony_ci __igmp_group_dropped(i, gfp); 16928c2ecf20Sopenharmony_ci ip_mc_clear_src(i); 16938c2ecf20Sopenharmony_ci 16948c2ecf20Sopenharmony_ci if (!in_dev->dead) 16958c2ecf20Sopenharmony_ci ip_rt_multicast_event(in_dev); 16968c2ecf20Sopenharmony_ci 16978c2ecf20Sopenharmony_ci ip_ma_put(i); 16988c2ecf20Sopenharmony_ci return; 16998c2ecf20Sopenharmony_ci } 17008c2ecf20Sopenharmony_ci break; 17018c2ecf20Sopenharmony_ci } 17028c2ecf20Sopenharmony_ci } 17038c2ecf20Sopenharmony_ci} 17048c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__ip_mc_dec_group); 17058c2ecf20Sopenharmony_ci 17068c2ecf20Sopenharmony_ci/* Device changing type */ 17078c2ecf20Sopenharmony_ci 17088c2ecf20Sopenharmony_civoid ip_mc_unmap(struct in_device *in_dev) 17098c2ecf20Sopenharmony_ci{ 17108c2ecf20Sopenharmony_ci struct ip_mc_list *pmc; 17118c2ecf20Sopenharmony_ci 17128c2ecf20Sopenharmony_ci ASSERT_RTNL(); 17138c2ecf20Sopenharmony_ci 17148c2ecf20Sopenharmony_ci for_each_pmc_rtnl(in_dev, pmc) 17158c2ecf20Sopenharmony_ci igmp_group_dropped(pmc); 17168c2ecf20Sopenharmony_ci} 17178c2ecf20Sopenharmony_ci 17188c2ecf20Sopenharmony_civoid ip_mc_remap(struct in_device *in_dev) 17198c2ecf20Sopenharmony_ci{ 17208c2ecf20Sopenharmony_ci struct ip_mc_list *pmc; 17218c2ecf20Sopenharmony_ci 17228c2ecf20Sopenharmony_ci ASSERT_RTNL(); 17238c2ecf20Sopenharmony_ci 17248c2ecf20Sopenharmony_ci for_each_pmc_rtnl(in_dev, pmc) { 17258c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_MULTICAST 17268c2ecf20Sopenharmony_ci igmpv3_del_delrec(in_dev, pmc); 17278c2ecf20Sopenharmony_ci#endif 17288c2ecf20Sopenharmony_ci igmp_group_added(pmc); 17298c2ecf20Sopenharmony_ci } 17308c2ecf20Sopenharmony_ci} 17318c2ecf20Sopenharmony_ci 17328c2ecf20Sopenharmony_ci/* Device going down */ 17338c2ecf20Sopenharmony_ci 17348c2ecf20Sopenharmony_civoid ip_mc_down(struct in_device *in_dev) 17358c2ecf20Sopenharmony_ci{ 17368c2ecf20Sopenharmony_ci struct ip_mc_list *pmc; 17378c2ecf20Sopenharmony_ci 17388c2ecf20Sopenharmony_ci ASSERT_RTNL(); 17398c2ecf20Sopenharmony_ci 17408c2ecf20Sopenharmony_ci for_each_pmc_rtnl(in_dev, pmc) 17418c2ecf20Sopenharmony_ci igmp_group_dropped(pmc); 17428c2ecf20Sopenharmony_ci 17438c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_MULTICAST 17448c2ecf20Sopenharmony_ci WRITE_ONCE(in_dev->mr_ifc_count, 0); 17458c2ecf20Sopenharmony_ci if (del_timer(&in_dev->mr_ifc_timer)) 17468c2ecf20Sopenharmony_ci __in_dev_put(in_dev); 17478c2ecf20Sopenharmony_ci in_dev->mr_gq_running = 0; 17488c2ecf20Sopenharmony_ci if (del_timer(&in_dev->mr_gq_timer)) 17498c2ecf20Sopenharmony_ci __in_dev_put(in_dev); 17508c2ecf20Sopenharmony_ci#endif 17518c2ecf20Sopenharmony_ci 17528c2ecf20Sopenharmony_ci ip_mc_dec_group(in_dev, IGMP_ALL_HOSTS); 17538c2ecf20Sopenharmony_ci} 17548c2ecf20Sopenharmony_ci 17558c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_MULTICAST 17568c2ecf20Sopenharmony_cistatic void ip_mc_reset(struct in_device *in_dev) 17578c2ecf20Sopenharmony_ci{ 17588c2ecf20Sopenharmony_ci struct net *net = dev_net(in_dev->dev); 17598c2ecf20Sopenharmony_ci 17608c2ecf20Sopenharmony_ci in_dev->mr_qi = IGMP_QUERY_INTERVAL; 17618c2ecf20Sopenharmony_ci in_dev->mr_qri = IGMP_QUERY_RESPONSE_INTERVAL; 17628c2ecf20Sopenharmony_ci in_dev->mr_qrv = READ_ONCE(net->ipv4.sysctl_igmp_qrv); 17638c2ecf20Sopenharmony_ci} 17648c2ecf20Sopenharmony_ci#else 17658c2ecf20Sopenharmony_cistatic void ip_mc_reset(struct in_device *in_dev) 17668c2ecf20Sopenharmony_ci{ 17678c2ecf20Sopenharmony_ci} 17688c2ecf20Sopenharmony_ci#endif 17698c2ecf20Sopenharmony_ci 17708c2ecf20Sopenharmony_civoid ip_mc_init_dev(struct in_device *in_dev) 17718c2ecf20Sopenharmony_ci{ 17728c2ecf20Sopenharmony_ci ASSERT_RTNL(); 17738c2ecf20Sopenharmony_ci 17748c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_MULTICAST 17758c2ecf20Sopenharmony_ci timer_setup(&in_dev->mr_gq_timer, igmp_gq_timer_expire, 0); 17768c2ecf20Sopenharmony_ci timer_setup(&in_dev->mr_ifc_timer, igmp_ifc_timer_expire, 0); 17778c2ecf20Sopenharmony_ci#endif 17788c2ecf20Sopenharmony_ci ip_mc_reset(in_dev); 17798c2ecf20Sopenharmony_ci 17808c2ecf20Sopenharmony_ci spin_lock_init(&in_dev->mc_tomb_lock); 17818c2ecf20Sopenharmony_ci} 17828c2ecf20Sopenharmony_ci 17838c2ecf20Sopenharmony_ci/* Device going up */ 17848c2ecf20Sopenharmony_ci 17858c2ecf20Sopenharmony_civoid ip_mc_up(struct in_device *in_dev) 17868c2ecf20Sopenharmony_ci{ 17878c2ecf20Sopenharmony_ci struct ip_mc_list *pmc; 17888c2ecf20Sopenharmony_ci 17898c2ecf20Sopenharmony_ci ASSERT_RTNL(); 17908c2ecf20Sopenharmony_ci 17918c2ecf20Sopenharmony_ci ip_mc_reset(in_dev); 17928c2ecf20Sopenharmony_ci ip_mc_inc_group(in_dev, IGMP_ALL_HOSTS); 17938c2ecf20Sopenharmony_ci 17948c2ecf20Sopenharmony_ci for_each_pmc_rtnl(in_dev, pmc) { 17958c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_MULTICAST 17968c2ecf20Sopenharmony_ci igmpv3_del_delrec(in_dev, pmc); 17978c2ecf20Sopenharmony_ci#endif 17988c2ecf20Sopenharmony_ci igmp_group_added(pmc); 17998c2ecf20Sopenharmony_ci } 18008c2ecf20Sopenharmony_ci} 18018c2ecf20Sopenharmony_ci 18028c2ecf20Sopenharmony_ci/* 18038c2ecf20Sopenharmony_ci * Device is about to be destroyed: clean up. 18048c2ecf20Sopenharmony_ci */ 18058c2ecf20Sopenharmony_ci 18068c2ecf20Sopenharmony_civoid ip_mc_destroy_dev(struct in_device *in_dev) 18078c2ecf20Sopenharmony_ci{ 18088c2ecf20Sopenharmony_ci struct ip_mc_list *i; 18098c2ecf20Sopenharmony_ci 18108c2ecf20Sopenharmony_ci ASSERT_RTNL(); 18118c2ecf20Sopenharmony_ci 18128c2ecf20Sopenharmony_ci /* Deactivate timers */ 18138c2ecf20Sopenharmony_ci ip_mc_down(in_dev); 18148c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_MULTICAST 18158c2ecf20Sopenharmony_ci igmpv3_clear_delrec(in_dev); 18168c2ecf20Sopenharmony_ci#endif 18178c2ecf20Sopenharmony_ci 18188c2ecf20Sopenharmony_ci while ((i = rtnl_dereference(in_dev->mc_list)) != NULL) { 18198c2ecf20Sopenharmony_ci in_dev->mc_list = i->next_rcu; 18208c2ecf20Sopenharmony_ci in_dev->mc_count--; 18218c2ecf20Sopenharmony_ci ip_mc_clear_src(i); 18228c2ecf20Sopenharmony_ci ip_ma_put(i); 18238c2ecf20Sopenharmony_ci } 18248c2ecf20Sopenharmony_ci} 18258c2ecf20Sopenharmony_ci 18268c2ecf20Sopenharmony_ci/* RTNL is locked */ 18278c2ecf20Sopenharmony_cistatic struct in_device *ip_mc_find_dev(struct net *net, struct ip_mreqn *imr) 18288c2ecf20Sopenharmony_ci{ 18298c2ecf20Sopenharmony_ci struct net_device *dev = NULL; 18308c2ecf20Sopenharmony_ci struct in_device *idev = NULL; 18318c2ecf20Sopenharmony_ci 18328c2ecf20Sopenharmony_ci if (imr->imr_ifindex) { 18338c2ecf20Sopenharmony_ci idev = inetdev_by_index(net, imr->imr_ifindex); 18348c2ecf20Sopenharmony_ci return idev; 18358c2ecf20Sopenharmony_ci } 18368c2ecf20Sopenharmony_ci if (imr->imr_address.s_addr) { 18378c2ecf20Sopenharmony_ci dev = __ip_dev_find(net, imr->imr_address.s_addr, false); 18388c2ecf20Sopenharmony_ci if (!dev) 18398c2ecf20Sopenharmony_ci return NULL; 18408c2ecf20Sopenharmony_ci } 18418c2ecf20Sopenharmony_ci 18428c2ecf20Sopenharmony_ci if (!dev) { 18438c2ecf20Sopenharmony_ci struct rtable *rt = ip_route_output(net, 18448c2ecf20Sopenharmony_ci imr->imr_multiaddr.s_addr, 18458c2ecf20Sopenharmony_ci 0, 0, 0); 18468c2ecf20Sopenharmony_ci if (!IS_ERR(rt)) { 18478c2ecf20Sopenharmony_ci dev = rt->dst.dev; 18488c2ecf20Sopenharmony_ci ip_rt_put(rt); 18498c2ecf20Sopenharmony_ci } 18508c2ecf20Sopenharmony_ci } 18518c2ecf20Sopenharmony_ci if (dev) { 18528c2ecf20Sopenharmony_ci imr->imr_ifindex = dev->ifindex; 18538c2ecf20Sopenharmony_ci idev = __in_dev_get_rtnl(dev); 18548c2ecf20Sopenharmony_ci } 18558c2ecf20Sopenharmony_ci return idev; 18568c2ecf20Sopenharmony_ci} 18578c2ecf20Sopenharmony_ci 18588c2ecf20Sopenharmony_ci/* 18598c2ecf20Sopenharmony_ci * Join a socket to a group 18608c2ecf20Sopenharmony_ci */ 18618c2ecf20Sopenharmony_ci 18628c2ecf20Sopenharmony_cistatic int ip_mc_del1_src(struct ip_mc_list *pmc, int sfmode, 18638c2ecf20Sopenharmony_ci __be32 *psfsrc) 18648c2ecf20Sopenharmony_ci{ 18658c2ecf20Sopenharmony_ci struct ip_sf_list *psf, *psf_prev; 18668c2ecf20Sopenharmony_ci int rv = 0; 18678c2ecf20Sopenharmony_ci 18688c2ecf20Sopenharmony_ci psf_prev = NULL; 18698c2ecf20Sopenharmony_ci for (psf = pmc->sources; psf; psf = psf->sf_next) { 18708c2ecf20Sopenharmony_ci if (psf->sf_inaddr == *psfsrc) 18718c2ecf20Sopenharmony_ci break; 18728c2ecf20Sopenharmony_ci psf_prev = psf; 18738c2ecf20Sopenharmony_ci } 18748c2ecf20Sopenharmony_ci if (!psf || psf->sf_count[sfmode] == 0) { 18758c2ecf20Sopenharmony_ci /* source filter not found, or count wrong => bug */ 18768c2ecf20Sopenharmony_ci return -ESRCH; 18778c2ecf20Sopenharmony_ci } 18788c2ecf20Sopenharmony_ci psf->sf_count[sfmode]--; 18798c2ecf20Sopenharmony_ci if (psf->sf_count[sfmode] == 0) { 18808c2ecf20Sopenharmony_ci ip_rt_multicast_event(pmc->interface); 18818c2ecf20Sopenharmony_ci } 18828c2ecf20Sopenharmony_ci if (!psf->sf_count[MCAST_INCLUDE] && !psf->sf_count[MCAST_EXCLUDE]) { 18838c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_MULTICAST 18848c2ecf20Sopenharmony_ci struct in_device *in_dev = pmc->interface; 18858c2ecf20Sopenharmony_ci struct net *net = dev_net(in_dev->dev); 18868c2ecf20Sopenharmony_ci#endif 18878c2ecf20Sopenharmony_ci 18888c2ecf20Sopenharmony_ci /* no more filters for this source */ 18898c2ecf20Sopenharmony_ci if (psf_prev) 18908c2ecf20Sopenharmony_ci psf_prev->sf_next = psf->sf_next; 18918c2ecf20Sopenharmony_ci else 18928c2ecf20Sopenharmony_ci pmc->sources = psf->sf_next; 18938c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_MULTICAST 18948c2ecf20Sopenharmony_ci if (psf->sf_oldin && 18958c2ecf20Sopenharmony_ci !IGMP_V1_SEEN(in_dev) && !IGMP_V2_SEEN(in_dev)) { 18968c2ecf20Sopenharmony_ci psf->sf_crcount = in_dev->mr_qrv ?: READ_ONCE(net->ipv4.sysctl_igmp_qrv); 18978c2ecf20Sopenharmony_ci psf->sf_next = pmc->tomb; 18988c2ecf20Sopenharmony_ci pmc->tomb = psf; 18998c2ecf20Sopenharmony_ci rv = 1; 19008c2ecf20Sopenharmony_ci } else 19018c2ecf20Sopenharmony_ci#endif 19028c2ecf20Sopenharmony_ci kfree(psf); 19038c2ecf20Sopenharmony_ci } 19048c2ecf20Sopenharmony_ci return rv; 19058c2ecf20Sopenharmony_ci} 19068c2ecf20Sopenharmony_ci 19078c2ecf20Sopenharmony_ci#ifndef CONFIG_IP_MULTICAST 19088c2ecf20Sopenharmony_ci#define igmp_ifc_event(x) do { } while (0) 19098c2ecf20Sopenharmony_ci#endif 19108c2ecf20Sopenharmony_ci 19118c2ecf20Sopenharmony_cistatic int ip_mc_del_src(struct in_device *in_dev, __be32 *pmca, int sfmode, 19128c2ecf20Sopenharmony_ci int sfcount, __be32 *psfsrc, int delta) 19138c2ecf20Sopenharmony_ci{ 19148c2ecf20Sopenharmony_ci struct ip_mc_list *pmc; 19158c2ecf20Sopenharmony_ci int changerec = 0; 19168c2ecf20Sopenharmony_ci int i, err; 19178c2ecf20Sopenharmony_ci 19188c2ecf20Sopenharmony_ci if (!in_dev) 19198c2ecf20Sopenharmony_ci return -ENODEV; 19208c2ecf20Sopenharmony_ci rcu_read_lock(); 19218c2ecf20Sopenharmony_ci for_each_pmc_rcu(in_dev, pmc) { 19228c2ecf20Sopenharmony_ci if (*pmca == pmc->multiaddr) 19238c2ecf20Sopenharmony_ci break; 19248c2ecf20Sopenharmony_ci } 19258c2ecf20Sopenharmony_ci if (!pmc) { 19268c2ecf20Sopenharmony_ci /* MCA not found?? bug */ 19278c2ecf20Sopenharmony_ci rcu_read_unlock(); 19288c2ecf20Sopenharmony_ci return -ESRCH; 19298c2ecf20Sopenharmony_ci } 19308c2ecf20Sopenharmony_ci spin_lock_bh(&pmc->lock); 19318c2ecf20Sopenharmony_ci rcu_read_unlock(); 19328c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_MULTICAST 19338c2ecf20Sopenharmony_ci sf_markstate(pmc); 19348c2ecf20Sopenharmony_ci#endif 19358c2ecf20Sopenharmony_ci if (!delta) { 19368c2ecf20Sopenharmony_ci err = -EINVAL; 19378c2ecf20Sopenharmony_ci if (!pmc->sfcount[sfmode]) 19388c2ecf20Sopenharmony_ci goto out_unlock; 19398c2ecf20Sopenharmony_ci pmc->sfcount[sfmode]--; 19408c2ecf20Sopenharmony_ci } 19418c2ecf20Sopenharmony_ci err = 0; 19428c2ecf20Sopenharmony_ci for (i = 0; i < sfcount; i++) { 19438c2ecf20Sopenharmony_ci int rv = ip_mc_del1_src(pmc, sfmode, &psfsrc[i]); 19448c2ecf20Sopenharmony_ci 19458c2ecf20Sopenharmony_ci changerec |= rv > 0; 19468c2ecf20Sopenharmony_ci if (!err && rv < 0) 19478c2ecf20Sopenharmony_ci err = rv; 19488c2ecf20Sopenharmony_ci } 19498c2ecf20Sopenharmony_ci if (pmc->sfmode == MCAST_EXCLUDE && 19508c2ecf20Sopenharmony_ci pmc->sfcount[MCAST_EXCLUDE] == 0 && 19518c2ecf20Sopenharmony_ci pmc->sfcount[MCAST_INCLUDE]) { 19528c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_MULTICAST 19538c2ecf20Sopenharmony_ci struct ip_sf_list *psf; 19548c2ecf20Sopenharmony_ci struct net *net = dev_net(in_dev->dev); 19558c2ecf20Sopenharmony_ci#endif 19568c2ecf20Sopenharmony_ci 19578c2ecf20Sopenharmony_ci /* filter mode change */ 19588c2ecf20Sopenharmony_ci pmc->sfmode = MCAST_INCLUDE; 19598c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_MULTICAST 19608c2ecf20Sopenharmony_ci pmc->crcount = in_dev->mr_qrv ?: READ_ONCE(net->ipv4.sysctl_igmp_qrv); 19618c2ecf20Sopenharmony_ci WRITE_ONCE(in_dev->mr_ifc_count, pmc->crcount); 19628c2ecf20Sopenharmony_ci for (psf = pmc->sources; psf; psf = psf->sf_next) 19638c2ecf20Sopenharmony_ci psf->sf_crcount = 0; 19648c2ecf20Sopenharmony_ci igmp_ifc_event(pmc->interface); 19658c2ecf20Sopenharmony_ci } else if (sf_setstate(pmc) || changerec) { 19668c2ecf20Sopenharmony_ci igmp_ifc_event(pmc->interface); 19678c2ecf20Sopenharmony_ci#endif 19688c2ecf20Sopenharmony_ci } 19698c2ecf20Sopenharmony_ciout_unlock: 19708c2ecf20Sopenharmony_ci spin_unlock_bh(&pmc->lock); 19718c2ecf20Sopenharmony_ci return err; 19728c2ecf20Sopenharmony_ci} 19738c2ecf20Sopenharmony_ci 19748c2ecf20Sopenharmony_ci/* 19758c2ecf20Sopenharmony_ci * Add multicast single-source filter to the interface list 19768c2ecf20Sopenharmony_ci */ 19778c2ecf20Sopenharmony_cistatic int ip_mc_add1_src(struct ip_mc_list *pmc, int sfmode, 19788c2ecf20Sopenharmony_ci __be32 *psfsrc) 19798c2ecf20Sopenharmony_ci{ 19808c2ecf20Sopenharmony_ci struct ip_sf_list *psf, *psf_prev; 19818c2ecf20Sopenharmony_ci 19828c2ecf20Sopenharmony_ci psf_prev = NULL; 19838c2ecf20Sopenharmony_ci for (psf = pmc->sources; psf; psf = psf->sf_next) { 19848c2ecf20Sopenharmony_ci if (psf->sf_inaddr == *psfsrc) 19858c2ecf20Sopenharmony_ci break; 19868c2ecf20Sopenharmony_ci psf_prev = psf; 19878c2ecf20Sopenharmony_ci } 19888c2ecf20Sopenharmony_ci if (!psf) { 19898c2ecf20Sopenharmony_ci psf = kzalloc(sizeof(*psf), GFP_ATOMIC); 19908c2ecf20Sopenharmony_ci if (!psf) 19918c2ecf20Sopenharmony_ci return -ENOBUFS; 19928c2ecf20Sopenharmony_ci psf->sf_inaddr = *psfsrc; 19938c2ecf20Sopenharmony_ci if (psf_prev) { 19948c2ecf20Sopenharmony_ci psf_prev->sf_next = psf; 19958c2ecf20Sopenharmony_ci } else 19968c2ecf20Sopenharmony_ci pmc->sources = psf; 19978c2ecf20Sopenharmony_ci } 19988c2ecf20Sopenharmony_ci psf->sf_count[sfmode]++; 19998c2ecf20Sopenharmony_ci if (psf->sf_count[sfmode] == 1) { 20008c2ecf20Sopenharmony_ci ip_rt_multicast_event(pmc->interface); 20018c2ecf20Sopenharmony_ci } 20028c2ecf20Sopenharmony_ci return 0; 20038c2ecf20Sopenharmony_ci} 20048c2ecf20Sopenharmony_ci 20058c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_MULTICAST 20068c2ecf20Sopenharmony_cistatic void sf_markstate(struct ip_mc_list *pmc) 20078c2ecf20Sopenharmony_ci{ 20088c2ecf20Sopenharmony_ci struct ip_sf_list *psf; 20098c2ecf20Sopenharmony_ci int mca_xcount = pmc->sfcount[MCAST_EXCLUDE]; 20108c2ecf20Sopenharmony_ci 20118c2ecf20Sopenharmony_ci for (psf = pmc->sources; psf; psf = psf->sf_next) 20128c2ecf20Sopenharmony_ci if (pmc->sfcount[MCAST_EXCLUDE]) { 20138c2ecf20Sopenharmony_ci psf->sf_oldin = mca_xcount == 20148c2ecf20Sopenharmony_ci psf->sf_count[MCAST_EXCLUDE] && 20158c2ecf20Sopenharmony_ci !psf->sf_count[MCAST_INCLUDE]; 20168c2ecf20Sopenharmony_ci } else 20178c2ecf20Sopenharmony_ci psf->sf_oldin = psf->sf_count[MCAST_INCLUDE] != 0; 20188c2ecf20Sopenharmony_ci} 20198c2ecf20Sopenharmony_ci 20208c2ecf20Sopenharmony_cistatic int sf_setstate(struct ip_mc_list *pmc) 20218c2ecf20Sopenharmony_ci{ 20228c2ecf20Sopenharmony_ci struct ip_sf_list *psf, *dpsf; 20238c2ecf20Sopenharmony_ci int mca_xcount = pmc->sfcount[MCAST_EXCLUDE]; 20248c2ecf20Sopenharmony_ci int qrv = pmc->interface->mr_qrv; 20258c2ecf20Sopenharmony_ci int new_in, rv; 20268c2ecf20Sopenharmony_ci 20278c2ecf20Sopenharmony_ci rv = 0; 20288c2ecf20Sopenharmony_ci for (psf = pmc->sources; psf; psf = psf->sf_next) { 20298c2ecf20Sopenharmony_ci if (pmc->sfcount[MCAST_EXCLUDE]) { 20308c2ecf20Sopenharmony_ci new_in = mca_xcount == psf->sf_count[MCAST_EXCLUDE] && 20318c2ecf20Sopenharmony_ci !psf->sf_count[MCAST_INCLUDE]; 20328c2ecf20Sopenharmony_ci } else 20338c2ecf20Sopenharmony_ci new_in = psf->sf_count[MCAST_INCLUDE] != 0; 20348c2ecf20Sopenharmony_ci if (new_in) { 20358c2ecf20Sopenharmony_ci if (!psf->sf_oldin) { 20368c2ecf20Sopenharmony_ci struct ip_sf_list *prev = NULL; 20378c2ecf20Sopenharmony_ci 20388c2ecf20Sopenharmony_ci for (dpsf = pmc->tomb; dpsf; dpsf = dpsf->sf_next) { 20398c2ecf20Sopenharmony_ci if (dpsf->sf_inaddr == psf->sf_inaddr) 20408c2ecf20Sopenharmony_ci break; 20418c2ecf20Sopenharmony_ci prev = dpsf; 20428c2ecf20Sopenharmony_ci } 20438c2ecf20Sopenharmony_ci if (dpsf) { 20448c2ecf20Sopenharmony_ci if (prev) 20458c2ecf20Sopenharmony_ci prev->sf_next = dpsf->sf_next; 20468c2ecf20Sopenharmony_ci else 20478c2ecf20Sopenharmony_ci pmc->tomb = dpsf->sf_next; 20488c2ecf20Sopenharmony_ci kfree(dpsf); 20498c2ecf20Sopenharmony_ci } 20508c2ecf20Sopenharmony_ci psf->sf_crcount = qrv; 20518c2ecf20Sopenharmony_ci rv++; 20528c2ecf20Sopenharmony_ci } 20538c2ecf20Sopenharmony_ci } else if (psf->sf_oldin) { 20548c2ecf20Sopenharmony_ci 20558c2ecf20Sopenharmony_ci psf->sf_crcount = 0; 20568c2ecf20Sopenharmony_ci /* 20578c2ecf20Sopenharmony_ci * add or update "delete" records if an active filter 20588c2ecf20Sopenharmony_ci * is now inactive 20598c2ecf20Sopenharmony_ci */ 20608c2ecf20Sopenharmony_ci for (dpsf = pmc->tomb; dpsf; dpsf = dpsf->sf_next) 20618c2ecf20Sopenharmony_ci if (dpsf->sf_inaddr == psf->sf_inaddr) 20628c2ecf20Sopenharmony_ci break; 20638c2ecf20Sopenharmony_ci if (!dpsf) { 20648c2ecf20Sopenharmony_ci dpsf = kmalloc(sizeof(*dpsf), GFP_ATOMIC); 20658c2ecf20Sopenharmony_ci if (!dpsf) 20668c2ecf20Sopenharmony_ci continue; 20678c2ecf20Sopenharmony_ci *dpsf = *psf; 20688c2ecf20Sopenharmony_ci /* pmc->lock held by callers */ 20698c2ecf20Sopenharmony_ci dpsf->sf_next = pmc->tomb; 20708c2ecf20Sopenharmony_ci pmc->tomb = dpsf; 20718c2ecf20Sopenharmony_ci } 20728c2ecf20Sopenharmony_ci dpsf->sf_crcount = qrv; 20738c2ecf20Sopenharmony_ci rv++; 20748c2ecf20Sopenharmony_ci } 20758c2ecf20Sopenharmony_ci } 20768c2ecf20Sopenharmony_ci return rv; 20778c2ecf20Sopenharmony_ci} 20788c2ecf20Sopenharmony_ci#endif 20798c2ecf20Sopenharmony_ci 20808c2ecf20Sopenharmony_ci/* 20818c2ecf20Sopenharmony_ci * Add multicast source filter list to the interface list 20828c2ecf20Sopenharmony_ci */ 20838c2ecf20Sopenharmony_cistatic int ip_mc_add_src(struct in_device *in_dev, __be32 *pmca, int sfmode, 20848c2ecf20Sopenharmony_ci int sfcount, __be32 *psfsrc, int delta) 20858c2ecf20Sopenharmony_ci{ 20868c2ecf20Sopenharmony_ci struct ip_mc_list *pmc; 20878c2ecf20Sopenharmony_ci int isexclude; 20888c2ecf20Sopenharmony_ci int i, err; 20898c2ecf20Sopenharmony_ci 20908c2ecf20Sopenharmony_ci if (!in_dev) 20918c2ecf20Sopenharmony_ci return -ENODEV; 20928c2ecf20Sopenharmony_ci rcu_read_lock(); 20938c2ecf20Sopenharmony_ci for_each_pmc_rcu(in_dev, pmc) { 20948c2ecf20Sopenharmony_ci if (*pmca == pmc->multiaddr) 20958c2ecf20Sopenharmony_ci break; 20968c2ecf20Sopenharmony_ci } 20978c2ecf20Sopenharmony_ci if (!pmc) { 20988c2ecf20Sopenharmony_ci /* MCA not found?? bug */ 20998c2ecf20Sopenharmony_ci rcu_read_unlock(); 21008c2ecf20Sopenharmony_ci return -ESRCH; 21018c2ecf20Sopenharmony_ci } 21028c2ecf20Sopenharmony_ci spin_lock_bh(&pmc->lock); 21038c2ecf20Sopenharmony_ci rcu_read_unlock(); 21048c2ecf20Sopenharmony_ci 21058c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_MULTICAST 21068c2ecf20Sopenharmony_ci sf_markstate(pmc); 21078c2ecf20Sopenharmony_ci#endif 21088c2ecf20Sopenharmony_ci isexclude = pmc->sfmode == MCAST_EXCLUDE; 21098c2ecf20Sopenharmony_ci if (!delta) 21108c2ecf20Sopenharmony_ci pmc->sfcount[sfmode]++; 21118c2ecf20Sopenharmony_ci err = 0; 21128c2ecf20Sopenharmony_ci for (i = 0; i < sfcount; i++) { 21138c2ecf20Sopenharmony_ci err = ip_mc_add1_src(pmc, sfmode, &psfsrc[i]); 21148c2ecf20Sopenharmony_ci if (err) 21158c2ecf20Sopenharmony_ci break; 21168c2ecf20Sopenharmony_ci } 21178c2ecf20Sopenharmony_ci if (err) { 21188c2ecf20Sopenharmony_ci int j; 21198c2ecf20Sopenharmony_ci 21208c2ecf20Sopenharmony_ci if (!delta) 21218c2ecf20Sopenharmony_ci pmc->sfcount[sfmode]--; 21228c2ecf20Sopenharmony_ci for (j = 0; j < i; j++) 21238c2ecf20Sopenharmony_ci (void) ip_mc_del1_src(pmc, sfmode, &psfsrc[j]); 21248c2ecf20Sopenharmony_ci } else if (isexclude != (pmc->sfcount[MCAST_EXCLUDE] != 0)) { 21258c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_MULTICAST 21268c2ecf20Sopenharmony_ci struct ip_sf_list *psf; 21278c2ecf20Sopenharmony_ci struct net *net = dev_net(pmc->interface->dev); 21288c2ecf20Sopenharmony_ci in_dev = pmc->interface; 21298c2ecf20Sopenharmony_ci#endif 21308c2ecf20Sopenharmony_ci 21318c2ecf20Sopenharmony_ci /* filter mode change */ 21328c2ecf20Sopenharmony_ci if (pmc->sfcount[MCAST_EXCLUDE]) 21338c2ecf20Sopenharmony_ci pmc->sfmode = MCAST_EXCLUDE; 21348c2ecf20Sopenharmony_ci else if (pmc->sfcount[MCAST_INCLUDE]) 21358c2ecf20Sopenharmony_ci pmc->sfmode = MCAST_INCLUDE; 21368c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_MULTICAST 21378c2ecf20Sopenharmony_ci /* else no filters; keep old mode for reports */ 21388c2ecf20Sopenharmony_ci 21398c2ecf20Sopenharmony_ci pmc->crcount = in_dev->mr_qrv ?: READ_ONCE(net->ipv4.sysctl_igmp_qrv); 21408c2ecf20Sopenharmony_ci WRITE_ONCE(in_dev->mr_ifc_count, pmc->crcount); 21418c2ecf20Sopenharmony_ci for (psf = pmc->sources; psf; psf = psf->sf_next) 21428c2ecf20Sopenharmony_ci psf->sf_crcount = 0; 21438c2ecf20Sopenharmony_ci igmp_ifc_event(in_dev); 21448c2ecf20Sopenharmony_ci } else if (sf_setstate(pmc)) { 21458c2ecf20Sopenharmony_ci igmp_ifc_event(in_dev); 21468c2ecf20Sopenharmony_ci#endif 21478c2ecf20Sopenharmony_ci } 21488c2ecf20Sopenharmony_ci spin_unlock_bh(&pmc->lock); 21498c2ecf20Sopenharmony_ci return err; 21508c2ecf20Sopenharmony_ci} 21518c2ecf20Sopenharmony_ci 21528c2ecf20Sopenharmony_cistatic void ip_mc_clear_src(struct ip_mc_list *pmc) 21538c2ecf20Sopenharmony_ci{ 21548c2ecf20Sopenharmony_ci struct ip_sf_list *tomb, *sources; 21558c2ecf20Sopenharmony_ci 21568c2ecf20Sopenharmony_ci spin_lock_bh(&pmc->lock); 21578c2ecf20Sopenharmony_ci tomb = pmc->tomb; 21588c2ecf20Sopenharmony_ci pmc->tomb = NULL; 21598c2ecf20Sopenharmony_ci sources = pmc->sources; 21608c2ecf20Sopenharmony_ci pmc->sources = NULL; 21618c2ecf20Sopenharmony_ci pmc->sfmode = MCAST_EXCLUDE; 21628c2ecf20Sopenharmony_ci pmc->sfcount[MCAST_INCLUDE] = 0; 21638c2ecf20Sopenharmony_ci pmc->sfcount[MCAST_EXCLUDE] = 1; 21648c2ecf20Sopenharmony_ci spin_unlock_bh(&pmc->lock); 21658c2ecf20Sopenharmony_ci 21668c2ecf20Sopenharmony_ci ip_sf_list_clear_all(tomb); 21678c2ecf20Sopenharmony_ci ip_sf_list_clear_all(sources); 21688c2ecf20Sopenharmony_ci} 21698c2ecf20Sopenharmony_ci 21708c2ecf20Sopenharmony_ci/* Join a multicast group 21718c2ecf20Sopenharmony_ci */ 21728c2ecf20Sopenharmony_cistatic int __ip_mc_join_group(struct sock *sk, struct ip_mreqn *imr, 21738c2ecf20Sopenharmony_ci unsigned int mode) 21748c2ecf20Sopenharmony_ci{ 21758c2ecf20Sopenharmony_ci __be32 addr = imr->imr_multiaddr.s_addr; 21768c2ecf20Sopenharmony_ci struct ip_mc_socklist *iml, *i; 21778c2ecf20Sopenharmony_ci struct in_device *in_dev; 21788c2ecf20Sopenharmony_ci struct inet_sock *inet = inet_sk(sk); 21798c2ecf20Sopenharmony_ci struct net *net = sock_net(sk); 21808c2ecf20Sopenharmony_ci int ifindex; 21818c2ecf20Sopenharmony_ci int count = 0; 21828c2ecf20Sopenharmony_ci int err; 21838c2ecf20Sopenharmony_ci 21848c2ecf20Sopenharmony_ci ASSERT_RTNL(); 21858c2ecf20Sopenharmony_ci 21868c2ecf20Sopenharmony_ci if (!ipv4_is_multicast(addr)) 21878c2ecf20Sopenharmony_ci return -EINVAL; 21888c2ecf20Sopenharmony_ci 21898c2ecf20Sopenharmony_ci in_dev = ip_mc_find_dev(net, imr); 21908c2ecf20Sopenharmony_ci 21918c2ecf20Sopenharmony_ci if (!in_dev) { 21928c2ecf20Sopenharmony_ci err = -ENODEV; 21938c2ecf20Sopenharmony_ci goto done; 21948c2ecf20Sopenharmony_ci } 21958c2ecf20Sopenharmony_ci 21968c2ecf20Sopenharmony_ci err = -EADDRINUSE; 21978c2ecf20Sopenharmony_ci ifindex = imr->imr_ifindex; 21988c2ecf20Sopenharmony_ci for_each_pmc_rtnl(inet, i) { 21998c2ecf20Sopenharmony_ci if (i->multi.imr_multiaddr.s_addr == addr && 22008c2ecf20Sopenharmony_ci i->multi.imr_ifindex == ifindex) 22018c2ecf20Sopenharmony_ci goto done; 22028c2ecf20Sopenharmony_ci count++; 22038c2ecf20Sopenharmony_ci } 22048c2ecf20Sopenharmony_ci err = -ENOBUFS; 22058c2ecf20Sopenharmony_ci if (count >= READ_ONCE(net->ipv4.sysctl_igmp_max_memberships)) 22068c2ecf20Sopenharmony_ci goto done; 22078c2ecf20Sopenharmony_ci iml = sock_kmalloc(sk, sizeof(*iml), GFP_KERNEL); 22088c2ecf20Sopenharmony_ci if (!iml) 22098c2ecf20Sopenharmony_ci goto done; 22108c2ecf20Sopenharmony_ci 22118c2ecf20Sopenharmony_ci memcpy(&iml->multi, imr, sizeof(*imr)); 22128c2ecf20Sopenharmony_ci iml->next_rcu = inet->mc_list; 22138c2ecf20Sopenharmony_ci iml->sflist = NULL; 22148c2ecf20Sopenharmony_ci iml->sfmode = mode; 22158c2ecf20Sopenharmony_ci rcu_assign_pointer(inet->mc_list, iml); 22168c2ecf20Sopenharmony_ci ____ip_mc_inc_group(in_dev, addr, mode, GFP_KERNEL); 22178c2ecf20Sopenharmony_ci err = 0; 22188c2ecf20Sopenharmony_cidone: 22198c2ecf20Sopenharmony_ci return err; 22208c2ecf20Sopenharmony_ci} 22218c2ecf20Sopenharmony_ci 22228c2ecf20Sopenharmony_ci/* Join ASM (Any-Source Multicast) group 22238c2ecf20Sopenharmony_ci */ 22248c2ecf20Sopenharmony_ciint ip_mc_join_group(struct sock *sk, struct ip_mreqn *imr) 22258c2ecf20Sopenharmony_ci{ 22268c2ecf20Sopenharmony_ci return __ip_mc_join_group(sk, imr, MCAST_EXCLUDE); 22278c2ecf20Sopenharmony_ci} 22288c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ip_mc_join_group); 22298c2ecf20Sopenharmony_ci 22308c2ecf20Sopenharmony_ci/* Join SSM (Source-Specific Multicast) group 22318c2ecf20Sopenharmony_ci */ 22328c2ecf20Sopenharmony_ciint ip_mc_join_group_ssm(struct sock *sk, struct ip_mreqn *imr, 22338c2ecf20Sopenharmony_ci unsigned int mode) 22348c2ecf20Sopenharmony_ci{ 22358c2ecf20Sopenharmony_ci return __ip_mc_join_group(sk, imr, mode); 22368c2ecf20Sopenharmony_ci} 22378c2ecf20Sopenharmony_ci 22388c2ecf20Sopenharmony_cistatic int ip_mc_leave_src(struct sock *sk, struct ip_mc_socklist *iml, 22398c2ecf20Sopenharmony_ci struct in_device *in_dev) 22408c2ecf20Sopenharmony_ci{ 22418c2ecf20Sopenharmony_ci struct ip_sf_socklist *psf = rtnl_dereference(iml->sflist); 22428c2ecf20Sopenharmony_ci int err; 22438c2ecf20Sopenharmony_ci 22448c2ecf20Sopenharmony_ci if (!psf) { 22458c2ecf20Sopenharmony_ci /* any-source empty exclude case */ 22468c2ecf20Sopenharmony_ci return ip_mc_del_src(in_dev, &iml->multi.imr_multiaddr.s_addr, 22478c2ecf20Sopenharmony_ci iml->sfmode, 0, NULL, 0); 22488c2ecf20Sopenharmony_ci } 22498c2ecf20Sopenharmony_ci err = ip_mc_del_src(in_dev, &iml->multi.imr_multiaddr.s_addr, 22508c2ecf20Sopenharmony_ci iml->sfmode, psf->sl_count, psf->sl_addr, 0); 22518c2ecf20Sopenharmony_ci RCU_INIT_POINTER(iml->sflist, NULL); 22528c2ecf20Sopenharmony_ci /* decrease mem now to avoid the memleak warning */ 22538c2ecf20Sopenharmony_ci atomic_sub(IP_SFLSIZE(psf->sl_max), &sk->sk_omem_alloc); 22548c2ecf20Sopenharmony_ci kfree_rcu(psf, rcu); 22558c2ecf20Sopenharmony_ci return err; 22568c2ecf20Sopenharmony_ci} 22578c2ecf20Sopenharmony_ci 22588c2ecf20Sopenharmony_ciint ip_mc_leave_group(struct sock *sk, struct ip_mreqn *imr) 22598c2ecf20Sopenharmony_ci{ 22608c2ecf20Sopenharmony_ci struct inet_sock *inet = inet_sk(sk); 22618c2ecf20Sopenharmony_ci struct ip_mc_socklist *iml; 22628c2ecf20Sopenharmony_ci struct ip_mc_socklist __rcu **imlp; 22638c2ecf20Sopenharmony_ci struct in_device *in_dev; 22648c2ecf20Sopenharmony_ci struct net *net = sock_net(sk); 22658c2ecf20Sopenharmony_ci __be32 group = imr->imr_multiaddr.s_addr; 22668c2ecf20Sopenharmony_ci u32 ifindex; 22678c2ecf20Sopenharmony_ci int ret = -EADDRNOTAVAIL; 22688c2ecf20Sopenharmony_ci 22698c2ecf20Sopenharmony_ci ASSERT_RTNL(); 22708c2ecf20Sopenharmony_ci 22718c2ecf20Sopenharmony_ci in_dev = ip_mc_find_dev(net, imr); 22728c2ecf20Sopenharmony_ci if (!imr->imr_ifindex && !imr->imr_address.s_addr && !in_dev) { 22738c2ecf20Sopenharmony_ci ret = -ENODEV; 22748c2ecf20Sopenharmony_ci goto out; 22758c2ecf20Sopenharmony_ci } 22768c2ecf20Sopenharmony_ci ifindex = imr->imr_ifindex; 22778c2ecf20Sopenharmony_ci for (imlp = &inet->mc_list; 22788c2ecf20Sopenharmony_ci (iml = rtnl_dereference(*imlp)) != NULL; 22798c2ecf20Sopenharmony_ci imlp = &iml->next_rcu) { 22808c2ecf20Sopenharmony_ci if (iml->multi.imr_multiaddr.s_addr != group) 22818c2ecf20Sopenharmony_ci continue; 22828c2ecf20Sopenharmony_ci if (ifindex) { 22838c2ecf20Sopenharmony_ci if (iml->multi.imr_ifindex != ifindex) 22848c2ecf20Sopenharmony_ci continue; 22858c2ecf20Sopenharmony_ci } else if (imr->imr_address.s_addr && imr->imr_address.s_addr != 22868c2ecf20Sopenharmony_ci iml->multi.imr_address.s_addr) 22878c2ecf20Sopenharmony_ci continue; 22888c2ecf20Sopenharmony_ci 22898c2ecf20Sopenharmony_ci (void) ip_mc_leave_src(sk, iml, in_dev); 22908c2ecf20Sopenharmony_ci 22918c2ecf20Sopenharmony_ci *imlp = iml->next_rcu; 22928c2ecf20Sopenharmony_ci 22938c2ecf20Sopenharmony_ci if (in_dev) 22948c2ecf20Sopenharmony_ci ip_mc_dec_group(in_dev, group); 22958c2ecf20Sopenharmony_ci 22968c2ecf20Sopenharmony_ci /* decrease mem now to avoid the memleak warning */ 22978c2ecf20Sopenharmony_ci atomic_sub(sizeof(*iml), &sk->sk_omem_alloc); 22988c2ecf20Sopenharmony_ci kfree_rcu(iml, rcu); 22998c2ecf20Sopenharmony_ci return 0; 23008c2ecf20Sopenharmony_ci } 23018c2ecf20Sopenharmony_ciout: 23028c2ecf20Sopenharmony_ci return ret; 23038c2ecf20Sopenharmony_ci} 23048c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ip_mc_leave_group); 23058c2ecf20Sopenharmony_ci 23068c2ecf20Sopenharmony_ciint ip_mc_source(int add, int omode, struct sock *sk, struct 23078c2ecf20Sopenharmony_ci ip_mreq_source *mreqs, int ifindex) 23088c2ecf20Sopenharmony_ci{ 23098c2ecf20Sopenharmony_ci int err; 23108c2ecf20Sopenharmony_ci struct ip_mreqn imr; 23118c2ecf20Sopenharmony_ci __be32 addr = mreqs->imr_multiaddr; 23128c2ecf20Sopenharmony_ci struct ip_mc_socklist *pmc; 23138c2ecf20Sopenharmony_ci struct in_device *in_dev = NULL; 23148c2ecf20Sopenharmony_ci struct inet_sock *inet = inet_sk(sk); 23158c2ecf20Sopenharmony_ci struct ip_sf_socklist *psl; 23168c2ecf20Sopenharmony_ci struct net *net = sock_net(sk); 23178c2ecf20Sopenharmony_ci int leavegroup = 0; 23188c2ecf20Sopenharmony_ci int i, j, rv; 23198c2ecf20Sopenharmony_ci 23208c2ecf20Sopenharmony_ci if (!ipv4_is_multicast(addr)) 23218c2ecf20Sopenharmony_ci return -EINVAL; 23228c2ecf20Sopenharmony_ci 23238c2ecf20Sopenharmony_ci ASSERT_RTNL(); 23248c2ecf20Sopenharmony_ci 23258c2ecf20Sopenharmony_ci imr.imr_multiaddr.s_addr = mreqs->imr_multiaddr; 23268c2ecf20Sopenharmony_ci imr.imr_address.s_addr = mreqs->imr_interface; 23278c2ecf20Sopenharmony_ci imr.imr_ifindex = ifindex; 23288c2ecf20Sopenharmony_ci in_dev = ip_mc_find_dev(net, &imr); 23298c2ecf20Sopenharmony_ci 23308c2ecf20Sopenharmony_ci if (!in_dev) { 23318c2ecf20Sopenharmony_ci err = -ENODEV; 23328c2ecf20Sopenharmony_ci goto done; 23338c2ecf20Sopenharmony_ci } 23348c2ecf20Sopenharmony_ci err = -EADDRNOTAVAIL; 23358c2ecf20Sopenharmony_ci 23368c2ecf20Sopenharmony_ci for_each_pmc_rtnl(inet, pmc) { 23378c2ecf20Sopenharmony_ci if ((pmc->multi.imr_multiaddr.s_addr == 23388c2ecf20Sopenharmony_ci imr.imr_multiaddr.s_addr) && 23398c2ecf20Sopenharmony_ci (pmc->multi.imr_ifindex == imr.imr_ifindex)) 23408c2ecf20Sopenharmony_ci break; 23418c2ecf20Sopenharmony_ci } 23428c2ecf20Sopenharmony_ci if (!pmc) { /* must have a prior join */ 23438c2ecf20Sopenharmony_ci err = -EINVAL; 23448c2ecf20Sopenharmony_ci goto done; 23458c2ecf20Sopenharmony_ci } 23468c2ecf20Sopenharmony_ci /* if a source filter was set, must be the same mode as before */ 23478c2ecf20Sopenharmony_ci if (pmc->sflist) { 23488c2ecf20Sopenharmony_ci if (pmc->sfmode != omode) { 23498c2ecf20Sopenharmony_ci err = -EINVAL; 23508c2ecf20Sopenharmony_ci goto done; 23518c2ecf20Sopenharmony_ci } 23528c2ecf20Sopenharmony_ci } else if (pmc->sfmode != omode) { 23538c2ecf20Sopenharmony_ci /* allow mode switches for empty-set filters */ 23548c2ecf20Sopenharmony_ci ip_mc_add_src(in_dev, &mreqs->imr_multiaddr, omode, 0, NULL, 0); 23558c2ecf20Sopenharmony_ci ip_mc_del_src(in_dev, &mreqs->imr_multiaddr, pmc->sfmode, 0, 23568c2ecf20Sopenharmony_ci NULL, 0); 23578c2ecf20Sopenharmony_ci pmc->sfmode = omode; 23588c2ecf20Sopenharmony_ci } 23598c2ecf20Sopenharmony_ci 23608c2ecf20Sopenharmony_ci psl = rtnl_dereference(pmc->sflist); 23618c2ecf20Sopenharmony_ci if (!add) { 23628c2ecf20Sopenharmony_ci if (!psl) 23638c2ecf20Sopenharmony_ci goto done; /* err = -EADDRNOTAVAIL */ 23648c2ecf20Sopenharmony_ci rv = !0; 23658c2ecf20Sopenharmony_ci for (i = 0; i < psl->sl_count; i++) { 23668c2ecf20Sopenharmony_ci rv = memcmp(&psl->sl_addr[i], &mreqs->imr_sourceaddr, 23678c2ecf20Sopenharmony_ci sizeof(__be32)); 23688c2ecf20Sopenharmony_ci if (rv == 0) 23698c2ecf20Sopenharmony_ci break; 23708c2ecf20Sopenharmony_ci } 23718c2ecf20Sopenharmony_ci if (rv) /* source not found */ 23728c2ecf20Sopenharmony_ci goto done; /* err = -EADDRNOTAVAIL */ 23738c2ecf20Sopenharmony_ci 23748c2ecf20Sopenharmony_ci /* special case - (INCLUDE, empty) == LEAVE_GROUP */ 23758c2ecf20Sopenharmony_ci if (psl->sl_count == 1 && omode == MCAST_INCLUDE) { 23768c2ecf20Sopenharmony_ci leavegroup = 1; 23778c2ecf20Sopenharmony_ci goto done; 23788c2ecf20Sopenharmony_ci } 23798c2ecf20Sopenharmony_ci 23808c2ecf20Sopenharmony_ci /* update the interface filter */ 23818c2ecf20Sopenharmony_ci ip_mc_del_src(in_dev, &mreqs->imr_multiaddr, omode, 1, 23828c2ecf20Sopenharmony_ci &mreqs->imr_sourceaddr, 1); 23838c2ecf20Sopenharmony_ci 23848c2ecf20Sopenharmony_ci for (j = i+1; j < psl->sl_count; j++) 23858c2ecf20Sopenharmony_ci psl->sl_addr[j-1] = psl->sl_addr[j]; 23868c2ecf20Sopenharmony_ci psl->sl_count--; 23878c2ecf20Sopenharmony_ci err = 0; 23888c2ecf20Sopenharmony_ci goto done; 23898c2ecf20Sopenharmony_ci } 23908c2ecf20Sopenharmony_ci /* else, add a new source to the filter */ 23918c2ecf20Sopenharmony_ci 23928c2ecf20Sopenharmony_ci if (psl && psl->sl_count >= READ_ONCE(net->ipv4.sysctl_igmp_max_msf)) { 23938c2ecf20Sopenharmony_ci err = -ENOBUFS; 23948c2ecf20Sopenharmony_ci goto done; 23958c2ecf20Sopenharmony_ci } 23968c2ecf20Sopenharmony_ci if (!psl || psl->sl_count == psl->sl_max) { 23978c2ecf20Sopenharmony_ci struct ip_sf_socklist *newpsl; 23988c2ecf20Sopenharmony_ci int count = IP_SFBLOCK; 23998c2ecf20Sopenharmony_ci 24008c2ecf20Sopenharmony_ci if (psl) 24018c2ecf20Sopenharmony_ci count += psl->sl_max; 24028c2ecf20Sopenharmony_ci newpsl = sock_kmalloc(sk, IP_SFLSIZE(count), GFP_KERNEL); 24038c2ecf20Sopenharmony_ci if (!newpsl) { 24048c2ecf20Sopenharmony_ci err = -ENOBUFS; 24058c2ecf20Sopenharmony_ci goto done; 24068c2ecf20Sopenharmony_ci } 24078c2ecf20Sopenharmony_ci newpsl->sl_max = count; 24088c2ecf20Sopenharmony_ci newpsl->sl_count = count - IP_SFBLOCK; 24098c2ecf20Sopenharmony_ci if (psl) { 24108c2ecf20Sopenharmony_ci for (i = 0; i < psl->sl_count; i++) 24118c2ecf20Sopenharmony_ci newpsl->sl_addr[i] = psl->sl_addr[i]; 24128c2ecf20Sopenharmony_ci /* decrease mem now to avoid the memleak warning */ 24138c2ecf20Sopenharmony_ci atomic_sub(IP_SFLSIZE(psl->sl_max), &sk->sk_omem_alloc); 24148c2ecf20Sopenharmony_ci } 24158c2ecf20Sopenharmony_ci rcu_assign_pointer(pmc->sflist, newpsl); 24168c2ecf20Sopenharmony_ci if (psl) 24178c2ecf20Sopenharmony_ci kfree_rcu(psl, rcu); 24188c2ecf20Sopenharmony_ci psl = newpsl; 24198c2ecf20Sopenharmony_ci } 24208c2ecf20Sopenharmony_ci rv = 1; /* > 0 for insert logic below if sl_count is 0 */ 24218c2ecf20Sopenharmony_ci for (i = 0; i < psl->sl_count; i++) { 24228c2ecf20Sopenharmony_ci rv = memcmp(&psl->sl_addr[i], &mreqs->imr_sourceaddr, 24238c2ecf20Sopenharmony_ci sizeof(__be32)); 24248c2ecf20Sopenharmony_ci if (rv == 0) 24258c2ecf20Sopenharmony_ci break; 24268c2ecf20Sopenharmony_ci } 24278c2ecf20Sopenharmony_ci if (rv == 0) /* address already there is an error */ 24288c2ecf20Sopenharmony_ci goto done; 24298c2ecf20Sopenharmony_ci for (j = psl->sl_count-1; j >= i; j--) 24308c2ecf20Sopenharmony_ci psl->sl_addr[j+1] = psl->sl_addr[j]; 24318c2ecf20Sopenharmony_ci psl->sl_addr[i] = mreqs->imr_sourceaddr; 24328c2ecf20Sopenharmony_ci psl->sl_count++; 24338c2ecf20Sopenharmony_ci err = 0; 24348c2ecf20Sopenharmony_ci /* update the interface list */ 24358c2ecf20Sopenharmony_ci ip_mc_add_src(in_dev, &mreqs->imr_multiaddr, omode, 1, 24368c2ecf20Sopenharmony_ci &mreqs->imr_sourceaddr, 1); 24378c2ecf20Sopenharmony_cidone: 24388c2ecf20Sopenharmony_ci if (leavegroup) 24398c2ecf20Sopenharmony_ci err = ip_mc_leave_group(sk, &imr); 24408c2ecf20Sopenharmony_ci return err; 24418c2ecf20Sopenharmony_ci} 24428c2ecf20Sopenharmony_ci 24438c2ecf20Sopenharmony_ciint ip_mc_msfilter(struct sock *sk, struct ip_msfilter *msf, int ifindex) 24448c2ecf20Sopenharmony_ci{ 24458c2ecf20Sopenharmony_ci int err = 0; 24468c2ecf20Sopenharmony_ci struct ip_mreqn imr; 24478c2ecf20Sopenharmony_ci __be32 addr = msf->imsf_multiaddr; 24488c2ecf20Sopenharmony_ci struct ip_mc_socklist *pmc; 24498c2ecf20Sopenharmony_ci struct in_device *in_dev; 24508c2ecf20Sopenharmony_ci struct inet_sock *inet = inet_sk(sk); 24518c2ecf20Sopenharmony_ci struct ip_sf_socklist *newpsl, *psl; 24528c2ecf20Sopenharmony_ci struct net *net = sock_net(sk); 24538c2ecf20Sopenharmony_ci int leavegroup = 0; 24548c2ecf20Sopenharmony_ci 24558c2ecf20Sopenharmony_ci if (!ipv4_is_multicast(addr)) 24568c2ecf20Sopenharmony_ci return -EINVAL; 24578c2ecf20Sopenharmony_ci if (msf->imsf_fmode != MCAST_INCLUDE && 24588c2ecf20Sopenharmony_ci msf->imsf_fmode != MCAST_EXCLUDE) 24598c2ecf20Sopenharmony_ci return -EINVAL; 24608c2ecf20Sopenharmony_ci 24618c2ecf20Sopenharmony_ci ASSERT_RTNL(); 24628c2ecf20Sopenharmony_ci 24638c2ecf20Sopenharmony_ci imr.imr_multiaddr.s_addr = msf->imsf_multiaddr; 24648c2ecf20Sopenharmony_ci imr.imr_address.s_addr = msf->imsf_interface; 24658c2ecf20Sopenharmony_ci imr.imr_ifindex = ifindex; 24668c2ecf20Sopenharmony_ci in_dev = ip_mc_find_dev(net, &imr); 24678c2ecf20Sopenharmony_ci 24688c2ecf20Sopenharmony_ci if (!in_dev) { 24698c2ecf20Sopenharmony_ci err = -ENODEV; 24708c2ecf20Sopenharmony_ci goto done; 24718c2ecf20Sopenharmony_ci } 24728c2ecf20Sopenharmony_ci 24738c2ecf20Sopenharmony_ci /* special case - (INCLUDE, empty) == LEAVE_GROUP */ 24748c2ecf20Sopenharmony_ci if (msf->imsf_fmode == MCAST_INCLUDE && msf->imsf_numsrc == 0) { 24758c2ecf20Sopenharmony_ci leavegroup = 1; 24768c2ecf20Sopenharmony_ci goto done; 24778c2ecf20Sopenharmony_ci } 24788c2ecf20Sopenharmony_ci 24798c2ecf20Sopenharmony_ci for_each_pmc_rtnl(inet, pmc) { 24808c2ecf20Sopenharmony_ci if (pmc->multi.imr_multiaddr.s_addr == msf->imsf_multiaddr && 24818c2ecf20Sopenharmony_ci pmc->multi.imr_ifindex == imr.imr_ifindex) 24828c2ecf20Sopenharmony_ci break; 24838c2ecf20Sopenharmony_ci } 24848c2ecf20Sopenharmony_ci if (!pmc) { /* must have a prior join */ 24858c2ecf20Sopenharmony_ci err = -EINVAL; 24868c2ecf20Sopenharmony_ci goto done; 24878c2ecf20Sopenharmony_ci } 24888c2ecf20Sopenharmony_ci if (msf->imsf_numsrc) { 24898c2ecf20Sopenharmony_ci newpsl = sock_kmalloc(sk, IP_SFLSIZE(msf->imsf_numsrc), 24908c2ecf20Sopenharmony_ci GFP_KERNEL); 24918c2ecf20Sopenharmony_ci if (!newpsl) { 24928c2ecf20Sopenharmony_ci err = -ENOBUFS; 24938c2ecf20Sopenharmony_ci goto done; 24948c2ecf20Sopenharmony_ci } 24958c2ecf20Sopenharmony_ci newpsl->sl_max = newpsl->sl_count = msf->imsf_numsrc; 24968c2ecf20Sopenharmony_ci memcpy(newpsl->sl_addr, msf->imsf_slist, 24978c2ecf20Sopenharmony_ci msf->imsf_numsrc * sizeof(msf->imsf_slist[0])); 24988c2ecf20Sopenharmony_ci err = ip_mc_add_src(in_dev, &msf->imsf_multiaddr, 24998c2ecf20Sopenharmony_ci msf->imsf_fmode, newpsl->sl_count, newpsl->sl_addr, 0); 25008c2ecf20Sopenharmony_ci if (err) { 25018c2ecf20Sopenharmony_ci sock_kfree_s(sk, newpsl, IP_SFLSIZE(newpsl->sl_max)); 25028c2ecf20Sopenharmony_ci goto done; 25038c2ecf20Sopenharmony_ci } 25048c2ecf20Sopenharmony_ci } else { 25058c2ecf20Sopenharmony_ci newpsl = NULL; 25068c2ecf20Sopenharmony_ci (void) ip_mc_add_src(in_dev, &msf->imsf_multiaddr, 25078c2ecf20Sopenharmony_ci msf->imsf_fmode, 0, NULL, 0); 25088c2ecf20Sopenharmony_ci } 25098c2ecf20Sopenharmony_ci psl = rtnl_dereference(pmc->sflist); 25108c2ecf20Sopenharmony_ci if (psl) { 25118c2ecf20Sopenharmony_ci (void) ip_mc_del_src(in_dev, &msf->imsf_multiaddr, pmc->sfmode, 25128c2ecf20Sopenharmony_ci psl->sl_count, psl->sl_addr, 0); 25138c2ecf20Sopenharmony_ci /* decrease mem now to avoid the memleak warning */ 25148c2ecf20Sopenharmony_ci atomic_sub(IP_SFLSIZE(psl->sl_max), &sk->sk_omem_alloc); 25158c2ecf20Sopenharmony_ci } else { 25168c2ecf20Sopenharmony_ci (void) ip_mc_del_src(in_dev, &msf->imsf_multiaddr, pmc->sfmode, 25178c2ecf20Sopenharmony_ci 0, NULL, 0); 25188c2ecf20Sopenharmony_ci } 25198c2ecf20Sopenharmony_ci rcu_assign_pointer(pmc->sflist, newpsl); 25208c2ecf20Sopenharmony_ci if (psl) 25218c2ecf20Sopenharmony_ci kfree_rcu(psl, rcu); 25228c2ecf20Sopenharmony_ci pmc->sfmode = msf->imsf_fmode; 25238c2ecf20Sopenharmony_ci err = 0; 25248c2ecf20Sopenharmony_cidone: 25258c2ecf20Sopenharmony_ci if (leavegroup) 25268c2ecf20Sopenharmony_ci err = ip_mc_leave_group(sk, &imr); 25278c2ecf20Sopenharmony_ci return err; 25288c2ecf20Sopenharmony_ci} 25298c2ecf20Sopenharmony_ci 25308c2ecf20Sopenharmony_ciint ip_mc_msfget(struct sock *sk, struct ip_msfilter *msf, 25318c2ecf20Sopenharmony_ci struct ip_msfilter __user *optval, int __user *optlen) 25328c2ecf20Sopenharmony_ci{ 25338c2ecf20Sopenharmony_ci int err, len, count, copycount; 25348c2ecf20Sopenharmony_ci struct ip_mreqn imr; 25358c2ecf20Sopenharmony_ci __be32 addr = msf->imsf_multiaddr; 25368c2ecf20Sopenharmony_ci struct ip_mc_socklist *pmc; 25378c2ecf20Sopenharmony_ci struct in_device *in_dev; 25388c2ecf20Sopenharmony_ci struct inet_sock *inet = inet_sk(sk); 25398c2ecf20Sopenharmony_ci struct ip_sf_socklist *psl; 25408c2ecf20Sopenharmony_ci struct net *net = sock_net(sk); 25418c2ecf20Sopenharmony_ci 25428c2ecf20Sopenharmony_ci ASSERT_RTNL(); 25438c2ecf20Sopenharmony_ci 25448c2ecf20Sopenharmony_ci if (!ipv4_is_multicast(addr)) 25458c2ecf20Sopenharmony_ci return -EINVAL; 25468c2ecf20Sopenharmony_ci 25478c2ecf20Sopenharmony_ci imr.imr_multiaddr.s_addr = msf->imsf_multiaddr; 25488c2ecf20Sopenharmony_ci imr.imr_address.s_addr = msf->imsf_interface; 25498c2ecf20Sopenharmony_ci imr.imr_ifindex = 0; 25508c2ecf20Sopenharmony_ci in_dev = ip_mc_find_dev(net, &imr); 25518c2ecf20Sopenharmony_ci 25528c2ecf20Sopenharmony_ci if (!in_dev) { 25538c2ecf20Sopenharmony_ci err = -ENODEV; 25548c2ecf20Sopenharmony_ci goto done; 25558c2ecf20Sopenharmony_ci } 25568c2ecf20Sopenharmony_ci err = -EADDRNOTAVAIL; 25578c2ecf20Sopenharmony_ci 25588c2ecf20Sopenharmony_ci for_each_pmc_rtnl(inet, pmc) { 25598c2ecf20Sopenharmony_ci if (pmc->multi.imr_multiaddr.s_addr == msf->imsf_multiaddr && 25608c2ecf20Sopenharmony_ci pmc->multi.imr_ifindex == imr.imr_ifindex) 25618c2ecf20Sopenharmony_ci break; 25628c2ecf20Sopenharmony_ci } 25638c2ecf20Sopenharmony_ci if (!pmc) /* must have a prior join */ 25648c2ecf20Sopenharmony_ci goto done; 25658c2ecf20Sopenharmony_ci msf->imsf_fmode = pmc->sfmode; 25668c2ecf20Sopenharmony_ci psl = rtnl_dereference(pmc->sflist); 25678c2ecf20Sopenharmony_ci if (!psl) { 25688c2ecf20Sopenharmony_ci len = 0; 25698c2ecf20Sopenharmony_ci count = 0; 25708c2ecf20Sopenharmony_ci } else { 25718c2ecf20Sopenharmony_ci count = psl->sl_count; 25728c2ecf20Sopenharmony_ci } 25738c2ecf20Sopenharmony_ci copycount = count < msf->imsf_numsrc ? count : msf->imsf_numsrc; 25748c2ecf20Sopenharmony_ci len = copycount * sizeof(psl->sl_addr[0]); 25758c2ecf20Sopenharmony_ci msf->imsf_numsrc = count; 25768c2ecf20Sopenharmony_ci if (put_user(IP_MSFILTER_SIZE(copycount), optlen) || 25778c2ecf20Sopenharmony_ci copy_to_user(optval, msf, IP_MSFILTER_SIZE(0))) { 25788c2ecf20Sopenharmony_ci return -EFAULT; 25798c2ecf20Sopenharmony_ci } 25808c2ecf20Sopenharmony_ci if (len && 25818c2ecf20Sopenharmony_ci copy_to_user(&optval->imsf_slist[0], psl->sl_addr, len)) 25828c2ecf20Sopenharmony_ci return -EFAULT; 25838c2ecf20Sopenharmony_ci return 0; 25848c2ecf20Sopenharmony_cidone: 25858c2ecf20Sopenharmony_ci return err; 25868c2ecf20Sopenharmony_ci} 25878c2ecf20Sopenharmony_ci 25888c2ecf20Sopenharmony_ciint ip_mc_gsfget(struct sock *sk, struct group_filter *gsf, 25898c2ecf20Sopenharmony_ci struct sockaddr_storage __user *p) 25908c2ecf20Sopenharmony_ci{ 25918c2ecf20Sopenharmony_ci int i, count, copycount; 25928c2ecf20Sopenharmony_ci struct sockaddr_in *psin; 25938c2ecf20Sopenharmony_ci __be32 addr; 25948c2ecf20Sopenharmony_ci struct ip_mc_socklist *pmc; 25958c2ecf20Sopenharmony_ci struct inet_sock *inet = inet_sk(sk); 25968c2ecf20Sopenharmony_ci struct ip_sf_socklist *psl; 25978c2ecf20Sopenharmony_ci 25988c2ecf20Sopenharmony_ci ASSERT_RTNL(); 25998c2ecf20Sopenharmony_ci 26008c2ecf20Sopenharmony_ci psin = (struct sockaddr_in *)&gsf->gf_group; 26018c2ecf20Sopenharmony_ci if (psin->sin_family != AF_INET) 26028c2ecf20Sopenharmony_ci return -EINVAL; 26038c2ecf20Sopenharmony_ci addr = psin->sin_addr.s_addr; 26048c2ecf20Sopenharmony_ci if (!ipv4_is_multicast(addr)) 26058c2ecf20Sopenharmony_ci return -EINVAL; 26068c2ecf20Sopenharmony_ci 26078c2ecf20Sopenharmony_ci for_each_pmc_rtnl(inet, pmc) { 26088c2ecf20Sopenharmony_ci if (pmc->multi.imr_multiaddr.s_addr == addr && 26098c2ecf20Sopenharmony_ci pmc->multi.imr_ifindex == gsf->gf_interface) 26108c2ecf20Sopenharmony_ci break; 26118c2ecf20Sopenharmony_ci } 26128c2ecf20Sopenharmony_ci if (!pmc) /* must have a prior join */ 26138c2ecf20Sopenharmony_ci return -EADDRNOTAVAIL; 26148c2ecf20Sopenharmony_ci gsf->gf_fmode = pmc->sfmode; 26158c2ecf20Sopenharmony_ci psl = rtnl_dereference(pmc->sflist); 26168c2ecf20Sopenharmony_ci count = psl ? psl->sl_count : 0; 26178c2ecf20Sopenharmony_ci copycount = count < gsf->gf_numsrc ? count : gsf->gf_numsrc; 26188c2ecf20Sopenharmony_ci gsf->gf_numsrc = count; 26198c2ecf20Sopenharmony_ci for (i = 0; i < copycount; i++, p++) { 26208c2ecf20Sopenharmony_ci struct sockaddr_storage ss; 26218c2ecf20Sopenharmony_ci 26228c2ecf20Sopenharmony_ci psin = (struct sockaddr_in *)&ss; 26238c2ecf20Sopenharmony_ci memset(&ss, 0, sizeof(ss)); 26248c2ecf20Sopenharmony_ci psin->sin_family = AF_INET; 26258c2ecf20Sopenharmony_ci psin->sin_addr.s_addr = psl->sl_addr[i]; 26268c2ecf20Sopenharmony_ci if (copy_to_user(p, &ss, sizeof(ss))) 26278c2ecf20Sopenharmony_ci return -EFAULT; 26288c2ecf20Sopenharmony_ci } 26298c2ecf20Sopenharmony_ci return 0; 26308c2ecf20Sopenharmony_ci} 26318c2ecf20Sopenharmony_ci 26328c2ecf20Sopenharmony_ci/* 26338c2ecf20Sopenharmony_ci * check if a multicast source filter allows delivery for a given <src,dst,intf> 26348c2ecf20Sopenharmony_ci */ 26358c2ecf20Sopenharmony_ciint ip_mc_sf_allow(struct sock *sk, __be32 loc_addr, __be32 rmt_addr, 26368c2ecf20Sopenharmony_ci int dif, int sdif) 26378c2ecf20Sopenharmony_ci{ 26388c2ecf20Sopenharmony_ci struct inet_sock *inet = inet_sk(sk); 26398c2ecf20Sopenharmony_ci struct ip_mc_socklist *pmc; 26408c2ecf20Sopenharmony_ci struct ip_sf_socklist *psl; 26418c2ecf20Sopenharmony_ci int i; 26428c2ecf20Sopenharmony_ci int ret; 26438c2ecf20Sopenharmony_ci 26448c2ecf20Sopenharmony_ci ret = 1; 26458c2ecf20Sopenharmony_ci if (!ipv4_is_multicast(loc_addr)) 26468c2ecf20Sopenharmony_ci goto out; 26478c2ecf20Sopenharmony_ci 26488c2ecf20Sopenharmony_ci rcu_read_lock(); 26498c2ecf20Sopenharmony_ci for_each_pmc_rcu(inet, pmc) { 26508c2ecf20Sopenharmony_ci if (pmc->multi.imr_multiaddr.s_addr == loc_addr && 26518c2ecf20Sopenharmony_ci (pmc->multi.imr_ifindex == dif || 26528c2ecf20Sopenharmony_ci (sdif && pmc->multi.imr_ifindex == sdif))) 26538c2ecf20Sopenharmony_ci break; 26548c2ecf20Sopenharmony_ci } 26558c2ecf20Sopenharmony_ci ret = inet->mc_all; 26568c2ecf20Sopenharmony_ci if (!pmc) 26578c2ecf20Sopenharmony_ci goto unlock; 26588c2ecf20Sopenharmony_ci psl = rcu_dereference(pmc->sflist); 26598c2ecf20Sopenharmony_ci ret = (pmc->sfmode == MCAST_EXCLUDE); 26608c2ecf20Sopenharmony_ci if (!psl) 26618c2ecf20Sopenharmony_ci goto unlock; 26628c2ecf20Sopenharmony_ci 26638c2ecf20Sopenharmony_ci for (i = 0; i < psl->sl_count; i++) { 26648c2ecf20Sopenharmony_ci if (psl->sl_addr[i] == rmt_addr) 26658c2ecf20Sopenharmony_ci break; 26668c2ecf20Sopenharmony_ci } 26678c2ecf20Sopenharmony_ci ret = 0; 26688c2ecf20Sopenharmony_ci if (pmc->sfmode == MCAST_INCLUDE && i >= psl->sl_count) 26698c2ecf20Sopenharmony_ci goto unlock; 26708c2ecf20Sopenharmony_ci if (pmc->sfmode == MCAST_EXCLUDE && i < psl->sl_count) 26718c2ecf20Sopenharmony_ci goto unlock; 26728c2ecf20Sopenharmony_ci ret = 1; 26738c2ecf20Sopenharmony_ciunlock: 26748c2ecf20Sopenharmony_ci rcu_read_unlock(); 26758c2ecf20Sopenharmony_ciout: 26768c2ecf20Sopenharmony_ci return ret; 26778c2ecf20Sopenharmony_ci} 26788c2ecf20Sopenharmony_ci 26798c2ecf20Sopenharmony_ci/* 26808c2ecf20Sopenharmony_ci * A socket is closing. 26818c2ecf20Sopenharmony_ci */ 26828c2ecf20Sopenharmony_ci 26838c2ecf20Sopenharmony_civoid ip_mc_drop_socket(struct sock *sk) 26848c2ecf20Sopenharmony_ci{ 26858c2ecf20Sopenharmony_ci struct inet_sock *inet = inet_sk(sk); 26868c2ecf20Sopenharmony_ci struct ip_mc_socklist *iml; 26878c2ecf20Sopenharmony_ci struct net *net = sock_net(sk); 26888c2ecf20Sopenharmony_ci 26898c2ecf20Sopenharmony_ci if (!inet->mc_list) 26908c2ecf20Sopenharmony_ci return; 26918c2ecf20Sopenharmony_ci 26928c2ecf20Sopenharmony_ci rtnl_lock(); 26938c2ecf20Sopenharmony_ci while ((iml = rtnl_dereference(inet->mc_list)) != NULL) { 26948c2ecf20Sopenharmony_ci struct in_device *in_dev; 26958c2ecf20Sopenharmony_ci 26968c2ecf20Sopenharmony_ci inet->mc_list = iml->next_rcu; 26978c2ecf20Sopenharmony_ci in_dev = inetdev_by_index(net, iml->multi.imr_ifindex); 26988c2ecf20Sopenharmony_ci (void) ip_mc_leave_src(sk, iml, in_dev); 26998c2ecf20Sopenharmony_ci if (in_dev) 27008c2ecf20Sopenharmony_ci ip_mc_dec_group(in_dev, iml->multi.imr_multiaddr.s_addr); 27018c2ecf20Sopenharmony_ci /* decrease mem now to avoid the memleak warning */ 27028c2ecf20Sopenharmony_ci atomic_sub(sizeof(*iml), &sk->sk_omem_alloc); 27038c2ecf20Sopenharmony_ci kfree_rcu(iml, rcu); 27048c2ecf20Sopenharmony_ci } 27058c2ecf20Sopenharmony_ci rtnl_unlock(); 27068c2ecf20Sopenharmony_ci} 27078c2ecf20Sopenharmony_ci 27088c2ecf20Sopenharmony_ci/* called with rcu_read_lock() */ 27098c2ecf20Sopenharmony_ciint ip_check_mc_rcu(struct in_device *in_dev, __be32 mc_addr, __be32 src_addr, u8 proto) 27108c2ecf20Sopenharmony_ci{ 27118c2ecf20Sopenharmony_ci struct ip_mc_list *im; 27128c2ecf20Sopenharmony_ci struct ip_mc_list __rcu **mc_hash; 27138c2ecf20Sopenharmony_ci struct ip_sf_list *psf; 27148c2ecf20Sopenharmony_ci int rv = 0; 27158c2ecf20Sopenharmony_ci 27168c2ecf20Sopenharmony_ci mc_hash = rcu_dereference(in_dev->mc_hash); 27178c2ecf20Sopenharmony_ci if (mc_hash) { 27188c2ecf20Sopenharmony_ci u32 hash = hash_32((__force u32)mc_addr, MC_HASH_SZ_LOG); 27198c2ecf20Sopenharmony_ci 27208c2ecf20Sopenharmony_ci for (im = rcu_dereference(mc_hash[hash]); 27218c2ecf20Sopenharmony_ci im != NULL; 27228c2ecf20Sopenharmony_ci im = rcu_dereference(im->next_hash)) { 27238c2ecf20Sopenharmony_ci if (im->multiaddr == mc_addr) 27248c2ecf20Sopenharmony_ci break; 27258c2ecf20Sopenharmony_ci } 27268c2ecf20Sopenharmony_ci } else { 27278c2ecf20Sopenharmony_ci for_each_pmc_rcu(in_dev, im) { 27288c2ecf20Sopenharmony_ci if (im->multiaddr == mc_addr) 27298c2ecf20Sopenharmony_ci break; 27308c2ecf20Sopenharmony_ci } 27318c2ecf20Sopenharmony_ci } 27328c2ecf20Sopenharmony_ci if (im && proto == IPPROTO_IGMP) { 27338c2ecf20Sopenharmony_ci rv = 1; 27348c2ecf20Sopenharmony_ci } else if (im) { 27358c2ecf20Sopenharmony_ci if (src_addr) { 27368c2ecf20Sopenharmony_ci spin_lock_bh(&im->lock); 27378c2ecf20Sopenharmony_ci for (psf = im->sources; psf; psf = psf->sf_next) { 27388c2ecf20Sopenharmony_ci if (psf->sf_inaddr == src_addr) 27398c2ecf20Sopenharmony_ci break; 27408c2ecf20Sopenharmony_ci } 27418c2ecf20Sopenharmony_ci if (psf) 27428c2ecf20Sopenharmony_ci rv = psf->sf_count[MCAST_INCLUDE] || 27438c2ecf20Sopenharmony_ci psf->sf_count[MCAST_EXCLUDE] != 27448c2ecf20Sopenharmony_ci im->sfcount[MCAST_EXCLUDE]; 27458c2ecf20Sopenharmony_ci else 27468c2ecf20Sopenharmony_ci rv = im->sfcount[MCAST_EXCLUDE] != 0; 27478c2ecf20Sopenharmony_ci spin_unlock_bh(&im->lock); 27488c2ecf20Sopenharmony_ci } else 27498c2ecf20Sopenharmony_ci rv = 1; /* unspecified source; tentatively allow */ 27508c2ecf20Sopenharmony_ci } 27518c2ecf20Sopenharmony_ci return rv; 27528c2ecf20Sopenharmony_ci} 27538c2ecf20Sopenharmony_ci 27548c2ecf20Sopenharmony_ci#if defined(CONFIG_PROC_FS) 27558c2ecf20Sopenharmony_cistruct igmp_mc_iter_state { 27568c2ecf20Sopenharmony_ci struct seq_net_private p; 27578c2ecf20Sopenharmony_ci struct net_device *dev; 27588c2ecf20Sopenharmony_ci struct in_device *in_dev; 27598c2ecf20Sopenharmony_ci}; 27608c2ecf20Sopenharmony_ci 27618c2ecf20Sopenharmony_ci#define igmp_mc_seq_private(seq) ((struct igmp_mc_iter_state *)(seq)->private) 27628c2ecf20Sopenharmony_ci 27638c2ecf20Sopenharmony_cistatic inline struct ip_mc_list *igmp_mc_get_first(struct seq_file *seq) 27648c2ecf20Sopenharmony_ci{ 27658c2ecf20Sopenharmony_ci struct net *net = seq_file_net(seq); 27668c2ecf20Sopenharmony_ci struct ip_mc_list *im = NULL; 27678c2ecf20Sopenharmony_ci struct igmp_mc_iter_state *state = igmp_mc_seq_private(seq); 27688c2ecf20Sopenharmony_ci 27698c2ecf20Sopenharmony_ci state->in_dev = NULL; 27708c2ecf20Sopenharmony_ci for_each_netdev_rcu(net, state->dev) { 27718c2ecf20Sopenharmony_ci struct in_device *in_dev; 27728c2ecf20Sopenharmony_ci 27738c2ecf20Sopenharmony_ci in_dev = __in_dev_get_rcu(state->dev); 27748c2ecf20Sopenharmony_ci if (!in_dev) 27758c2ecf20Sopenharmony_ci continue; 27768c2ecf20Sopenharmony_ci im = rcu_dereference(in_dev->mc_list); 27778c2ecf20Sopenharmony_ci if (im) { 27788c2ecf20Sopenharmony_ci state->in_dev = in_dev; 27798c2ecf20Sopenharmony_ci break; 27808c2ecf20Sopenharmony_ci } 27818c2ecf20Sopenharmony_ci } 27828c2ecf20Sopenharmony_ci return im; 27838c2ecf20Sopenharmony_ci} 27848c2ecf20Sopenharmony_ci 27858c2ecf20Sopenharmony_cistatic struct ip_mc_list *igmp_mc_get_next(struct seq_file *seq, struct ip_mc_list *im) 27868c2ecf20Sopenharmony_ci{ 27878c2ecf20Sopenharmony_ci struct igmp_mc_iter_state *state = igmp_mc_seq_private(seq); 27888c2ecf20Sopenharmony_ci 27898c2ecf20Sopenharmony_ci im = rcu_dereference(im->next_rcu); 27908c2ecf20Sopenharmony_ci while (!im) { 27918c2ecf20Sopenharmony_ci state->dev = next_net_device_rcu(state->dev); 27928c2ecf20Sopenharmony_ci if (!state->dev) { 27938c2ecf20Sopenharmony_ci state->in_dev = NULL; 27948c2ecf20Sopenharmony_ci break; 27958c2ecf20Sopenharmony_ci } 27968c2ecf20Sopenharmony_ci state->in_dev = __in_dev_get_rcu(state->dev); 27978c2ecf20Sopenharmony_ci if (!state->in_dev) 27988c2ecf20Sopenharmony_ci continue; 27998c2ecf20Sopenharmony_ci im = rcu_dereference(state->in_dev->mc_list); 28008c2ecf20Sopenharmony_ci } 28018c2ecf20Sopenharmony_ci return im; 28028c2ecf20Sopenharmony_ci} 28038c2ecf20Sopenharmony_ci 28048c2ecf20Sopenharmony_cistatic struct ip_mc_list *igmp_mc_get_idx(struct seq_file *seq, loff_t pos) 28058c2ecf20Sopenharmony_ci{ 28068c2ecf20Sopenharmony_ci struct ip_mc_list *im = igmp_mc_get_first(seq); 28078c2ecf20Sopenharmony_ci if (im) 28088c2ecf20Sopenharmony_ci while (pos && (im = igmp_mc_get_next(seq, im)) != NULL) 28098c2ecf20Sopenharmony_ci --pos; 28108c2ecf20Sopenharmony_ci return pos ? NULL : im; 28118c2ecf20Sopenharmony_ci} 28128c2ecf20Sopenharmony_ci 28138c2ecf20Sopenharmony_cistatic void *igmp_mc_seq_start(struct seq_file *seq, loff_t *pos) 28148c2ecf20Sopenharmony_ci __acquires(rcu) 28158c2ecf20Sopenharmony_ci{ 28168c2ecf20Sopenharmony_ci rcu_read_lock(); 28178c2ecf20Sopenharmony_ci return *pos ? igmp_mc_get_idx(seq, *pos - 1) : SEQ_START_TOKEN; 28188c2ecf20Sopenharmony_ci} 28198c2ecf20Sopenharmony_ci 28208c2ecf20Sopenharmony_cistatic void *igmp_mc_seq_next(struct seq_file *seq, void *v, loff_t *pos) 28218c2ecf20Sopenharmony_ci{ 28228c2ecf20Sopenharmony_ci struct ip_mc_list *im; 28238c2ecf20Sopenharmony_ci if (v == SEQ_START_TOKEN) 28248c2ecf20Sopenharmony_ci im = igmp_mc_get_first(seq); 28258c2ecf20Sopenharmony_ci else 28268c2ecf20Sopenharmony_ci im = igmp_mc_get_next(seq, v); 28278c2ecf20Sopenharmony_ci ++*pos; 28288c2ecf20Sopenharmony_ci return im; 28298c2ecf20Sopenharmony_ci} 28308c2ecf20Sopenharmony_ci 28318c2ecf20Sopenharmony_cistatic void igmp_mc_seq_stop(struct seq_file *seq, void *v) 28328c2ecf20Sopenharmony_ci __releases(rcu) 28338c2ecf20Sopenharmony_ci{ 28348c2ecf20Sopenharmony_ci struct igmp_mc_iter_state *state = igmp_mc_seq_private(seq); 28358c2ecf20Sopenharmony_ci 28368c2ecf20Sopenharmony_ci state->in_dev = NULL; 28378c2ecf20Sopenharmony_ci state->dev = NULL; 28388c2ecf20Sopenharmony_ci rcu_read_unlock(); 28398c2ecf20Sopenharmony_ci} 28408c2ecf20Sopenharmony_ci 28418c2ecf20Sopenharmony_cistatic int igmp_mc_seq_show(struct seq_file *seq, void *v) 28428c2ecf20Sopenharmony_ci{ 28438c2ecf20Sopenharmony_ci if (v == SEQ_START_TOKEN) 28448c2ecf20Sopenharmony_ci seq_puts(seq, 28458c2ecf20Sopenharmony_ci "Idx\tDevice : Count Querier\tGroup Users Timer\tReporter\n"); 28468c2ecf20Sopenharmony_ci else { 28478c2ecf20Sopenharmony_ci struct ip_mc_list *im = (struct ip_mc_list *)v; 28488c2ecf20Sopenharmony_ci struct igmp_mc_iter_state *state = igmp_mc_seq_private(seq); 28498c2ecf20Sopenharmony_ci char *querier; 28508c2ecf20Sopenharmony_ci long delta; 28518c2ecf20Sopenharmony_ci 28528c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_MULTICAST 28538c2ecf20Sopenharmony_ci querier = IGMP_V1_SEEN(state->in_dev) ? "V1" : 28548c2ecf20Sopenharmony_ci IGMP_V2_SEEN(state->in_dev) ? "V2" : 28558c2ecf20Sopenharmony_ci "V3"; 28568c2ecf20Sopenharmony_ci#else 28578c2ecf20Sopenharmony_ci querier = "NONE"; 28588c2ecf20Sopenharmony_ci#endif 28598c2ecf20Sopenharmony_ci 28608c2ecf20Sopenharmony_ci if (rcu_access_pointer(state->in_dev->mc_list) == im) { 28618c2ecf20Sopenharmony_ci seq_printf(seq, "%d\t%-10s: %5d %7s\n", 28628c2ecf20Sopenharmony_ci state->dev->ifindex, state->dev->name, state->in_dev->mc_count, querier); 28638c2ecf20Sopenharmony_ci } 28648c2ecf20Sopenharmony_ci 28658c2ecf20Sopenharmony_ci delta = im->timer.expires - jiffies; 28668c2ecf20Sopenharmony_ci seq_printf(seq, 28678c2ecf20Sopenharmony_ci "\t\t\t\t%08X %5d %d:%08lX\t\t%d\n", 28688c2ecf20Sopenharmony_ci im->multiaddr, im->users, 28698c2ecf20Sopenharmony_ci im->tm_running, 28708c2ecf20Sopenharmony_ci im->tm_running ? jiffies_delta_to_clock_t(delta) : 0, 28718c2ecf20Sopenharmony_ci im->reporter); 28728c2ecf20Sopenharmony_ci } 28738c2ecf20Sopenharmony_ci return 0; 28748c2ecf20Sopenharmony_ci} 28758c2ecf20Sopenharmony_ci 28768c2ecf20Sopenharmony_cistatic const struct seq_operations igmp_mc_seq_ops = { 28778c2ecf20Sopenharmony_ci .start = igmp_mc_seq_start, 28788c2ecf20Sopenharmony_ci .next = igmp_mc_seq_next, 28798c2ecf20Sopenharmony_ci .stop = igmp_mc_seq_stop, 28808c2ecf20Sopenharmony_ci .show = igmp_mc_seq_show, 28818c2ecf20Sopenharmony_ci}; 28828c2ecf20Sopenharmony_ci 28838c2ecf20Sopenharmony_cistruct igmp_mcf_iter_state { 28848c2ecf20Sopenharmony_ci struct seq_net_private p; 28858c2ecf20Sopenharmony_ci struct net_device *dev; 28868c2ecf20Sopenharmony_ci struct in_device *idev; 28878c2ecf20Sopenharmony_ci struct ip_mc_list *im; 28888c2ecf20Sopenharmony_ci}; 28898c2ecf20Sopenharmony_ci 28908c2ecf20Sopenharmony_ci#define igmp_mcf_seq_private(seq) ((struct igmp_mcf_iter_state *)(seq)->private) 28918c2ecf20Sopenharmony_ci 28928c2ecf20Sopenharmony_cistatic inline struct ip_sf_list *igmp_mcf_get_first(struct seq_file *seq) 28938c2ecf20Sopenharmony_ci{ 28948c2ecf20Sopenharmony_ci struct net *net = seq_file_net(seq); 28958c2ecf20Sopenharmony_ci struct ip_sf_list *psf = NULL; 28968c2ecf20Sopenharmony_ci struct ip_mc_list *im = NULL; 28978c2ecf20Sopenharmony_ci struct igmp_mcf_iter_state *state = igmp_mcf_seq_private(seq); 28988c2ecf20Sopenharmony_ci 28998c2ecf20Sopenharmony_ci state->idev = NULL; 29008c2ecf20Sopenharmony_ci state->im = NULL; 29018c2ecf20Sopenharmony_ci for_each_netdev_rcu(net, state->dev) { 29028c2ecf20Sopenharmony_ci struct in_device *idev; 29038c2ecf20Sopenharmony_ci idev = __in_dev_get_rcu(state->dev); 29048c2ecf20Sopenharmony_ci if (unlikely(!idev)) 29058c2ecf20Sopenharmony_ci continue; 29068c2ecf20Sopenharmony_ci im = rcu_dereference(idev->mc_list); 29078c2ecf20Sopenharmony_ci if (likely(im)) { 29088c2ecf20Sopenharmony_ci spin_lock_bh(&im->lock); 29098c2ecf20Sopenharmony_ci psf = im->sources; 29108c2ecf20Sopenharmony_ci if (likely(psf)) { 29118c2ecf20Sopenharmony_ci state->im = im; 29128c2ecf20Sopenharmony_ci state->idev = idev; 29138c2ecf20Sopenharmony_ci break; 29148c2ecf20Sopenharmony_ci } 29158c2ecf20Sopenharmony_ci spin_unlock_bh(&im->lock); 29168c2ecf20Sopenharmony_ci } 29178c2ecf20Sopenharmony_ci } 29188c2ecf20Sopenharmony_ci return psf; 29198c2ecf20Sopenharmony_ci} 29208c2ecf20Sopenharmony_ci 29218c2ecf20Sopenharmony_cistatic struct ip_sf_list *igmp_mcf_get_next(struct seq_file *seq, struct ip_sf_list *psf) 29228c2ecf20Sopenharmony_ci{ 29238c2ecf20Sopenharmony_ci struct igmp_mcf_iter_state *state = igmp_mcf_seq_private(seq); 29248c2ecf20Sopenharmony_ci 29258c2ecf20Sopenharmony_ci psf = psf->sf_next; 29268c2ecf20Sopenharmony_ci while (!psf) { 29278c2ecf20Sopenharmony_ci spin_unlock_bh(&state->im->lock); 29288c2ecf20Sopenharmony_ci state->im = state->im->next; 29298c2ecf20Sopenharmony_ci while (!state->im) { 29308c2ecf20Sopenharmony_ci state->dev = next_net_device_rcu(state->dev); 29318c2ecf20Sopenharmony_ci if (!state->dev) { 29328c2ecf20Sopenharmony_ci state->idev = NULL; 29338c2ecf20Sopenharmony_ci goto out; 29348c2ecf20Sopenharmony_ci } 29358c2ecf20Sopenharmony_ci state->idev = __in_dev_get_rcu(state->dev); 29368c2ecf20Sopenharmony_ci if (!state->idev) 29378c2ecf20Sopenharmony_ci continue; 29388c2ecf20Sopenharmony_ci state->im = rcu_dereference(state->idev->mc_list); 29398c2ecf20Sopenharmony_ci } 29408c2ecf20Sopenharmony_ci if (!state->im) 29418c2ecf20Sopenharmony_ci break; 29428c2ecf20Sopenharmony_ci spin_lock_bh(&state->im->lock); 29438c2ecf20Sopenharmony_ci psf = state->im->sources; 29448c2ecf20Sopenharmony_ci } 29458c2ecf20Sopenharmony_ciout: 29468c2ecf20Sopenharmony_ci return psf; 29478c2ecf20Sopenharmony_ci} 29488c2ecf20Sopenharmony_ci 29498c2ecf20Sopenharmony_cistatic struct ip_sf_list *igmp_mcf_get_idx(struct seq_file *seq, loff_t pos) 29508c2ecf20Sopenharmony_ci{ 29518c2ecf20Sopenharmony_ci struct ip_sf_list *psf = igmp_mcf_get_first(seq); 29528c2ecf20Sopenharmony_ci if (psf) 29538c2ecf20Sopenharmony_ci while (pos && (psf = igmp_mcf_get_next(seq, psf)) != NULL) 29548c2ecf20Sopenharmony_ci --pos; 29558c2ecf20Sopenharmony_ci return pos ? NULL : psf; 29568c2ecf20Sopenharmony_ci} 29578c2ecf20Sopenharmony_ci 29588c2ecf20Sopenharmony_cistatic void *igmp_mcf_seq_start(struct seq_file *seq, loff_t *pos) 29598c2ecf20Sopenharmony_ci __acquires(rcu) 29608c2ecf20Sopenharmony_ci{ 29618c2ecf20Sopenharmony_ci rcu_read_lock(); 29628c2ecf20Sopenharmony_ci return *pos ? igmp_mcf_get_idx(seq, *pos - 1) : SEQ_START_TOKEN; 29638c2ecf20Sopenharmony_ci} 29648c2ecf20Sopenharmony_ci 29658c2ecf20Sopenharmony_cistatic void *igmp_mcf_seq_next(struct seq_file *seq, void *v, loff_t *pos) 29668c2ecf20Sopenharmony_ci{ 29678c2ecf20Sopenharmony_ci struct ip_sf_list *psf; 29688c2ecf20Sopenharmony_ci if (v == SEQ_START_TOKEN) 29698c2ecf20Sopenharmony_ci psf = igmp_mcf_get_first(seq); 29708c2ecf20Sopenharmony_ci else 29718c2ecf20Sopenharmony_ci psf = igmp_mcf_get_next(seq, v); 29728c2ecf20Sopenharmony_ci ++*pos; 29738c2ecf20Sopenharmony_ci return psf; 29748c2ecf20Sopenharmony_ci} 29758c2ecf20Sopenharmony_ci 29768c2ecf20Sopenharmony_cistatic void igmp_mcf_seq_stop(struct seq_file *seq, void *v) 29778c2ecf20Sopenharmony_ci __releases(rcu) 29788c2ecf20Sopenharmony_ci{ 29798c2ecf20Sopenharmony_ci struct igmp_mcf_iter_state *state = igmp_mcf_seq_private(seq); 29808c2ecf20Sopenharmony_ci if (likely(state->im)) { 29818c2ecf20Sopenharmony_ci spin_unlock_bh(&state->im->lock); 29828c2ecf20Sopenharmony_ci state->im = NULL; 29838c2ecf20Sopenharmony_ci } 29848c2ecf20Sopenharmony_ci state->idev = NULL; 29858c2ecf20Sopenharmony_ci state->dev = NULL; 29868c2ecf20Sopenharmony_ci rcu_read_unlock(); 29878c2ecf20Sopenharmony_ci} 29888c2ecf20Sopenharmony_ci 29898c2ecf20Sopenharmony_cistatic int igmp_mcf_seq_show(struct seq_file *seq, void *v) 29908c2ecf20Sopenharmony_ci{ 29918c2ecf20Sopenharmony_ci struct ip_sf_list *psf = (struct ip_sf_list *)v; 29928c2ecf20Sopenharmony_ci struct igmp_mcf_iter_state *state = igmp_mcf_seq_private(seq); 29938c2ecf20Sopenharmony_ci 29948c2ecf20Sopenharmony_ci if (v == SEQ_START_TOKEN) { 29958c2ecf20Sopenharmony_ci seq_puts(seq, "Idx Device MCA SRC INC EXC\n"); 29968c2ecf20Sopenharmony_ci } else { 29978c2ecf20Sopenharmony_ci seq_printf(seq, 29988c2ecf20Sopenharmony_ci "%3d %6.6s 0x%08x " 29998c2ecf20Sopenharmony_ci "0x%08x %6lu %6lu\n", 30008c2ecf20Sopenharmony_ci state->dev->ifindex, state->dev->name, 30018c2ecf20Sopenharmony_ci ntohl(state->im->multiaddr), 30028c2ecf20Sopenharmony_ci ntohl(psf->sf_inaddr), 30038c2ecf20Sopenharmony_ci psf->sf_count[MCAST_INCLUDE], 30048c2ecf20Sopenharmony_ci psf->sf_count[MCAST_EXCLUDE]); 30058c2ecf20Sopenharmony_ci } 30068c2ecf20Sopenharmony_ci return 0; 30078c2ecf20Sopenharmony_ci} 30088c2ecf20Sopenharmony_ci 30098c2ecf20Sopenharmony_cistatic const struct seq_operations igmp_mcf_seq_ops = { 30108c2ecf20Sopenharmony_ci .start = igmp_mcf_seq_start, 30118c2ecf20Sopenharmony_ci .next = igmp_mcf_seq_next, 30128c2ecf20Sopenharmony_ci .stop = igmp_mcf_seq_stop, 30138c2ecf20Sopenharmony_ci .show = igmp_mcf_seq_show, 30148c2ecf20Sopenharmony_ci}; 30158c2ecf20Sopenharmony_ci 30168c2ecf20Sopenharmony_cistatic int __net_init igmp_net_init(struct net *net) 30178c2ecf20Sopenharmony_ci{ 30188c2ecf20Sopenharmony_ci struct proc_dir_entry *pde; 30198c2ecf20Sopenharmony_ci int err; 30208c2ecf20Sopenharmony_ci 30218c2ecf20Sopenharmony_ci pde = proc_create_net("igmp", 0444, net->proc_net, &igmp_mc_seq_ops, 30228c2ecf20Sopenharmony_ci sizeof(struct igmp_mc_iter_state)); 30238c2ecf20Sopenharmony_ci if (!pde) 30248c2ecf20Sopenharmony_ci goto out_igmp; 30258c2ecf20Sopenharmony_ci pde = proc_create_net("mcfilter", 0444, net->proc_net, 30268c2ecf20Sopenharmony_ci &igmp_mcf_seq_ops, sizeof(struct igmp_mcf_iter_state)); 30278c2ecf20Sopenharmony_ci if (!pde) 30288c2ecf20Sopenharmony_ci goto out_mcfilter; 30298c2ecf20Sopenharmony_ci err = inet_ctl_sock_create(&net->ipv4.mc_autojoin_sk, AF_INET, 30308c2ecf20Sopenharmony_ci SOCK_DGRAM, 0, net); 30318c2ecf20Sopenharmony_ci if (err < 0) { 30328c2ecf20Sopenharmony_ci pr_err("Failed to initialize the IGMP autojoin socket (err %d)\n", 30338c2ecf20Sopenharmony_ci err); 30348c2ecf20Sopenharmony_ci goto out_sock; 30358c2ecf20Sopenharmony_ci } 30368c2ecf20Sopenharmony_ci 30378c2ecf20Sopenharmony_ci return 0; 30388c2ecf20Sopenharmony_ci 30398c2ecf20Sopenharmony_ciout_sock: 30408c2ecf20Sopenharmony_ci remove_proc_entry("mcfilter", net->proc_net); 30418c2ecf20Sopenharmony_ciout_mcfilter: 30428c2ecf20Sopenharmony_ci remove_proc_entry("igmp", net->proc_net); 30438c2ecf20Sopenharmony_ciout_igmp: 30448c2ecf20Sopenharmony_ci return -ENOMEM; 30458c2ecf20Sopenharmony_ci} 30468c2ecf20Sopenharmony_ci 30478c2ecf20Sopenharmony_cistatic void __net_exit igmp_net_exit(struct net *net) 30488c2ecf20Sopenharmony_ci{ 30498c2ecf20Sopenharmony_ci remove_proc_entry("mcfilter", net->proc_net); 30508c2ecf20Sopenharmony_ci remove_proc_entry("igmp", net->proc_net); 30518c2ecf20Sopenharmony_ci inet_ctl_sock_destroy(net->ipv4.mc_autojoin_sk); 30528c2ecf20Sopenharmony_ci} 30538c2ecf20Sopenharmony_ci 30548c2ecf20Sopenharmony_cistatic struct pernet_operations igmp_net_ops = { 30558c2ecf20Sopenharmony_ci .init = igmp_net_init, 30568c2ecf20Sopenharmony_ci .exit = igmp_net_exit, 30578c2ecf20Sopenharmony_ci}; 30588c2ecf20Sopenharmony_ci#endif 30598c2ecf20Sopenharmony_ci 30608c2ecf20Sopenharmony_cistatic int igmp_netdev_event(struct notifier_block *this, 30618c2ecf20Sopenharmony_ci unsigned long event, void *ptr) 30628c2ecf20Sopenharmony_ci{ 30638c2ecf20Sopenharmony_ci struct net_device *dev = netdev_notifier_info_to_dev(ptr); 30648c2ecf20Sopenharmony_ci struct in_device *in_dev; 30658c2ecf20Sopenharmony_ci 30668c2ecf20Sopenharmony_ci switch (event) { 30678c2ecf20Sopenharmony_ci case NETDEV_RESEND_IGMP: 30688c2ecf20Sopenharmony_ci in_dev = __in_dev_get_rtnl(dev); 30698c2ecf20Sopenharmony_ci if (in_dev) 30708c2ecf20Sopenharmony_ci ip_mc_rejoin_groups(in_dev); 30718c2ecf20Sopenharmony_ci break; 30728c2ecf20Sopenharmony_ci default: 30738c2ecf20Sopenharmony_ci break; 30748c2ecf20Sopenharmony_ci } 30758c2ecf20Sopenharmony_ci return NOTIFY_DONE; 30768c2ecf20Sopenharmony_ci} 30778c2ecf20Sopenharmony_ci 30788c2ecf20Sopenharmony_cistatic struct notifier_block igmp_notifier = { 30798c2ecf20Sopenharmony_ci .notifier_call = igmp_netdev_event, 30808c2ecf20Sopenharmony_ci}; 30818c2ecf20Sopenharmony_ci 30828c2ecf20Sopenharmony_ciint __init igmp_mc_init(void) 30838c2ecf20Sopenharmony_ci{ 30848c2ecf20Sopenharmony_ci#if defined(CONFIG_PROC_FS) 30858c2ecf20Sopenharmony_ci int err; 30868c2ecf20Sopenharmony_ci 30878c2ecf20Sopenharmony_ci err = register_pernet_subsys(&igmp_net_ops); 30888c2ecf20Sopenharmony_ci if (err) 30898c2ecf20Sopenharmony_ci return err; 30908c2ecf20Sopenharmony_ci err = register_netdevice_notifier(&igmp_notifier); 30918c2ecf20Sopenharmony_ci if (err) 30928c2ecf20Sopenharmony_ci goto reg_notif_fail; 30938c2ecf20Sopenharmony_ci return 0; 30948c2ecf20Sopenharmony_ci 30958c2ecf20Sopenharmony_cireg_notif_fail: 30968c2ecf20Sopenharmony_ci unregister_pernet_subsys(&igmp_net_ops); 30978c2ecf20Sopenharmony_ci return err; 30988c2ecf20Sopenharmony_ci#else 30998c2ecf20Sopenharmony_ci return register_netdevice_notifier(&igmp_notifier); 31008c2ecf20Sopenharmony_ci#endif 31018c2ecf20Sopenharmony_ci} 3102