12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 21da177e4SLinus Torvalds /* 31da177e4SLinus Torvalds * Linux NET3: Internet Group Management Protocol [IGMP] 41da177e4SLinus Torvalds * 51da177e4SLinus Torvalds * This code implements the IGMP protocol as defined in RFC1112. There has 61da177e4SLinus Torvalds * been a further revision of this protocol since which is now supported. 71da177e4SLinus Torvalds * 81da177e4SLinus Torvalds * If you have trouble with this module be careful what gcc you have used, 91da177e4SLinus Torvalds * the older version didn't come out right using gcc 2.5.8, the newer one 101da177e4SLinus Torvalds * seems to fall out with gcc 2.6.2. 111da177e4SLinus Torvalds * 121da177e4SLinus Torvalds * Authors: 13113aa838SAlan Cox * Alan Cox <alan@lxorguk.ukuu.org.uk> 141da177e4SLinus Torvalds * 151da177e4SLinus Torvalds * Fixes: 161da177e4SLinus Torvalds * 171da177e4SLinus Torvalds * Alan Cox : Added lots of __inline__ to optimise 181da177e4SLinus Torvalds * the memory usage of all the tiny little 191da177e4SLinus Torvalds * functions. 201da177e4SLinus Torvalds * Alan Cox : Dumped the header building experiment. 211da177e4SLinus Torvalds * Alan Cox : Minor tweaks ready for multicast routing 221da177e4SLinus Torvalds * and extended IGMP protocol. 231da177e4SLinus Torvalds * Alan Cox : Removed a load of inline directives. Gcc 2.5.8 241da177e4SLinus Torvalds * writes utterly bogus code otherwise (sigh) 251da177e4SLinus Torvalds * fixed IGMP loopback to behave in the manner 261da177e4SLinus Torvalds * desired by mrouted, fixed the fact it has been 271da177e4SLinus Torvalds * broken since 1.3.6 and cleaned up a few minor 281da177e4SLinus Torvalds * points. 291da177e4SLinus Torvalds * 301da177e4SLinus Torvalds * Chih-Jen Chang : Tried to revise IGMP to Version 2 311da177e4SLinus Torvalds * Tsu-Sheng Tsao E-mail: chihjenc@scf.usc.edu and tsusheng@scf.usc.edu 321da177e4SLinus Torvalds * The enhancements are mainly based on Steve Deering's 331da177e4SLinus Torvalds * ipmulti-3.5 source code. 341da177e4SLinus Torvalds * Chih-Jen Chang : Added the igmp_get_mrouter_info and 351da177e4SLinus Torvalds * Tsu-Sheng Tsao igmp_set_mrouter_info to keep track of 361da177e4SLinus Torvalds * the mrouted version on that device. 371da177e4SLinus Torvalds * Chih-Jen Chang : Added the max_resp_time parameter to 381da177e4SLinus Torvalds * Tsu-Sheng Tsao igmp_heard_query(). Using this parameter 391da177e4SLinus Torvalds * to identify the multicast router version 401da177e4SLinus Torvalds * and do what the IGMP version 2 specified. 411da177e4SLinus Torvalds * Chih-Jen Chang : Added a timer to revert to IGMP V2 router 421da177e4SLinus Torvalds * Tsu-Sheng Tsao if the specified time expired. 431da177e4SLinus Torvalds * Alan Cox : Stop IGMP from 0.0.0.0 being accepted. 441da177e4SLinus Torvalds * Alan Cox : Use GFP_ATOMIC in the right places. 451da177e4SLinus Torvalds * Christian Daudt : igmp timer wasn't set for local group 461da177e4SLinus Torvalds * memberships but was being deleted, 471da177e4SLinus Torvalds * which caused a "del_timer() called 481da177e4SLinus Torvalds * from %p with timer not initialized\n" 491da177e4SLinus Torvalds * message (960131). 501da177e4SLinus Torvalds * Christian Daudt : removed del_timer from 511da177e4SLinus Torvalds * igmp_timer_expire function (960205). 521da177e4SLinus Torvalds * Christian Daudt : igmp_heard_report now only calls 531da177e4SLinus Torvalds * igmp_timer_expire if tm->running is 541da177e4SLinus Torvalds * true (960216). 551da177e4SLinus Torvalds * Malcolm Beattie : ttl comparison wrong in igmp_rcv made 561da177e4SLinus Torvalds * igmp_heard_query never trigger. Expiry 571da177e4SLinus Torvalds * miscalculation fixed in igmp_heard_query 581da177e4SLinus Torvalds * and random() made to return unsigned to 591da177e4SLinus Torvalds * prevent negative expiry times. 601da177e4SLinus Torvalds * Alexey Kuznetsov: Wrong group leaving behaviour, backport 611da177e4SLinus Torvalds * fix from pending 2.1.x patches. 621da177e4SLinus Torvalds * Alan Cox: Forget to enable FDDI support earlier. 631da177e4SLinus Torvalds * Alexey Kuznetsov: Fixed leaving groups on device down. 641da177e4SLinus Torvalds * Alexey Kuznetsov: Accordance to igmp-v2-06 draft. 651da177e4SLinus Torvalds * David L Stevens: IGMPv3 support, with help from 661da177e4SLinus Torvalds * Vinay Kulkarni 671da177e4SLinus Torvalds */ 681da177e4SLinus Torvalds 691da177e4SLinus Torvalds #include <linux/module.h> 705a0e3ad6STejun Heo #include <linux/slab.h> 717c0f6ba6SLinus Torvalds #include <linux/uaccess.h> 721da177e4SLinus Torvalds #include <linux/types.h> 731da177e4SLinus Torvalds #include <linux/kernel.h> 741da177e4SLinus Torvalds #include <linux/jiffies.h> 751da177e4SLinus Torvalds #include <linux/string.h> 761da177e4SLinus Torvalds #include <linux/socket.h> 771da177e4SLinus Torvalds #include <linux/sockios.h> 781da177e4SLinus Torvalds #include <linux/in.h> 791da177e4SLinus Torvalds #include <linux/inet.h> 801da177e4SLinus Torvalds #include <linux/netdevice.h> 811da177e4SLinus Torvalds #include <linux/skbuff.h> 821da177e4SLinus Torvalds #include <linux/inetdevice.h> 831da177e4SLinus Torvalds #include <linux/igmp.h> 841da177e4SLinus Torvalds #include <linux/if_arp.h> 851da177e4SLinus Torvalds #include <linux/rtnetlink.h> 861da177e4SLinus Torvalds #include <linux/times.h> 879d4a0314SHannes Frederic Sowa #include <linux/pkt_sched.h> 88a46182b0SKevin Cernekee #include <linux/byteorder/generic.h> 8914c85021SArnaldo Carvalho de Melo 90457c4cbcSEric W. Biederman #include <net/net_namespace.h> 9114c85021SArnaldo Carvalho de Melo #include <net/arp.h> 921da177e4SLinus Torvalds #include <net/ip.h> 931da177e4SLinus Torvalds #include <net/protocol.h> 941da177e4SLinus Torvalds #include <net/route.h> 951da177e4SLinus Torvalds #include <net/sock.h> 961da177e4SLinus Torvalds #include <net/checksum.h> 9793a714d6SMadhu Challa #include <net/inet_common.h> 981da177e4SLinus Torvalds #include <linux/netfilter_ipv4.h> 991da177e4SLinus Torvalds #ifdef CONFIG_IP_MROUTE 1001da177e4SLinus Torvalds #include <linux/mroute.h> 1011da177e4SLinus Torvalds #endif 1021da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS 1031da177e4SLinus Torvalds #include <linux/proc_fs.h> 1041da177e4SLinus Torvalds #include <linux/seq_file.h> 1051da177e4SLinus Torvalds #endif 1061da177e4SLinus Torvalds 1071da177e4SLinus Torvalds #ifdef CONFIG_IP_MULTICAST 1081da177e4SLinus Torvalds /* Parameter names and values are taken from igmp-v2-06 draft */ 1091da177e4SLinus Torvalds 110966c37f2SHangbin Liu #define IGMP_QUERY_INTERVAL (125*HZ) 111436f7c20SFabian Frederick #define IGMP_QUERY_RESPONSE_INTERVAL (10*HZ) 1121da177e4SLinus Torvalds 113436f7c20SFabian Frederick #define IGMP_INITIAL_REPORT_DELAY (1) 1141da177e4SLinus Torvalds 115436f7c20SFabian Frederick /* IGMP_INITIAL_REPORT_DELAY is not from IGMP specs! 1161da177e4SLinus Torvalds * IGMP specs require to report membership immediately after 1171da177e4SLinus Torvalds * joining a group, but we delay the first report by a 1181da177e4SLinus Torvalds * small interval. It seems more natural and still does not 1191da177e4SLinus Torvalds * contradict to specs provided this delay is small enough. 1201da177e4SLinus Torvalds */ 1211da177e4SLinus Torvalds 12242f811b8SHerbert Xu #define IGMP_V1_SEEN(in_dev) \ 123c346dca1SYOSHIFUJI Hideaki (IPV4_DEVCONF_ALL(dev_net(in_dev->dev), FORCE_IGMP_VERSION) == 1 || \ 12442f811b8SHerbert Xu IN_DEV_CONF_GET((in_dev), FORCE_IGMP_VERSION) == 1 || \ 1251da177e4SLinus Torvalds ((in_dev)->mr_v1_seen && \ 1261da177e4SLinus Torvalds time_before(jiffies, (in_dev)->mr_v1_seen))) 12742f811b8SHerbert Xu #define IGMP_V2_SEEN(in_dev) \ 128c346dca1SYOSHIFUJI Hideaki (IPV4_DEVCONF_ALL(dev_net(in_dev->dev), FORCE_IGMP_VERSION) == 2 || \ 12942f811b8SHerbert Xu IN_DEV_CONF_GET((in_dev), FORCE_IGMP_VERSION) == 2 || \ 1301da177e4SLinus Torvalds ((in_dev)->mr_v2_seen && \ 1311da177e4SLinus Torvalds time_before(jiffies, (in_dev)->mr_v2_seen))) 1321da177e4SLinus Torvalds 133cab70040SWilliam Manley static int unsolicited_report_interval(struct in_device *in_dev) 134cab70040SWilliam Manley { 1352690048cSWilliam Manley int interval_ms, interval_jiffies; 1362690048cSWilliam Manley 137cab70040SWilliam Manley if (IGMP_V1_SEEN(in_dev) || IGMP_V2_SEEN(in_dev)) 1382690048cSWilliam Manley interval_ms = IN_DEV_CONF_GET( 1392690048cSWilliam Manley in_dev, 1402690048cSWilliam Manley IGMPV2_UNSOLICITED_REPORT_INTERVAL); 141cab70040SWilliam Manley else /* v3 */ 1422690048cSWilliam Manley interval_ms = IN_DEV_CONF_GET( 1432690048cSWilliam Manley in_dev, 1442690048cSWilliam Manley IGMPV3_UNSOLICITED_REPORT_INTERVAL); 1452690048cSWilliam Manley 1462690048cSWilliam Manley interval_jiffies = msecs_to_jiffies(interval_ms); 1472690048cSWilliam Manley 1482690048cSWilliam Manley /* _timer functions can't handle a delay of 0 jiffies so ensure 1492690048cSWilliam Manley * we always return a positive value. 1502690048cSWilliam Manley */ 1512690048cSWilliam Manley if (interval_jiffies <= 0) 1522690048cSWilliam Manley interval_jiffies = 1; 1532690048cSWilliam Manley return interval_jiffies; 154cab70040SWilliam Manley } 155cab70040SWilliam Manley 1569fb20801SFlorian Fainelli static void igmpv3_add_delrec(struct in_device *in_dev, struct ip_mc_list *im, 1579fb20801SFlorian Fainelli gfp_t gfp); 15824803f38SHangbin Liu static void igmpv3_del_delrec(struct in_device *in_dev, struct ip_mc_list *im); 1591da177e4SLinus Torvalds static void igmpv3_clear_delrec(struct in_device *in_dev); 1601da177e4SLinus Torvalds static int sf_setstate(struct ip_mc_list *pmc); 1611da177e4SLinus Torvalds static void sf_markstate(struct ip_mc_list *pmc); 1621da177e4SLinus Torvalds #endif 1631da177e4SLinus Torvalds static void ip_mc_clear_src(struct ip_mc_list *pmc); 1648f935bbdSAl Viro static int ip_mc_add_src(struct in_device *in_dev, __be32 *pmca, int sfmode, 1658f935bbdSAl Viro int sfcount, __be32 *psfsrc, int delta); 1661da177e4SLinus Torvalds 1671da177e4SLinus Torvalds static void ip_ma_put(struct ip_mc_list *im) 1681da177e4SLinus Torvalds { 1698851ab52SReshetova, Elena if (refcount_dec_and_test(&im->refcnt)) { 1701da177e4SLinus Torvalds in_dev_put(im->interface); 17142ea299dSLai Jiangshan kfree_rcu(im, rcu); 1721da177e4SLinus Torvalds } 1731da177e4SLinus Torvalds } 1741da177e4SLinus Torvalds 175d9aa9380SDavid S. Miller #define for_each_pmc_rcu(in_dev, pmc) \ 176d9aa9380SDavid S. Miller for (pmc = rcu_dereference(in_dev->mc_list); \ 177d9aa9380SDavid S. Miller pmc != NULL; \ 178d9aa9380SDavid S. Miller pmc = rcu_dereference(pmc->next_rcu)) 179d9aa9380SDavid S. Miller 180d9aa9380SDavid S. Miller #define for_each_pmc_rtnl(in_dev, pmc) \ 181d9aa9380SDavid S. Miller for (pmc = rtnl_dereference(in_dev->mc_list); \ 182d9aa9380SDavid S. Miller pmc != NULL; \ 183d9aa9380SDavid S. Miller pmc = rtnl_dereference(pmc->next_rcu)) 184d9aa9380SDavid S. Miller 185903869bdSEric Dumazet static void ip_sf_list_clear_all(struct ip_sf_list *psf) 186903869bdSEric Dumazet { 187903869bdSEric Dumazet struct ip_sf_list *next; 188903869bdSEric Dumazet 189903869bdSEric Dumazet while (psf) { 190903869bdSEric Dumazet next = psf->sf_next; 191903869bdSEric Dumazet kfree(psf); 192903869bdSEric Dumazet psf = next; 193903869bdSEric Dumazet } 194903869bdSEric Dumazet } 195903869bdSEric Dumazet 1961da177e4SLinus Torvalds #ifdef CONFIG_IP_MULTICAST 1971da177e4SLinus Torvalds 1981da177e4SLinus Torvalds /* 1991da177e4SLinus Torvalds * Timer management 2001da177e4SLinus Torvalds */ 2011da177e4SLinus Torvalds 2021d7138deSEric Dumazet static void igmp_stop_timer(struct ip_mc_list *im) 2031da177e4SLinus Torvalds { 2041da177e4SLinus Torvalds spin_lock_bh(&im->lock); 2051da177e4SLinus Torvalds if (del_timer(&im->timer)) 2068851ab52SReshetova, Elena refcount_dec(&im->refcnt); 2071da177e4SLinus Torvalds im->tm_running = 0; 2081da177e4SLinus Torvalds im->reporter = 0; 2091da177e4SLinus Torvalds im->unsolicit_count = 0; 2101da177e4SLinus Torvalds spin_unlock_bh(&im->lock); 2111da177e4SLinus Torvalds } 2121da177e4SLinus Torvalds 2131da177e4SLinus Torvalds /* It must be called with locked im->lock */ 2141da177e4SLinus Torvalds static void igmp_start_timer(struct ip_mc_list *im, int max_delay) 2151da177e4SLinus Torvalds { 2168032bf12SJason A. Donenfeld int tv = get_random_u32_below(max_delay); 2171da177e4SLinus Torvalds 2181da177e4SLinus Torvalds im->tm_running = 1; 2191da177e4SLinus Torvalds if (!mod_timer(&im->timer, jiffies+tv+2)) 2208851ab52SReshetova, Elena refcount_inc(&im->refcnt); 2211da177e4SLinus Torvalds } 2221da177e4SLinus Torvalds 2231da177e4SLinus Torvalds static void igmp_gq_start_timer(struct in_device *in_dev) 2241da177e4SLinus Torvalds { 2258032bf12SJason A. Donenfeld int tv = get_random_u32_below(in_dev->mr_maxdelay); 2267ababb78SMichal Tesar unsigned long exp = jiffies + tv + 2; 2277ababb78SMichal Tesar 2287ababb78SMichal Tesar if (in_dev->mr_gq_running && 2297ababb78SMichal Tesar time_after_eq(exp, (in_dev->mr_gq_timer).expires)) 2307ababb78SMichal Tesar return; 2311da177e4SLinus Torvalds 2321da177e4SLinus Torvalds in_dev->mr_gq_running = 1; 2337ababb78SMichal Tesar if (!mod_timer(&in_dev->mr_gq_timer, exp)) 2341da177e4SLinus Torvalds in_dev_hold(in_dev); 2351da177e4SLinus Torvalds } 2361da177e4SLinus Torvalds 2371da177e4SLinus Torvalds static void igmp_ifc_start_timer(struct in_device *in_dev, int delay) 2381da177e4SLinus Torvalds { 2398032bf12SJason A. Donenfeld int tv = get_random_u32_below(delay); 2401da177e4SLinus Torvalds 2411da177e4SLinus Torvalds if (!mod_timer(&in_dev->mr_ifc_timer, jiffies+tv+2)) 2421da177e4SLinus Torvalds in_dev_hold(in_dev); 2431da177e4SLinus Torvalds } 2441da177e4SLinus Torvalds 2451da177e4SLinus Torvalds static void igmp_mod_timer(struct ip_mc_list *im, int max_delay) 2461da177e4SLinus Torvalds { 2471da177e4SLinus Torvalds spin_lock_bh(&im->lock); 2481da177e4SLinus Torvalds im->unsolicit_count = 0; 2491da177e4SLinus Torvalds if (del_timer(&im->timer)) { 2501da177e4SLinus Torvalds if ((long)(im->timer.expires-jiffies) < max_delay) { 2511da177e4SLinus Torvalds add_timer(&im->timer); 2521da177e4SLinus Torvalds im->tm_running = 1; 2531da177e4SLinus Torvalds spin_unlock_bh(&im->lock); 2541da177e4SLinus Torvalds return; 2551da177e4SLinus Torvalds } 2568851ab52SReshetova, Elena refcount_dec(&im->refcnt); 2571da177e4SLinus Torvalds } 2581da177e4SLinus Torvalds igmp_start_timer(im, max_delay); 2591da177e4SLinus Torvalds spin_unlock_bh(&im->lock); 2601da177e4SLinus Torvalds } 2611da177e4SLinus Torvalds 2621da177e4SLinus Torvalds 2631da177e4SLinus Torvalds /* 2641da177e4SLinus Torvalds * Send an IGMP report. 2651da177e4SLinus Torvalds */ 2661da177e4SLinus Torvalds 2671da177e4SLinus Torvalds #define IGMP_SIZE (sizeof(struct igmphdr)+sizeof(struct iphdr)+4) 2681da177e4SLinus Torvalds 2691da177e4SLinus Torvalds 2701da177e4SLinus Torvalds static int is_in(struct ip_mc_list *pmc, struct ip_sf_list *psf, int type, 2711da177e4SLinus Torvalds int gdeleted, int sdeleted) 2721da177e4SLinus Torvalds { 2731da177e4SLinus Torvalds switch (type) { 2741da177e4SLinus Torvalds case IGMPV3_MODE_IS_INCLUDE: 2751da177e4SLinus Torvalds case IGMPV3_MODE_IS_EXCLUDE: 2761da177e4SLinus Torvalds if (gdeleted || sdeleted) 2771da177e4SLinus Torvalds return 0; 278ad12583fSDavid L Stevens if (!(pmc->gsquery && !psf->sf_gsresp)) { 279ad12583fSDavid L Stevens if (pmc->sfmode == MCAST_INCLUDE) 280ad12583fSDavid L Stevens return 1; 281ad12583fSDavid L Stevens /* don't include if this source is excluded 282ad12583fSDavid L Stevens * in all filters 283ad12583fSDavid L Stevens */ 284ad12583fSDavid L Stevens if (psf->sf_count[MCAST_INCLUDE]) 285ad12583fSDavid L Stevens return type == IGMPV3_MODE_IS_INCLUDE; 286ad12583fSDavid L Stevens return pmc->sfcount[MCAST_EXCLUDE] == 287ad12583fSDavid L Stevens psf->sf_count[MCAST_EXCLUDE]; 288ad12583fSDavid L Stevens } 289ad12583fSDavid L Stevens return 0; 2901da177e4SLinus Torvalds case IGMPV3_CHANGE_TO_INCLUDE: 2911da177e4SLinus Torvalds if (gdeleted || sdeleted) 2921da177e4SLinus Torvalds return 0; 2931da177e4SLinus Torvalds return psf->sf_count[MCAST_INCLUDE] != 0; 2941da177e4SLinus Torvalds case IGMPV3_CHANGE_TO_EXCLUDE: 2951da177e4SLinus Torvalds if (gdeleted || sdeleted) 2961da177e4SLinus Torvalds return 0; 2971da177e4SLinus Torvalds if (pmc->sfcount[MCAST_EXCLUDE] == 0 || 2981da177e4SLinus Torvalds psf->sf_count[MCAST_INCLUDE]) 2991da177e4SLinus Torvalds return 0; 3001da177e4SLinus Torvalds return pmc->sfcount[MCAST_EXCLUDE] == 3011da177e4SLinus Torvalds psf->sf_count[MCAST_EXCLUDE]; 3021da177e4SLinus Torvalds case IGMPV3_ALLOW_NEW_SOURCES: 3031da177e4SLinus Torvalds if (gdeleted || !psf->sf_crcount) 3041da177e4SLinus Torvalds return 0; 3051da177e4SLinus Torvalds return (pmc->sfmode == MCAST_INCLUDE) ^ sdeleted; 3061da177e4SLinus Torvalds case IGMPV3_BLOCK_OLD_SOURCES: 3071da177e4SLinus Torvalds if (pmc->sfmode == MCAST_INCLUDE) 3081da177e4SLinus Torvalds return gdeleted || (psf->sf_crcount && sdeleted); 3091da177e4SLinus Torvalds return psf->sf_crcount && !gdeleted && !sdeleted; 3101da177e4SLinus Torvalds } 3111da177e4SLinus Torvalds return 0; 3121da177e4SLinus Torvalds } 3131da177e4SLinus Torvalds 3141da177e4SLinus Torvalds static int 3151da177e4SLinus Torvalds igmp_scount(struct ip_mc_list *pmc, int type, int gdeleted, int sdeleted) 3161da177e4SLinus Torvalds { 3171da177e4SLinus Torvalds struct ip_sf_list *psf; 3181da177e4SLinus Torvalds int scount = 0; 3191da177e4SLinus Torvalds 3201da177e4SLinus Torvalds for (psf = pmc->sources; psf; psf = psf->sf_next) { 3211da177e4SLinus Torvalds if (!is_in(pmc, psf, type, gdeleted, sdeleted)) 3221da177e4SLinus Torvalds continue; 3231da177e4SLinus Torvalds scount++; 3241da177e4SLinus Torvalds } 3251da177e4SLinus Torvalds return scount; 3261da177e4SLinus Torvalds } 3271da177e4SLinus Torvalds 328a46182b0SKevin Cernekee /* source address selection per RFC 3376 section 4.2.13 */ 329a46182b0SKevin Cernekee static __be32 igmpv3_get_srcaddr(struct net_device *dev, 330a46182b0SKevin Cernekee const struct flowi4 *fl4) 331a46182b0SKevin Cernekee { 332a46182b0SKevin Cernekee struct in_device *in_dev = __in_dev_get_rcu(dev); 333cd5a411dSFlorian Westphal const struct in_ifaddr *ifa; 334a46182b0SKevin Cernekee 335a46182b0SKevin Cernekee if (!in_dev) 336a46182b0SKevin Cernekee return htonl(INADDR_ANY); 337a46182b0SKevin Cernekee 338cd5a411dSFlorian Westphal in_dev_for_each_ifa_rcu(ifa, in_dev) { 339ad23b750SFelix Fietkau if (fl4->saddr == ifa->ifa_local) 340a46182b0SKevin Cernekee return fl4->saddr; 341cd5a411dSFlorian Westphal } 342a46182b0SKevin Cernekee 343a46182b0SKevin Cernekee return htonl(INADDR_ANY); 344a46182b0SKevin Cernekee } 345a46182b0SKevin Cernekee 3464c672e4bSDaniel Borkmann static struct sk_buff *igmpv3_newpack(struct net_device *dev, unsigned int mtu) 3471da177e4SLinus Torvalds { 3481da177e4SLinus Torvalds struct sk_buff *skb; 3491da177e4SLinus Torvalds struct rtable *rt; 3501da177e4SLinus Torvalds struct iphdr *pip; 3511da177e4SLinus Torvalds struct igmpv3_report *pig; 352877acedcSDaniel Lezcano struct net *net = dev_net(dev); 35331e4543dSDavid S. Miller struct flowi4 fl4; 35466088243SHerbert Xu int hlen = LL_RESERVED_SPACE(dev); 35566088243SHerbert Xu int tlen = dev->needed_tailroom; 356*c3b704d4SEric Dumazet unsigned int size; 3571da177e4SLinus Torvalds 358*c3b704d4SEric Dumazet size = min(mtu, IP_MAX_MTU); 35957e1ab6eSEric Dumazet while (1) { 36066088243SHerbert Xu skb = alloc_skb(size + hlen + tlen, 36157e1ab6eSEric Dumazet GFP_ATOMIC | __GFP_NOWARN); 36257e1ab6eSEric Dumazet if (skb) 36357e1ab6eSEric Dumazet break; 36457e1ab6eSEric Dumazet size >>= 1; 36557e1ab6eSEric Dumazet if (size < 256) 3661da177e4SLinus Torvalds return NULL; 36757e1ab6eSEric Dumazet } 3689d4a0314SHannes Frederic Sowa skb->priority = TC_PRIO_CONTROL; 3691da177e4SLinus Torvalds 37031e4543dSDavid S. Miller rt = ip_route_output_ports(net, &fl4, NULL, IGMPV3_ALL_MCR, 0, 37178fbfd8aSDavid S. Miller 0, 0, 37278fbfd8aSDavid S. Miller IPPROTO_IGMP, 0, dev->ifindex); 373b23dd4feSDavid S. Miller if (IS_ERR(rt)) { 3741da177e4SLinus Torvalds kfree_skb(skb); 3751da177e4SLinus Torvalds return NULL; 3761da177e4SLinus Torvalds } 3771da177e4SLinus Torvalds 378d8d1f30bSChangli Gao skb_dst_set(skb, &rt->dst); 3791da177e4SLinus Torvalds skb->dev = dev; 3801da177e4SLinus Torvalds 38166088243SHerbert Xu skb_reserve(skb, hlen); 3821837b2e2SBenjamin Poirier skb_tailroom_reserve(skb, mtu, tlen); 3831da177e4SLinus Torvalds 3847e28ecc2SArnaldo Carvalho de Melo skb_reset_network_header(skb); 385eddc9ec5SArnaldo Carvalho de Melo pip = ip_hdr(skb); 3867e28ecc2SArnaldo Carvalho de Melo skb_put(skb, sizeof(struct iphdr) + 4); 3871da177e4SLinus Torvalds 3881da177e4SLinus Torvalds pip->version = 4; 3891da177e4SLinus Torvalds pip->ihl = (sizeof(struct iphdr)+4)>>2; 3901da177e4SLinus Torvalds pip->tos = 0xc0; 3911da177e4SLinus Torvalds pip->frag_off = htons(IP_DF); 3921da177e4SLinus Torvalds pip->ttl = 1; 393492f64ceSDavid S. Miller pip->daddr = fl4.daddr; 394e7aadb27SEric Dumazet 395e7aadb27SEric Dumazet rcu_read_lock(); 396a46182b0SKevin Cernekee pip->saddr = igmpv3_get_srcaddr(dev, &fl4); 397e7aadb27SEric Dumazet rcu_read_unlock(); 398e7aadb27SEric Dumazet 3991da177e4SLinus Torvalds pip->protocol = IPPROTO_IGMP; 4001da177e4SLinus Torvalds pip->tot_len = 0; /* filled in later */ 401b6a7719aSHannes Frederic Sowa ip_select_ident(net, skb, NULL); 4021da177e4SLinus Torvalds ((u8 *)&pip[1])[0] = IPOPT_RA; 4031da177e4SLinus Torvalds ((u8 *)&pip[1])[1] = 4; 4041da177e4SLinus Torvalds ((u8 *)&pip[1])[2] = 0; 4051da177e4SLinus Torvalds ((u8 *)&pip[1])[3] = 0; 4061da177e4SLinus Torvalds 407b0e380b1SArnaldo Carvalho de Melo skb->transport_header = skb->network_header + sizeof(struct iphdr) + 4; 408d10ba34bSArnaldo Carvalho de Melo skb_put(skb, sizeof(*pig)); 409d9edf9e2SArnaldo Carvalho de Melo pig = igmpv3_report_hdr(skb); 4101da177e4SLinus Torvalds pig->type = IGMPV3_HOST_MEMBERSHIP_REPORT; 4111da177e4SLinus Torvalds pig->resv1 = 0; 4121da177e4SLinus Torvalds pig->csum = 0; 4131da177e4SLinus Torvalds pig->resv2 = 0; 4141da177e4SLinus Torvalds pig->ngrec = 0; 4151da177e4SLinus Torvalds return skb; 4161da177e4SLinus Torvalds } 4171da177e4SLinus Torvalds 4181da177e4SLinus Torvalds static int igmpv3_sendpack(struct sk_buff *skb) 4191da177e4SLinus Torvalds { 420d9edf9e2SArnaldo Carvalho de Melo struct igmphdr *pig = igmp_hdr(skb); 421f7c0c2aeSSimon Horman const int igmplen = skb_tail_pointer(skb) - skb_transport_header(skb); 4221da177e4SLinus Torvalds 423d9edf9e2SArnaldo Carvalho de Melo pig->csum = ip_compute_csum(igmp_hdr(skb), igmplen); 4241da177e4SLinus Torvalds 42533224b16SEric W. Biederman return ip_local_out(dev_net(skb_dst(skb)->dev), skb->sk, skb); 4261da177e4SLinus Torvalds } 4271da177e4SLinus Torvalds 4281da177e4SLinus Torvalds static int grec_size(struct ip_mc_list *pmc, int type, int gdel, int sdel) 4291da177e4SLinus Torvalds { 4301da177e4SLinus Torvalds return sizeof(struct igmpv3_grec) + 4*igmp_scount(pmc, type, gdel, sdel); 4311da177e4SLinus Torvalds } 4321da177e4SLinus Torvalds 4331da177e4SLinus Torvalds static struct sk_buff *add_grhead(struct sk_buff *skb, struct ip_mc_list *pmc, 434b5476022SEric Dumazet int type, struct igmpv3_grec **ppgr, unsigned int mtu) 4351da177e4SLinus Torvalds { 4361da177e4SLinus Torvalds struct net_device *dev = pmc->interface->dev; 4371da177e4SLinus Torvalds struct igmpv3_report *pih; 4381da177e4SLinus Torvalds struct igmpv3_grec *pgr; 4391da177e4SLinus Torvalds 440b5476022SEric Dumazet if (!skb) { 441b5476022SEric Dumazet skb = igmpv3_newpack(dev, mtu); 4421da177e4SLinus Torvalds if (!skb) 4431da177e4SLinus Torvalds return NULL; 444b5476022SEric Dumazet } 4454df864c1SJohannes Berg pgr = skb_put(skb, sizeof(struct igmpv3_grec)); 4461da177e4SLinus Torvalds pgr->grec_type = type; 4471da177e4SLinus Torvalds pgr->grec_auxwords = 0; 4481da177e4SLinus Torvalds pgr->grec_nsrcs = 0; 4491da177e4SLinus Torvalds pgr->grec_mca = pmc->multiaddr; 450d9edf9e2SArnaldo Carvalho de Melo pih = igmpv3_report_hdr(skb); 4511da177e4SLinus Torvalds pih->ngrec = htons(ntohs(pih->ngrec)+1); 4521da177e4SLinus Torvalds *ppgr = pgr; 4531da177e4SLinus Torvalds return skb; 4541da177e4SLinus Torvalds } 4551da177e4SLinus Torvalds 4564c672e4bSDaniel Borkmann #define AVAILABLE(skb) ((skb) ? skb_availroom(skb) : 0) 4571da177e4SLinus Torvalds 4581da177e4SLinus Torvalds static struct sk_buff *add_grec(struct sk_buff *skb, struct ip_mc_list *pmc, 4591da177e4SLinus Torvalds int type, int gdeleted, int sdeleted) 4601da177e4SLinus Torvalds { 4611da177e4SLinus Torvalds struct net_device *dev = pmc->interface->dev; 46287a8a2aeSNikolay Borisov struct net *net = dev_net(dev); 4631da177e4SLinus Torvalds struct igmpv3_report *pih; 4641da177e4SLinus Torvalds struct igmpv3_grec *pgr = NULL; 4651da177e4SLinus Torvalds struct ip_sf_list *psf, *psf_next, *psf_prev, **psf_list; 466ad12583fSDavid L Stevens int scount, stotal, first, isquery, truncate; 467b5476022SEric Dumazet unsigned int mtu; 4681da177e4SLinus Torvalds 4691da177e4SLinus Torvalds if (pmc->multiaddr == IGMP_ALL_HOSTS) 4701da177e4SLinus Torvalds return skb; 471f6da2267SKuniyuki Iwashima if (ipv4_is_local_multicast(pmc->multiaddr) && 472f6da2267SKuniyuki Iwashima !READ_ONCE(net->ipv4.sysctl_igmp_llm_reports)) 473df2cf4a7SPhilip Downey return skb; 4741da177e4SLinus Torvalds 475b5476022SEric Dumazet mtu = READ_ONCE(dev->mtu); 476b5476022SEric Dumazet if (mtu < IPV4_MIN_MTU) 477b5476022SEric Dumazet return skb; 478b5476022SEric Dumazet 4791da177e4SLinus Torvalds isquery = type == IGMPV3_MODE_IS_INCLUDE || 4801da177e4SLinus Torvalds type == IGMPV3_MODE_IS_EXCLUDE; 4811da177e4SLinus Torvalds truncate = type == IGMPV3_MODE_IS_EXCLUDE || 4821da177e4SLinus Torvalds type == IGMPV3_CHANGE_TO_EXCLUDE; 4831da177e4SLinus Torvalds 484ad12583fSDavid L Stevens stotal = scount = 0; 485ad12583fSDavid L Stevens 4861da177e4SLinus Torvalds psf_list = sdeleted ? &pmc->tomb : &pmc->sources; 4871da177e4SLinus Torvalds 488ad12583fSDavid L Stevens if (!*psf_list) 489ad12583fSDavid L Stevens goto empty_source; 490ad12583fSDavid L Stevens 491d9edf9e2SArnaldo Carvalho de Melo pih = skb ? igmpv3_report_hdr(skb) : NULL; 4921da177e4SLinus Torvalds 4931da177e4SLinus Torvalds /* EX and TO_EX get a fresh packet, if needed */ 4941da177e4SLinus Torvalds if (truncate) { 4951da177e4SLinus Torvalds if (pih && pih->ngrec && 4961da177e4SLinus Torvalds AVAILABLE(skb) < grec_size(pmc, type, gdeleted, sdeleted)) { 4971da177e4SLinus Torvalds if (skb) 4981da177e4SLinus Torvalds igmpv3_sendpack(skb); 499b5476022SEric Dumazet skb = igmpv3_newpack(dev, mtu); 5001da177e4SLinus Torvalds } 5011da177e4SLinus Torvalds } 5021da177e4SLinus Torvalds first = 1; 5031da177e4SLinus Torvalds psf_prev = NULL; 5041da177e4SLinus Torvalds for (psf = *psf_list; psf; psf = psf_next) { 505ea4d9e72SAl Viro __be32 *psrc; 5061da177e4SLinus Torvalds 5071da177e4SLinus Torvalds psf_next = psf->sf_next; 5081da177e4SLinus Torvalds 5091da177e4SLinus Torvalds if (!is_in(pmc, psf, type, gdeleted, sdeleted)) { 5101da177e4SLinus Torvalds psf_prev = psf; 5111da177e4SLinus Torvalds continue; 5121da177e4SLinus Torvalds } 5131da177e4SLinus Torvalds 514a052517aSHangbin Liu /* Based on RFC3376 5.1. Should not send source-list change 515a052517aSHangbin Liu * records when there is a filter mode change. 516a052517aSHangbin Liu */ 517a052517aSHangbin Liu if (((gdeleted && pmc->sfmode == MCAST_EXCLUDE) || 518a052517aSHangbin Liu (!gdeleted && pmc->crcount)) && 519a052517aSHangbin Liu (type == IGMPV3_ALLOW_NEW_SOURCES || 520a052517aSHangbin Liu type == IGMPV3_BLOCK_OLD_SOURCES) && psf->sf_crcount) 521a052517aSHangbin Liu goto decrease_sf_crcount; 522a052517aSHangbin Liu 5231da177e4SLinus Torvalds /* clear marks on query responses */ 5241da177e4SLinus Torvalds if (isquery) 5251da177e4SLinus Torvalds psf->sf_gsresp = 0; 5261da177e4SLinus Torvalds 52763007727SAl Viro if (AVAILABLE(skb) < sizeof(__be32) + 5281da177e4SLinus Torvalds first*sizeof(struct igmpv3_grec)) { 5291da177e4SLinus Torvalds if (truncate && !first) 5301da177e4SLinus Torvalds break; /* truncate these */ 5311da177e4SLinus Torvalds if (pgr) 5321da177e4SLinus Torvalds pgr->grec_nsrcs = htons(scount); 5331da177e4SLinus Torvalds if (skb) 5341da177e4SLinus Torvalds igmpv3_sendpack(skb); 535b5476022SEric Dumazet skb = igmpv3_newpack(dev, mtu); 5361da177e4SLinus Torvalds first = 1; 5371da177e4SLinus Torvalds scount = 0; 5381da177e4SLinus Torvalds } 5391da177e4SLinus Torvalds if (first) { 540b5476022SEric Dumazet skb = add_grhead(skb, pmc, type, &pgr, mtu); 5411da177e4SLinus Torvalds first = 0; 5421da177e4SLinus Torvalds } 543cc63f70bSAlexey Dobriyan if (!skb) 544cc63f70bSAlexey Dobriyan return NULL; 5454df864c1SJohannes Berg psrc = skb_put(skb, sizeof(__be32)); 5461da177e4SLinus Torvalds *psrc = psf->sf_inaddr; 547ad12583fSDavid L Stevens scount++; stotal++; 5481da177e4SLinus Torvalds if ((type == IGMPV3_ALLOW_NEW_SOURCES || 5491da177e4SLinus Torvalds type == IGMPV3_BLOCK_OLD_SOURCES) && psf->sf_crcount) { 550a052517aSHangbin Liu decrease_sf_crcount: 5511da177e4SLinus Torvalds psf->sf_crcount--; 5521da177e4SLinus Torvalds if ((sdeleted || gdeleted) && psf->sf_crcount == 0) { 5531da177e4SLinus Torvalds if (psf_prev) 5541da177e4SLinus Torvalds psf_prev->sf_next = psf->sf_next; 5551da177e4SLinus Torvalds else 5561da177e4SLinus Torvalds *psf_list = psf->sf_next; 5571da177e4SLinus Torvalds kfree(psf); 5581da177e4SLinus Torvalds continue; 5591da177e4SLinus Torvalds } 5601da177e4SLinus Torvalds } 5611da177e4SLinus Torvalds psf_prev = psf; 5621da177e4SLinus Torvalds } 563ad12583fSDavid L Stevens 564ad12583fSDavid L Stevens empty_source: 565ad12583fSDavid L Stevens if (!stotal) { 566ad12583fSDavid L Stevens if (type == IGMPV3_ALLOW_NEW_SOURCES || 567ad12583fSDavid L Stevens type == IGMPV3_BLOCK_OLD_SOURCES) 568ad12583fSDavid L Stevens return skb; 569ad12583fSDavid L Stevens if (pmc->crcount || isquery) { 570ad12583fSDavid L Stevens /* make sure we have room for group header */ 571ad12583fSDavid L Stevens if (skb && AVAILABLE(skb) < sizeof(struct igmpv3_grec)) { 572ad12583fSDavid L Stevens igmpv3_sendpack(skb); 573ad12583fSDavid L Stevens skb = NULL; /* add_grhead will get a new one */ 574ad12583fSDavid L Stevens } 575b5476022SEric Dumazet skb = add_grhead(skb, pmc, type, &pgr, mtu); 576ad12583fSDavid L Stevens } 577ad12583fSDavid L Stevens } 5781da177e4SLinus Torvalds if (pgr) 5791da177e4SLinus Torvalds pgr->grec_nsrcs = htons(scount); 5801da177e4SLinus Torvalds 5811da177e4SLinus Torvalds if (isquery) 5821da177e4SLinus Torvalds pmc->gsquery = 0; /* clear query state on report */ 5831da177e4SLinus Torvalds return skb; 5841da177e4SLinus Torvalds } 5851da177e4SLinus Torvalds 5861da177e4SLinus Torvalds static int igmpv3_send_report(struct in_device *in_dev, struct ip_mc_list *pmc) 5871da177e4SLinus Torvalds { 5881da177e4SLinus Torvalds struct sk_buff *skb = NULL; 58987a8a2aeSNikolay Borisov struct net *net = dev_net(in_dev->dev); 5901da177e4SLinus Torvalds int type; 5911da177e4SLinus Torvalds 5921da177e4SLinus Torvalds if (!pmc) { 5931d7138deSEric Dumazet rcu_read_lock(); 5941d7138deSEric Dumazet for_each_pmc_rcu(in_dev, pmc) { 5951da177e4SLinus Torvalds if (pmc->multiaddr == IGMP_ALL_HOSTS) 5961da177e4SLinus Torvalds continue; 597df2cf4a7SPhilip Downey if (ipv4_is_local_multicast(pmc->multiaddr) && 598f6da2267SKuniyuki Iwashima !READ_ONCE(net->ipv4.sysctl_igmp_llm_reports)) 599df2cf4a7SPhilip Downey continue; 6001da177e4SLinus Torvalds spin_lock_bh(&pmc->lock); 6011da177e4SLinus Torvalds if (pmc->sfcount[MCAST_EXCLUDE]) 6021da177e4SLinus Torvalds type = IGMPV3_MODE_IS_EXCLUDE; 6031da177e4SLinus Torvalds else 6041da177e4SLinus Torvalds type = IGMPV3_MODE_IS_INCLUDE; 6051da177e4SLinus Torvalds skb = add_grec(skb, pmc, type, 0, 0); 6061da177e4SLinus Torvalds spin_unlock_bh(&pmc->lock); 6071da177e4SLinus Torvalds } 6081d7138deSEric Dumazet rcu_read_unlock(); 6091da177e4SLinus Torvalds } else { 6101da177e4SLinus Torvalds spin_lock_bh(&pmc->lock); 6111da177e4SLinus Torvalds if (pmc->sfcount[MCAST_EXCLUDE]) 6121da177e4SLinus Torvalds type = IGMPV3_MODE_IS_EXCLUDE; 6131da177e4SLinus Torvalds else 6141da177e4SLinus Torvalds type = IGMPV3_MODE_IS_INCLUDE; 6151da177e4SLinus Torvalds skb = add_grec(skb, pmc, type, 0, 0); 6161da177e4SLinus Torvalds spin_unlock_bh(&pmc->lock); 6171da177e4SLinus Torvalds } 6181da177e4SLinus Torvalds if (!skb) 6191da177e4SLinus Torvalds return 0; 6201da177e4SLinus Torvalds return igmpv3_sendpack(skb); 6211da177e4SLinus Torvalds } 6221da177e4SLinus Torvalds 6231da177e4SLinus Torvalds /* 6241da177e4SLinus Torvalds * remove zero-count source records from a source filter list 6251da177e4SLinus Torvalds */ 6261da177e4SLinus Torvalds static void igmpv3_clear_zeros(struct ip_sf_list **ppsf) 6271da177e4SLinus Torvalds { 6281da177e4SLinus Torvalds struct ip_sf_list *psf_prev, *psf_next, *psf; 6291da177e4SLinus Torvalds 6301da177e4SLinus Torvalds psf_prev = NULL; 6311da177e4SLinus Torvalds for (psf = *ppsf; psf; psf = psf_next) { 6321da177e4SLinus Torvalds psf_next = psf->sf_next; 6331da177e4SLinus Torvalds if (psf->sf_crcount == 0) { 6341da177e4SLinus Torvalds if (psf_prev) 6351da177e4SLinus Torvalds psf_prev->sf_next = psf->sf_next; 6361da177e4SLinus Torvalds else 6371da177e4SLinus Torvalds *ppsf = psf->sf_next; 6381da177e4SLinus Torvalds kfree(psf); 6391da177e4SLinus Torvalds } else 6401da177e4SLinus Torvalds psf_prev = psf; 6411da177e4SLinus Torvalds } 6421da177e4SLinus Torvalds } 6431da177e4SLinus Torvalds 6443580d04aSEric Dumazet static void kfree_pmc(struct ip_mc_list *pmc) 6453580d04aSEric Dumazet { 6463580d04aSEric Dumazet ip_sf_list_clear_all(pmc->sources); 6473580d04aSEric Dumazet ip_sf_list_clear_all(pmc->tomb); 6483580d04aSEric Dumazet kfree(pmc); 6493580d04aSEric Dumazet } 6503580d04aSEric Dumazet 6511da177e4SLinus Torvalds static void igmpv3_send_cr(struct in_device *in_dev) 6521da177e4SLinus Torvalds { 6531da177e4SLinus Torvalds struct ip_mc_list *pmc, *pmc_prev, *pmc_next; 6541da177e4SLinus Torvalds struct sk_buff *skb = NULL; 6551da177e4SLinus Torvalds int type, dtype; 6561da177e4SLinus Torvalds 6571d7138deSEric Dumazet rcu_read_lock(); 6581da177e4SLinus Torvalds spin_lock_bh(&in_dev->mc_tomb_lock); 6591da177e4SLinus Torvalds 6601da177e4SLinus Torvalds /* deleted MCA's */ 6611da177e4SLinus Torvalds pmc_prev = NULL; 6621da177e4SLinus Torvalds for (pmc = in_dev->mc_tomb; pmc; pmc = pmc_next) { 6631da177e4SLinus Torvalds pmc_next = pmc->next; 6641da177e4SLinus Torvalds if (pmc->sfmode == MCAST_INCLUDE) { 6651da177e4SLinus Torvalds type = IGMPV3_BLOCK_OLD_SOURCES; 6661da177e4SLinus Torvalds dtype = IGMPV3_BLOCK_OLD_SOURCES; 6671da177e4SLinus Torvalds skb = add_grec(skb, pmc, type, 1, 0); 6681da177e4SLinus Torvalds skb = add_grec(skb, pmc, dtype, 1, 1); 6691da177e4SLinus Torvalds } 6701da177e4SLinus Torvalds if (pmc->crcount) { 6711da177e4SLinus Torvalds if (pmc->sfmode == MCAST_EXCLUDE) { 6721da177e4SLinus Torvalds type = IGMPV3_CHANGE_TO_INCLUDE; 6731da177e4SLinus Torvalds skb = add_grec(skb, pmc, type, 1, 0); 6741da177e4SLinus Torvalds } 675ad12583fSDavid L Stevens pmc->crcount--; 6761da177e4SLinus Torvalds if (pmc->crcount == 0) { 6771da177e4SLinus Torvalds igmpv3_clear_zeros(&pmc->tomb); 6781da177e4SLinus Torvalds igmpv3_clear_zeros(&pmc->sources); 6791da177e4SLinus Torvalds } 6801da177e4SLinus Torvalds } 6811da177e4SLinus Torvalds if (pmc->crcount == 0 && !pmc->tomb && !pmc->sources) { 6821da177e4SLinus Torvalds if (pmc_prev) 6831da177e4SLinus Torvalds pmc_prev->next = pmc_next; 6841da177e4SLinus Torvalds else 6851da177e4SLinus Torvalds in_dev->mc_tomb = pmc_next; 6861da177e4SLinus Torvalds in_dev_put(pmc->interface); 6873580d04aSEric Dumazet kfree_pmc(pmc); 6881da177e4SLinus Torvalds } else 6891da177e4SLinus Torvalds pmc_prev = pmc; 6901da177e4SLinus Torvalds } 6911da177e4SLinus Torvalds spin_unlock_bh(&in_dev->mc_tomb_lock); 6921da177e4SLinus Torvalds 6931da177e4SLinus Torvalds /* change recs */ 6941d7138deSEric Dumazet for_each_pmc_rcu(in_dev, pmc) { 6951da177e4SLinus Torvalds spin_lock_bh(&pmc->lock); 6961da177e4SLinus Torvalds if (pmc->sfcount[MCAST_EXCLUDE]) { 6971da177e4SLinus Torvalds type = IGMPV3_BLOCK_OLD_SOURCES; 6981da177e4SLinus Torvalds dtype = IGMPV3_ALLOW_NEW_SOURCES; 6991da177e4SLinus Torvalds } else { 7001da177e4SLinus Torvalds type = IGMPV3_ALLOW_NEW_SOURCES; 7011da177e4SLinus Torvalds dtype = IGMPV3_BLOCK_OLD_SOURCES; 7021da177e4SLinus Torvalds } 7031da177e4SLinus Torvalds skb = add_grec(skb, pmc, type, 0, 0); 7041da177e4SLinus Torvalds skb = add_grec(skb, pmc, dtype, 0, 1); /* deleted sources */ 7051da177e4SLinus Torvalds 7061da177e4SLinus Torvalds /* filter mode changes */ 7071da177e4SLinus Torvalds if (pmc->crcount) { 7081da177e4SLinus Torvalds if (pmc->sfmode == MCAST_EXCLUDE) 7091da177e4SLinus Torvalds type = IGMPV3_CHANGE_TO_EXCLUDE; 7101da177e4SLinus Torvalds else 7111da177e4SLinus Torvalds type = IGMPV3_CHANGE_TO_INCLUDE; 7121da177e4SLinus Torvalds skb = add_grec(skb, pmc, type, 0, 0); 713ad12583fSDavid L Stevens pmc->crcount--; 7141da177e4SLinus Torvalds } 7151da177e4SLinus Torvalds spin_unlock_bh(&pmc->lock); 7161da177e4SLinus Torvalds } 7171d7138deSEric Dumazet rcu_read_unlock(); 7181da177e4SLinus Torvalds 7191da177e4SLinus Torvalds if (!skb) 7201da177e4SLinus Torvalds return; 7211da177e4SLinus Torvalds (void) igmpv3_sendpack(skb); 7221da177e4SLinus Torvalds } 7231da177e4SLinus Torvalds 7241da177e4SLinus Torvalds static int igmp_send_report(struct in_device *in_dev, struct ip_mc_list *pmc, 7251da177e4SLinus Torvalds int type) 7261da177e4SLinus Torvalds { 7271da177e4SLinus Torvalds struct sk_buff *skb; 7281da177e4SLinus Torvalds struct iphdr *iph; 7291da177e4SLinus Torvalds struct igmphdr *ih; 7301da177e4SLinus Torvalds struct rtable *rt; 7311da177e4SLinus Torvalds struct net_device *dev = in_dev->dev; 732877acedcSDaniel Lezcano struct net *net = dev_net(dev); 73363007727SAl Viro __be32 group = pmc ? pmc->multiaddr : 0; 73431e4543dSDavid S. Miller struct flowi4 fl4; 73563007727SAl Viro __be32 dst; 73666088243SHerbert Xu int hlen, tlen; 7371da177e4SLinus Torvalds 7381da177e4SLinus Torvalds if (type == IGMPV3_HOST_MEMBERSHIP_REPORT) 7391da177e4SLinus Torvalds return igmpv3_send_report(in_dev, pmc); 740df2cf4a7SPhilip Downey 741f6da2267SKuniyuki Iwashima if (ipv4_is_local_multicast(group) && 742f6da2267SKuniyuki Iwashima !READ_ONCE(net->ipv4.sysctl_igmp_llm_reports)) 743df2cf4a7SPhilip Downey return 0; 744df2cf4a7SPhilip Downey 745df2cf4a7SPhilip Downey if (type == IGMP_HOST_LEAVE_MESSAGE) 7461da177e4SLinus Torvalds dst = IGMP_ALL_ROUTER; 7471da177e4SLinus Torvalds else 7481da177e4SLinus Torvalds dst = group; 7491da177e4SLinus Torvalds 75031e4543dSDavid S. Miller rt = ip_route_output_ports(net, &fl4, NULL, dst, 0, 75178fbfd8aSDavid S. Miller 0, 0, 75278fbfd8aSDavid S. Miller IPPROTO_IGMP, 0, dev->ifindex); 753b23dd4feSDavid S. Miller if (IS_ERR(rt)) 7541da177e4SLinus Torvalds return -1; 75578fbfd8aSDavid S. Miller 75666088243SHerbert Xu hlen = LL_RESERVED_SPACE(dev); 75766088243SHerbert Xu tlen = dev->needed_tailroom; 75866088243SHerbert Xu skb = alloc_skb(IGMP_SIZE + hlen + tlen, GFP_ATOMIC); 75951456b29SIan Morris if (!skb) { 7601da177e4SLinus Torvalds ip_rt_put(rt); 7611da177e4SLinus Torvalds return -1; 7621da177e4SLinus Torvalds } 7639d4a0314SHannes Frederic Sowa skb->priority = TC_PRIO_CONTROL; 7641da177e4SLinus Torvalds 765d8d1f30bSChangli Gao skb_dst_set(skb, &rt->dst); 7661da177e4SLinus Torvalds 76766088243SHerbert Xu skb_reserve(skb, hlen); 7681da177e4SLinus Torvalds 7697e28ecc2SArnaldo Carvalho de Melo skb_reset_network_header(skb); 770eddc9ec5SArnaldo Carvalho de Melo iph = ip_hdr(skb); 7717e28ecc2SArnaldo Carvalho de Melo skb_put(skb, sizeof(struct iphdr) + 4); 7721da177e4SLinus Torvalds 7731da177e4SLinus Torvalds iph->version = 4; 7741da177e4SLinus Torvalds iph->ihl = (sizeof(struct iphdr)+4)>>2; 7751da177e4SLinus Torvalds iph->tos = 0xc0; 7761da177e4SLinus Torvalds iph->frag_off = htons(IP_DF); 7771da177e4SLinus Torvalds iph->ttl = 1; 7781da177e4SLinus Torvalds iph->daddr = dst; 779492f64ceSDavid S. Miller iph->saddr = fl4.saddr; 7801da177e4SLinus Torvalds iph->protocol = IPPROTO_IGMP; 781b6a7719aSHannes Frederic Sowa ip_select_ident(net, skb, NULL); 7821da177e4SLinus Torvalds ((u8 *)&iph[1])[0] = IPOPT_RA; 7831da177e4SLinus Torvalds ((u8 *)&iph[1])[1] = 4; 7841da177e4SLinus Torvalds ((u8 *)&iph[1])[2] = 0; 7851da177e4SLinus Torvalds ((u8 *)&iph[1])[3] = 0; 7861da177e4SLinus Torvalds 7874df864c1SJohannes Berg ih = skb_put(skb, sizeof(struct igmphdr)); 7881da177e4SLinus Torvalds ih->type = type; 7891da177e4SLinus Torvalds ih->code = 0; 7901da177e4SLinus Torvalds ih->csum = 0; 7911da177e4SLinus Torvalds ih->group = group; 7921da177e4SLinus Torvalds ih->csum = ip_compute_csum((void *)ih, sizeof(struct igmphdr)); 7931da177e4SLinus Torvalds 79433224b16SEric W. Biederman return ip_local_out(net, skb->sk, skb); 7951da177e4SLinus Torvalds } 7961da177e4SLinus Torvalds 797e99e88a9SKees Cook static void igmp_gq_timer_expire(struct timer_list *t) 7981da177e4SLinus Torvalds { 799e99e88a9SKees Cook struct in_device *in_dev = from_timer(in_dev, t, mr_gq_timer); 8001da177e4SLinus Torvalds 8011da177e4SLinus Torvalds in_dev->mr_gq_running = 0; 8021da177e4SLinus Torvalds igmpv3_send_report(in_dev, NULL); 803e2401654SSalam Noureddine in_dev_put(in_dev); 8041da177e4SLinus Torvalds } 8051da177e4SLinus Torvalds 806e99e88a9SKees Cook static void igmp_ifc_timer_expire(struct timer_list *t) 8071da177e4SLinus Torvalds { 808e99e88a9SKees Cook struct in_device *in_dev = from_timer(in_dev, t, mr_ifc_timer); 809b69dd5b3SEric Dumazet u32 mr_ifc_count; 8101da177e4SLinus Torvalds 8111da177e4SLinus Torvalds igmpv3_send_cr(in_dev); 8124a2b285eSEric Dumazet restart: 8134a2b285eSEric Dumazet mr_ifc_count = READ_ONCE(in_dev->mr_ifc_count); 8144a2b285eSEric Dumazet 8154a2b285eSEric Dumazet if (mr_ifc_count) { 8164a2b285eSEric Dumazet if (cmpxchg(&in_dev->mr_ifc_count, 8174a2b285eSEric Dumazet mr_ifc_count, 8184a2b285eSEric Dumazet mr_ifc_count - 1) != mr_ifc_count) 8194a2b285eSEric Dumazet goto restart; 820cab70040SWilliam Manley igmp_ifc_start_timer(in_dev, 821cab70040SWilliam Manley unsolicited_report_interval(in_dev)); 8221da177e4SLinus Torvalds } 823e2401654SSalam Noureddine in_dev_put(in_dev); 8241da177e4SLinus Torvalds } 8251da177e4SLinus Torvalds 8261da177e4SLinus Torvalds static void igmp_ifc_event(struct in_device *in_dev) 8271da177e4SLinus Torvalds { 828165094afSNikolay Borisov struct net *net = dev_net(in_dev->dev); 8291da177e4SLinus Torvalds if (IGMP_V1_SEEN(in_dev) || IGMP_V2_SEEN(in_dev)) 8301da177e4SLinus Torvalds return; 8318ebcc62cSKuniyuki Iwashima WRITE_ONCE(in_dev->mr_ifc_count, in_dev->mr_qrv ?: READ_ONCE(net->ipv4.sysctl_igmp_qrv)); 8321da177e4SLinus Torvalds igmp_ifc_start_timer(in_dev, 1); 8331da177e4SLinus Torvalds } 8341da177e4SLinus Torvalds 8351da177e4SLinus Torvalds 836e99e88a9SKees Cook static void igmp_timer_expire(struct timer_list *t) 8371da177e4SLinus Torvalds { 838e99e88a9SKees Cook struct ip_mc_list *im = from_timer(im, t, timer); 8391da177e4SLinus Torvalds struct in_device *in_dev = im->interface; 8401da177e4SLinus Torvalds 8411da177e4SLinus Torvalds spin_lock(&im->lock); 8421da177e4SLinus Torvalds im->tm_running = 0; 8431da177e4SLinus Torvalds 8444fb7253eSHangbin Liu if (im->unsolicit_count && --im->unsolicit_count) 845cab70040SWilliam Manley igmp_start_timer(im, unsolicited_report_interval(in_dev)); 8464fb7253eSHangbin Liu 8471da177e4SLinus Torvalds im->reporter = 1; 8481da177e4SLinus Torvalds spin_unlock(&im->lock); 8491da177e4SLinus Torvalds 8501da177e4SLinus Torvalds if (IGMP_V1_SEEN(in_dev)) 8511da177e4SLinus Torvalds igmp_send_report(in_dev, im, IGMP_HOST_MEMBERSHIP_REPORT); 8521da177e4SLinus Torvalds else if (IGMP_V2_SEEN(in_dev)) 8531da177e4SLinus Torvalds igmp_send_report(in_dev, im, IGMPV2_HOST_MEMBERSHIP_REPORT); 8541da177e4SLinus Torvalds else 8551da177e4SLinus Torvalds igmp_send_report(in_dev, im, IGMPV3_HOST_MEMBERSHIP_REPORT); 8561da177e4SLinus Torvalds 8571da177e4SLinus Torvalds ip_ma_put(im); 8581da177e4SLinus Torvalds } 8591da177e4SLinus Torvalds 860ad12583fSDavid L Stevens /* mark EXCLUDE-mode sources */ 861ea4d9e72SAl Viro static int igmp_xmarksources(struct ip_mc_list *pmc, int nsrcs, __be32 *srcs) 8621da177e4SLinus Torvalds { 8631da177e4SLinus Torvalds struct ip_sf_list *psf; 8641da177e4SLinus Torvalds int i, scount; 8651da177e4SLinus Torvalds 8661da177e4SLinus Torvalds scount = 0; 8671da177e4SLinus Torvalds for (psf = pmc->sources; psf; psf = psf->sf_next) { 8681da177e4SLinus Torvalds if (scount == nsrcs) 8691da177e4SLinus Torvalds break; 870ad12583fSDavid L Stevens for (i = 0; i < nsrcs; i++) { 871ad12583fSDavid L Stevens /* skip inactive filters */ 872e05c4ad3SYan, Zheng if (psf->sf_count[MCAST_INCLUDE] || 873ad12583fSDavid L Stevens pmc->sfcount[MCAST_EXCLUDE] != 874ad12583fSDavid L Stevens psf->sf_count[MCAST_EXCLUDE]) 875ce713ee5SRongQing.Li break; 876ad12583fSDavid L Stevens if (srcs[i] == psf->sf_inaddr) { 877ad12583fSDavid L Stevens scount++; 878ad12583fSDavid L Stevens break; 879ad12583fSDavid L Stevens } 880ad12583fSDavid L Stevens } 881ad12583fSDavid L Stevens } 882ad12583fSDavid L Stevens pmc->gsquery = 0; 883ad12583fSDavid L Stevens if (scount == nsrcs) /* all sources excluded */ 884ad12583fSDavid L Stevens return 0; 885ad12583fSDavid L Stevens return 1; 886ad12583fSDavid L Stevens } 887ad12583fSDavid L Stevens 888ea4d9e72SAl Viro static int igmp_marksources(struct ip_mc_list *pmc, int nsrcs, __be32 *srcs) 889ad12583fSDavid L Stevens { 890ad12583fSDavid L Stevens struct ip_sf_list *psf; 891ad12583fSDavid L Stevens int i, scount; 892ad12583fSDavid L Stevens 893ad12583fSDavid L Stevens if (pmc->sfmode == MCAST_EXCLUDE) 894ad12583fSDavid L Stevens return igmp_xmarksources(pmc, nsrcs, srcs); 895ad12583fSDavid L Stevens 896ad12583fSDavid L Stevens /* mark INCLUDE-mode sources */ 897ad12583fSDavid L Stevens scount = 0; 898ad12583fSDavid L Stevens for (psf = pmc->sources; psf; psf = psf->sf_next) { 899ad12583fSDavid L Stevens if (scount == nsrcs) 900ad12583fSDavid L Stevens break; 9011da177e4SLinus Torvalds for (i = 0; i < nsrcs; i++) 9021da177e4SLinus Torvalds if (srcs[i] == psf->sf_inaddr) { 9031da177e4SLinus Torvalds psf->sf_gsresp = 1; 9041da177e4SLinus Torvalds scount++; 9051da177e4SLinus Torvalds break; 9061da177e4SLinus Torvalds } 9071da177e4SLinus Torvalds } 908ad12583fSDavid L Stevens if (!scount) { 909ad12583fSDavid L Stevens pmc->gsquery = 0; 910ad12583fSDavid L Stevens return 0; 911ad12583fSDavid L Stevens } 912ad12583fSDavid L Stevens pmc->gsquery = 1; 913ad12583fSDavid L Stevens return 1; 9141da177e4SLinus Torvalds } 9151da177e4SLinus Torvalds 916d679c532SEric Dumazet /* return true if packet was dropped */ 917d679c532SEric Dumazet static bool igmp_heard_report(struct in_device *in_dev, __be32 group) 9181da177e4SLinus Torvalds { 9191da177e4SLinus Torvalds struct ip_mc_list *im; 92087a8a2aeSNikolay Borisov struct net *net = dev_net(in_dev->dev); 9211da177e4SLinus Torvalds 9221da177e4SLinus Torvalds /* Timers are only set for non-local groups */ 9231da177e4SLinus Torvalds 9241da177e4SLinus Torvalds if (group == IGMP_ALL_HOSTS) 925d679c532SEric Dumazet return false; 926f6da2267SKuniyuki Iwashima if (ipv4_is_local_multicast(group) && 927f6da2267SKuniyuki Iwashima !READ_ONCE(net->ipv4.sysctl_igmp_llm_reports)) 928df2cf4a7SPhilip Downey return false; 9291da177e4SLinus Torvalds 9301d7138deSEric Dumazet rcu_read_lock(); 9311d7138deSEric Dumazet for_each_pmc_rcu(in_dev, im) { 9321da177e4SLinus Torvalds if (im->multiaddr == group) { 9331da177e4SLinus Torvalds igmp_stop_timer(im); 9341da177e4SLinus Torvalds break; 9351da177e4SLinus Torvalds } 9361da177e4SLinus Torvalds } 9371d7138deSEric Dumazet rcu_read_unlock(); 938d679c532SEric Dumazet return false; 9391da177e4SLinus Torvalds } 9401da177e4SLinus Torvalds 941d679c532SEric Dumazet /* return true if packet was dropped */ 942d679c532SEric Dumazet static bool igmp_heard_query(struct in_device *in_dev, struct sk_buff *skb, 9431da177e4SLinus Torvalds int len) 9441da177e4SLinus Torvalds { 945d9edf9e2SArnaldo Carvalho de Melo struct igmphdr *ih = igmp_hdr(skb); 946d9edf9e2SArnaldo Carvalho de Melo struct igmpv3_query *ih3 = igmpv3_query_hdr(skb); 9471da177e4SLinus Torvalds struct ip_mc_list *im; 94863007727SAl Viro __be32 group = ih->group; 9491da177e4SLinus Torvalds int max_delay; 9501da177e4SLinus Torvalds int mark = 0; 95187a8a2aeSNikolay Borisov struct net *net = dev_net(in_dev->dev); 9521da177e4SLinus Torvalds 9531da177e4SLinus Torvalds 9545b7c8406SDavid Stevens if (len == 8) { 9551da177e4SLinus Torvalds if (ih->code == 0) { 9561da177e4SLinus Torvalds /* Alas, old v1 router presents here. */ 9571da177e4SLinus Torvalds 958436f7c20SFabian Frederick max_delay = IGMP_QUERY_RESPONSE_INTERVAL; 9591da177e4SLinus Torvalds in_dev->mr_v1_seen = jiffies + 960966c37f2SHangbin Liu (in_dev->mr_qrv * in_dev->mr_qi) + 961966c37f2SHangbin Liu in_dev->mr_qri; 9621da177e4SLinus Torvalds group = 0; 9631da177e4SLinus Torvalds } else { 9641da177e4SLinus Torvalds /* v2 router present */ 9651da177e4SLinus Torvalds max_delay = ih->code*(HZ/IGMP_TIMER_SCALE); 9661da177e4SLinus Torvalds in_dev->mr_v2_seen = jiffies + 967966c37f2SHangbin Liu (in_dev->mr_qrv * in_dev->mr_qi) + 968966c37f2SHangbin Liu in_dev->mr_qri; 9691da177e4SLinus Torvalds } 9701da177e4SLinus Torvalds /* cancel the interface change timer */ 9714a2b285eSEric Dumazet WRITE_ONCE(in_dev->mr_ifc_count, 0); 9721da177e4SLinus Torvalds if (del_timer(&in_dev->mr_ifc_timer)) 9731da177e4SLinus Torvalds __in_dev_put(in_dev); 9741da177e4SLinus Torvalds /* clear deleted report items */ 9751da177e4SLinus Torvalds igmpv3_clear_delrec(in_dev); 9761da177e4SLinus Torvalds } else if (len < 12) { 977d679c532SEric Dumazet return true; /* ignore bogus packet; freed by caller */ 9785b7c8406SDavid Stevens } else if (IGMP_V1_SEEN(in_dev)) { 9795b7c8406SDavid Stevens /* This is a v3 query with v1 queriers present */ 980436f7c20SFabian Frederick max_delay = IGMP_QUERY_RESPONSE_INTERVAL; 9815b7c8406SDavid Stevens group = 0; 9825b7c8406SDavid Stevens } else if (IGMP_V2_SEEN(in_dev)) { 9835b7c8406SDavid Stevens /* this is a v3 query with v2 queriers present; 9845b7c8406SDavid Stevens * Interpretation of the max_delay code is problematic here. 9855b7c8406SDavid Stevens * A real v2 host would use ih_code directly, while v3 has a 9865b7c8406SDavid Stevens * different encoding. We use the v3 encoding as more likely 9875b7c8406SDavid Stevens * to be intended in a v3 query. 9885b7c8406SDavid Stevens */ 9895b7c8406SDavid Stevens max_delay = IGMPV3_MRC(ih3->code)*(HZ/IGMP_TIMER_SCALE); 990a8c1f65cSBen Hutchings if (!max_delay) 991a8c1f65cSBen Hutchings max_delay = 1; /* can't mod w/ 0 */ 9921da177e4SLinus Torvalds } else { /* v3 */ 9931da177e4SLinus Torvalds if (!pskb_may_pull(skb, sizeof(struct igmpv3_query))) 994d679c532SEric Dumazet return true; 9951da177e4SLinus Torvalds 996d9edf9e2SArnaldo Carvalho de Melo ih3 = igmpv3_query_hdr(skb); 9971da177e4SLinus Torvalds if (ih3->nsrcs) { 9981da177e4SLinus Torvalds if (!pskb_may_pull(skb, sizeof(struct igmpv3_query) 99963007727SAl Viro + ntohs(ih3->nsrcs)*sizeof(__be32))) 1000d679c532SEric Dumazet return true; 1001d9edf9e2SArnaldo Carvalho de Melo ih3 = igmpv3_query_hdr(skb); 10021da177e4SLinus Torvalds } 10031da177e4SLinus Torvalds 10041da177e4SLinus Torvalds max_delay = IGMPV3_MRC(ih3->code)*(HZ/IGMP_TIMER_SCALE); 10051da177e4SLinus Torvalds if (!max_delay) 10061da177e4SLinus Torvalds max_delay = 1; /* can't mod w/ 0 */ 10071da177e4SLinus Torvalds in_dev->mr_maxdelay = max_delay; 1008966c37f2SHangbin Liu 1009966c37f2SHangbin Liu /* RFC3376, 4.1.6. QRV and 4.1.7. QQIC, when the most recently 1010966c37f2SHangbin Liu * received value was zero, use the default or statically 1011966c37f2SHangbin Liu * configured value. 1012966c37f2SHangbin Liu */ 10138ebcc62cSKuniyuki Iwashima in_dev->mr_qrv = ih3->qrv ?: READ_ONCE(net->ipv4.sysctl_igmp_qrv); 1014966c37f2SHangbin Liu in_dev->mr_qi = IGMPV3_QQIC(ih3->qqic)*HZ ?: IGMP_QUERY_INTERVAL; 1015966c37f2SHangbin Liu 1016966c37f2SHangbin Liu /* RFC3376, 8.3. Query Response Interval: 1017966c37f2SHangbin Liu * The number of seconds represented by the [Query Response 1018966c37f2SHangbin Liu * Interval] must be less than the [Query Interval]. 1019966c37f2SHangbin Liu */ 1020966c37f2SHangbin Liu if (in_dev->mr_qri >= in_dev->mr_qi) 1021966c37f2SHangbin Liu in_dev->mr_qri = (in_dev->mr_qi/HZ - 1)*HZ; 1022966c37f2SHangbin Liu 10231da177e4SLinus Torvalds if (!group) { /* general query */ 10241da177e4SLinus Torvalds if (ih3->nsrcs) 1025b47bd8d2SDaniel Borkmann return true; /* no sources allowed */ 10261da177e4SLinus Torvalds igmp_gq_start_timer(in_dev); 1027d679c532SEric Dumazet return false; 10281da177e4SLinus Torvalds } 10291da177e4SLinus Torvalds /* mark sources to include, if group & source-specific */ 10301da177e4SLinus Torvalds mark = ih3->nsrcs != 0; 10311da177e4SLinus Torvalds } 10321da177e4SLinus Torvalds 10331da177e4SLinus Torvalds /* 10341da177e4SLinus Torvalds * - Start the timers in all of our membership records 10351da177e4SLinus Torvalds * that the query applies to for the interface on 10361da177e4SLinus Torvalds * which the query arrived excl. those that belong 10371da177e4SLinus Torvalds * to a "local" group (224.0.0.X) 10381da177e4SLinus Torvalds * - For timers already running check if they need to 10391da177e4SLinus Torvalds * be reset. 10401da177e4SLinus Torvalds * - Use the igmp->igmp_code field as the maximum 10411da177e4SLinus Torvalds * delay possible 10421da177e4SLinus Torvalds */ 10431d7138deSEric Dumazet rcu_read_lock(); 10441d7138deSEric Dumazet for_each_pmc_rcu(in_dev, im) { 1045ad12583fSDavid L Stevens int changed; 1046ad12583fSDavid L Stevens 10471da177e4SLinus Torvalds if (group && group != im->multiaddr) 10481da177e4SLinus Torvalds continue; 10491da177e4SLinus Torvalds if (im->multiaddr == IGMP_ALL_HOSTS) 10501da177e4SLinus Torvalds continue; 1051df2cf4a7SPhilip Downey if (ipv4_is_local_multicast(im->multiaddr) && 1052f6da2267SKuniyuki Iwashima !READ_ONCE(net->ipv4.sysctl_igmp_llm_reports)) 1053df2cf4a7SPhilip Downey continue; 10541da177e4SLinus Torvalds spin_lock_bh(&im->lock); 10551da177e4SLinus Torvalds if (im->tm_running) 10561da177e4SLinus Torvalds im->gsquery = im->gsquery && mark; 10571da177e4SLinus Torvalds else 10581da177e4SLinus Torvalds im->gsquery = mark; 1059ad12583fSDavid L Stevens changed = !im->gsquery || 10601da177e4SLinus Torvalds igmp_marksources(im, ntohs(ih3->nsrcs), ih3->srcs); 10611da177e4SLinus Torvalds spin_unlock_bh(&im->lock); 1062ad12583fSDavid L Stevens if (changed) 10631da177e4SLinus Torvalds igmp_mod_timer(im, max_delay); 10641da177e4SLinus Torvalds } 10651d7138deSEric Dumazet rcu_read_unlock(); 1066d679c532SEric Dumazet return false; 10671da177e4SLinus Torvalds } 10681da177e4SLinus Torvalds 10699a57a9d2SEric Dumazet /* called in rcu_read_lock() section */ 10701da177e4SLinus Torvalds int igmp_rcv(struct sk_buff *skb) 10711da177e4SLinus Torvalds { 10721da177e4SLinus Torvalds /* This basically follows the spec line by line -- see RFC1112 */ 10731da177e4SLinus Torvalds struct igmphdr *ih; 1074c7b725beSDavid Ahern struct net_device *dev = skb->dev; 1075c7b725beSDavid Ahern struct in_device *in_dev; 10761da177e4SLinus Torvalds int len = skb->len; 1077d679c532SEric Dumazet bool dropped = true; 10781da177e4SLinus Torvalds 1079c7b725beSDavid Ahern if (netif_is_l3_master(dev)) { 1080c7b725beSDavid Ahern dev = dev_get_by_index_rcu(dev_net(dev), IPCB(skb)->iif); 1081c7b725beSDavid Ahern if (!dev) 1082c7b725beSDavid Ahern goto drop; 1083c7b725beSDavid Ahern } 1084c7b725beSDavid Ahern 1085c7b725beSDavid Ahern in_dev = __in_dev_get_rcu(dev); 108651456b29SIan Morris if (!in_dev) 1087cd557bc1SDenis V. Lunev goto drop; 10881da177e4SLinus Torvalds 1089fb286bb2SHerbert Xu if (!pskb_may_pull(skb, sizeof(struct igmphdr))) 10909a57a9d2SEric Dumazet goto drop; 1091fb286bb2SHerbert Xu 1092de08dc1aSTom Herbert if (skb_checksum_simple_validate(skb)) 10939a57a9d2SEric Dumazet goto drop; 10941da177e4SLinus Torvalds 1095d9edf9e2SArnaldo Carvalho de Melo ih = igmp_hdr(skb); 10961da177e4SLinus Torvalds switch (ih->type) { 10971da177e4SLinus Torvalds case IGMP_HOST_MEMBERSHIP_QUERY: 1098d679c532SEric Dumazet dropped = igmp_heard_query(in_dev, skb, len); 10991da177e4SLinus Torvalds break; 11001da177e4SLinus Torvalds case IGMP_HOST_MEMBERSHIP_REPORT: 11011da177e4SLinus Torvalds case IGMPV2_HOST_MEMBERSHIP_REPORT: 11021da177e4SLinus Torvalds /* Is it our report looped back? */ 1103c7537967SDavid S. Miller if (rt_is_output_route(skb_rtable(skb))) 11041da177e4SLinus Torvalds break; 110524c69275SDavid Stevens /* don't rely on MC router hearing unicast reports */ 110624c69275SDavid Stevens if (skb->pkt_type == PACKET_MULTICAST || 110724c69275SDavid Stevens skb->pkt_type == PACKET_BROADCAST) 1108d679c532SEric Dumazet dropped = igmp_heard_report(in_dev, ih->group); 11091da177e4SLinus Torvalds break; 11101da177e4SLinus Torvalds case IGMP_PIM: 11111da177e4SLinus Torvalds #ifdef CONFIG_IP_PIMSM_V1 11121da177e4SLinus Torvalds return pim_rcv_v1(skb); 11131da177e4SLinus Torvalds #endif 1114c6b471e6SHerbert Xu case IGMPV3_HOST_MEMBERSHIP_REPORT: 11151da177e4SLinus Torvalds case IGMP_DVMRP: 11161da177e4SLinus Torvalds case IGMP_TRACE: 11171da177e4SLinus Torvalds case IGMP_HOST_LEAVE_MESSAGE: 11181da177e4SLinus Torvalds case IGMP_MTRACE: 11191da177e4SLinus Torvalds case IGMP_MTRACE_RESP: 11201da177e4SLinus Torvalds break; 11211da177e4SLinus Torvalds default: 1122dd1c1853SLinus Torvalds break; 11231da177e4SLinus Torvalds } 1124fb286bb2SHerbert Xu 1125cd557bc1SDenis V. Lunev drop: 1126d679c532SEric Dumazet if (dropped) 11271da177e4SLinus Torvalds kfree_skb(skb); 1128d679c532SEric Dumazet else 1129d679c532SEric Dumazet consume_skb(skb); 11301da177e4SLinus Torvalds return 0; 11311da177e4SLinus Torvalds } 11321da177e4SLinus Torvalds 11331da177e4SLinus Torvalds #endif 11341da177e4SLinus Torvalds 11351da177e4SLinus Torvalds 11361da177e4SLinus Torvalds /* 11371da177e4SLinus Torvalds * Add a filter to a device 11381da177e4SLinus Torvalds */ 11391da177e4SLinus Torvalds 114063007727SAl Viro static void ip_mc_filter_add(struct in_device *in_dev, __be32 addr) 11411da177e4SLinus Torvalds { 11421da177e4SLinus Torvalds char buf[MAX_ADDR_LEN]; 11431da177e4SLinus Torvalds struct net_device *dev = in_dev->dev; 11441da177e4SLinus Torvalds 11451da177e4SLinus Torvalds /* Checking for IFF_MULTICAST here is WRONG-WRONG-WRONG. 11461da177e4SLinus Torvalds We will get multicast token leakage, when IFF_MULTICAST 1147b81693d9SJiri Pirko is changed. This check should be done in ndo_set_rx_mode 11481da177e4SLinus Torvalds routine. Something sort of: 11491da177e4SLinus Torvalds if (dev->mc_list && dev->flags&IFF_MULTICAST) { do it; } 11501da177e4SLinus Torvalds --ANK 11511da177e4SLinus Torvalds */ 11521da177e4SLinus Torvalds if (arp_mc_map(addr, buf, dev, 0) == 0) 115322bedad3SJiri Pirko dev_mc_add(dev, buf); 11541da177e4SLinus Torvalds } 11551da177e4SLinus Torvalds 11561da177e4SLinus Torvalds /* 11571da177e4SLinus Torvalds * Remove a filter from a device 11581da177e4SLinus Torvalds */ 11591da177e4SLinus Torvalds 116063007727SAl Viro static void ip_mc_filter_del(struct in_device *in_dev, __be32 addr) 11611da177e4SLinus Torvalds { 11621da177e4SLinus Torvalds char buf[MAX_ADDR_LEN]; 11631da177e4SLinus Torvalds struct net_device *dev = in_dev->dev; 11641da177e4SLinus Torvalds 11651da177e4SLinus Torvalds if (arp_mc_map(addr, buf, dev, 0) == 0) 116622bedad3SJiri Pirko dev_mc_del(dev, buf); 11671da177e4SLinus Torvalds } 11681da177e4SLinus Torvalds 11691da177e4SLinus Torvalds #ifdef CONFIG_IP_MULTICAST 11701da177e4SLinus Torvalds /* 11711da177e4SLinus Torvalds * deleted ip_mc_list manipulation 11721da177e4SLinus Torvalds */ 11739fb20801SFlorian Fainelli static void igmpv3_add_delrec(struct in_device *in_dev, struct ip_mc_list *im, 11749fb20801SFlorian Fainelli gfp_t gfp) 11751da177e4SLinus Torvalds { 11761da177e4SLinus Torvalds struct ip_mc_list *pmc; 1177165094afSNikolay Borisov struct net *net = dev_net(in_dev->dev); 11781da177e4SLinus Torvalds 11791da177e4SLinus Torvalds /* this is an "ip_mc_list" for convenience; only the fields below 11801da177e4SLinus Torvalds * are actually used. In particular, the refcnt and users are not 11811da177e4SLinus Torvalds * used for management of the delete list. Using the same structure 11821da177e4SLinus Torvalds * for deleted items allows change reports to use common code with 11831da177e4SLinus Torvalds * non-deleted or query-response MCA's. 11841da177e4SLinus Torvalds */ 11859fb20801SFlorian Fainelli pmc = kzalloc(sizeof(*pmc), gfp); 11861da177e4SLinus Torvalds if (!pmc) 11871da177e4SLinus Torvalds return; 1188b4846fc3SWANG Cong spin_lock_init(&pmc->lock); 11891da177e4SLinus Torvalds spin_lock_bh(&im->lock); 11901da177e4SLinus Torvalds pmc->interface = im->interface; 11911da177e4SLinus Torvalds in_dev_hold(in_dev); 11921da177e4SLinus Torvalds pmc->multiaddr = im->multiaddr; 11938ebcc62cSKuniyuki Iwashima pmc->crcount = in_dev->mr_qrv ?: READ_ONCE(net->ipv4.sysctl_igmp_qrv); 11941da177e4SLinus Torvalds pmc->sfmode = im->sfmode; 11951da177e4SLinus Torvalds if (pmc->sfmode == MCAST_INCLUDE) { 11961da177e4SLinus Torvalds struct ip_sf_list *psf; 11971da177e4SLinus Torvalds 11981da177e4SLinus Torvalds pmc->tomb = im->tomb; 11991da177e4SLinus Torvalds pmc->sources = im->sources; 12001da177e4SLinus Torvalds im->tomb = im->sources = NULL; 12011da177e4SLinus Torvalds for (psf = pmc->sources; psf; psf = psf->sf_next) 12021da177e4SLinus Torvalds psf->sf_crcount = pmc->crcount; 12031da177e4SLinus Torvalds } 12041da177e4SLinus Torvalds spin_unlock_bh(&im->lock); 12051da177e4SLinus Torvalds 12061da177e4SLinus Torvalds spin_lock_bh(&in_dev->mc_tomb_lock); 12071da177e4SLinus Torvalds pmc->next = in_dev->mc_tomb; 12081da177e4SLinus Torvalds in_dev->mc_tomb = pmc; 12091da177e4SLinus Torvalds spin_unlock_bh(&in_dev->mc_tomb_lock); 12101da177e4SLinus Torvalds } 12111da177e4SLinus Torvalds 121224803f38SHangbin Liu /* 121324803f38SHangbin Liu * restore ip_mc_list deleted records 121424803f38SHangbin Liu */ 121524803f38SHangbin Liu static void igmpv3_del_delrec(struct in_device *in_dev, struct ip_mc_list *im) 12161da177e4SLinus Torvalds { 12171da177e4SLinus Torvalds struct ip_mc_list *pmc, *pmc_prev; 121824803f38SHangbin Liu struct ip_sf_list *psf; 121924803f38SHangbin Liu struct net *net = dev_net(in_dev->dev); 122024803f38SHangbin Liu __be32 multiaddr = im->multiaddr; 12211da177e4SLinus Torvalds 12221da177e4SLinus Torvalds spin_lock_bh(&in_dev->mc_tomb_lock); 12231da177e4SLinus Torvalds pmc_prev = NULL; 12241da177e4SLinus Torvalds for (pmc = in_dev->mc_tomb; pmc; pmc = pmc->next) { 12251da177e4SLinus Torvalds if (pmc->multiaddr == multiaddr) 12261da177e4SLinus Torvalds break; 12271da177e4SLinus Torvalds pmc_prev = pmc; 12281da177e4SLinus Torvalds } 12291da177e4SLinus Torvalds if (pmc) { 12301da177e4SLinus Torvalds if (pmc_prev) 12311da177e4SLinus Torvalds pmc_prev->next = pmc->next; 12321da177e4SLinus Torvalds else 12331da177e4SLinus Torvalds in_dev->mc_tomb = pmc->next; 12341da177e4SLinus Torvalds } 12351da177e4SLinus Torvalds spin_unlock_bh(&in_dev->mc_tomb_lock); 123624803f38SHangbin Liu 123724803f38SHangbin Liu spin_lock_bh(&im->lock); 12381da177e4SLinus Torvalds if (pmc) { 123924803f38SHangbin Liu im->interface = pmc->interface; 124008d3ffccSHangbin Liu if (im->sfmode == MCAST_INCLUDE) { 1241e5b1c6c6SEric Dumazet swap(im->tomb, pmc->tomb); 1242e5b1c6c6SEric Dumazet swap(im->sources, pmc->sources); 124324803f38SHangbin Liu for (psf = im->sources; psf; psf = psf->sf_next) 12448ebcc62cSKuniyuki Iwashima psf->sf_crcount = in_dev->mr_qrv ?: 12458ebcc62cSKuniyuki Iwashima READ_ONCE(net->ipv4.sysctl_igmp_qrv); 12466e2059b5SHangbin Liu } else { 12478ebcc62cSKuniyuki Iwashima im->crcount = in_dev->mr_qrv ?: 12488ebcc62cSKuniyuki Iwashima READ_ONCE(net->ipv4.sysctl_igmp_qrv); 12491da177e4SLinus Torvalds } 12501da177e4SLinus Torvalds in_dev_put(pmc->interface); 12513580d04aSEric Dumazet kfree_pmc(pmc); 12521da177e4SLinus Torvalds } 125324803f38SHangbin Liu spin_unlock_bh(&im->lock); 12541da177e4SLinus Torvalds } 12551da177e4SLinus Torvalds 125624803f38SHangbin Liu /* 125724803f38SHangbin Liu * flush ip_mc_list deleted records 125824803f38SHangbin Liu */ 12591da177e4SLinus Torvalds static void igmpv3_clear_delrec(struct in_device *in_dev) 12601da177e4SLinus Torvalds { 12611da177e4SLinus Torvalds struct ip_mc_list *pmc, *nextpmc; 12621da177e4SLinus Torvalds 12631da177e4SLinus Torvalds spin_lock_bh(&in_dev->mc_tomb_lock); 12641da177e4SLinus Torvalds pmc = in_dev->mc_tomb; 12651da177e4SLinus Torvalds in_dev->mc_tomb = NULL; 12661da177e4SLinus Torvalds spin_unlock_bh(&in_dev->mc_tomb_lock); 12671da177e4SLinus Torvalds 12681da177e4SLinus Torvalds for (; pmc; pmc = nextpmc) { 12691da177e4SLinus Torvalds nextpmc = pmc->next; 12701da177e4SLinus Torvalds ip_mc_clear_src(pmc); 12711da177e4SLinus Torvalds in_dev_put(pmc->interface); 12723580d04aSEric Dumazet kfree_pmc(pmc); 12731da177e4SLinus Torvalds } 12741da177e4SLinus Torvalds /* clear dead sources, too */ 12751d7138deSEric Dumazet rcu_read_lock(); 12761d7138deSEric Dumazet for_each_pmc_rcu(in_dev, pmc) { 12773580d04aSEric Dumazet struct ip_sf_list *psf; 12781da177e4SLinus Torvalds 12791da177e4SLinus Torvalds spin_lock_bh(&pmc->lock); 12801da177e4SLinus Torvalds psf = pmc->tomb; 12811da177e4SLinus Torvalds pmc->tomb = NULL; 12821da177e4SLinus Torvalds spin_unlock_bh(&pmc->lock); 12833580d04aSEric Dumazet ip_sf_list_clear_all(psf); 12841da177e4SLinus Torvalds } 12851d7138deSEric Dumazet rcu_read_unlock(); 12861da177e4SLinus Torvalds } 12871da177e4SLinus Torvalds #endif 12881da177e4SLinus Torvalds 12899fb20801SFlorian Fainelli static void __igmp_group_dropped(struct ip_mc_list *im, gfp_t gfp) 12901da177e4SLinus Torvalds { 12911da177e4SLinus Torvalds struct in_device *in_dev = im->interface; 12921da177e4SLinus Torvalds #ifdef CONFIG_IP_MULTICAST 129387a8a2aeSNikolay Borisov struct net *net = dev_net(in_dev->dev); 12941da177e4SLinus Torvalds int reporter; 12951da177e4SLinus Torvalds #endif 12961da177e4SLinus Torvalds 12971da177e4SLinus Torvalds if (im->loaded) { 12981da177e4SLinus Torvalds im->loaded = 0; 12991da177e4SLinus Torvalds ip_mc_filter_del(in_dev, im->multiaddr); 13001da177e4SLinus Torvalds } 13011da177e4SLinus Torvalds 13021da177e4SLinus Torvalds #ifdef CONFIG_IP_MULTICAST 13031da177e4SLinus Torvalds if (im->multiaddr == IGMP_ALL_HOSTS) 13041da177e4SLinus Torvalds return; 1305f6da2267SKuniyuki Iwashima if (ipv4_is_local_multicast(im->multiaddr) && 1306f6da2267SKuniyuki Iwashima !READ_ONCE(net->ipv4.sysctl_igmp_llm_reports)) 1307df2cf4a7SPhilip Downey return; 13081da177e4SLinus Torvalds 13091da177e4SLinus Torvalds reporter = im->reporter; 13101da177e4SLinus Torvalds igmp_stop_timer(im); 13111da177e4SLinus Torvalds 13121da177e4SLinus Torvalds if (!in_dev->dead) { 13131da177e4SLinus Torvalds if (IGMP_V1_SEEN(in_dev)) 131424cf3af3SVeaceslav Falico return; 13151da177e4SLinus Torvalds if (IGMP_V2_SEEN(in_dev)) { 13161da177e4SLinus Torvalds if (reporter) 13171da177e4SLinus Torvalds igmp_send_report(in_dev, im, IGMP_HOST_LEAVE_MESSAGE); 131824cf3af3SVeaceslav Falico return; 13191da177e4SLinus Torvalds } 13201da177e4SLinus Torvalds /* IGMPv3 */ 13219fb20801SFlorian Fainelli igmpv3_add_delrec(in_dev, im, gfp); 13221da177e4SLinus Torvalds 13231da177e4SLinus Torvalds igmp_ifc_event(in_dev); 13241da177e4SLinus Torvalds } 13251da177e4SLinus Torvalds #endif 13261da177e4SLinus Torvalds } 13271da177e4SLinus Torvalds 13289fb20801SFlorian Fainelli static void igmp_group_dropped(struct ip_mc_list *im) 13299fb20801SFlorian Fainelli { 13309fb20801SFlorian Fainelli __igmp_group_dropped(im, GFP_KERNEL); 13319fb20801SFlorian Fainelli } 13329fb20801SFlorian Fainelli 13330ae0d60aSHangbin Liu static void igmp_group_added(struct ip_mc_list *im) 13341da177e4SLinus Torvalds { 13351da177e4SLinus Torvalds struct in_device *in_dev = im->interface; 1336dcd87999SNikolay Borisov #ifdef CONFIG_IP_MULTICAST 133787a8a2aeSNikolay Borisov struct net *net = dev_net(in_dev->dev); 1338dcd87999SNikolay Borisov #endif 13391da177e4SLinus Torvalds 13401da177e4SLinus Torvalds if (im->loaded == 0) { 13411da177e4SLinus Torvalds im->loaded = 1; 13421da177e4SLinus Torvalds ip_mc_filter_add(in_dev, im->multiaddr); 13431da177e4SLinus Torvalds } 13441da177e4SLinus Torvalds 13451da177e4SLinus Torvalds #ifdef CONFIG_IP_MULTICAST 13461da177e4SLinus Torvalds if (im->multiaddr == IGMP_ALL_HOSTS) 13471da177e4SLinus Torvalds return; 1348f6da2267SKuniyuki Iwashima if (ipv4_is_local_multicast(im->multiaddr) && 1349f6da2267SKuniyuki Iwashima !READ_ONCE(net->ipv4.sysctl_igmp_llm_reports)) 1350df2cf4a7SPhilip Downey return; 13511da177e4SLinus Torvalds 13521da177e4SLinus Torvalds if (in_dev->dead) 13531da177e4SLinus Torvalds return; 1354ff06525fSHangbin Liu 13558ebcc62cSKuniyuki Iwashima im->unsolicit_count = READ_ONCE(net->ipv4.sysctl_igmp_qrv); 13561da177e4SLinus Torvalds if (IGMP_V1_SEEN(in_dev) || IGMP_V2_SEEN(in_dev)) { 13571da177e4SLinus Torvalds spin_lock_bh(&im->lock); 1358436f7c20SFabian Frederick igmp_start_timer(im, IGMP_INITIAL_REPORT_DELAY); 13591da177e4SLinus Torvalds spin_unlock_bh(&im->lock); 13601da177e4SLinus Torvalds return; 13611da177e4SLinus Torvalds } 13621da177e4SLinus Torvalds /* else, v3 */ 13631da177e4SLinus Torvalds 13646e2059b5SHangbin Liu /* Based on RFC3376 5.1, for newly added INCLUDE SSM, we should 13656e2059b5SHangbin Liu * not send filter-mode change record as the mode should be from 13666e2059b5SHangbin Liu * IN() to IN(A). 13676e2059b5SHangbin Liu */ 13680ae0d60aSHangbin Liu if (im->sfmode == MCAST_EXCLUDE) 13698ebcc62cSKuniyuki Iwashima im->crcount = in_dev->mr_qrv ?: READ_ONCE(net->ipv4.sysctl_igmp_qrv); 13706e2059b5SHangbin Liu 13711da177e4SLinus Torvalds igmp_ifc_event(in_dev); 13721da177e4SLinus Torvalds #endif 13731da177e4SLinus Torvalds } 13741da177e4SLinus Torvalds 13751da177e4SLinus Torvalds 13761da177e4SLinus Torvalds /* 13771da177e4SLinus Torvalds * Multicast list managers 13781da177e4SLinus Torvalds */ 13791da177e4SLinus Torvalds 1380e9897071SEric Dumazet static u32 ip_mc_hash(const struct ip_mc_list *im) 1381e9897071SEric Dumazet { 1382c70eba74SEric Dumazet return hash_32((__force u32)im->multiaddr, MC_HASH_SZ_LOG); 1383e9897071SEric Dumazet } 1384e9897071SEric Dumazet 1385e9897071SEric Dumazet static void ip_mc_hash_add(struct in_device *in_dev, 1386e9897071SEric Dumazet struct ip_mc_list *im) 1387e9897071SEric Dumazet { 1388e9897071SEric Dumazet struct ip_mc_list __rcu **mc_hash; 1389e9897071SEric Dumazet u32 hash; 1390e9897071SEric Dumazet 1391e9897071SEric Dumazet mc_hash = rtnl_dereference(in_dev->mc_hash); 1392e9897071SEric Dumazet if (mc_hash) { 1393e9897071SEric Dumazet hash = ip_mc_hash(im); 1394c70eba74SEric Dumazet im->next_hash = mc_hash[hash]; 1395e9897071SEric Dumazet rcu_assign_pointer(mc_hash[hash], im); 1396e9897071SEric Dumazet return; 1397e9897071SEric Dumazet } 1398e9897071SEric Dumazet 1399e9897071SEric Dumazet /* do not use a hash table for small number of items */ 1400e9897071SEric Dumazet if (in_dev->mc_count < 4) 1401e9897071SEric Dumazet return; 1402e9897071SEric Dumazet 1403e9897071SEric Dumazet mc_hash = kzalloc(sizeof(struct ip_mc_list *) << MC_HASH_SZ_LOG, 1404e9897071SEric Dumazet GFP_KERNEL); 1405e9897071SEric Dumazet if (!mc_hash) 1406e9897071SEric Dumazet return; 1407e9897071SEric Dumazet 1408e9897071SEric Dumazet for_each_pmc_rtnl(in_dev, im) { 1409e9897071SEric Dumazet hash = ip_mc_hash(im); 1410c70eba74SEric Dumazet im->next_hash = mc_hash[hash]; 1411e9897071SEric Dumazet RCU_INIT_POINTER(mc_hash[hash], im); 1412e9897071SEric Dumazet } 1413e9897071SEric Dumazet 1414e9897071SEric Dumazet rcu_assign_pointer(in_dev->mc_hash, mc_hash); 1415e9897071SEric Dumazet } 1416e9897071SEric Dumazet 1417e9897071SEric Dumazet static void ip_mc_hash_remove(struct in_device *in_dev, 1418e9897071SEric Dumazet struct ip_mc_list *im) 1419e9897071SEric Dumazet { 1420e9897071SEric Dumazet struct ip_mc_list __rcu **mc_hash = rtnl_dereference(in_dev->mc_hash); 1421e9897071SEric Dumazet struct ip_mc_list *aux; 1422e9897071SEric Dumazet 1423e9897071SEric Dumazet if (!mc_hash) 1424e9897071SEric Dumazet return; 1425e9897071SEric Dumazet mc_hash += ip_mc_hash(im); 1426e9897071SEric Dumazet while ((aux = rtnl_dereference(*mc_hash)) != im) 1427e9897071SEric Dumazet mc_hash = &aux->next_hash; 1428e9897071SEric Dumazet *mc_hash = im->next_hash; 1429e9897071SEric Dumazet } 1430e9897071SEric Dumazet 14311da177e4SLinus Torvalds 14321da177e4SLinus Torvalds /* 14331da177e4SLinus Torvalds * A socket has joined a multicast group on device dev. 14341da177e4SLinus Torvalds */ 14359fb20801SFlorian Fainelli static void ____ip_mc_inc_group(struct in_device *in_dev, __be32 addr, 14369fb20801SFlorian Fainelli unsigned int mode, gfp_t gfp) 14371da177e4SLinus Torvalds { 14381da177e4SLinus Torvalds struct ip_mc_list *im; 14391da177e4SLinus Torvalds 14401da177e4SLinus Torvalds ASSERT_RTNL(); 14411da177e4SLinus Torvalds 14421d7138deSEric Dumazet for_each_pmc_rtnl(in_dev, im) { 14431da177e4SLinus Torvalds if (im->multiaddr == addr) { 14441da177e4SLinus Torvalds im->users++; 14456e2059b5SHangbin Liu ip_mc_add_src(in_dev, &addr, mode, 0, NULL, 0); 14461da177e4SLinus Torvalds goto out; 14471da177e4SLinus Torvalds } 14481da177e4SLinus Torvalds } 14491da177e4SLinus Torvalds 14509fb20801SFlorian Fainelli im = kzalloc(sizeof(*im), gfp); 14511da177e4SLinus Torvalds if (!im) 14521da177e4SLinus Torvalds goto out; 14531da177e4SLinus Torvalds 14541da177e4SLinus Torvalds im->users = 1; 14551da177e4SLinus Torvalds im->interface = in_dev; 14561da177e4SLinus Torvalds in_dev_hold(in_dev); 14571da177e4SLinus Torvalds im->multiaddr = addr; 14581da177e4SLinus Torvalds /* initial mode is (EX, empty) */ 14596e2059b5SHangbin Liu im->sfmode = mode; 14606e2059b5SHangbin Liu im->sfcount[mode] = 1; 14618851ab52SReshetova, Elena refcount_set(&im->refcnt, 1); 14621da177e4SLinus Torvalds spin_lock_init(&im->lock); 14631da177e4SLinus Torvalds #ifdef CONFIG_IP_MULTICAST 1464e99e88a9SKees Cook timer_setup(&im->timer, igmp_timer_expire, 0); 14651da177e4SLinus Torvalds #endif 14661d7138deSEric Dumazet 14671d7138deSEric Dumazet im->next_rcu = in_dev->mc_list; 1468b8bae41eSRami Rosen in_dev->mc_count++; 1469cf778b00SEric Dumazet rcu_assign_pointer(in_dev->mc_list, im); 14701d7138deSEric Dumazet 1471e9897071SEric Dumazet ip_mc_hash_add(in_dev, im); 1472e9897071SEric Dumazet 14731da177e4SLinus Torvalds #ifdef CONFIG_IP_MULTICAST 147424803f38SHangbin Liu igmpv3_del_delrec(in_dev, im); 14751da177e4SLinus Torvalds #endif 14760ae0d60aSHangbin Liu igmp_group_added(im); 14771da177e4SLinus Torvalds if (!in_dev->dead) 14781da177e4SLinus Torvalds ip_rt_multicast_event(in_dev); 14791da177e4SLinus Torvalds out: 14801da177e4SLinus Torvalds return; 14811da177e4SLinus Torvalds } 14826e2059b5SHangbin Liu 14839fb20801SFlorian Fainelli void __ip_mc_inc_group(struct in_device *in_dev, __be32 addr, gfp_t gfp) 14849fb20801SFlorian Fainelli { 14859fb20801SFlorian Fainelli ____ip_mc_inc_group(in_dev, addr, MCAST_EXCLUDE, gfp); 14869fb20801SFlorian Fainelli } 14879fb20801SFlorian Fainelli EXPORT_SYMBOL(__ip_mc_inc_group); 14889fb20801SFlorian Fainelli 14896e2059b5SHangbin Liu void ip_mc_inc_group(struct in_device *in_dev, __be32 addr) 14906e2059b5SHangbin Liu { 1491a1c4cd67SLi RongQing __ip_mc_inc_group(in_dev, addr, GFP_KERNEL); 14926e2059b5SHangbin Liu } 14934bc2f18bSEric Dumazet EXPORT_SYMBOL(ip_mc_inc_group); 14941da177e4SLinus Torvalds 14959afd85c9SLinus Lüssing static int ip_mc_check_iphdr(struct sk_buff *skb) 14969afd85c9SLinus Lüssing { 14979afd85c9SLinus Lüssing const struct iphdr *iph; 14989afd85c9SLinus Lüssing unsigned int len; 14999afd85c9SLinus Lüssing unsigned int offset = skb_network_offset(skb) + sizeof(*iph); 15009afd85c9SLinus Lüssing 15019afd85c9SLinus Lüssing if (!pskb_may_pull(skb, offset)) 15029afd85c9SLinus Lüssing return -EINVAL; 15039afd85c9SLinus Lüssing 15049afd85c9SLinus Lüssing iph = ip_hdr(skb); 15059afd85c9SLinus Lüssing 15069afd85c9SLinus Lüssing if (iph->version != 4 || ip_hdrlen(skb) < sizeof(*iph)) 15079afd85c9SLinus Lüssing return -EINVAL; 15089afd85c9SLinus Lüssing 15099afd85c9SLinus Lüssing offset += ip_hdrlen(skb) - sizeof(*iph); 15109afd85c9SLinus Lüssing 15119afd85c9SLinus Lüssing if (!pskb_may_pull(skb, offset)) 15129afd85c9SLinus Lüssing return -EINVAL; 15139afd85c9SLinus Lüssing 15149afd85c9SLinus Lüssing iph = ip_hdr(skb); 15159afd85c9SLinus Lüssing 15169afd85c9SLinus Lüssing if (unlikely(ip_fast_csum((u8 *)iph, iph->ihl))) 15179afd85c9SLinus Lüssing return -EINVAL; 15189afd85c9SLinus Lüssing 15199afd85c9SLinus Lüssing len = skb_network_offset(skb) + ntohs(iph->tot_len); 15209afd85c9SLinus Lüssing if (skb->len < len || len < offset) 15219afd85c9SLinus Lüssing return -EINVAL; 15229afd85c9SLinus Lüssing 15239afd85c9SLinus Lüssing skb_set_transport_header(skb, offset); 15249afd85c9SLinus Lüssing 15259afd85c9SLinus Lüssing return 0; 15269afd85c9SLinus Lüssing } 15279afd85c9SLinus Lüssing 15289afd85c9SLinus Lüssing static int ip_mc_check_igmp_reportv3(struct sk_buff *skb) 15299afd85c9SLinus Lüssing { 15309afd85c9SLinus Lüssing unsigned int len = skb_transport_offset(skb); 15319afd85c9SLinus Lüssing 15329afd85c9SLinus Lüssing len += sizeof(struct igmpv3_report); 15339afd85c9SLinus Lüssing 1534a2e2ca3bSLinus Lüssing return ip_mc_may_pull(skb, len) ? 0 : -EINVAL; 15359afd85c9SLinus Lüssing } 15369afd85c9SLinus Lüssing 15379afd85c9SLinus Lüssing static int ip_mc_check_igmp_query(struct sk_buff *skb) 15389afd85c9SLinus Lüssing { 1539a2e2ca3bSLinus Lüssing unsigned int transport_len = ip_transport_len(skb); 1540a2e2ca3bSLinus Lüssing unsigned int len; 15419afd85c9SLinus Lüssing 15429afd85c9SLinus Lüssing /* IGMPv{1,2}? */ 1543a2e2ca3bSLinus Lüssing if (transport_len != sizeof(struct igmphdr)) { 15449afd85c9SLinus Lüssing /* or IGMPv3? */ 1545a2e2ca3bSLinus Lüssing if (transport_len < sizeof(struct igmpv3_query)) 1546a2e2ca3bSLinus Lüssing return -EINVAL; 1547a2e2ca3bSLinus Lüssing 1548a2e2ca3bSLinus Lüssing len = skb_transport_offset(skb) + sizeof(struct igmpv3_query); 1549a2e2ca3bSLinus Lüssing if (!ip_mc_may_pull(skb, len)) 15509afd85c9SLinus Lüssing return -EINVAL; 15519afd85c9SLinus Lüssing } 15529afd85c9SLinus Lüssing 15539afd85c9SLinus Lüssing /* RFC2236+RFC3376 (IGMPv2+IGMPv3) require the multicast link layer 15549afd85c9SLinus Lüssing * all-systems destination addresses (224.0.0.1) for general queries 15559afd85c9SLinus Lüssing */ 15569afd85c9SLinus Lüssing if (!igmp_hdr(skb)->group && 15579afd85c9SLinus Lüssing ip_hdr(skb)->daddr != htonl(INADDR_ALLHOSTS_GROUP)) 15589afd85c9SLinus Lüssing return -EINVAL; 15599afd85c9SLinus Lüssing 15609afd85c9SLinus Lüssing return 0; 15619afd85c9SLinus Lüssing } 15629afd85c9SLinus Lüssing 15639afd85c9SLinus Lüssing static int ip_mc_check_igmp_msg(struct sk_buff *skb) 15649afd85c9SLinus Lüssing { 15659afd85c9SLinus Lüssing switch (igmp_hdr(skb)->type) { 15669afd85c9SLinus Lüssing case IGMP_HOST_LEAVE_MESSAGE: 15679afd85c9SLinus Lüssing case IGMP_HOST_MEMBERSHIP_REPORT: 15689afd85c9SLinus Lüssing case IGMPV2_HOST_MEMBERSHIP_REPORT: 15699afd85c9SLinus Lüssing return 0; 15709afd85c9SLinus Lüssing case IGMPV3_HOST_MEMBERSHIP_REPORT: 15719afd85c9SLinus Lüssing return ip_mc_check_igmp_reportv3(skb); 15729afd85c9SLinus Lüssing case IGMP_HOST_MEMBERSHIP_QUERY: 15739afd85c9SLinus Lüssing return ip_mc_check_igmp_query(skb); 15749afd85c9SLinus Lüssing default: 15759afd85c9SLinus Lüssing return -ENOMSG; 15769afd85c9SLinus Lüssing } 15779afd85c9SLinus Lüssing } 15789afd85c9SLinus Lüssing 15795a43f697SAlexey Dobriyan static __sum16 ip_mc_validate_checksum(struct sk_buff *skb) 15809afd85c9SLinus Lüssing { 15819afd85c9SLinus Lüssing return skb_checksum_simple_validate(skb); 15829afd85c9SLinus Lüssing } 15839afd85c9SLinus Lüssing 1584a2e2ca3bSLinus Lüssing static int ip_mc_check_igmp_csum(struct sk_buff *skb) 15859afd85c9SLinus Lüssing { 15869afd85c9SLinus Lüssing unsigned int len = skb_transport_offset(skb) + sizeof(struct igmphdr); 1587a2e2ca3bSLinus Lüssing unsigned int transport_len = ip_transport_len(skb); 1588a2e2ca3bSLinus Lüssing struct sk_buff *skb_chk; 15899afd85c9SLinus Lüssing 1590a2e2ca3bSLinus Lüssing if (!ip_mc_may_pull(skb, len)) 1591a2e2ca3bSLinus Lüssing return -EINVAL; 15929afd85c9SLinus Lüssing 15939afd85c9SLinus Lüssing skb_chk = skb_checksum_trimmed(skb, transport_len, 15949afd85c9SLinus Lüssing ip_mc_validate_checksum); 15959afd85c9SLinus Lüssing if (!skb_chk) 1596a2e2ca3bSLinus Lüssing return -EINVAL; 15979afd85c9SLinus Lüssing 1598a2e2ca3bSLinus Lüssing if (skb_chk != skb) 1599a516993fSLinus Lüssing kfree_skb(skb_chk); 1600a516993fSLinus Lüssing 1601a2e2ca3bSLinus Lüssing return 0; 16029afd85c9SLinus Lüssing } 16039afd85c9SLinus Lüssing 16049afd85c9SLinus Lüssing /** 16059afd85c9SLinus Lüssing * ip_mc_check_igmp - checks whether this is a sane IGMP packet 16069afd85c9SLinus Lüssing * @skb: the skb to validate 16079afd85c9SLinus Lüssing * 16089afd85c9SLinus Lüssing * Checks whether an IPv4 packet is a valid IGMP packet. If so sets 1609a516993fSLinus Lüssing * skb transport header accordingly and returns zero. 16109afd85c9SLinus Lüssing * 16119afd85c9SLinus Lüssing * -EINVAL: A broken packet was detected, i.e. it violates some internet 16129afd85c9SLinus Lüssing * standard 16139afd85c9SLinus Lüssing * -ENOMSG: IP header validation succeeded but it is not an IGMP packet. 16149afd85c9SLinus Lüssing * -ENOMEM: A memory allocation failure happened. 16159afd85c9SLinus Lüssing * 1616a516993fSLinus Lüssing * Caller needs to set the skb network header and free any returned skb if it 1617a516993fSLinus Lüssing * differs from the provided skb. 16189afd85c9SLinus Lüssing */ 1619ba5ea614SLinus Lüssing int ip_mc_check_igmp(struct sk_buff *skb) 16209afd85c9SLinus Lüssing { 16219afd85c9SLinus Lüssing int ret = ip_mc_check_iphdr(skb); 16229afd85c9SLinus Lüssing 16239afd85c9SLinus Lüssing if (ret < 0) 16249afd85c9SLinus Lüssing return ret; 16259afd85c9SLinus Lüssing 16269afd85c9SLinus Lüssing if (ip_hdr(skb)->protocol != IPPROTO_IGMP) 16279afd85c9SLinus Lüssing return -ENOMSG; 16289afd85c9SLinus Lüssing 1629a2e2ca3bSLinus Lüssing ret = ip_mc_check_igmp_csum(skb); 1630a2e2ca3bSLinus Lüssing if (ret < 0) 1631a2e2ca3bSLinus Lüssing return ret; 1632a2e2ca3bSLinus Lüssing 1633a2e2ca3bSLinus Lüssing return ip_mc_check_igmp_msg(skb); 16349afd85c9SLinus Lüssing } 16359afd85c9SLinus Lüssing EXPORT_SYMBOL(ip_mc_check_igmp); 16369afd85c9SLinus Lüssing 16371da177e4SLinus Torvalds /* 16384aa5dee4SJiri Pirko * Resend IGMP JOIN report; used by netdev notifier. 1639a816c7c7SJay Vosburgh */ 16404aa5dee4SJiri Pirko static void ip_mc_rejoin_groups(struct in_device *in_dev) 1641a816c7c7SJay Vosburgh { 164208882669SGeert Uytterhoeven #ifdef CONFIG_IP_MULTICAST 1643866f3b25SEric Dumazet struct ip_mc_list *im; 1644866f3b25SEric Dumazet int type; 164587a8a2aeSNikolay Borisov struct net *net = dev_net(in_dev->dev); 1646a816c7c7SJay Vosburgh 16474aa5dee4SJiri Pirko ASSERT_RTNL(); 16484aa5dee4SJiri Pirko 16494aa5dee4SJiri Pirko for_each_pmc_rtnl(in_dev, im) { 1650a816c7c7SJay Vosburgh if (im->multiaddr == IGMP_ALL_HOSTS) 1651866f3b25SEric Dumazet continue; 1652df2cf4a7SPhilip Downey if (ipv4_is_local_multicast(im->multiaddr) && 1653f6da2267SKuniyuki Iwashima !READ_ONCE(net->ipv4.sysctl_igmp_llm_reports)) 1654df2cf4a7SPhilip Downey continue; 1655a816c7c7SJay Vosburgh 1656e12b4539SFlavio Leitner /* a failover is happening and switches 1657866f3b25SEric Dumazet * must be notified immediately 1658866f3b25SEric Dumazet */ 1659e12b4539SFlavio Leitner if (IGMP_V1_SEEN(in_dev)) 1660866f3b25SEric Dumazet type = IGMP_HOST_MEMBERSHIP_REPORT; 1661e12b4539SFlavio Leitner else if (IGMP_V2_SEEN(in_dev)) 1662866f3b25SEric Dumazet type = IGMPV2_HOST_MEMBERSHIP_REPORT; 1663e12b4539SFlavio Leitner else 1664866f3b25SEric Dumazet type = IGMPV3_HOST_MEMBERSHIP_REPORT; 1665866f3b25SEric Dumazet igmp_send_report(in_dev, im, type); 1666866f3b25SEric Dumazet } 1667a816c7c7SJay Vosburgh #endif 1668a816c7c7SJay Vosburgh } 1669a816c7c7SJay Vosburgh 1670a816c7c7SJay Vosburgh /* 16711da177e4SLinus Torvalds * A socket has left a multicast group on device dev 16721da177e4SLinus Torvalds */ 16731da177e4SLinus Torvalds 16749fb20801SFlorian Fainelli void __ip_mc_dec_group(struct in_device *in_dev, __be32 addr, gfp_t gfp) 16751da177e4SLinus Torvalds { 16761d7138deSEric Dumazet struct ip_mc_list *i; 16771d7138deSEric Dumazet struct ip_mc_list __rcu **ip; 16781da177e4SLinus Torvalds 16791da177e4SLinus Torvalds ASSERT_RTNL(); 16801da177e4SLinus Torvalds 16811d7138deSEric Dumazet for (ip = &in_dev->mc_list; 16821d7138deSEric Dumazet (i = rtnl_dereference(*ip)) != NULL; 16831d7138deSEric Dumazet ip = &i->next_rcu) { 16841da177e4SLinus Torvalds if (i->multiaddr == addr) { 16851da177e4SLinus Torvalds if (--i->users == 0) { 1686e9897071SEric Dumazet ip_mc_hash_remove(in_dev, i); 16871d7138deSEric Dumazet *ip = i->next_rcu; 1688b8bae41eSRami Rosen in_dev->mc_count--; 16899fb20801SFlorian Fainelli __igmp_group_dropped(i, gfp); 169024cf3af3SVeaceslav Falico ip_mc_clear_src(i); 16911da177e4SLinus Torvalds 16921da177e4SLinus Torvalds if (!in_dev->dead) 16931da177e4SLinus Torvalds ip_rt_multicast_event(in_dev); 16941da177e4SLinus Torvalds 16951da177e4SLinus Torvalds ip_ma_put(i); 16961da177e4SLinus Torvalds return; 16971da177e4SLinus Torvalds } 16981da177e4SLinus Torvalds break; 16991da177e4SLinus Torvalds } 17001da177e4SLinus Torvalds } 17011da177e4SLinus Torvalds } 17029fb20801SFlorian Fainelli EXPORT_SYMBOL(__ip_mc_dec_group); 17031da177e4SLinus Torvalds 170475c78500SMoni Shoua /* Device changing type */ 170575c78500SMoni Shoua 170675c78500SMoni Shoua void ip_mc_unmap(struct in_device *in_dev) 170775c78500SMoni Shoua { 17081d7138deSEric Dumazet struct ip_mc_list *pmc; 170975c78500SMoni Shoua 171075c78500SMoni Shoua ASSERT_RTNL(); 171175c78500SMoni Shoua 17121d7138deSEric Dumazet for_each_pmc_rtnl(in_dev, pmc) 17131d7138deSEric Dumazet igmp_group_dropped(pmc); 171475c78500SMoni Shoua } 171575c78500SMoni Shoua 171675c78500SMoni Shoua void ip_mc_remap(struct in_device *in_dev) 171775c78500SMoni Shoua { 17181d7138deSEric Dumazet struct ip_mc_list *pmc; 171975c78500SMoni Shoua 172075c78500SMoni Shoua ASSERT_RTNL(); 172175c78500SMoni Shoua 172224803f38SHangbin Liu for_each_pmc_rtnl(in_dev, pmc) { 172324803f38SHangbin Liu #ifdef CONFIG_IP_MULTICAST 172424803f38SHangbin Liu igmpv3_del_delrec(in_dev, pmc); 172524803f38SHangbin Liu #endif 17260ae0d60aSHangbin Liu igmp_group_added(pmc); 172775c78500SMoni Shoua } 172824803f38SHangbin Liu } 172975c78500SMoni Shoua 17301da177e4SLinus Torvalds /* Device going down */ 17311da177e4SLinus Torvalds 17321da177e4SLinus Torvalds void ip_mc_down(struct in_device *in_dev) 17331da177e4SLinus Torvalds { 17341d7138deSEric Dumazet struct ip_mc_list *pmc; 17351da177e4SLinus Torvalds 17361da177e4SLinus Torvalds ASSERT_RTNL(); 17371da177e4SLinus Torvalds 17381d7138deSEric Dumazet for_each_pmc_rtnl(in_dev, pmc) 17391d7138deSEric Dumazet igmp_group_dropped(pmc); 17401da177e4SLinus Torvalds 17411da177e4SLinus Torvalds #ifdef CONFIG_IP_MULTICAST 17424a2b285eSEric Dumazet WRITE_ONCE(in_dev->mr_ifc_count, 0); 17431da177e4SLinus Torvalds if (del_timer(&in_dev->mr_ifc_timer)) 17441da177e4SLinus Torvalds __in_dev_put(in_dev); 17451da177e4SLinus Torvalds in_dev->mr_gq_running = 0; 17461da177e4SLinus Torvalds if (del_timer(&in_dev->mr_gq_timer)) 17471da177e4SLinus Torvalds __in_dev_put(in_dev); 17481da177e4SLinus Torvalds #endif 17491da177e4SLinus Torvalds 17501da177e4SLinus Torvalds ip_mc_dec_group(in_dev, IGMP_ALL_HOSTS); 17511da177e4SLinus Torvalds } 17521da177e4SLinus Torvalds 1753966c37f2SHangbin Liu #ifdef CONFIG_IP_MULTICAST 1754966c37f2SHangbin Liu static void ip_mc_reset(struct in_device *in_dev) 1755966c37f2SHangbin Liu { 1756966c37f2SHangbin Liu struct net *net = dev_net(in_dev->dev); 1757966c37f2SHangbin Liu 1758966c37f2SHangbin Liu in_dev->mr_qi = IGMP_QUERY_INTERVAL; 1759966c37f2SHangbin Liu in_dev->mr_qri = IGMP_QUERY_RESPONSE_INTERVAL; 17608ebcc62cSKuniyuki Iwashima in_dev->mr_qrv = READ_ONCE(net->ipv4.sysctl_igmp_qrv); 1761966c37f2SHangbin Liu } 1762966c37f2SHangbin Liu #else 1763966c37f2SHangbin Liu static void ip_mc_reset(struct in_device *in_dev) 1764966c37f2SHangbin Liu { 1765966c37f2SHangbin Liu } 1766966c37f2SHangbin Liu #endif 1767966c37f2SHangbin Liu 17681da177e4SLinus Torvalds void ip_mc_init_dev(struct in_device *in_dev) 17691da177e4SLinus Torvalds { 17701da177e4SLinus Torvalds ASSERT_RTNL(); 17711da177e4SLinus Torvalds 17721da177e4SLinus Torvalds #ifdef CONFIG_IP_MULTICAST 1773e99e88a9SKees Cook timer_setup(&in_dev->mr_gq_timer, igmp_gq_timer_expire, 0); 1774e99e88a9SKees Cook timer_setup(&in_dev->mr_ifc_timer, igmp_ifc_timer_expire, 0); 17751da177e4SLinus Torvalds #endif 1776966c37f2SHangbin Liu ip_mc_reset(in_dev); 17771da177e4SLinus Torvalds 17781da177e4SLinus Torvalds spin_lock_init(&in_dev->mc_tomb_lock); 17791da177e4SLinus Torvalds } 17801da177e4SLinus Torvalds 17811da177e4SLinus Torvalds /* Device going up */ 17821da177e4SLinus Torvalds 17831da177e4SLinus Torvalds void ip_mc_up(struct in_device *in_dev) 17841da177e4SLinus Torvalds { 17851d7138deSEric Dumazet struct ip_mc_list *pmc; 17861da177e4SLinus Torvalds 17871da177e4SLinus Torvalds ASSERT_RTNL(); 17881da177e4SLinus Torvalds 1789966c37f2SHangbin Liu ip_mc_reset(in_dev); 17901da177e4SLinus Torvalds ip_mc_inc_group(in_dev, IGMP_ALL_HOSTS); 17911da177e4SLinus Torvalds 179224803f38SHangbin Liu for_each_pmc_rtnl(in_dev, pmc) { 179324803f38SHangbin Liu #ifdef CONFIG_IP_MULTICAST 179424803f38SHangbin Liu igmpv3_del_delrec(in_dev, pmc); 179524803f38SHangbin Liu #endif 17960ae0d60aSHangbin Liu igmp_group_added(pmc); 17971da177e4SLinus Torvalds } 179824803f38SHangbin Liu } 17991da177e4SLinus Torvalds 18001da177e4SLinus Torvalds /* 18011da177e4SLinus Torvalds * Device is about to be destroyed: clean up. 18021da177e4SLinus Torvalds */ 18031da177e4SLinus Torvalds 18041da177e4SLinus Torvalds void ip_mc_destroy_dev(struct in_device *in_dev) 18051da177e4SLinus Torvalds { 18061da177e4SLinus Torvalds struct ip_mc_list *i; 18071da177e4SLinus Torvalds 18081da177e4SLinus Torvalds ASSERT_RTNL(); 18091da177e4SLinus Torvalds 18101da177e4SLinus Torvalds /* Deactivate timers */ 18111da177e4SLinus Torvalds ip_mc_down(in_dev); 181224803f38SHangbin Liu #ifdef CONFIG_IP_MULTICAST 181324803f38SHangbin Liu igmpv3_clear_delrec(in_dev); 181424803f38SHangbin Liu #endif 18151da177e4SLinus Torvalds 18161d7138deSEric Dumazet while ((i = rtnl_dereference(in_dev->mc_list)) != NULL) { 18171d7138deSEric Dumazet in_dev->mc_list = i->next_rcu; 1818b8bae41eSRami Rosen in_dev->mc_count--; 1819d8e29730SChengyang Fan ip_mc_clear_src(i); 18201da177e4SLinus Torvalds ip_ma_put(i); 18211da177e4SLinus Torvalds } 18221da177e4SLinus Torvalds } 18231da177e4SLinus Torvalds 18249e917dcaSEric Dumazet /* RTNL is locked */ 1825877acedcSDaniel Lezcano static struct in_device *ip_mc_find_dev(struct net *net, struct ip_mreqn *imr) 18261da177e4SLinus Torvalds { 18271da177e4SLinus Torvalds struct net_device *dev = NULL; 18281da177e4SLinus Torvalds struct in_device *idev = NULL; 18291da177e4SLinus Torvalds 18301da177e4SLinus Torvalds if (imr->imr_ifindex) { 1831877acedcSDaniel Lezcano idev = inetdev_by_index(net, imr->imr_ifindex); 18321da177e4SLinus Torvalds return idev; 18331da177e4SLinus Torvalds } 18341da177e4SLinus Torvalds if (imr->imr_address.s_addr) { 18359e917dcaSEric Dumazet dev = __ip_dev_find(net, imr->imr_address.s_addr, false); 18361da177e4SLinus Torvalds if (!dev) 18371da177e4SLinus Torvalds return NULL; 18381da177e4SLinus Torvalds } 18391da177e4SLinus Torvalds 1840b23dd4feSDavid S. Miller if (!dev) { 184178fbfd8aSDavid S. Miller struct rtable *rt = ip_route_output(net, 184278fbfd8aSDavid S. Miller imr->imr_multiaddr.s_addr, 184378fbfd8aSDavid S. Miller 0, 0, 0); 1844b23dd4feSDavid S. Miller if (!IS_ERR(rt)) { 1845d8d1f30bSChangli Gao dev = rt->dst.dev; 18461da177e4SLinus Torvalds ip_rt_put(rt); 18471da177e4SLinus Torvalds } 1848b23dd4feSDavid S. Miller } 18491da177e4SLinus Torvalds if (dev) { 18501da177e4SLinus Torvalds imr->imr_ifindex = dev->ifindex; 1851e5ed6399SHerbert Xu idev = __in_dev_get_rtnl(dev); 18521da177e4SLinus Torvalds } 18531da177e4SLinus Torvalds return idev; 18541da177e4SLinus Torvalds } 18551da177e4SLinus Torvalds 18561da177e4SLinus Torvalds /* 18571da177e4SLinus Torvalds * Join a socket to a group 18581da177e4SLinus Torvalds */ 18591da177e4SLinus Torvalds 18601da177e4SLinus Torvalds static int ip_mc_del1_src(struct ip_mc_list *pmc, int sfmode, 18618f935bbdSAl Viro __be32 *psfsrc) 18621da177e4SLinus Torvalds { 18631da177e4SLinus Torvalds struct ip_sf_list *psf, *psf_prev; 18641da177e4SLinus Torvalds int rv = 0; 18651da177e4SLinus Torvalds 18661da177e4SLinus Torvalds psf_prev = NULL; 18671da177e4SLinus Torvalds for (psf = pmc->sources; psf; psf = psf->sf_next) { 18681da177e4SLinus Torvalds if (psf->sf_inaddr == *psfsrc) 18691da177e4SLinus Torvalds break; 18701da177e4SLinus Torvalds psf_prev = psf; 18711da177e4SLinus Torvalds } 18721da177e4SLinus Torvalds if (!psf || psf->sf_count[sfmode] == 0) { 18731da177e4SLinus Torvalds /* source filter not found, or count wrong => bug */ 18741da177e4SLinus Torvalds return -ESRCH; 18751da177e4SLinus Torvalds } 18761da177e4SLinus Torvalds psf->sf_count[sfmode]--; 18771da177e4SLinus Torvalds if (psf->sf_count[sfmode] == 0) { 18781da177e4SLinus Torvalds ip_rt_multicast_event(pmc->interface); 18791da177e4SLinus Torvalds } 18801da177e4SLinus Torvalds if (!psf->sf_count[MCAST_INCLUDE] && !psf->sf_count[MCAST_EXCLUDE]) { 18811da177e4SLinus Torvalds #ifdef CONFIG_IP_MULTICAST 18821da177e4SLinus Torvalds struct in_device *in_dev = pmc->interface; 1883165094afSNikolay Borisov struct net *net = dev_net(in_dev->dev); 18841da177e4SLinus Torvalds #endif 18851da177e4SLinus Torvalds 18861da177e4SLinus Torvalds /* no more filters for this source */ 18871da177e4SLinus Torvalds if (psf_prev) 18881da177e4SLinus Torvalds psf_prev->sf_next = psf->sf_next; 18891da177e4SLinus Torvalds else 18901da177e4SLinus Torvalds pmc->sources = psf->sf_next; 18911da177e4SLinus Torvalds #ifdef CONFIG_IP_MULTICAST 18921da177e4SLinus Torvalds if (psf->sf_oldin && 18931da177e4SLinus Torvalds !IGMP_V1_SEEN(in_dev) && !IGMP_V2_SEEN(in_dev)) { 18948ebcc62cSKuniyuki Iwashima psf->sf_crcount = in_dev->mr_qrv ?: READ_ONCE(net->ipv4.sysctl_igmp_qrv); 18951da177e4SLinus Torvalds psf->sf_next = pmc->tomb; 18961da177e4SLinus Torvalds pmc->tomb = psf; 18971da177e4SLinus Torvalds rv = 1; 18981da177e4SLinus Torvalds } else 18991da177e4SLinus Torvalds #endif 19001da177e4SLinus Torvalds kfree(psf); 19011da177e4SLinus Torvalds } 19021da177e4SLinus Torvalds return rv; 19031da177e4SLinus Torvalds } 19041da177e4SLinus Torvalds 19051da177e4SLinus Torvalds #ifndef CONFIG_IP_MULTICAST 19061da177e4SLinus Torvalds #define igmp_ifc_event(x) do { } while (0) 19071da177e4SLinus Torvalds #endif 19081da177e4SLinus Torvalds 19098f935bbdSAl Viro static int ip_mc_del_src(struct in_device *in_dev, __be32 *pmca, int sfmode, 19108f935bbdSAl Viro int sfcount, __be32 *psfsrc, int delta) 19111da177e4SLinus Torvalds { 19121da177e4SLinus Torvalds struct ip_mc_list *pmc; 19131da177e4SLinus Torvalds int changerec = 0; 19141da177e4SLinus Torvalds int i, err; 19151da177e4SLinus Torvalds 19161da177e4SLinus Torvalds if (!in_dev) 19171da177e4SLinus Torvalds return -ENODEV; 19181d7138deSEric Dumazet rcu_read_lock(); 19191d7138deSEric Dumazet for_each_pmc_rcu(in_dev, pmc) { 19201da177e4SLinus Torvalds if (*pmca == pmc->multiaddr) 19211da177e4SLinus Torvalds break; 19221da177e4SLinus Torvalds } 19231da177e4SLinus Torvalds if (!pmc) { 19241da177e4SLinus Torvalds /* MCA not found?? bug */ 19251d7138deSEric Dumazet rcu_read_unlock(); 19261da177e4SLinus Torvalds return -ESRCH; 19271da177e4SLinus Torvalds } 19281da177e4SLinus Torvalds spin_lock_bh(&pmc->lock); 19291d7138deSEric Dumazet rcu_read_unlock(); 19301da177e4SLinus Torvalds #ifdef CONFIG_IP_MULTICAST 19311da177e4SLinus Torvalds sf_markstate(pmc); 19321da177e4SLinus Torvalds #endif 19331da177e4SLinus Torvalds if (!delta) { 19341da177e4SLinus Torvalds err = -EINVAL; 19351da177e4SLinus Torvalds if (!pmc->sfcount[sfmode]) 19361da177e4SLinus Torvalds goto out_unlock; 19371da177e4SLinus Torvalds pmc->sfcount[sfmode]--; 19381da177e4SLinus Torvalds } 19391da177e4SLinus Torvalds err = 0; 19401da177e4SLinus Torvalds for (i = 0; i < sfcount; i++) { 19411da177e4SLinus Torvalds int rv = ip_mc_del1_src(pmc, sfmode, &psfsrc[i]); 19421da177e4SLinus Torvalds 19431da177e4SLinus Torvalds changerec |= rv > 0; 19441da177e4SLinus Torvalds if (!err && rv < 0) 19451da177e4SLinus Torvalds err = rv; 19461da177e4SLinus Torvalds } 19471da177e4SLinus Torvalds if (pmc->sfmode == MCAST_EXCLUDE && 19481da177e4SLinus Torvalds pmc->sfcount[MCAST_EXCLUDE] == 0 && 19491da177e4SLinus Torvalds pmc->sfcount[MCAST_INCLUDE]) { 19501da177e4SLinus Torvalds #ifdef CONFIG_IP_MULTICAST 19511da177e4SLinus Torvalds struct ip_sf_list *psf; 1952165094afSNikolay Borisov struct net *net = dev_net(in_dev->dev); 19531da177e4SLinus Torvalds #endif 19541da177e4SLinus Torvalds 19551da177e4SLinus Torvalds /* filter mode change */ 19561da177e4SLinus Torvalds pmc->sfmode = MCAST_INCLUDE; 19571da177e4SLinus Torvalds #ifdef CONFIG_IP_MULTICAST 19588ebcc62cSKuniyuki Iwashima pmc->crcount = in_dev->mr_qrv ?: READ_ONCE(net->ipv4.sysctl_igmp_qrv); 19594a2b285eSEric Dumazet WRITE_ONCE(in_dev->mr_ifc_count, pmc->crcount); 19601da177e4SLinus Torvalds for (psf = pmc->sources; psf; psf = psf->sf_next) 19611da177e4SLinus Torvalds psf->sf_crcount = 0; 19621da177e4SLinus Torvalds igmp_ifc_event(pmc->interface); 19631da177e4SLinus Torvalds } else if (sf_setstate(pmc) || changerec) { 19641da177e4SLinus Torvalds igmp_ifc_event(pmc->interface); 19651da177e4SLinus Torvalds #endif 19661da177e4SLinus Torvalds } 19671da177e4SLinus Torvalds out_unlock: 19681da177e4SLinus Torvalds spin_unlock_bh(&pmc->lock); 19691da177e4SLinus Torvalds return err; 19701da177e4SLinus Torvalds } 19711da177e4SLinus Torvalds 19721da177e4SLinus Torvalds /* 19731da177e4SLinus Torvalds * Add multicast single-source filter to the interface list 19741da177e4SLinus Torvalds */ 19751da177e4SLinus Torvalds static int ip_mc_add1_src(struct ip_mc_list *pmc, int sfmode, 19765eb81e89SJun Zhao __be32 *psfsrc) 19771da177e4SLinus Torvalds { 19781da177e4SLinus Torvalds struct ip_sf_list *psf, *psf_prev; 19791da177e4SLinus Torvalds 19801da177e4SLinus Torvalds psf_prev = NULL; 19811da177e4SLinus Torvalds for (psf = pmc->sources; psf; psf = psf->sf_next) { 19821da177e4SLinus Torvalds if (psf->sf_inaddr == *psfsrc) 19831da177e4SLinus Torvalds break; 19841da177e4SLinus Torvalds psf_prev = psf; 19851da177e4SLinus Torvalds } 19861da177e4SLinus Torvalds if (!psf) { 19870da974f4SPanagiotis Issaris psf = kzalloc(sizeof(*psf), GFP_ATOMIC); 19881da177e4SLinus Torvalds if (!psf) 19891da177e4SLinus Torvalds return -ENOBUFS; 19901da177e4SLinus Torvalds psf->sf_inaddr = *psfsrc; 19911da177e4SLinus Torvalds if (psf_prev) { 19921da177e4SLinus Torvalds psf_prev->sf_next = psf; 19931da177e4SLinus Torvalds } else 19941da177e4SLinus Torvalds pmc->sources = psf; 19951da177e4SLinus Torvalds } 19961da177e4SLinus Torvalds psf->sf_count[sfmode]++; 19971da177e4SLinus Torvalds if (psf->sf_count[sfmode] == 1) { 19981da177e4SLinus Torvalds ip_rt_multicast_event(pmc->interface); 19991da177e4SLinus Torvalds } 20001da177e4SLinus Torvalds return 0; 20011da177e4SLinus Torvalds } 20021da177e4SLinus Torvalds 20031da177e4SLinus Torvalds #ifdef CONFIG_IP_MULTICAST 20041da177e4SLinus Torvalds static void sf_markstate(struct ip_mc_list *pmc) 20051da177e4SLinus Torvalds { 20061da177e4SLinus Torvalds struct ip_sf_list *psf; 20071da177e4SLinus Torvalds int mca_xcount = pmc->sfcount[MCAST_EXCLUDE]; 20081da177e4SLinus Torvalds 20091da177e4SLinus Torvalds for (psf = pmc->sources; psf; psf = psf->sf_next) 20101da177e4SLinus Torvalds if (pmc->sfcount[MCAST_EXCLUDE]) { 20111da177e4SLinus Torvalds psf->sf_oldin = mca_xcount == 20121da177e4SLinus Torvalds psf->sf_count[MCAST_EXCLUDE] && 20131da177e4SLinus Torvalds !psf->sf_count[MCAST_INCLUDE]; 20141da177e4SLinus Torvalds } else 20151da177e4SLinus Torvalds psf->sf_oldin = psf->sf_count[MCAST_INCLUDE] != 0; 20161da177e4SLinus Torvalds } 20171da177e4SLinus Torvalds 20181da177e4SLinus Torvalds static int sf_setstate(struct ip_mc_list *pmc) 20191da177e4SLinus Torvalds { 2020ad12583fSDavid L Stevens struct ip_sf_list *psf, *dpsf; 20211da177e4SLinus Torvalds int mca_xcount = pmc->sfcount[MCAST_EXCLUDE]; 20221da177e4SLinus Torvalds int qrv = pmc->interface->mr_qrv; 20231da177e4SLinus Torvalds int new_in, rv; 20241da177e4SLinus Torvalds 20251da177e4SLinus Torvalds rv = 0; 20261da177e4SLinus Torvalds for (psf = pmc->sources; psf; psf = psf->sf_next) { 20271da177e4SLinus Torvalds if (pmc->sfcount[MCAST_EXCLUDE]) { 20281da177e4SLinus Torvalds new_in = mca_xcount == psf->sf_count[MCAST_EXCLUDE] && 20291da177e4SLinus Torvalds !psf->sf_count[MCAST_INCLUDE]; 20301da177e4SLinus Torvalds } else 20311da177e4SLinus Torvalds new_in = psf->sf_count[MCAST_INCLUDE] != 0; 2032ad12583fSDavid L Stevens if (new_in) { 2033ad12583fSDavid L Stevens if (!psf->sf_oldin) { 203476edc605SAl Viro struct ip_sf_list *prev = NULL; 2035ad12583fSDavid L Stevens 2036ad12583fSDavid L Stevens for (dpsf = pmc->tomb; dpsf; dpsf = dpsf->sf_next) { 2037ad12583fSDavid L Stevens if (dpsf->sf_inaddr == psf->sf_inaddr) 2038ad12583fSDavid L Stevens break; 2039ad12583fSDavid L Stevens prev = dpsf; 2040ad12583fSDavid L Stevens } 2041ad12583fSDavid L Stevens if (dpsf) { 2042ad12583fSDavid L Stevens if (prev) 2043ad12583fSDavid L Stevens prev->sf_next = dpsf->sf_next; 2044ad12583fSDavid L Stevens else 2045ad12583fSDavid L Stevens pmc->tomb = dpsf->sf_next; 2046ad12583fSDavid L Stevens kfree(dpsf); 2047ad12583fSDavid L Stevens } 20481da177e4SLinus Torvalds psf->sf_crcount = qrv; 20491da177e4SLinus Torvalds rv++; 20501da177e4SLinus Torvalds } 2051ad12583fSDavid L Stevens } else if (psf->sf_oldin) { 2052ad12583fSDavid L Stevens 2053ad12583fSDavid L Stevens psf->sf_crcount = 0; 2054ad12583fSDavid L Stevens /* 2055ad12583fSDavid L Stevens * add or update "delete" records if an active filter 2056ad12583fSDavid L Stevens * is now inactive 2057ad12583fSDavid L Stevens */ 2058ad12583fSDavid L Stevens for (dpsf = pmc->tomb; dpsf; dpsf = dpsf->sf_next) 2059ad12583fSDavid L Stevens if (dpsf->sf_inaddr == psf->sf_inaddr) 2060ad12583fSDavid L Stevens break; 2061ad12583fSDavid L Stevens if (!dpsf) { 20623ed37a6fSJoe Perches dpsf = kmalloc(sizeof(*dpsf), GFP_ATOMIC); 2063ad12583fSDavid L Stevens if (!dpsf) 2064ad12583fSDavid L Stevens continue; 2065ad12583fSDavid L Stevens *dpsf = *psf; 2066ad12583fSDavid L Stevens /* pmc->lock held by callers */ 2067ad12583fSDavid L Stevens dpsf->sf_next = pmc->tomb; 2068ad12583fSDavid L Stevens pmc->tomb = dpsf; 2069ad12583fSDavid L Stevens } 2070ad12583fSDavid L Stevens dpsf->sf_crcount = qrv; 2071ad12583fSDavid L Stevens rv++; 2072ad12583fSDavid L Stevens } 20731da177e4SLinus Torvalds } 20741da177e4SLinus Torvalds return rv; 20751da177e4SLinus Torvalds } 20761da177e4SLinus Torvalds #endif 20771da177e4SLinus Torvalds 20781da177e4SLinus Torvalds /* 20791da177e4SLinus Torvalds * Add multicast source filter list to the interface list 20801da177e4SLinus Torvalds */ 20818f935bbdSAl Viro static int ip_mc_add_src(struct in_device *in_dev, __be32 *pmca, int sfmode, 20828f935bbdSAl Viro int sfcount, __be32 *psfsrc, int delta) 20831da177e4SLinus Torvalds { 20841da177e4SLinus Torvalds struct ip_mc_list *pmc; 20851da177e4SLinus Torvalds int isexclude; 20861da177e4SLinus Torvalds int i, err; 20871da177e4SLinus Torvalds 20881da177e4SLinus Torvalds if (!in_dev) 20891da177e4SLinus Torvalds return -ENODEV; 20901d7138deSEric Dumazet rcu_read_lock(); 20911d7138deSEric Dumazet for_each_pmc_rcu(in_dev, pmc) { 20921da177e4SLinus Torvalds if (*pmca == pmc->multiaddr) 20931da177e4SLinus Torvalds break; 20941da177e4SLinus Torvalds } 20951da177e4SLinus Torvalds if (!pmc) { 20961da177e4SLinus Torvalds /* MCA not found?? bug */ 20971d7138deSEric Dumazet rcu_read_unlock(); 20981da177e4SLinus Torvalds return -ESRCH; 20991da177e4SLinus Torvalds } 21001da177e4SLinus Torvalds spin_lock_bh(&pmc->lock); 21011d7138deSEric Dumazet rcu_read_unlock(); 21021da177e4SLinus Torvalds 21031da177e4SLinus Torvalds #ifdef CONFIG_IP_MULTICAST 21041da177e4SLinus Torvalds sf_markstate(pmc); 21051da177e4SLinus Torvalds #endif 21061da177e4SLinus Torvalds isexclude = pmc->sfmode == MCAST_EXCLUDE; 21071da177e4SLinus Torvalds if (!delta) 21081da177e4SLinus Torvalds pmc->sfcount[sfmode]++; 21091da177e4SLinus Torvalds err = 0; 21101da177e4SLinus Torvalds for (i = 0; i < sfcount; i++) { 21115eb81e89SJun Zhao err = ip_mc_add1_src(pmc, sfmode, &psfsrc[i]); 21121da177e4SLinus Torvalds if (err) 21131da177e4SLinus Torvalds break; 21141da177e4SLinus Torvalds } 21151da177e4SLinus Torvalds if (err) { 21161da177e4SLinus Torvalds int j; 21171da177e4SLinus Torvalds 2118685f94e6SJun Zhao if (!delta) 21191da177e4SLinus Torvalds pmc->sfcount[sfmode]--; 21201da177e4SLinus Torvalds for (j = 0; j < i; j++) 2121a1889c0dSJulia Lawall (void) ip_mc_del1_src(pmc, sfmode, &psfsrc[j]); 21221da177e4SLinus Torvalds } else if (isexclude != (pmc->sfcount[MCAST_EXCLUDE] != 0)) { 21231da177e4SLinus Torvalds #ifdef CONFIG_IP_MULTICAST 21241da177e4SLinus Torvalds struct ip_sf_list *psf; 2125165094afSNikolay Borisov struct net *net = dev_net(pmc->interface->dev); 2126cfcabdccSStephen Hemminger in_dev = pmc->interface; 21271da177e4SLinus Torvalds #endif 21281da177e4SLinus Torvalds 21291da177e4SLinus Torvalds /* filter mode change */ 21301da177e4SLinus Torvalds if (pmc->sfcount[MCAST_EXCLUDE]) 21311da177e4SLinus Torvalds pmc->sfmode = MCAST_EXCLUDE; 21321da177e4SLinus Torvalds else if (pmc->sfcount[MCAST_INCLUDE]) 21331da177e4SLinus Torvalds pmc->sfmode = MCAST_INCLUDE; 21341da177e4SLinus Torvalds #ifdef CONFIG_IP_MULTICAST 21351da177e4SLinus Torvalds /* else no filters; keep old mode for reports */ 21361da177e4SLinus Torvalds 21378ebcc62cSKuniyuki Iwashima pmc->crcount = in_dev->mr_qrv ?: READ_ONCE(net->ipv4.sysctl_igmp_qrv); 21384a2b285eSEric Dumazet WRITE_ONCE(in_dev->mr_ifc_count, pmc->crcount); 21391da177e4SLinus Torvalds for (psf = pmc->sources; psf; psf = psf->sf_next) 21401da177e4SLinus Torvalds psf->sf_crcount = 0; 21411da177e4SLinus Torvalds igmp_ifc_event(in_dev); 21421da177e4SLinus Torvalds } else if (sf_setstate(pmc)) { 21431da177e4SLinus Torvalds igmp_ifc_event(in_dev); 21441da177e4SLinus Torvalds #endif 21451da177e4SLinus Torvalds } 21461da177e4SLinus Torvalds spin_unlock_bh(&pmc->lock); 21471da177e4SLinus Torvalds return err; 21481da177e4SLinus Torvalds } 21491da177e4SLinus Torvalds 21501da177e4SLinus Torvalds static void ip_mc_clear_src(struct ip_mc_list *pmc) 21511da177e4SLinus Torvalds { 21523580d04aSEric Dumazet struct ip_sf_list *tomb, *sources; 21531da177e4SLinus Torvalds 2154c38b7d32SWANG Cong spin_lock_bh(&pmc->lock); 2155c38b7d32SWANG Cong tomb = pmc->tomb; 21561da177e4SLinus Torvalds pmc->tomb = NULL; 2157c38b7d32SWANG Cong sources = pmc->sources; 21581da177e4SLinus Torvalds pmc->sources = NULL; 21591da177e4SLinus Torvalds pmc->sfmode = MCAST_EXCLUDE; 2160de9daad9SDenis Lukianov pmc->sfcount[MCAST_INCLUDE] = 0; 21611da177e4SLinus Torvalds pmc->sfcount[MCAST_EXCLUDE] = 1; 2162c38b7d32SWANG Cong spin_unlock_bh(&pmc->lock); 2163c38b7d32SWANG Cong 21643580d04aSEric Dumazet ip_sf_list_clear_all(tomb); 21653580d04aSEric Dumazet ip_sf_list_clear_all(sources); 21661da177e4SLinus Torvalds } 21671da177e4SLinus Torvalds 216854ff9ef3SMarcelo Ricardo Leitner /* Join a multicast group 216954ff9ef3SMarcelo Ricardo Leitner */ 21706e2059b5SHangbin Liu static int __ip_mc_join_group(struct sock *sk, struct ip_mreqn *imr, 21716e2059b5SHangbin Liu unsigned int mode) 21721da177e4SLinus Torvalds { 21738f935bbdSAl Viro __be32 addr = imr->imr_multiaddr.s_addr; 2174959d10f6SEric Dumazet struct ip_mc_socklist *iml, *i; 21751da177e4SLinus Torvalds struct in_device *in_dev; 21761da177e4SLinus Torvalds struct inet_sock *inet = inet_sk(sk); 2177877acedcSDaniel Lezcano struct net *net = sock_net(sk); 2178ca9b907dSDavid L Stevens int ifindex; 21791da177e4SLinus Torvalds int count = 0; 2180959d10f6SEric Dumazet int err; 2181959d10f6SEric Dumazet 2182959d10f6SEric Dumazet ASSERT_RTNL(); 21831da177e4SLinus Torvalds 2184f97c1e0cSJoe Perches if (!ipv4_is_multicast(addr)) 21851da177e4SLinus Torvalds return -EINVAL; 21861da177e4SLinus Torvalds 2187877acedcSDaniel Lezcano in_dev = ip_mc_find_dev(net, imr); 21881da177e4SLinus Torvalds 21891da177e4SLinus Torvalds if (!in_dev) { 21901da177e4SLinus Torvalds err = -ENODEV; 21911da177e4SLinus Torvalds goto done; 21921da177e4SLinus Torvalds } 21931da177e4SLinus Torvalds 21941da177e4SLinus Torvalds err = -EADDRINUSE; 2195ca9b907dSDavid L Stevens ifindex = imr->imr_ifindex; 21961d7138deSEric Dumazet for_each_pmc_rtnl(inet, i) { 2197ca9b907dSDavid L Stevens if (i->multi.imr_multiaddr.s_addr == addr && 2198ca9b907dSDavid L Stevens i->multi.imr_ifindex == ifindex) 21991da177e4SLinus Torvalds goto done; 22001da177e4SLinus Torvalds count++; 22011da177e4SLinus Torvalds } 22021da177e4SLinus Torvalds err = -ENOBUFS; 22036305d821SKuniyuki Iwashima if (count >= READ_ONCE(net->ipv4.sysctl_igmp_max_memberships)) 22041da177e4SLinus Torvalds goto done; 22058b3a7005SKris Katterjohn iml = sock_kmalloc(sk, sizeof(*iml), GFP_KERNEL); 220651456b29SIan Morris if (!iml) 2207ca9b907dSDavid L Stevens goto done; 2208ca9b907dSDavid L Stevens 22091da177e4SLinus Torvalds memcpy(&iml->multi, imr, sizeof(*imr)); 22101d7138deSEric Dumazet iml->next_rcu = inet->mc_list; 22111da177e4SLinus Torvalds iml->sflist = NULL; 22126e2059b5SHangbin Liu iml->sfmode = mode; 2213cf778b00SEric Dumazet rcu_assign_pointer(inet->mc_list, iml); 2214a1c4cd67SLi RongQing ____ip_mc_inc_group(in_dev, addr, mode, GFP_KERNEL); 22151da177e4SLinus Torvalds err = 0; 22161da177e4SLinus Torvalds done: 22171da177e4SLinus Torvalds return err; 22181da177e4SLinus Torvalds } 22196e2059b5SHangbin Liu 22206e2059b5SHangbin Liu /* Join ASM (Any-Source Multicast) group 22216e2059b5SHangbin Liu */ 22226e2059b5SHangbin Liu int ip_mc_join_group(struct sock *sk, struct ip_mreqn *imr) 22236e2059b5SHangbin Liu { 22246e2059b5SHangbin Liu return __ip_mc_join_group(sk, imr, MCAST_EXCLUDE); 22256e2059b5SHangbin Liu } 22264bc2f18bSEric Dumazet EXPORT_SYMBOL(ip_mc_join_group); 22271da177e4SLinus Torvalds 22286e2059b5SHangbin Liu /* Join SSM (Source-Specific Multicast) group 22296e2059b5SHangbin Liu */ 22306e2059b5SHangbin Liu int ip_mc_join_group_ssm(struct sock *sk, struct ip_mreqn *imr, 22316e2059b5SHangbin Liu unsigned int mode) 22326e2059b5SHangbin Liu { 22336e2059b5SHangbin Liu return __ip_mc_join_group(sk, imr, mode); 22346e2059b5SHangbin Liu } 22356e2059b5SHangbin Liu 22361da177e4SLinus Torvalds static int ip_mc_leave_src(struct sock *sk, struct ip_mc_socklist *iml, 22371da177e4SLinus Torvalds struct in_device *in_dev) 22381da177e4SLinus Torvalds { 22391d7138deSEric Dumazet struct ip_sf_socklist *psf = rtnl_dereference(iml->sflist); 22401da177e4SLinus Torvalds int err; 22411da177e4SLinus Torvalds 224251456b29SIan Morris if (!psf) { 22431da177e4SLinus Torvalds /* any-source empty exclude case */ 22441da177e4SLinus Torvalds return ip_mc_del_src(in_dev, &iml->multi.imr_multiaddr.s_addr, 22451da177e4SLinus Torvalds iml->sfmode, 0, NULL, 0); 22461da177e4SLinus Torvalds } 22471da177e4SLinus Torvalds err = ip_mc_del_src(in_dev, &iml->multi.imr_multiaddr.s_addr, 2248c85bb41eSFlavio Leitner iml->sfmode, psf->sl_count, psf->sl_addr, 0); 2249a9b3cd7fSStephen Hemminger RCU_INIT_POINTER(iml->sflist, NULL); 2250c85bb41eSFlavio Leitner /* decrease mem now to avoid the memleak warning */ 2251e6a1f7e0SGustavo A. R. Silva atomic_sub(struct_size(psf, sl_addr, psf->sl_max), &sk->sk_omem_alloc); 22527519cce4SLai Jiangshan kfree_rcu(psf, rcu); 22531da177e4SLinus Torvalds return err; 22541da177e4SLinus Torvalds } 22551da177e4SLinus Torvalds 225654ff9ef3SMarcelo Ricardo Leitner int ip_mc_leave_group(struct sock *sk, struct ip_mreqn *imr) 22571da177e4SLinus Torvalds { 22581da177e4SLinus Torvalds struct inet_sock *inet = inet_sk(sk); 22591d7138deSEric Dumazet struct ip_mc_socklist *iml; 22601d7138deSEric Dumazet struct ip_mc_socklist __rcu **imlp; 226184b42baeSDavid L Stevens struct in_device *in_dev; 2262877acedcSDaniel Lezcano struct net *net = sock_net(sk); 22638f935bbdSAl Viro __be32 group = imr->imr_multiaddr.s_addr; 226484b42baeSDavid L Stevens u32 ifindex; 2265acd6e00bSDavid L Stevens int ret = -EADDRNOTAVAIL; 22661da177e4SLinus Torvalds 2267959d10f6SEric Dumazet ASSERT_RTNL(); 2268959d10f6SEric Dumazet 2269877acedcSDaniel Lezcano in_dev = ip_mc_find_dev(net, imr); 22704eba7bb1SAndrew Lunn if (!imr->imr_ifindex && !imr->imr_address.s_addr && !in_dev) { 227152ad353aSdingtianhong ret = -ENODEV; 227252ad353aSdingtianhong goto out; 227352ad353aSdingtianhong } 227484b42baeSDavid L Stevens ifindex = imr->imr_ifindex; 22751d7138deSEric Dumazet for (imlp = &inet->mc_list; 22761d7138deSEric Dumazet (iml = rtnl_dereference(*imlp)) != NULL; 22771d7138deSEric Dumazet imlp = &iml->next_rcu) { 2278acd6e00bSDavid L Stevens if (iml->multi.imr_multiaddr.s_addr != group) 2279acd6e00bSDavid L Stevens continue; 2280acd6e00bSDavid L Stevens if (ifindex) { 2281acd6e00bSDavid L Stevens if (iml->multi.imr_ifindex != ifindex) 2282acd6e00bSDavid L Stevens continue; 2283acd6e00bSDavid L Stevens } else if (imr->imr_address.s_addr && imr->imr_address.s_addr != 2284acd6e00bSDavid L Stevens iml->multi.imr_address.s_addr) 2285acd6e00bSDavid L Stevens continue; 2286acd6e00bSDavid L Stevens 22871da177e4SLinus Torvalds (void) ip_mc_leave_src(sk, iml, in_dev); 22881da177e4SLinus Torvalds 22891d7138deSEric Dumazet *imlp = iml->next_rcu; 22901da177e4SLinus Torvalds 22914eba7bb1SAndrew Lunn if (in_dev) 229284b42baeSDavid L Stevens ip_mc_dec_group(in_dev, group); 2293959d10f6SEric Dumazet 2294c85bb41eSFlavio Leitner /* decrease mem now to avoid the memleak warning */ 2295c85bb41eSFlavio Leitner atomic_sub(sizeof(*iml), &sk->sk_omem_alloc); 229610d50e74SLai Jiangshan kfree_rcu(iml, rcu); 22971da177e4SLinus Torvalds return 0; 22981da177e4SLinus Torvalds } 229952ad353aSdingtianhong out: 2300959d10f6SEric Dumazet return ret; 2301959d10f6SEric Dumazet } 2302193ba924Sstephen hemminger EXPORT_SYMBOL(ip_mc_leave_group); 23031da177e4SLinus Torvalds 23041da177e4SLinus Torvalds int ip_mc_source(int add, int omode, struct sock *sk, struct 23051da177e4SLinus Torvalds ip_mreq_source *mreqs, int ifindex) 23061da177e4SLinus Torvalds { 23071da177e4SLinus Torvalds int err; 23081da177e4SLinus Torvalds struct ip_mreqn imr; 230963007727SAl Viro __be32 addr = mreqs->imr_multiaddr; 23101da177e4SLinus Torvalds struct ip_mc_socklist *pmc; 23111da177e4SLinus Torvalds struct in_device *in_dev = NULL; 23121da177e4SLinus Torvalds struct inet_sock *inet = inet_sk(sk); 23131da177e4SLinus Torvalds struct ip_sf_socklist *psl; 2314877acedcSDaniel Lezcano struct net *net = sock_net(sk); 23158cdaaa15SDavid L Stevens int leavegroup = 0; 23161da177e4SLinus Torvalds int i, j, rv; 23171da177e4SLinus Torvalds 2318f97c1e0cSJoe Perches if (!ipv4_is_multicast(addr)) 23191da177e4SLinus Torvalds return -EINVAL; 23201da177e4SLinus Torvalds 232154ff9ef3SMarcelo Ricardo Leitner ASSERT_RTNL(); 23221da177e4SLinus Torvalds 23231da177e4SLinus Torvalds imr.imr_multiaddr.s_addr = mreqs->imr_multiaddr; 23241da177e4SLinus Torvalds imr.imr_address.s_addr = mreqs->imr_interface; 23251da177e4SLinus Torvalds imr.imr_ifindex = ifindex; 2326877acedcSDaniel Lezcano in_dev = ip_mc_find_dev(net, &imr); 23271da177e4SLinus Torvalds 23281da177e4SLinus Torvalds if (!in_dev) { 23291da177e4SLinus Torvalds err = -ENODEV; 23301da177e4SLinus Torvalds goto done; 23311da177e4SLinus Torvalds } 23321da177e4SLinus Torvalds err = -EADDRNOTAVAIL; 23331da177e4SLinus Torvalds 23341d7138deSEric Dumazet for_each_pmc_rtnl(inet, pmc) { 2335f64f9e71SJoe Perches if ((pmc->multi.imr_multiaddr.s_addr == 2336f64f9e71SJoe Perches imr.imr_multiaddr.s_addr) && 2337f64f9e71SJoe Perches (pmc->multi.imr_ifindex == imr.imr_ifindex)) 23381da177e4SLinus Torvalds break; 23391da177e4SLinus Torvalds } 2340917f2f10SDavid L Stevens if (!pmc) { /* must have a prior join */ 2341917f2f10SDavid L Stevens err = -EINVAL; 23421da177e4SLinus Torvalds goto done; 2343917f2f10SDavid L Stevens } 23441da177e4SLinus Torvalds /* if a source filter was set, must be the same mode as before */ 23451da177e4SLinus Torvalds if (pmc->sflist) { 2346917f2f10SDavid L Stevens if (pmc->sfmode != omode) { 2347917f2f10SDavid L Stevens err = -EINVAL; 23481da177e4SLinus Torvalds goto done; 2349917f2f10SDavid L Stevens } 23501da177e4SLinus Torvalds } else if (pmc->sfmode != omode) { 23511da177e4SLinus Torvalds /* allow mode switches for empty-set filters */ 23521da177e4SLinus Torvalds ip_mc_add_src(in_dev, &mreqs->imr_multiaddr, omode, 0, NULL, 0); 23531da177e4SLinus Torvalds ip_mc_del_src(in_dev, &mreqs->imr_multiaddr, pmc->sfmode, 0, 23541da177e4SLinus Torvalds NULL, 0); 23551da177e4SLinus Torvalds pmc->sfmode = omode; 23561da177e4SLinus Torvalds } 23571da177e4SLinus Torvalds 23581d7138deSEric Dumazet psl = rtnl_dereference(pmc->sflist); 23591da177e4SLinus Torvalds if (!add) { 23601da177e4SLinus Torvalds if (!psl) 2361917f2f10SDavid L Stevens goto done; /* err = -EADDRNOTAVAIL */ 23621da177e4SLinus Torvalds rv = !0; 23631da177e4SLinus Torvalds for (i = 0; i < psl->sl_count; i++) { 23641da177e4SLinus Torvalds rv = memcmp(&psl->sl_addr[i], &mreqs->imr_sourceaddr, 236563007727SAl Viro sizeof(__be32)); 23661da177e4SLinus Torvalds if (rv == 0) 23671da177e4SLinus Torvalds break; 23681da177e4SLinus Torvalds } 23691da177e4SLinus Torvalds if (rv) /* source not found */ 2370917f2f10SDavid L Stevens goto done; /* err = -EADDRNOTAVAIL */ 23711da177e4SLinus Torvalds 23728cdaaa15SDavid L Stevens /* special case - (INCLUDE, empty) == LEAVE_GROUP */ 23738cdaaa15SDavid L Stevens if (psl->sl_count == 1 && omode == MCAST_INCLUDE) { 23748cdaaa15SDavid L Stevens leavegroup = 1; 23758cdaaa15SDavid L Stevens goto done; 23768cdaaa15SDavid L Stevens } 23778cdaaa15SDavid L Stevens 23781da177e4SLinus Torvalds /* update the interface filter */ 23791da177e4SLinus Torvalds ip_mc_del_src(in_dev, &mreqs->imr_multiaddr, omode, 1, 23801da177e4SLinus Torvalds &mreqs->imr_sourceaddr, 1); 23811da177e4SLinus Torvalds 23821da177e4SLinus Torvalds for (j = i+1; j < psl->sl_count; j++) 23831da177e4SLinus Torvalds psl->sl_addr[j-1] = psl->sl_addr[j]; 23841da177e4SLinus Torvalds psl->sl_count--; 23851da177e4SLinus Torvalds err = 0; 23861da177e4SLinus Torvalds goto done; 23871da177e4SLinus Torvalds } 23881da177e4SLinus Torvalds /* else, add a new source to the filter */ 23891da177e4SLinus Torvalds 23906ae0f2e5SKuniyuki Iwashima if (psl && psl->sl_count >= READ_ONCE(net->ipv4.sysctl_igmp_max_msf)) { 23911da177e4SLinus Torvalds err = -ENOBUFS; 23921da177e4SLinus Torvalds goto done; 23931da177e4SLinus Torvalds } 23941da177e4SLinus Torvalds if (!psl || psl->sl_count == psl->sl_max) { 23951da177e4SLinus Torvalds struct ip_sf_socklist *newpsl; 23961da177e4SLinus Torvalds int count = IP_SFBLOCK; 23971da177e4SLinus Torvalds 23981da177e4SLinus Torvalds if (psl) 23991da177e4SLinus Torvalds count += psl->sl_max; 2400e6a1f7e0SGustavo A. R. Silva newpsl = sock_kmalloc(sk, struct_size(newpsl, sl_addr, count), 2401e6a1f7e0SGustavo A. R. Silva GFP_KERNEL); 24021da177e4SLinus Torvalds if (!newpsl) { 24031da177e4SLinus Torvalds err = -ENOBUFS; 24041da177e4SLinus Torvalds goto done; 24051da177e4SLinus Torvalds } 24061da177e4SLinus Torvalds newpsl->sl_max = count; 24071da177e4SLinus Torvalds newpsl->sl_count = count - IP_SFBLOCK; 24081da177e4SLinus Torvalds if (psl) { 24091da177e4SLinus Torvalds for (i = 0; i < psl->sl_count; i++) 24101da177e4SLinus Torvalds newpsl->sl_addr[i] = psl->sl_addr[i]; 2411c85bb41eSFlavio Leitner /* decrease mem now to avoid the memleak warning */ 2412e6a1f7e0SGustavo A. R. Silva atomic_sub(struct_size(psl, sl_addr, psl->sl_max), 2413e6a1f7e0SGustavo A. R. Silva &sk->sk_omem_alloc); 24141da177e4SLinus Torvalds } 2415cf778b00SEric Dumazet rcu_assign_pointer(pmc->sflist, newpsl); 2416dba5bdd5SEric Dumazet if (psl) 2417dba5bdd5SEric Dumazet kfree_rcu(psl, rcu); 2418c85bb41eSFlavio Leitner psl = newpsl; 24191da177e4SLinus Torvalds } 24201da177e4SLinus Torvalds rv = 1; /* > 0 for insert logic below if sl_count is 0 */ 24211da177e4SLinus Torvalds for (i = 0; i < psl->sl_count; i++) { 24221da177e4SLinus Torvalds rv = memcmp(&psl->sl_addr[i], &mreqs->imr_sourceaddr, 242363007727SAl Viro sizeof(__be32)); 24241da177e4SLinus Torvalds if (rv == 0) 24251da177e4SLinus Torvalds break; 24261da177e4SLinus Torvalds } 24271da177e4SLinus Torvalds if (rv == 0) /* address already there is an error */ 24281da177e4SLinus Torvalds goto done; 24291da177e4SLinus Torvalds for (j = psl->sl_count-1; j >= i; j--) 24301da177e4SLinus Torvalds psl->sl_addr[j+1] = psl->sl_addr[j]; 24311da177e4SLinus Torvalds psl->sl_addr[i] = mreqs->imr_sourceaddr; 24321da177e4SLinus Torvalds psl->sl_count++; 24331da177e4SLinus Torvalds err = 0; 24341da177e4SLinus Torvalds /* update the interface list */ 24351da177e4SLinus Torvalds ip_mc_add_src(in_dev, &mreqs->imr_multiaddr, omode, 1, 24361da177e4SLinus Torvalds &mreqs->imr_sourceaddr, 1); 24371da177e4SLinus Torvalds done: 24388cdaaa15SDavid L Stevens if (leavegroup) 243954ff9ef3SMarcelo Ricardo Leitner err = ip_mc_leave_group(sk, &imr); 24401da177e4SLinus Torvalds return err; 24411da177e4SLinus Torvalds } 24421da177e4SLinus Torvalds 24431da177e4SLinus Torvalds int ip_mc_msfilter(struct sock *sk, struct ip_msfilter *msf, int ifindex) 24441da177e4SLinus Torvalds { 24459951f036SDavid L Stevens int err = 0; 24461da177e4SLinus Torvalds struct ip_mreqn imr; 244763007727SAl Viro __be32 addr = msf->imsf_multiaddr; 24481da177e4SLinus Torvalds struct ip_mc_socklist *pmc; 24491da177e4SLinus Torvalds struct in_device *in_dev; 24501da177e4SLinus Torvalds struct inet_sock *inet = inet_sk(sk); 24511da177e4SLinus Torvalds struct ip_sf_socklist *newpsl, *psl; 2452877acedcSDaniel Lezcano struct net *net = sock_net(sk); 24539951f036SDavid L Stevens int leavegroup = 0; 24541da177e4SLinus Torvalds 2455f97c1e0cSJoe Perches if (!ipv4_is_multicast(addr)) 24561da177e4SLinus Torvalds return -EINVAL; 24571da177e4SLinus Torvalds if (msf->imsf_fmode != MCAST_INCLUDE && 24581da177e4SLinus Torvalds msf->imsf_fmode != MCAST_EXCLUDE) 24591da177e4SLinus Torvalds return -EINVAL; 24601da177e4SLinus Torvalds 246154ff9ef3SMarcelo Ricardo Leitner ASSERT_RTNL(); 24621da177e4SLinus Torvalds 24631da177e4SLinus Torvalds imr.imr_multiaddr.s_addr = msf->imsf_multiaddr; 24641da177e4SLinus Torvalds imr.imr_address.s_addr = msf->imsf_interface; 24651da177e4SLinus Torvalds imr.imr_ifindex = ifindex; 2466877acedcSDaniel Lezcano in_dev = ip_mc_find_dev(net, &imr); 24671da177e4SLinus Torvalds 24681da177e4SLinus Torvalds if (!in_dev) { 24691da177e4SLinus Torvalds err = -ENODEV; 24701da177e4SLinus Torvalds goto done; 24711da177e4SLinus Torvalds } 24721da177e4SLinus Torvalds 24739951f036SDavid L Stevens /* special case - (INCLUDE, empty) == LEAVE_GROUP */ 24749951f036SDavid L Stevens if (msf->imsf_fmode == MCAST_INCLUDE && msf->imsf_numsrc == 0) { 24759951f036SDavid L Stevens leavegroup = 1; 24769951f036SDavid L Stevens goto done; 24779951f036SDavid L Stevens } 24789951f036SDavid L Stevens 24791d7138deSEric Dumazet for_each_pmc_rtnl(inet, pmc) { 24801da177e4SLinus Torvalds if (pmc->multi.imr_multiaddr.s_addr == msf->imsf_multiaddr && 24811da177e4SLinus Torvalds pmc->multi.imr_ifindex == imr.imr_ifindex) 24821da177e4SLinus Torvalds break; 24831da177e4SLinus Torvalds } 2484917f2f10SDavid L Stevens if (!pmc) { /* must have a prior join */ 2485917f2f10SDavid L Stevens err = -EINVAL; 24861da177e4SLinus Torvalds goto done; 2487917f2f10SDavid L Stevens } 24881da177e4SLinus Torvalds if (msf->imsf_numsrc) { 2489e6a1f7e0SGustavo A. R. Silva newpsl = sock_kmalloc(sk, struct_size(newpsl, sl_addr, 2490e6a1f7e0SGustavo A. R. Silva msf->imsf_numsrc), 24918b3a7005SKris Katterjohn GFP_KERNEL); 24921da177e4SLinus Torvalds if (!newpsl) { 24931da177e4SLinus Torvalds err = -ENOBUFS; 24941da177e4SLinus Torvalds goto done; 24951da177e4SLinus Torvalds } 24961da177e4SLinus Torvalds newpsl->sl_max = newpsl->sl_count = msf->imsf_numsrc; 24972d3e5cafSGustavo A. R. Silva memcpy(newpsl->sl_addr, msf->imsf_slist_flex, 24982d3e5cafSGustavo A. R. Silva flex_array_size(msf, imsf_slist_flex, msf->imsf_numsrc)); 24991da177e4SLinus Torvalds err = ip_mc_add_src(in_dev, &msf->imsf_multiaddr, 25001da177e4SLinus Torvalds msf->imsf_fmode, newpsl->sl_count, newpsl->sl_addr, 0); 25011da177e4SLinus Torvalds if (err) { 2502e6a1f7e0SGustavo A. R. Silva sock_kfree_s(sk, newpsl, 2503e6a1f7e0SGustavo A. R. Silva struct_size(newpsl, sl_addr, 2504e6a1f7e0SGustavo A. R. Silva newpsl->sl_max)); 25051da177e4SLinus Torvalds goto done; 25061da177e4SLinus Torvalds } 25078713dbf0SYan Zheng } else { 25081da177e4SLinus Torvalds newpsl = NULL; 25098713dbf0SYan Zheng (void) ip_mc_add_src(in_dev, &msf->imsf_multiaddr, 25108713dbf0SYan Zheng msf->imsf_fmode, 0, NULL, 0); 25118713dbf0SYan Zheng } 25121d7138deSEric Dumazet psl = rtnl_dereference(pmc->sflist); 25131da177e4SLinus Torvalds if (psl) { 25141da177e4SLinus Torvalds (void) ip_mc_del_src(in_dev, &msf->imsf_multiaddr, pmc->sfmode, 25151da177e4SLinus Torvalds psl->sl_count, psl->sl_addr, 0); 2516c85bb41eSFlavio Leitner /* decrease mem now to avoid the memleak warning */ 2517e6a1f7e0SGustavo A. R. Silva atomic_sub(struct_size(psl, sl_addr, psl->sl_max), 2518e6a1f7e0SGustavo A. R. Silva &sk->sk_omem_alloc); 2519dba5bdd5SEric Dumazet } else { 25201da177e4SLinus Torvalds (void) ip_mc_del_src(in_dev, &msf->imsf_multiaddr, pmc->sfmode, 25211da177e4SLinus Torvalds 0, NULL, 0); 2522dba5bdd5SEric Dumazet } 2523cf778b00SEric Dumazet rcu_assign_pointer(pmc->sflist, newpsl); 2524dba5bdd5SEric Dumazet if (psl) 2525dba5bdd5SEric Dumazet kfree_rcu(psl, rcu); 25261da177e4SLinus Torvalds pmc->sfmode = msf->imsf_fmode; 2527917f2f10SDavid L Stevens err = 0; 25281da177e4SLinus Torvalds done: 25299951f036SDavid L Stevens if (leavegroup) 25309951f036SDavid L Stevens err = ip_mc_leave_group(sk, &imr); 25311da177e4SLinus Torvalds return err; 25321da177e4SLinus Torvalds } 25331da177e4SLinus Torvalds int ip_mc_msfget(struct sock *sk, struct ip_msfilter *msf, 2534728f064cSMartin KaFai Lau sockptr_t optval, sockptr_t optlen) 25351da177e4SLinus Torvalds { 2536728f064cSMartin KaFai Lau int err, len, count, copycount, msf_size; 25371da177e4SLinus Torvalds struct ip_mreqn imr; 253863007727SAl Viro __be32 addr = msf->imsf_multiaddr; 25391da177e4SLinus Torvalds struct ip_mc_socklist *pmc; 25401da177e4SLinus Torvalds struct in_device *in_dev; 25411da177e4SLinus Torvalds struct inet_sock *inet = inet_sk(sk); 25421da177e4SLinus Torvalds struct ip_sf_socklist *psl; 2543877acedcSDaniel Lezcano struct net *net = sock_net(sk); 25441da177e4SLinus Torvalds 254587e9f031SWANG Cong ASSERT_RTNL(); 254687e9f031SWANG Cong 2547f97c1e0cSJoe Perches if (!ipv4_is_multicast(addr)) 25481da177e4SLinus Torvalds return -EINVAL; 25491da177e4SLinus Torvalds 25501da177e4SLinus Torvalds imr.imr_multiaddr.s_addr = msf->imsf_multiaddr; 25511da177e4SLinus Torvalds imr.imr_address.s_addr = msf->imsf_interface; 25521da177e4SLinus Torvalds imr.imr_ifindex = 0; 2553877acedcSDaniel Lezcano in_dev = ip_mc_find_dev(net, &imr); 25541da177e4SLinus Torvalds 25551da177e4SLinus Torvalds if (!in_dev) { 25561da177e4SLinus Torvalds err = -ENODEV; 25571da177e4SLinus Torvalds goto done; 25581da177e4SLinus Torvalds } 25591da177e4SLinus Torvalds err = -EADDRNOTAVAIL; 25601da177e4SLinus Torvalds 25611d7138deSEric Dumazet for_each_pmc_rtnl(inet, pmc) { 25621da177e4SLinus Torvalds if (pmc->multi.imr_multiaddr.s_addr == msf->imsf_multiaddr && 25631da177e4SLinus Torvalds pmc->multi.imr_ifindex == imr.imr_ifindex) 25641da177e4SLinus Torvalds break; 25651da177e4SLinus Torvalds } 25661da177e4SLinus Torvalds if (!pmc) /* must have a prior join */ 25671da177e4SLinus Torvalds goto done; 25681da177e4SLinus Torvalds msf->imsf_fmode = pmc->sfmode; 25691d7138deSEric Dumazet psl = rtnl_dereference(pmc->sflist); 25701da177e4SLinus Torvalds if (!psl) { 25711da177e4SLinus Torvalds count = 0; 25721da177e4SLinus Torvalds } else { 25731da177e4SLinus Torvalds count = psl->sl_count; 25741da177e4SLinus Torvalds } 25751da177e4SLinus Torvalds copycount = count < msf->imsf_numsrc ? count : msf->imsf_numsrc; 25762d3e5cafSGustavo A. R. Silva len = flex_array_size(psl, sl_addr, copycount); 25771da177e4SLinus Torvalds msf->imsf_numsrc = count; 2578728f064cSMartin KaFai Lau msf_size = IP_MSFILTER_SIZE(copycount); 2579728f064cSMartin KaFai Lau if (copy_to_sockptr(optlen, &msf_size, sizeof(int)) || 2580728f064cSMartin KaFai Lau copy_to_sockptr(optval, msf, IP_MSFILTER_SIZE(0))) { 25811da177e4SLinus Torvalds return -EFAULT; 25821da177e4SLinus Torvalds } 25831da177e4SLinus Torvalds if (len && 2584728f064cSMartin KaFai Lau copy_to_sockptr_offset(optval, 2585728f064cSMartin KaFai Lau offsetof(struct ip_msfilter, imsf_slist_flex), 2586728f064cSMartin KaFai Lau psl->sl_addr, len)) 25871da177e4SLinus Torvalds return -EFAULT; 25881da177e4SLinus Torvalds return 0; 25891da177e4SLinus Torvalds done: 25901da177e4SLinus Torvalds return err; 25911da177e4SLinus Torvalds } 25921da177e4SLinus Torvalds 25931da177e4SLinus Torvalds int ip_mc_gsfget(struct sock *sk, struct group_filter *gsf, 2594728f064cSMartin KaFai Lau sockptr_t optval, size_t ss_offset) 25951da177e4SLinus Torvalds { 2596931ca7abSAl Viro int i, count, copycount; 25971da177e4SLinus Torvalds struct sockaddr_in *psin; 259863007727SAl Viro __be32 addr; 25991da177e4SLinus Torvalds struct ip_mc_socklist *pmc; 26001da177e4SLinus Torvalds struct inet_sock *inet = inet_sk(sk); 26011da177e4SLinus Torvalds struct ip_sf_socklist *psl; 26021da177e4SLinus Torvalds 260387e9f031SWANG Cong ASSERT_RTNL(); 260487e9f031SWANG Cong 26051da177e4SLinus Torvalds psin = (struct sockaddr_in *)&gsf->gf_group; 26061da177e4SLinus Torvalds if (psin->sin_family != AF_INET) 26071da177e4SLinus Torvalds return -EINVAL; 26081da177e4SLinus Torvalds addr = psin->sin_addr.s_addr; 2609f97c1e0cSJoe Perches if (!ipv4_is_multicast(addr)) 26101da177e4SLinus Torvalds return -EINVAL; 26111da177e4SLinus Torvalds 26121d7138deSEric Dumazet for_each_pmc_rtnl(inet, pmc) { 26131da177e4SLinus Torvalds if (pmc->multi.imr_multiaddr.s_addr == addr && 26141da177e4SLinus Torvalds pmc->multi.imr_ifindex == gsf->gf_interface) 26151da177e4SLinus Torvalds break; 26161da177e4SLinus Torvalds } 26171da177e4SLinus Torvalds if (!pmc) /* must have a prior join */ 2618931ca7abSAl Viro return -EADDRNOTAVAIL; 26191da177e4SLinus Torvalds gsf->gf_fmode = pmc->sfmode; 26201d7138deSEric Dumazet psl = rtnl_dereference(pmc->sflist); 26211da177e4SLinus Torvalds count = psl ? psl->sl_count : 0; 26221da177e4SLinus Torvalds copycount = count < gsf->gf_numsrc ? count : gsf->gf_numsrc; 26231da177e4SLinus Torvalds gsf->gf_numsrc = count; 2624728f064cSMartin KaFai Lau for (i = 0; i < copycount; i++) { 26251da177e4SLinus Torvalds struct sockaddr_storage ss; 26261da177e4SLinus Torvalds 26271da177e4SLinus Torvalds psin = (struct sockaddr_in *)&ss; 26281da177e4SLinus Torvalds memset(&ss, 0, sizeof(ss)); 26291da177e4SLinus Torvalds psin->sin_family = AF_INET; 26301da177e4SLinus Torvalds psin->sin_addr.s_addr = psl->sl_addr[i]; 2631728f064cSMartin KaFai Lau if (copy_to_sockptr_offset(optval, ss_offset, 2632728f064cSMartin KaFai Lau &ss, sizeof(ss))) 26331da177e4SLinus Torvalds return -EFAULT; 2634728f064cSMartin KaFai Lau ss_offset += sizeof(ss); 26351da177e4SLinus Torvalds } 26361da177e4SLinus Torvalds return 0; 26371da177e4SLinus Torvalds } 26381da177e4SLinus Torvalds 26391da177e4SLinus Torvalds /* 26401da177e4SLinus Torvalds * check if a multicast source filter allows delivery for a given <src,dst,intf> 26411da177e4SLinus Torvalds */ 264233e972bdSEric Dumazet int ip_mc_sf_allow(const struct sock *sk, __be32 loc_addr, __be32 rmt_addr, 264360d9b031SDavid Ahern int dif, int sdif) 26441da177e4SLinus Torvalds { 264533e972bdSEric Dumazet const struct inet_sock *inet = inet_sk(sk); 26461da177e4SLinus Torvalds struct ip_mc_socklist *pmc; 26471da177e4SLinus Torvalds struct ip_sf_socklist *psl; 26481da177e4SLinus Torvalds int i; 2649c85bb41eSFlavio Leitner int ret; 26501da177e4SLinus Torvalds 2651c85bb41eSFlavio Leitner ret = 1; 2652f97c1e0cSJoe Perches if (!ipv4_is_multicast(loc_addr)) 2653c85bb41eSFlavio Leitner goto out; 26541da177e4SLinus Torvalds 2655c85bb41eSFlavio Leitner rcu_read_lock(); 26561d7138deSEric Dumazet for_each_pmc_rcu(inet, pmc) { 26571da177e4SLinus Torvalds if (pmc->multi.imr_multiaddr.s_addr == loc_addr && 265860d9b031SDavid Ahern (pmc->multi.imr_ifindex == dif || 265960d9b031SDavid Ahern (sdif && pmc->multi.imr_ifindex == sdif))) 26601da177e4SLinus Torvalds break; 26611da177e4SLinus Torvalds } 2662307b4ac6SEric Dumazet ret = inet_test_bit(MC_ALL, sk); 26631da177e4SLinus Torvalds if (!pmc) 2664c85bb41eSFlavio Leitner goto unlock; 26651d7138deSEric Dumazet psl = rcu_dereference(pmc->sflist); 2666c85bb41eSFlavio Leitner ret = (pmc->sfmode == MCAST_EXCLUDE); 26671da177e4SLinus Torvalds if (!psl) 2668c85bb41eSFlavio Leitner goto unlock; 26691da177e4SLinus Torvalds 26701da177e4SLinus Torvalds for (i = 0; i < psl->sl_count; i++) { 26711da177e4SLinus Torvalds if (psl->sl_addr[i] == rmt_addr) 26721da177e4SLinus Torvalds break; 26731da177e4SLinus Torvalds } 2674c85bb41eSFlavio Leitner ret = 0; 26751da177e4SLinus Torvalds if (pmc->sfmode == MCAST_INCLUDE && i >= psl->sl_count) 2676c85bb41eSFlavio Leitner goto unlock; 26771da177e4SLinus Torvalds if (pmc->sfmode == MCAST_EXCLUDE && i < psl->sl_count) 2678c85bb41eSFlavio Leitner goto unlock; 2679c85bb41eSFlavio Leitner ret = 1; 2680c85bb41eSFlavio Leitner unlock: 2681c85bb41eSFlavio Leitner rcu_read_unlock(); 2682c85bb41eSFlavio Leitner out: 2683c85bb41eSFlavio Leitner return ret; 26841da177e4SLinus Torvalds } 26851da177e4SLinus Torvalds 26861da177e4SLinus Torvalds /* 26871da177e4SLinus Torvalds * A socket is closing. 26881da177e4SLinus Torvalds */ 26891da177e4SLinus Torvalds 26901da177e4SLinus Torvalds void ip_mc_drop_socket(struct sock *sk) 26911da177e4SLinus Torvalds { 26921da177e4SLinus Torvalds struct inet_sock *inet = inet_sk(sk); 26931da177e4SLinus Torvalds struct ip_mc_socklist *iml; 2694877acedcSDaniel Lezcano struct net *net = sock_net(sk); 26951da177e4SLinus Torvalds 269651456b29SIan Morris if (!inet->mc_list) 26971da177e4SLinus Torvalds return; 26981da177e4SLinus Torvalds 26991da177e4SLinus Torvalds rtnl_lock(); 27001d7138deSEric Dumazet while ((iml = rtnl_dereference(inet->mc_list)) != NULL) { 27011da177e4SLinus Torvalds struct in_device *in_dev; 27021da177e4SLinus Torvalds 27031d7138deSEric Dumazet inet->mc_list = iml->next_rcu; 2704877acedcSDaniel Lezcano in_dev = inetdev_by_index(net, iml->multi.imr_ifindex); 27051da177e4SLinus Torvalds (void) ip_mc_leave_src(sk, iml, in_dev); 270600db4124SIan Morris if (in_dev) 27071da177e4SLinus Torvalds ip_mc_dec_group(in_dev, iml->multi.imr_multiaddr.s_addr); 2708c85bb41eSFlavio Leitner /* decrease mem now to avoid the memleak warning */ 2709c85bb41eSFlavio Leitner atomic_sub(sizeof(*iml), &sk->sk_omem_alloc); 271010d50e74SLai Jiangshan kfree_rcu(iml, rcu); 27111da177e4SLinus Torvalds } 27121da177e4SLinus Torvalds rtnl_unlock(); 27131da177e4SLinus Torvalds } 27141da177e4SLinus Torvalds 2715dbdd9a52SDavid S. Miller /* called with rcu_read_lock() */ 27162094acbbSAlexander Duyck int ip_check_mc_rcu(struct in_device *in_dev, __be32 mc_addr, __be32 src_addr, u8 proto) 27171da177e4SLinus Torvalds { 27181da177e4SLinus Torvalds struct ip_mc_list *im; 2719e9897071SEric Dumazet struct ip_mc_list __rcu **mc_hash; 27201da177e4SLinus Torvalds struct ip_sf_list *psf; 27211da177e4SLinus Torvalds int rv = 0; 27221da177e4SLinus Torvalds 2723e9897071SEric Dumazet mc_hash = rcu_dereference(in_dev->mc_hash); 2724e9897071SEric Dumazet if (mc_hash) { 2725c70eba74SEric Dumazet u32 hash = hash_32((__force u32)mc_addr, MC_HASH_SZ_LOG); 2726e9897071SEric Dumazet 2727e9897071SEric Dumazet for (im = rcu_dereference(mc_hash[hash]); 2728e9897071SEric Dumazet im != NULL; 2729e9897071SEric Dumazet im = rcu_dereference(im->next_hash)) { 2730e9897071SEric Dumazet if (im->multiaddr == mc_addr) 2731e9897071SEric Dumazet break; 2732e9897071SEric Dumazet } 2733e9897071SEric Dumazet } else { 27341d7138deSEric Dumazet for_each_pmc_rcu(in_dev, im) { 27351da177e4SLinus Torvalds if (im->multiaddr == mc_addr) 27361da177e4SLinus Torvalds break; 27371da177e4SLinus Torvalds } 2738e9897071SEric Dumazet } 27391da177e4SLinus Torvalds if (im && proto == IPPROTO_IGMP) { 27401da177e4SLinus Torvalds rv = 1; 27411da177e4SLinus Torvalds } else if (im) { 27421da177e4SLinus Torvalds if (src_addr) { 274323d2b940SLiu Jian spin_lock_bh(&im->lock); 27441da177e4SLinus Torvalds for (psf = im->sources; psf; psf = psf->sf_next) { 27451da177e4SLinus Torvalds if (psf->sf_inaddr == src_addr) 27461da177e4SLinus Torvalds break; 27471da177e4SLinus Torvalds } 27481da177e4SLinus Torvalds if (psf) 27491da177e4SLinus Torvalds rv = psf->sf_count[MCAST_INCLUDE] || 27501da177e4SLinus Torvalds psf->sf_count[MCAST_EXCLUDE] != 27511da177e4SLinus Torvalds im->sfcount[MCAST_EXCLUDE]; 27521da177e4SLinus Torvalds else 27531da177e4SLinus Torvalds rv = im->sfcount[MCAST_EXCLUDE] != 0; 275423d2b940SLiu Jian spin_unlock_bh(&im->lock); 27551da177e4SLinus Torvalds } else 27561da177e4SLinus Torvalds rv = 1; /* unspecified source; tentatively allow */ 27571da177e4SLinus Torvalds } 27581da177e4SLinus Torvalds return rv; 27591da177e4SLinus Torvalds } 27601da177e4SLinus Torvalds 27611da177e4SLinus Torvalds #if defined(CONFIG_PROC_FS) 27621da177e4SLinus Torvalds struct igmp_mc_iter_state { 27637091e728SAlexey Dobriyan struct seq_net_private p; 27641da177e4SLinus Torvalds struct net_device *dev; 27651da177e4SLinus Torvalds struct in_device *in_dev; 27661da177e4SLinus Torvalds }; 27671da177e4SLinus Torvalds 27681da177e4SLinus Torvalds #define igmp_mc_seq_private(seq) ((struct igmp_mc_iter_state *)(seq)->private) 27691da177e4SLinus Torvalds 27701da177e4SLinus Torvalds static inline struct ip_mc_list *igmp_mc_get_first(struct seq_file *seq) 27711da177e4SLinus Torvalds { 27727091e728SAlexey Dobriyan struct net *net = seq_file_net(seq); 27731da177e4SLinus Torvalds struct ip_mc_list *im = NULL; 27741da177e4SLinus Torvalds struct igmp_mc_iter_state *state = igmp_mc_seq_private(seq); 27751da177e4SLinus Torvalds 27767562f876SPavel Emelianov state->in_dev = NULL; 277761fbab77Sstephen hemminger for_each_netdev_rcu(net, state->dev) { 27781da177e4SLinus Torvalds struct in_device *in_dev; 27796baff150SEric Dumazet 27806baff150SEric Dumazet in_dev = __in_dev_get_rcu(state->dev); 27811da177e4SLinus Torvalds if (!in_dev) 27821da177e4SLinus Torvalds continue; 27831d7138deSEric Dumazet im = rcu_dereference(in_dev->mc_list); 27841da177e4SLinus Torvalds if (im) { 27851da177e4SLinus Torvalds state->in_dev = in_dev; 27861da177e4SLinus Torvalds break; 27871da177e4SLinus Torvalds } 27881da177e4SLinus Torvalds } 27891da177e4SLinus Torvalds return im; 27901da177e4SLinus Torvalds } 27911da177e4SLinus Torvalds 27921da177e4SLinus Torvalds static struct ip_mc_list *igmp_mc_get_next(struct seq_file *seq, struct ip_mc_list *im) 27931da177e4SLinus Torvalds { 27941da177e4SLinus Torvalds struct igmp_mc_iter_state *state = igmp_mc_seq_private(seq); 27956baff150SEric Dumazet 27961d7138deSEric Dumazet im = rcu_dereference(im->next_rcu); 27971d7138deSEric Dumazet while (!im) { 27986baff150SEric Dumazet state->dev = next_net_device_rcu(state->dev); 27991da177e4SLinus Torvalds if (!state->dev) { 28001da177e4SLinus Torvalds state->in_dev = NULL; 28011da177e4SLinus Torvalds break; 28021da177e4SLinus Torvalds } 28036baff150SEric Dumazet state->in_dev = __in_dev_get_rcu(state->dev); 28041da177e4SLinus Torvalds if (!state->in_dev) 28051da177e4SLinus Torvalds continue; 28061d7138deSEric Dumazet im = rcu_dereference(state->in_dev->mc_list); 28071da177e4SLinus Torvalds } 28081da177e4SLinus Torvalds return im; 28091da177e4SLinus Torvalds } 28101da177e4SLinus Torvalds 28111da177e4SLinus Torvalds static struct ip_mc_list *igmp_mc_get_idx(struct seq_file *seq, loff_t pos) 28121da177e4SLinus Torvalds { 28131da177e4SLinus Torvalds struct ip_mc_list *im = igmp_mc_get_first(seq); 28141da177e4SLinus Torvalds if (im) 28151da177e4SLinus Torvalds while (pos && (im = igmp_mc_get_next(seq, im)) != NULL) 28161da177e4SLinus Torvalds --pos; 28171da177e4SLinus Torvalds return pos ? NULL : im; 28181da177e4SLinus Torvalds } 28191da177e4SLinus Torvalds 28201da177e4SLinus Torvalds static void *igmp_mc_seq_start(struct seq_file *seq, loff_t *pos) 282161fbab77Sstephen hemminger __acquires(rcu) 28221da177e4SLinus Torvalds { 282361fbab77Sstephen hemminger rcu_read_lock(); 28241da177e4SLinus Torvalds return *pos ? igmp_mc_get_idx(seq, *pos - 1) : SEQ_START_TOKEN; 28251da177e4SLinus Torvalds } 28261da177e4SLinus Torvalds 28271da177e4SLinus Torvalds static void *igmp_mc_seq_next(struct seq_file *seq, void *v, loff_t *pos) 28281da177e4SLinus Torvalds { 28291da177e4SLinus Torvalds struct ip_mc_list *im; 28301da177e4SLinus Torvalds if (v == SEQ_START_TOKEN) 28311da177e4SLinus Torvalds im = igmp_mc_get_first(seq); 28321da177e4SLinus Torvalds else 28331da177e4SLinus Torvalds im = igmp_mc_get_next(seq, v); 28341da177e4SLinus Torvalds ++*pos; 28351da177e4SLinus Torvalds return im; 28361da177e4SLinus Torvalds } 28371da177e4SLinus Torvalds 28381da177e4SLinus Torvalds static void igmp_mc_seq_stop(struct seq_file *seq, void *v) 283961fbab77Sstephen hemminger __releases(rcu) 28401da177e4SLinus Torvalds { 28411da177e4SLinus Torvalds struct igmp_mc_iter_state *state = igmp_mc_seq_private(seq); 28421d7138deSEric Dumazet 28431da177e4SLinus Torvalds state->in_dev = NULL; 28441da177e4SLinus Torvalds state->dev = NULL; 284561fbab77Sstephen hemminger rcu_read_unlock(); 28461da177e4SLinus Torvalds } 28471da177e4SLinus Torvalds 28481da177e4SLinus Torvalds static int igmp_mc_seq_show(struct seq_file *seq, void *v) 28491da177e4SLinus Torvalds { 28501da177e4SLinus Torvalds if (v == SEQ_START_TOKEN) 28511da177e4SLinus Torvalds seq_puts(seq, 28521da177e4SLinus Torvalds "Idx\tDevice : Count Querier\tGroup Users Timer\tReporter\n"); 28531da177e4SLinus Torvalds else { 28542e47eeceSYu Zhe struct ip_mc_list *im = v; 28551da177e4SLinus Torvalds struct igmp_mc_iter_state *state = igmp_mc_seq_private(seq); 28561da177e4SLinus Torvalds char *querier; 2857a399a805SEric Dumazet long delta; 2858a399a805SEric Dumazet 28591da177e4SLinus Torvalds #ifdef CONFIG_IP_MULTICAST 28601da177e4SLinus Torvalds querier = IGMP_V1_SEEN(state->in_dev) ? "V1" : 28611da177e4SLinus Torvalds IGMP_V2_SEEN(state->in_dev) ? "V2" : 28621da177e4SLinus Torvalds "V3"; 28631da177e4SLinus Torvalds #else 28641da177e4SLinus Torvalds querier = "NONE"; 28651da177e4SLinus Torvalds #endif 28661da177e4SLinus Torvalds 2867e6b68883SAndreea-Cristina Bernat if (rcu_access_pointer(state->in_dev->mc_list) == im) { 28681da177e4SLinus Torvalds seq_printf(seq, "%d\t%-10s: %5d %7s\n", 2869b8bae41eSRami Rosen state->dev->ifindex, state->dev->name, state->in_dev->mc_count, querier); 28701da177e4SLinus Torvalds } 28711da177e4SLinus Torvalds 2872a399a805SEric Dumazet delta = im->timer.expires - jiffies; 28731da177e4SLinus Torvalds seq_printf(seq, 2874338fcf98SAlexey Dobriyan "\t\t\t\t%08X %5d %d:%08lX\t\t%d\n", 28751da177e4SLinus Torvalds im->multiaddr, im->users, 2876a399a805SEric Dumazet im->tm_running, 2877a399a805SEric Dumazet im->tm_running ? jiffies_delta_to_clock_t(delta) : 0, 28781da177e4SLinus Torvalds im->reporter); 28791da177e4SLinus Torvalds } 28801da177e4SLinus Torvalds return 0; 28811da177e4SLinus Torvalds } 28821da177e4SLinus Torvalds 2883f690808eSStephen Hemminger static const struct seq_operations igmp_mc_seq_ops = { 28841da177e4SLinus Torvalds .start = igmp_mc_seq_start, 28851da177e4SLinus Torvalds .next = igmp_mc_seq_next, 28861da177e4SLinus Torvalds .stop = igmp_mc_seq_stop, 28871da177e4SLinus Torvalds .show = igmp_mc_seq_show, 28881da177e4SLinus Torvalds }; 28891da177e4SLinus Torvalds 28901da177e4SLinus Torvalds struct igmp_mcf_iter_state { 28917091e728SAlexey Dobriyan struct seq_net_private p; 28921da177e4SLinus Torvalds struct net_device *dev; 28931da177e4SLinus Torvalds struct in_device *idev; 28941da177e4SLinus Torvalds struct ip_mc_list *im; 28951da177e4SLinus Torvalds }; 28961da177e4SLinus Torvalds 28971da177e4SLinus Torvalds #define igmp_mcf_seq_private(seq) ((struct igmp_mcf_iter_state *)(seq)->private) 28981da177e4SLinus Torvalds 28991da177e4SLinus Torvalds static inline struct ip_sf_list *igmp_mcf_get_first(struct seq_file *seq) 29001da177e4SLinus Torvalds { 29017091e728SAlexey Dobriyan struct net *net = seq_file_net(seq); 29021da177e4SLinus Torvalds struct ip_sf_list *psf = NULL; 29031da177e4SLinus Torvalds struct ip_mc_list *im = NULL; 29041da177e4SLinus Torvalds struct igmp_mcf_iter_state *state = igmp_mcf_seq_private(seq); 29051da177e4SLinus Torvalds 29067562f876SPavel Emelianov state->idev = NULL; 29077562f876SPavel Emelianov state->im = NULL; 290861fbab77Sstephen hemminger for_each_netdev_rcu(net, state->dev) { 29091da177e4SLinus Torvalds struct in_device *idev; 29106baff150SEric Dumazet idev = __in_dev_get_rcu(state->dev); 291151456b29SIan Morris if (unlikely(!idev)) 29121da177e4SLinus Torvalds continue; 29131d7138deSEric Dumazet im = rcu_dereference(idev->mc_list); 291400db4124SIan Morris if (likely(im)) { 29151da177e4SLinus Torvalds spin_lock_bh(&im->lock); 29161da177e4SLinus Torvalds psf = im->sources; 291700db4124SIan Morris if (likely(psf)) { 29181da177e4SLinus Torvalds state->im = im; 29191da177e4SLinus Torvalds state->idev = idev; 29201da177e4SLinus Torvalds break; 29211da177e4SLinus Torvalds } 29221da177e4SLinus Torvalds spin_unlock_bh(&im->lock); 29231da177e4SLinus Torvalds } 29241da177e4SLinus Torvalds } 29251da177e4SLinus Torvalds return psf; 29261da177e4SLinus Torvalds } 29271da177e4SLinus Torvalds 29281da177e4SLinus Torvalds static struct ip_sf_list *igmp_mcf_get_next(struct seq_file *seq, struct ip_sf_list *psf) 29291da177e4SLinus Torvalds { 29301da177e4SLinus Torvalds struct igmp_mcf_iter_state *state = igmp_mcf_seq_private(seq); 29311da177e4SLinus Torvalds 29321da177e4SLinus Torvalds psf = psf->sf_next; 29331da177e4SLinus Torvalds while (!psf) { 29341da177e4SLinus Torvalds spin_unlock_bh(&state->im->lock); 29351da177e4SLinus Torvalds state->im = state->im->next; 29361da177e4SLinus Torvalds while (!state->im) { 29376baff150SEric Dumazet state->dev = next_net_device_rcu(state->dev); 29381da177e4SLinus Torvalds if (!state->dev) { 29391da177e4SLinus Torvalds state->idev = NULL; 29401da177e4SLinus Torvalds goto out; 29411da177e4SLinus Torvalds } 29426baff150SEric Dumazet state->idev = __in_dev_get_rcu(state->dev); 29431da177e4SLinus Torvalds if (!state->idev) 29441da177e4SLinus Torvalds continue; 29451d7138deSEric Dumazet state->im = rcu_dereference(state->idev->mc_list); 29461da177e4SLinus Torvalds } 29471da177e4SLinus Torvalds if (!state->im) 29481da177e4SLinus Torvalds break; 29491da177e4SLinus Torvalds spin_lock_bh(&state->im->lock); 29501da177e4SLinus Torvalds psf = state->im->sources; 29511da177e4SLinus Torvalds } 29521da177e4SLinus Torvalds out: 29531da177e4SLinus Torvalds return psf; 29541da177e4SLinus Torvalds } 29551da177e4SLinus Torvalds 29561da177e4SLinus Torvalds static struct ip_sf_list *igmp_mcf_get_idx(struct seq_file *seq, loff_t pos) 29571da177e4SLinus Torvalds { 29581da177e4SLinus Torvalds struct ip_sf_list *psf = igmp_mcf_get_first(seq); 29591da177e4SLinus Torvalds if (psf) 29601da177e4SLinus Torvalds while (pos && (psf = igmp_mcf_get_next(seq, psf)) != NULL) 29611da177e4SLinus Torvalds --pos; 29621da177e4SLinus Torvalds return pos ? NULL : psf; 29631da177e4SLinus Torvalds } 29641da177e4SLinus Torvalds 29651da177e4SLinus Torvalds static void *igmp_mcf_seq_start(struct seq_file *seq, loff_t *pos) 296661fbab77Sstephen hemminger __acquires(rcu) 29671da177e4SLinus Torvalds { 296861fbab77Sstephen hemminger rcu_read_lock(); 29691da177e4SLinus Torvalds return *pos ? igmp_mcf_get_idx(seq, *pos - 1) : SEQ_START_TOKEN; 29701da177e4SLinus Torvalds } 29711da177e4SLinus Torvalds 29721da177e4SLinus Torvalds static void *igmp_mcf_seq_next(struct seq_file *seq, void *v, loff_t *pos) 29731da177e4SLinus Torvalds { 29741da177e4SLinus Torvalds struct ip_sf_list *psf; 29751da177e4SLinus Torvalds if (v == SEQ_START_TOKEN) 29761da177e4SLinus Torvalds psf = igmp_mcf_get_first(seq); 29771da177e4SLinus Torvalds else 29781da177e4SLinus Torvalds psf = igmp_mcf_get_next(seq, v); 29791da177e4SLinus Torvalds ++*pos; 29801da177e4SLinus Torvalds return psf; 29811da177e4SLinus Torvalds } 29821da177e4SLinus Torvalds 29831da177e4SLinus Torvalds static void igmp_mcf_seq_stop(struct seq_file *seq, void *v) 298461fbab77Sstephen hemminger __releases(rcu) 29851da177e4SLinus Torvalds { 29861da177e4SLinus Torvalds struct igmp_mcf_iter_state *state = igmp_mcf_seq_private(seq); 298700db4124SIan Morris if (likely(state->im)) { 29881da177e4SLinus Torvalds spin_unlock_bh(&state->im->lock); 29891da177e4SLinus Torvalds state->im = NULL; 29901da177e4SLinus Torvalds } 29911da177e4SLinus Torvalds state->idev = NULL; 29921da177e4SLinus Torvalds state->dev = NULL; 299361fbab77Sstephen hemminger rcu_read_unlock(); 29941da177e4SLinus Torvalds } 29951da177e4SLinus Torvalds 29961da177e4SLinus Torvalds static int igmp_mcf_seq_show(struct seq_file *seq, void *v) 29971da177e4SLinus Torvalds { 29982e47eeceSYu Zhe struct ip_sf_list *psf = v; 29991da177e4SLinus Torvalds struct igmp_mcf_iter_state *state = igmp_mcf_seq_private(seq); 30001da177e4SLinus Torvalds 30011da177e4SLinus Torvalds if (v == SEQ_START_TOKEN) { 30021744bea1SJoe Perches seq_puts(seq, "Idx Device MCA SRC INC EXC\n"); 30031da177e4SLinus Torvalds } else { 30041da177e4SLinus Torvalds seq_printf(seq, 30051da177e4SLinus Torvalds "%3d %6.6s 0x%08x " 30061da177e4SLinus Torvalds "0x%08x %6lu %6lu\n", 30071da177e4SLinus Torvalds state->dev->ifindex, state->dev->name, 30081da177e4SLinus Torvalds ntohl(state->im->multiaddr), 30091da177e4SLinus Torvalds ntohl(psf->sf_inaddr), 30101da177e4SLinus Torvalds psf->sf_count[MCAST_INCLUDE], 30111da177e4SLinus Torvalds psf->sf_count[MCAST_EXCLUDE]); 30121da177e4SLinus Torvalds } 30131da177e4SLinus Torvalds return 0; 30141da177e4SLinus Torvalds } 30151da177e4SLinus Torvalds 3016f690808eSStephen Hemminger static const struct seq_operations igmp_mcf_seq_ops = { 30171da177e4SLinus Torvalds .start = igmp_mcf_seq_start, 30181da177e4SLinus Torvalds .next = igmp_mcf_seq_next, 30191da177e4SLinus Torvalds .stop = igmp_mcf_seq_stop, 30201da177e4SLinus Torvalds .show = igmp_mcf_seq_show, 30211da177e4SLinus Torvalds }; 30221da177e4SLinus Torvalds 30232c8c1e72SAlexey Dobriyan static int __net_init igmp_net_init(struct net *net) 30247091e728SAlexey Dobriyan { 30257091e728SAlexey Dobriyan struct proc_dir_entry *pde; 302693a714d6SMadhu Challa int err; 30277091e728SAlexey Dobriyan 3028c3506372SChristoph Hellwig pde = proc_create_net("igmp", 0444, net->proc_net, &igmp_mc_seq_ops, 3029c3506372SChristoph Hellwig sizeof(struct igmp_mc_iter_state)); 30307091e728SAlexey Dobriyan if (!pde) 30317091e728SAlexey Dobriyan goto out_igmp; 3032c3506372SChristoph Hellwig pde = proc_create_net("mcfilter", 0444, net->proc_net, 3033c3506372SChristoph Hellwig &igmp_mcf_seq_ops, sizeof(struct igmp_mcf_iter_state)); 30347091e728SAlexey Dobriyan if (!pde) 30357091e728SAlexey Dobriyan goto out_mcfilter; 303693a714d6SMadhu Challa err = inet_ctl_sock_create(&net->ipv4.mc_autojoin_sk, AF_INET, 303793a714d6SMadhu Challa SOCK_DGRAM, 0, net); 303893a714d6SMadhu Challa if (err < 0) { 303993a714d6SMadhu Challa pr_err("Failed to initialize the IGMP autojoin socket (err %d)\n", 304093a714d6SMadhu Challa err); 304193a714d6SMadhu Challa goto out_sock; 304293a714d6SMadhu Challa } 304393a714d6SMadhu Challa 30447091e728SAlexey Dobriyan return 0; 30457091e728SAlexey Dobriyan 304693a714d6SMadhu Challa out_sock: 304793a714d6SMadhu Challa remove_proc_entry("mcfilter", net->proc_net); 30487091e728SAlexey Dobriyan out_mcfilter: 3049ece31ffdSGao feng remove_proc_entry("igmp", net->proc_net); 30507091e728SAlexey Dobriyan out_igmp: 30517091e728SAlexey Dobriyan return -ENOMEM; 30527091e728SAlexey Dobriyan } 30537091e728SAlexey Dobriyan 30542c8c1e72SAlexey Dobriyan static void __net_exit igmp_net_exit(struct net *net) 30557091e728SAlexey Dobriyan { 3056ece31ffdSGao feng remove_proc_entry("mcfilter", net->proc_net); 3057ece31ffdSGao feng remove_proc_entry("igmp", net->proc_net); 305893a714d6SMadhu Challa inet_ctl_sock_destroy(net->ipv4.mc_autojoin_sk); 30597091e728SAlexey Dobriyan } 30607091e728SAlexey Dobriyan 30617091e728SAlexey Dobriyan static struct pernet_operations igmp_net_ops = { 30627091e728SAlexey Dobriyan .init = igmp_net_init, 30637091e728SAlexey Dobriyan .exit = igmp_net_exit, 30641da177e4SLinus Torvalds }; 306572c1d3bdSWANG Cong #endif 30661da177e4SLinus Torvalds 30674aa5dee4SJiri Pirko static int igmp_netdev_event(struct notifier_block *this, 30684aa5dee4SJiri Pirko unsigned long event, void *ptr) 30694aa5dee4SJiri Pirko { 30704aa5dee4SJiri Pirko struct net_device *dev = netdev_notifier_info_to_dev(ptr); 30714aa5dee4SJiri Pirko struct in_device *in_dev; 30724aa5dee4SJiri Pirko 30734aa5dee4SJiri Pirko switch (event) { 30744aa5dee4SJiri Pirko case NETDEV_RESEND_IGMP: 30754aa5dee4SJiri Pirko in_dev = __in_dev_get_rtnl(dev); 30764aa5dee4SJiri Pirko if (in_dev) 30774aa5dee4SJiri Pirko ip_mc_rejoin_groups(in_dev); 30784aa5dee4SJiri Pirko break; 30794aa5dee4SJiri Pirko default: 30804aa5dee4SJiri Pirko break; 30814aa5dee4SJiri Pirko } 30824aa5dee4SJiri Pirko return NOTIFY_DONE; 30834aa5dee4SJiri Pirko } 30844aa5dee4SJiri Pirko 30854aa5dee4SJiri Pirko static struct notifier_block igmp_notifier = { 30864aa5dee4SJiri Pirko .notifier_call = igmp_netdev_event, 30874aa5dee4SJiri Pirko }; 30884aa5dee4SJiri Pirko 308972c1d3bdSWANG Cong int __init igmp_mc_init(void) 30901da177e4SLinus Torvalds { 309172c1d3bdSWANG Cong #if defined(CONFIG_PROC_FS) 30924aa5dee4SJiri Pirko int err; 30934aa5dee4SJiri Pirko 30944aa5dee4SJiri Pirko err = register_pernet_subsys(&igmp_net_ops); 30954aa5dee4SJiri Pirko if (err) 30964aa5dee4SJiri Pirko return err; 30974aa5dee4SJiri Pirko err = register_netdevice_notifier(&igmp_notifier); 30984aa5dee4SJiri Pirko if (err) 30994aa5dee4SJiri Pirko goto reg_notif_fail; 31004aa5dee4SJiri Pirko return 0; 31014aa5dee4SJiri Pirko 31024aa5dee4SJiri Pirko reg_notif_fail: 31034aa5dee4SJiri Pirko unregister_pernet_subsys(&igmp_net_ops); 31044aa5dee4SJiri Pirko return err; 310572c1d3bdSWANG Cong #else 310672c1d3bdSWANG Cong return register_netdevice_notifier(&igmp_notifier); 31071da177e4SLinus Torvalds #endif 310872c1d3bdSWANG Cong } 3109