11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * Linux NET3: Internet Group Management Protocol [IGMP] 31da177e4SLinus Torvalds * 41da177e4SLinus Torvalds * This code implements the IGMP protocol as defined in RFC1112. There has 51da177e4SLinus Torvalds * been a further revision of this protocol since which is now supported. 61da177e4SLinus Torvalds * 71da177e4SLinus Torvalds * If you have trouble with this module be careful what gcc you have used, 81da177e4SLinus Torvalds * the older version didn't come out right using gcc 2.5.8, the newer one 91da177e4SLinus Torvalds * seems to fall out with gcc 2.6.2. 101da177e4SLinus Torvalds * 111da177e4SLinus Torvalds * Authors: 12113aa838SAlan Cox * Alan Cox <alan@lxorguk.ukuu.org.uk> 131da177e4SLinus Torvalds * 141da177e4SLinus Torvalds * This program is free software; you can redistribute it and/or 151da177e4SLinus Torvalds * modify it under the terms of the GNU General Public License 161da177e4SLinus Torvalds * as published by the Free Software Foundation; either version 171da177e4SLinus Torvalds * 2 of the License, or (at your option) any later version. 181da177e4SLinus Torvalds * 191da177e4SLinus Torvalds * Fixes: 201da177e4SLinus Torvalds * 211da177e4SLinus Torvalds * Alan Cox : Added lots of __inline__ to optimise 221da177e4SLinus Torvalds * the memory usage of all the tiny little 231da177e4SLinus Torvalds * functions. 241da177e4SLinus Torvalds * Alan Cox : Dumped the header building experiment. 251da177e4SLinus Torvalds * Alan Cox : Minor tweaks ready for multicast routing 261da177e4SLinus Torvalds * and extended IGMP protocol. 271da177e4SLinus Torvalds * Alan Cox : Removed a load of inline directives. Gcc 2.5.8 281da177e4SLinus Torvalds * writes utterly bogus code otherwise (sigh) 291da177e4SLinus Torvalds * fixed IGMP loopback to behave in the manner 301da177e4SLinus Torvalds * desired by mrouted, fixed the fact it has been 311da177e4SLinus Torvalds * broken since 1.3.6 and cleaned up a few minor 321da177e4SLinus Torvalds * points. 331da177e4SLinus Torvalds * 341da177e4SLinus Torvalds * Chih-Jen Chang : Tried to revise IGMP to Version 2 351da177e4SLinus Torvalds * Tsu-Sheng Tsao E-mail: chihjenc@scf.usc.edu and tsusheng@scf.usc.edu 361da177e4SLinus Torvalds * The enhancements are mainly based on Steve Deering's 371da177e4SLinus Torvalds * ipmulti-3.5 source code. 381da177e4SLinus Torvalds * Chih-Jen Chang : Added the igmp_get_mrouter_info and 391da177e4SLinus Torvalds * Tsu-Sheng Tsao igmp_set_mrouter_info to keep track of 401da177e4SLinus Torvalds * the mrouted version on that device. 411da177e4SLinus Torvalds * Chih-Jen Chang : Added the max_resp_time parameter to 421da177e4SLinus Torvalds * Tsu-Sheng Tsao igmp_heard_query(). Using this parameter 431da177e4SLinus Torvalds * to identify the multicast router version 441da177e4SLinus Torvalds * and do what the IGMP version 2 specified. 451da177e4SLinus Torvalds * Chih-Jen Chang : Added a timer to revert to IGMP V2 router 461da177e4SLinus Torvalds * Tsu-Sheng Tsao if the specified time expired. 471da177e4SLinus Torvalds * Alan Cox : Stop IGMP from 0.0.0.0 being accepted. 481da177e4SLinus Torvalds * Alan Cox : Use GFP_ATOMIC in the right places. 491da177e4SLinus Torvalds * Christian Daudt : igmp timer wasn't set for local group 501da177e4SLinus Torvalds * memberships but was being deleted, 511da177e4SLinus Torvalds * which caused a "del_timer() called 521da177e4SLinus Torvalds * from %p with timer not initialized\n" 531da177e4SLinus Torvalds * message (960131). 541da177e4SLinus Torvalds * Christian Daudt : removed del_timer from 551da177e4SLinus Torvalds * igmp_timer_expire function (960205). 561da177e4SLinus Torvalds * Christian Daudt : igmp_heard_report now only calls 571da177e4SLinus Torvalds * igmp_timer_expire if tm->running is 581da177e4SLinus Torvalds * true (960216). 591da177e4SLinus Torvalds * Malcolm Beattie : ttl comparison wrong in igmp_rcv made 601da177e4SLinus Torvalds * igmp_heard_query never trigger. Expiry 611da177e4SLinus Torvalds * miscalculation fixed in igmp_heard_query 621da177e4SLinus Torvalds * and random() made to return unsigned to 631da177e4SLinus Torvalds * prevent negative expiry times. 641da177e4SLinus Torvalds * Alexey Kuznetsov: Wrong group leaving behaviour, backport 651da177e4SLinus Torvalds * fix from pending 2.1.x patches. 661da177e4SLinus Torvalds * Alan Cox: Forget to enable FDDI support earlier. 671da177e4SLinus Torvalds * Alexey Kuznetsov: Fixed leaving groups on device down. 681da177e4SLinus Torvalds * Alexey Kuznetsov: Accordance to igmp-v2-06 draft. 691da177e4SLinus Torvalds * David L Stevens: IGMPv3 support, with help from 701da177e4SLinus Torvalds * Vinay Kulkarni 711da177e4SLinus Torvalds */ 721da177e4SLinus Torvalds 731da177e4SLinus Torvalds #include <linux/module.h> 745a0e3ad6STejun Heo #include <linux/slab.h> 757c0f6ba6SLinus Torvalds #include <linux/uaccess.h> 761da177e4SLinus Torvalds #include <linux/types.h> 771da177e4SLinus Torvalds #include <linux/kernel.h> 781da177e4SLinus Torvalds #include <linux/jiffies.h> 791da177e4SLinus Torvalds #include <linux/string.h> 801da177e4SLinus Torvalds #include <linux/socket.h> 811da177e4SLinus Torvalds #include <linux/sockios.h> 821da177e4SLinus Torvalds #include <linux/in.h> 831da177e4SLinus Torvalds #include <linux/inet.h> 841da177e4SLinus Torvalds #include <linux/netdevice.h> 851da177e4SLinus Torvalds #include <linux/skbuff.h> 861da177e4SLinus Torvalds #include <linux/inetdevice.h> 871da177e4SLinus Torvalds #include <linux/igmp.h> 881da177e4SLinus Torvalds #include <linux/if_arp.h> 891da177e4SLinus Torvalds #include <linux/rtnetlink.h> 901da177e4SLinus Torvalds #include <linux/times.h> 919d4a0314SHannes Frederic Sowa #include <linux/pkt_sched.h> 9214c85021SArnaldo Carvalho de Melo 93457c4cbcSEric W. Biederman #include <net/net_namespace.h> 9414c85021SArnaldo Carvalho de Melo #include <net/arp.h> 951da177e4SLinus Torvalds #include <net/ip.h> 961da177e4SLinus Torvalds #include <net/protocol.h> 971da177e4SLinus Torvalds #include <net/route.h> 981da177e4SLinus Torvalds #include <net/sock.h> 991da177e4SLinus Torvalds #include <net/checksum.h> 10093a714d6SMadhu Challa #include <net/inet_common.h> 1011da177e4SLinus Torvalds #include <linux/netfilter_ipv4.h> 1021da177e4SLinus Torvalds #ifdef CONFIG_IP_MROUTE 1031da177e4SLinus Torvalds #include <linux/mroute.h> 1041da177e4SLinus Torvalds #endif 1051da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS 1061da177e4SLinus Torvalds #include <linux/proc_fs.h> 1071da177e4SLinus Torvalds #include <linux/seq_file.h> 1081da177e4SLinus Torvalds #endif 1091da177e4SLinus Torvalds 1101da177e4SLinus Torvalds #ifdef CONFIG_IP_MULTICAST 1111da177e4SLinus Torvalds /* Parameter names and values are taken from igmp-v2-06 draft */ 1121da177e4SLinus Torvalds 113436f7c20SFabian Frederick #define IGMP_V1_ROUTER_PRESENT_TIMEOUT (400*HZ) 114436f7c20SFabian Frederick #define IGMP_V2_ROUTER_PRESENT_TIMEOUT (400*HZ) 115436f7c20SFabian Frederick #define IGMP_V2_UNSOLICITED_REPORT_INTERVAL (10*HZ) 116436f7c20SFabian Frederick #define IGMP_V3_UNSOLICITED_REPORT_INTERVAL (1*HZ) 117436f7c20SFabian Frederick #define IGMP_QUERY_RESPONSE_INTERVAL (10*HZ) 118436f7c20SFabian Frederick #define IGMP_QUERY_ROBUSTNESS_VARIABLE 2 1191da177e4SLinus Torvalds 1201da177e4SLinus Torvalds 121436f7c20SFabian Frederick #define IGMP_INITIAL_REPORT_DELAY (1) 1221da177e4SLinus Torvalds 123436f7c20SFabian Frederick /* IGMP_INITIAL_REPORT_DELAY is not from IGMP specs! 1241da177e4SLinus Torvalds * IGMP specs require to report membership immediately after 1251da177e4SLinus Torvalds * joining a group, but we delay the first report by a 1261da177e4SLinus Torvalds * small interval. It seems more natural and still does not 1271da177e4SLinus Torvalds * contradict to specs provided this delay is small enough. 1281da177e4SLinus Torvalds */ 1291da177e4SLinus Torvalds 13042f811b8SHerbert Xu #define IGMP_V1_SEEN(in_dev) \ 131c346dca1SYOSHIFUJI Hideaki (IPV4_DEVCONF_ALL(dev_net(in_dev->dev), FORCE_IGMP_VERSION) == 1 || \ 13242f811b8SHerbert Xu IN_DEV_CONF_GET((in_dev), FORCE_IGMP_VERSION) == 1 || \ 1331da177e4SLinus Torvalds ((in_dev)->mr_v1_seen && \ 1341da177e4SLinus Torvalds time_before(jiffies, (in_dev)->mr_v1_seen))) 13542f811b8SHerbert Xu #define IGMP_V2_SEEN(in_dev) \ 136c346dca1SYOSHIFUJI Hideaki (IPV4_DEVCONF_ALL(dev_net(in_dev->dev), FORCE_IGMP_VERSION) == 2 || \ 13742f811b8SHerbert Xu IN_DEV_CONF_GET((in_dev), FORCE_IGMP_VERSION) == 2 || \ 1381da177e4SLinus Torvalds ((in_dev)->mr_v2_seen && \ 1391da177e4SLinus Torvalds time_before(jiffies, (in_dev)->mr_v2_seen))) 1401da177e4SLinus Torvalds 141cab70040SWilliam Manley static int unsolicited_report_interval(struct in_device *in_dev) 142cab70040SWilliam Manley { 1432690048cSWilliam Manley int interval_ms, interval_jiffies; 1442690048cSWilliam Manley 145cab70040SWilliam Manley if (IGMP_V1_SEEN(in_dev) || IGMP_V2_SEEN(in_dev)) 1462690048cSWilliam Manley interval_ms = IN_DEV_CONF_GET( 1472690048cSWilliam Manley in_dev, 1482690048cSWilliam Manley IGMPV2_UNSOLICITED_REPORT_INTERVAL); 149cab70040SWilliam Manley else /* v3 */ 1502690048cSWilliam Manley interval_ms = IN_DEV_CONF_GET( 1512690048cSWilliam Manley in_dev, 1522690048cSWilliam Manley IGMPV3_UNSOLICITED_REPORT_INTERVAL); 1532690048cSWilliam Manley 1542690048cSWilliam Manley interval_jiffies = msecs_to_jiffies(interval_ms); 1552690048cSWilliam Manley 1562690048cSWilliam Manley /* _timer functions can't handle a delay of 0 jiffies so ensure 1572690048cSWilliam Manley * we always return a positive value. 1582690048cSWilliam Manley */ 1592690048cSWilliam Manley if (interval_jiffies <= 0) 1602690048cSWilliam Manley interval_jiffies = 1; 1612690048cSWilliam Manley return interval_jiffies; 162cab70040SWilliam Manley } 163cab70040SWilliam Manley 1641da177e4SLinus Torvalds static void igmpv3_add_delrec(struct in_device *in_dev, struct ip_mc_list *im); 16524803f38SHangbin Liu static void igmpv3_del_delrec(struct in_device *in_dev, struct ip_mc_list *im); 1661da177e4SLinus Torvalds static void igmpv3_clear_delrec(struct in_device *in_dev); 1671da177e4SLinus Torvalds static int sf_setstate(struct ip_mc_list *pmc); 1681da177e4SLinus Torvalds static void sf_markstate(struct ip_mc_list *pmc); 1691da177e4SLinus Torvalds #endif 1701da177e4SLinus Torvalds static void ip_mc_clear_src(struct ip_mc_list *pmc); 1718f935bbdSAl Viro static int ip_mc_add_src(struct in_device *in_dev, __be32 *pmca, int sfmode, 1728f935bbdSAl Viro int sfcount, __be32 *psfsrc, int delta); 1731da177e4SLinus Torvalds 1741da177e4SLinus Torvalds static void ip_ma_put(struct ip_mc_list *im) 1751da177e4SLinus Torvalds { 1761da177e4SLinus Torvalds if (atomic_dec_and_test(&im->refcnt)) { 1771da177e4SLinus Torvalds in_dev_put(im->interface); 17842ea299dSLai Jiangshan kfree_rcu(im, rcu); 1791da177e4SLinus Torvalds } 1801da177e4SLinus Torvalds } 1811da177e4SLinus Torvalds 182d9aa9380SDavid S. Miller #define for_each_pmc_rcu(in_dev, pmc) \ 183d9aa9380SDavid S. Miller for (pmc = rcu_dereference(in_dev->mc_list); \ 184d9aa9380SDavid S. Miller pmc != NULL; \ 185d9aa9380SDavid S. Miller pmc = rcu_dereference(pmc->next_rcu)) 186d9aa9380SDavid S. Miller 187d9aa9380SDavid S. Miller #define for_each_pmc_rtnl(in_dev, pmc) \ 188d9aa9380SDavid S. Miller for (pmc = rtnl_dereference(in_dev->mc_list); \ 189d9aa9380SDavid S. Miller pmc != NULL; \ 190d9aa9380SDavid S. Miller pmc = rtnl_dereference(pmc->next_rcu)) 191d9aa9380SDavid S. Miller 1921da177e4SLinus Torvalds #ifdef CONFIG_IP_MULTICAST 1931da177e4SLinus Torvalds 1941da177e4SLinus Torvalds /* 1951da177e4SLinus Torvalds * Timer management 1961da177e4SLinus Torvalds */ 1971da177e4SLinus Torvalds 1981d7138deSEric Dumazet static void igmp_stop_timer(struct ip_mc_list *im) 1991da177e4SLinus Torvalds { 2001da177e4SLinus Torvalds spin_lock_bh(&im->lock); 2011da177e4SLinus Torvalds if (del_timer(&im->timer)) 2021da177e4SLinus Torvalds atomic_dec(&im->refcnt); 2031da177e4SLinus Torvalds im->tm_running = 0; 2041da177e4SLinus Torvalds im->reporter = 0; 2051da177e4SLinus Torvalds im->unsolicit_count = 0; 2061da177e4SLinus Torvalds spin_unlock_bh(&im->lock); 2071da177e4SLinus Torvalds } 2081da177e4SLinus Torvalds 2091da177e4SLinus Torvalds /* It must be called with locked im->lock */ 2101da177e4SLinus Torvalds static void igmp_start_timer(struct ip_mc_list *im, int max_delay) 2111da177e4SLinus Torvalds { 21263862b5bSAruna-Hewapathirane int tv = prandom_u32() % max_delay; 2131da177e4SLinus Torvalds 2141da177e4SLinus Torvalds im->tm_running = 1; 2151da177e4SLinus Torvalds if (!mod_timer(&im->timer, jiffies+tv+2)) 2161da177e4SLinus Torvalds atomic_inc(&im->refcnt); 2171da177e4SLinus Torvalds } 2181da177e4SLinus Torvalds 2191da177e4SLinus Torvalds static void igmp_gq_start_timer(struct in_device *in_dev) 2201da177e4SLinus Torvalds { 22163862b5bSAruna-Hewapathirane int tv = prandom_u32() % in_dev->mr_maxdelay; 2227ababb78SMichal Tesar unsigned long exp = jiffies + tv + 2; 2237ababb78SMichal Tesar 2247ababb78SMichal Tesar if (in_dev->mr_gq_running && 2257ababb78SMichal Tesar time_after_eq(exp, (in_dev->mr_gq_timer).expires)) 2267ababb78SMichal Tesar return; 2271da177e4SLinus Torvalds 2281da177e4SLinus Torvalds in_dev->mr_gq_running = 1; 2297ababb78SMichal Tesar if (!mod_timer(&in_dev->mr_gq_timer, exp)) 2301da177e4SLinus Torvalds in_dev_hold(in_dev); 2311da177e4SLinus Torvalds } 2321da177e4SLinus Torvalds 2331da177e4SLinus Torvalds static void igmp_ifc_start_timer(struct in_device *in_dev, int delay) 2341da177e4SLinus Torvalds { 23563862b5bSAruna-Hewapathirane int tv = prandom_u32() % delay; 2361da177e4SLinus Torvalds 2371da177e4SLinus Torvalds if (!mod_timer(&in_dev->mr_ifc_timer, jiffies+tv+2)) 2381da177e4SLinus Torvalds in_dev_hold(in_dev); 2391da177e4SLinus Torvalds } 2401da177e4SLinus Torvalds 2411da177e4SLinus Torvalds static void igmp_mod_timer(struct ip_mc_list *im, int max_delay) 2421da177e4SLinus Torvalds { 2431da177e4SLinus Torvalds spin_lock_bh(&im->lock); 2441da177e4SLinus Torvalds im->unsolicit_count = 0; 2451da177e4SLinus Torvalds if (del_timer(&im->timer)) { 2461da177e4SLinus Torvalds if ((long)(im->timer.expires-jiffies) < max_delay) { 2471da177e4SLinus Torvalds add_timer(&im->timer); 2481da177e4SLinus Torvalds im->tm_running = 1; 2491da177e4SLinus Torvalds spin_unlock_bh(&im->lock); 2501da177e4SLinus Torvalds return; 2511da177e4SLinus Torvalds } 2521da177e4SLinus Torvalds atomic_dec(&im->refcnt); 2531da177e4SLinus Torvalds } 2541da177e4SLinus Torvalds igmp_start_timer(im, max_delay); 2551da177e4SLinus Torvalds spin_unlock_bh(&im->lock); 2561da177e4SLinus Torvalds } 2571da177e4SLinus Torvalds 2581da177e4SLinus Torvalds 2591da177e4SLinus Torvalds /* 2601da177e4SLinus Torvalds * Send an IGMP report. 2611da177e4SLinus Torvalds */ 2621da177e4SLinus Torvalds 2631da177e4SLinus Torvalds #define IGMP_SIZE (sizeof(struct igmphdr)+sizeof(struct iphdr)+4) 2641da177e4SLinus Torvalds 2651da177e4SLinus Torvalds 2661da177e4SLinus Torvalds static int is_in(struct ip_mc_list *pmc, struct ip_sf_list *psf, int type, 2671da177e4SLinus Torvalds int gdeleted, int sdeleted) 2681da177e4SLinus Torvalds { 2691da177e4SLinus Torvalds switch (type) { 2701da177e4SLinus Torvalds case IGMPV3_MODE_IS_INCLUDE: 2711da177e4SLinus Torvalds case IGMPV3_MODE_IS_EXCLUDE: 2721da177e4SLinus Torvalds if (gdeleted || sdeleted) 2731da177e4SLinus Torvalds return 0; 274ad12583fSDavid L Stevens if (!(pmc->gsquery && !psf->sf_gsresp)) { 275ad12583fSDavid L Stevens if (pmc->sfmode == MCAST_INCLUDE) 276ad12583fSDavid L Stevens return 1; 277ad12583fSDavid L Stevens /* don't include if this source is excluded 278ad12583fSDavid L Stevens * in all filters 279ad12583fSDavid L Stevens */ 280ad12583fSDavid L Stevens if (psf->sf_count[MCAST_INCLUDE]) 281ad12583fSDavid L Stevens return type == IGMPV3_MODE_IS_INCLUDE; 282ad12583fSDavid L Stevens return pmc->sfcount[MCAST_EXCLUDE] == 283ad12583fSDavid L Stevens psf->sf_count[MCAST_EXCLUDE]; 284ad12583fSDavid L Stevens } 285ad12583fSDavid L Stevens return 0; 2861da177e4SLinus Torvalds case IGMPV3_CHANGE_TO_INCLUDE: 2871da177e4SLinus Torvalds if (gdeleted || sdeleted) 2881da177e4SLinus Torvalds return 0; 2891da177e4SLinus Torvalds return psf->sf_count[MCAST_INCLUDE] != 0; 2901da177e4SLinus Torvalds case IGMPV3_CHANGE_TO_EXCLUDE: 2911da177e4SLinus Torvalds if (gdeleted || sdeleted) 2921da177e4SLinus Torvalds return 0; 2931da177e4SLinus Torvalds if (pmc->sfcount[MCAST_EXCLUDE] == 0 || 2941da177e4SLinus Torvalds psf->sf_count[MCAST_INCLUDE]) 2951da177e4SLinus Torvalds return 0; 2961da177e4SLinus Torvalds return pmc->sfcount[MCAST_EXCLUDE] == 2971da177e4SLinus Torvalds psf->sf_count[MCAST_EXCLUDE]; 2981da177e4SLinus Torvalds case IGMPV3_ALLOW_NEW_SOURCES: 2991da177e4SLinus Torvalds if (gdeleted || !psf->sf_crcount) 3001da177e4SLinus Torvalds return 0; 3011da177e4SLinus Torvalds return (pmc->sfmode == MCAST_INCLUDE) ^ sdeleted; 3021da177e4SLinus Torvalds case IGMPV3_BLOCK_OLD_SOURCES: 3031da177e4SLinus Torvalds if (pmc->sfmode == MCAST_INCLUDE) 3041da177e4SLinus Torvalds return gdeleted || (psf->sf_crcount && sdeleted); 3051da177e4SLinus Torvalds return psf->sf_crcount && !gdeleted && !sdeleted; 3061da177e4SLinus Torvalds } 3071da177e4SLinus Torvalds return 0; 3081da177e4SLinus Torvalds } 3091da177e4SLinus Torvalds 3101da177e4SLinus Torvalds static int 3111da177e4SLinus Torvalds igmp_scount(struct ip_mc_list *pmc, int type, int gdeleted, int sdeleted) 3121da177e4SLinus Torvalds { 3131da177e4SLinus Torvalds struct ip_sf_list *psf; 3141da177e4SLinus Torvalds int scount = 0; 3151da177e4SLinus Torvalds 3161da177e4SLinus Torvalds for (psf = pmc->sources; psf; psf = psf->sf_next) { 3171da177e4SLinus Torvalds if (!is_in(pmc, psf, type, gdeleted, sdeleted)) 3181da177e4SLinus Torvalds continue; 3191da177e4SLinus Torvalds scount++; 3201da177e4SLinus Torvalds } 3211da177e4SLinus Torvalds return scount; 3221da177e4SLinus Torvalds } 3231da177e4SLinus Torvalds 3244c672e4bSDaniel Borkmann static struct sk_buff *igmpv3_newpack(struct net_device *dev, unsigned int mtu) 3251da177e4SLinus Torvalds { 3261da177e4SLinus Torvalds struct sk_buff *skb; 3271da177e4SLinus Torvalds struct rtable *rt; 3281da177e4SLinus Torvalds struct iphdr *pip; 3291da177e4SLinus Torvalds struct igmpv3_report *pig; 330877acedcSDaniel Lezcano struct net *net = dev_net(dev); 33131e4543dSDavid S. Miller struct flowi4 fl4; 33266088243SHerbert Xu int hlen = LL_RESERVED_SPACE(dev); 33366088243SHerbert Xu int tlen = dev->needed_tailroom; 3344c672e4bSDaniel Borkmann unsigned int size = mtu; 3351da177e4SLinus Torvalds 33657e1ab6eSEric Dumazet while (1) { 33766088243SHerbert Xu skb = alloc_skb(size + hlen + tlen, 33857e1ab6eSEric Dumazet GFP_ATOMIC | __GFP_NOWARN); 33957e1ab6eSEric Dumazet if (skb) 34057e1ab6eSEric Dumazet break; 34157e1ab6eSEric Dumazet size >>= 1; 34257e1ab6eSEric Dumazet if (size < 256) 3431da177e4SLinus Torvalds return NULL; 34457e1ab6eSEric Dumazet } 3459d4a0314SHannes Frederic Sowa skb->priority = TC_PRIO_CONTROL; 3461da177e4SLinus Torvalds 34731e4543dSDavid S. Miller rt = ip_route_output_ports(net, &fl4, NULL, IGMPV3_ALL_MCR, 0, 34878fbfd8aSDavid S. Miller 0, 0, 34978fbfd8aSDavid S. Miller IPPROTO_IGMP, 0, dev->ifindex); 350b23dd4feSDavid S. Miller if (IS_ERR(rt)) { 3511da177e4SLinus Torvalds kfree_skb(skb); 3521da177e4SLinus Torvalds return NULL; 3531da177e4SLinus Torvalds } 3541da177e4SLinus Torvalds 355d8d1f30bSChangli Gao skb_dst_set(skb, &rt->dst); 3561da177e4SLinus Torvalds skb->dev = dev; 3571da177e4SLinus Torvalds 35866088243SHerbert Xu skb_reserve(skb, hlen); 3591837b2e2SBenjamin Poirier skb_tailroom_reserve(skb, mtu, tlen); 3601da177e4SLinus Torvalds 3617e28ecc2SArnaldo Carvalho de Melo skb_reset_network_header(skb); 362eddc9ec5SArnaldo Carvalho de Melo pip = ip_hdr(skb); 3637e28ecc2SArnaldo Carvalho de Melo skb_put(skb, sizeof(struct iphdr) + 4); 3641da177e4SLinus Torvalds 3651da177e4SLinus Torvalds pip->version = 4; 3661da177e4SLinus Torvalds pip->ihl = (sizeof(struct iphdr)+4)>>2; 3671da177e4SLinus Torvalds pip->tos = 0xc0; 3681da177e4SLinus Torvalds pip->frag_off = htons(IP_DF); 3691da177e4SLinus Torvalds pip->ttl = 1; 370492f64ceSDavid S. Miller pip->daddr = fl4.daddr; 371492f64ceSDavid S. Miller pip->saddr = fl4.saddr; 3721da177e4SLinus Torvalds pip->protocol = IPPROTO_IGMP; 3731da177e4SLinus Torvalds pip->tot_len = 0; /* filled in later */ 374b6a7719aSHannes Frederic Sowa ip_select_ident(net, skb, NULL); 3751da177e4SLinus Torvalds ((u8 *)&pip[1])[0] = IPOPT_RA; 3761da177e4SLinus Torvalds ((u8 *)&pip[1])[1] = 4; 3771da177e4SLinus Torvalds ((u8 *)&pip[1])[2] = 0; 3781da177e4SLinus Torvalds ((u8 *)&pip[1])[3] = 0; 3791da177e4SLinus Torvalds 380b0e380b1SArnaldo Carvalho de Melo skb->transport_header = skb->network_header + sizeof(struct iphdr) + 4; 381d10ba34bSArnaldo Carvalho de Melo skb_put(skb, sizeof(*pig)); 382d9edf9e2SArnaldo Carvalho de Melo pig = igmpv3_report_hdr(skb); 3831da177e4SLinus Torvalds pig->type = IGMPV3_HOST_MEMBERSHIP_REPORT; 3841da177e4SLinus Torvalds pig->resv1 = 0; 3851da177e4SLinus Torvalds pig->csum = 0; 3861da177e4SLinus Torvalds pig->resv2 = 0; 3871da177e4SLinus Torvalds pig->ngrec = 0; 3881da177e4SLinus Torvalds return skb; 3891da177e4SLinus Torvalds } 3901da177e4SLinus Torvalds 3911da177e4SLinus Torvalds static int igmpv3_sendpack(struct sk_buff *skb) 3921da177e4SLinus Torvalds { 393d9edf9e2SArnaldo Carvalho de Melo struct igmphdr *pig = igmp_hdr(skb); 394f7c0c2aeSSimon Horman const int igmplen = skb_tail_pointer(skb) - skb_transport_header(skb); 3951da177e4SLinus Torvalds 396d9edf9e2SArnaldo Carvalho de Melo pig->csum = ip_compute_csum(igmp_hdr(skb), igmplen); 3971da177e4SLinus Torvalds 39833224b16SEric W. Biederman return ip_local_out(dev_net(skb_dst(skb)->dev), skb->sk, skb); 3991da177e4SLinus Torvalds } 4001da177e4SLinus Torvalds 4011da177e4SLinus Torvalds static int grec_size(struct ip_mc_list *pmc, int type, int gdel, int sdel) 4021da177e4SLinus Torvalds { 4031da177e4SLinus Torvalds return sizeof(struct igmpv3_grec) + 4*igmp_scount(pmc, type, gdel, sdel); 4041da177e4SLinus Torvalds } 4051da177e4SLinus Torvalds 4061da177e4SLinus Torvalds static struct sk_buff *add_grhead(struct sk_buff *skb, struct ip_mc_list *pmc, 4071da177e4SLinus Torvalds int type, struct igmpv3_grec **ppgr) 4081da177e4SLinus Torvalds { 4091da177e4SLinus Torvalds struct net_device *dev = pmc->interface->dev; 4101da177e4SLinus Torvalds struct igmpv3_report *pih; 4111da177e4SLinus Torvalds struct igmpv3_grec *pgr; 4121da177e4SLinus Torvalds 4131da177e4SLinus Torvalds if (!skb) 4141da177e4SLinus Torvalds skb = igmpv3_newpack(dev, dev->mtu); 4151da177e4SLinus Torvalds if (!skb) 4161da177e4SLinus Torvalds return NULL; 4171da177e4SLinus Torvalds pgr = (struct igmpv3_grec *)skb_put(skb, sizeof(struct igmpv3_grec)); 4181da177e4SLinus Torvalds pgr->grec_type = type; 4191da177e4SLinus Torvalds pgr->grec_auxwords = 0; 4201da177e4SLinus Torvalds pgr->grec_nsrcs = 0; 4211da177e4SLinus Torvalds pgr->grec_mca = pmc->multiaddr; 422d9edf9e2SArnaldo Carvalho de Melo pih = igmpv3_report_hdr(skb); 4231da177e4SLinus Torvalds pih->ngrec = htons(ntohs(pih->ngrec)+1); 4241da177e4SLinus Torvalds *ppgr = pgr; 4251da177e4SLinus Torvalds return skb; 4261da177e4SLinus Torvalds } 4271da177e4SLinus Torvalds 4284c672e4bSDaniel Borkmann #define AVAILABLE(skb) ((skb) ? skb_availroom(skb) : 0) 4291da177e4SLinus Torvalds 4301da177e4SLinus Torvalds static struct sk_buff *add_grec(struct sk_buff *skb, struct ip_mc_list *pmc, 4311da177e4SLinus Torvalds int type, int gdeleted, int sdeleted) 4321da177e4SLinus Torvalds { 4331da177e4SLinus Torvalds struct net_device *dev = pmc->interface->dev; 43487a8a2aeSNikolay Borisov struct net *net = dev_net(dev); 4351da177e4SLinus Torvalds struct igmpv3_report *pih; 4361da177e4SLinus Torvalds struct igmpv3_grec *pgr = NULL; 4371da177e4SLinus Torvalds struct ip_sf_list *psf, *psf_next, *psf_prev, **psf_list; 438ad12583fSDavid L Stevens int scount, stotal, first, isquery, truncate; 4391da177e4SLinus Torvalds 4401da177e4SLinus Torvalds if (pmc->multiaddr == IGMP_ALL_HOSTS) 4411da177e4SLinus Torvalds return skb; 44287a8a2aeSNikolay Borisov if (ipv4_is_local_multicast(pmc->multiaddr) && !net->ipv4.sysctl_igmp_llm_reports) 443df2cf4a7SPhilip Downey return skb; 4441da177e4SLinus Torvalds 4451da177e4SLinus Torvalds isquery = type == IGMPV3_MODE_IS_INCLUDE || 4461da177e4SLinus Torvalds type == IGMPV3_MODE_IS_EXCLUDE; 4471da177e4SLinus Torvalds truncate = type == IGMPV3_MODE_IS_EXCLUDE || 4481da177e4SLinus Torvalds type == IGMPV3_CHANGE_TO_EXCLUDE; 4491da177e4SLinus Torvalds 450ad12583fSDavid L Stevens stotal = scount = 0; 451ad12583fSDavid L Stevens 4521da177e4SLinus Torvalds psf_list = sdeleted ? &pmc->tomb : &pmc->sources; 4531da177e4SLinus Torvalds 454ad12583fSDavid L Stevens if (!*psf_list) 455ad12583fSDavid L Stevens goto empty_source; 456ad12583fSDavid L Stevens 457d9edf9e2SArnaldo Carvalho de Melo pih = skb ? igmpv3_report_hdr(skb) : NULL; 4581da177e4SLinus Torvalds 4591da177e4SLinus Torvalds /* EX and TO_EX get a fresh packet, if needed */ 4601da177e4SLinus Torvalds if (truncate) { 4611da177e4SLinus Torvalds if (pih && pih->ngrec && 4621da177e4SLinus Torvalds AVAILABLE(skb) < grec_size(pmc, type, gdeleted, sdeleted)) { 4631da177e4SLinus Torvalds if (skb) 4641da177e4SLinus Torvalds igmpv3_sendpack(skb); 4651da177e4SLinus Torvalds skb = igmpv3_newpack(dev, dev->mtu); 4661da177e4SLinus Torvalds } 4671da177e4SLinus Torvalds } 4681da177e4SLinus Torvalds first = 1; 4691da177e4SLinus Torvalds psf_prev = NULL; 4701da177e4SLinus Torvalds for (psf = *psf_list; psf; psf = psf_next) { 471ea4d9e72SAl Viro __be32 *psrc; 4721da177e4SLinus Torvalds 4731da177e4SLinus Torvalds psf_next = psf->sf_next; 4741da177e4SLinus Torvalds 4751da177e4SLinus Torvalds if (!is_in(pmc, psf, type, gdeleted, sdeleted)) { 4761da177e4SLinus Torvalds psf_prev = psf; 4771da177e4SLinus Torvalds continue; 4781da177e4SLinus Torvalds } 4791da177e4SLinus Torvalds 480a052517aSHangbin Liu /* Based on RFC3376 5.1. Should not send source-list change 481a052517aSHangbin Liu * records when there is a filter mode change. 482a052517aSHangbin Liu */ 483a052517aSHangbin Liu if (((gdeleted && pmc->sfmode == MCAST_EXCLUDE) || 484a052517aSHangbin Liu (!gdeleted && pmc->crcount)) && 485a052517aSHangbin Liu (type == IGMPV3_ALLOW_NEW_SOURCES || 486a052517aSHangbin Liu type == IGMPV3_BLOCK_OLD_SOURCES) && psf->sf_crcount) 487a052517aSHangbin Liu goto decrease_sf_crcount; 488a052517aSHangbin Liu 4891da177e4SLinus Torvalds /* clear marks on query responses */ 4901da177e4SLinus Torvalds if (isquery) 4911da177e4SLinus Torvalds psf->sf_gsresp = 0; 4921da177e4SLinus Torvalds 49363007727SAl Viro if (AVAILABLE(skb) < sizeof(__be32) + 4941da177e4SLinus Torvalds first*sizeof(struct igmpv3_grec)) { 4951da177e4SLinus Torvalds if (truncate && !first) 4961da177e4SLinus Torvalds break; /* truncate these */ 4971da177e4SLinus Torvalds if (pgr) 4981da177e4SLinus Torvalds pgr->grec_nsrcs = htons(scount); 4991da177e4SLinus Torvalds if (skb) 5001da177e4SLinus Torvalds igmpv3_sendpack(skb); 5011da177e4SLinus Torvalds skb = igmpv3_newpack(dev, dev->mtu); 5021da177e4SLinus Torvalds first = 1; 5031da177e4SLinus Torvalds scount = 0; 5041da177e4SLinus Torvalds } 5051da177e4SLinus Torvalds if (first) { 5061da177e4SLinus Torvalds skb = add_grhead(skb, pmc, type, &pgr); 5071da177e4SLinus Torvalds first = 0; 5081da177e4SLinus Torvalds } 509cc63f70bSAlexey Dobriyan if (!skb) 510cc63f70bSAlexey Dobriyan return NULL; 51163007727SAl Viro psrc = (__be32 *)skb_put(skb, sizeof(__be32)); 5121da177e4SLinus Torvalds *psrc = psf->sf_inaddr; 513ad12583fSDavid L Stevens scount++; stotal++; 5141da177e4SLinus Torvalds if ((type == IGMPV3_ALLOW_NEW_SOURCES || 5151da177e4SLinus Torvalds type == IGMPV3_BLOCK_OLD_SOURCES) && psf->sf_crcount) { 516a052517aSHangbin Liu decrease_sf_crcount: 5171da177e4SLinus Torvalds psf->sf_crcount--; 5181da177e4SLinus Torvalds if ((sdeleted || gdeleted) && psf->sf_crcount == 0) { 5191da177e4SLinus Torvalds if (psf_prev) 5201da177e4SLinus Torvalds psf_prev->sf_next = psf->sf_next; 5211da177e4SLinus Torvalds else 5221da177e4SLinus Torvalds *psf_list = psf->sf_next; 5231da177e4SLinus Torvalds kfree(psf); 5241da177e4SLinus Torvalds continue; 5251da177e4SLinus Torvalds } 5261da177e4SLinus Torvalds } 5271da177e4SLinus Torvalds psf_prev = psf; 5281da177e4SLinus Torvalds } 529ad12583fSDavid L Stevens 530ad12583fSDavid L Stevens empty_source: 531ad12583fSDavid L Stevens if (!stotal) { 532ad12583fSDavid L Stevens if (type == IGMPV3_ALLOW_NEW_SOURCES || 533ad12583fSDavid L Stevens type == IGMPV3_BLOCK_OLD_SOURCES) 534ad12583fSDavid L Stevens return skb; 535ad12583fSDavid L Stevens if (pmc->crcount || isquery) { 536ad12583fSDavid L Stevens /* make sure we have room for group header */ 537ad12583fSDavid L Stevens if (skb && AVAILABLE(skb) < sizeof(struct igmpv3_grec)) { 538ad12583fSDavid L Stevens igmpv3_sendpack(skb); 539ad12583fSDavid L Stevens skb = NULL; /* add_grhead will get a new one */ 540ad12583fSDavid L Stevens } 541ad12583fSDavid L Stevens skb = add_grhead(skb, pmc, type, &pgr); 542ad12583fSDavid L Stevens } 543ad12583fSDavid L Stevens } 5441da177e4SLinus Torvalds if (pgr) 5451da177e4SLinus Torvalds pgr->grec_nsrcs = htons(scount); 5461da177e4SLinus Torvalds 5471da177e4SLinus Torvalds if (isquery) 5481da177e4SLinus Torvalds pmc->gsquery = 0; /* clear query state on report */ 5491da177e4SLinus Torvalds return skb; 5501da177e4SLinus Torvalds } 5511da177e4SLinus Torvalds 5521da177e4SLinus Torvalds static int igmpv3_send_report(struct in_device *in_dev, struct ip_mc_list *pmc) 5531da177e4SLinus Torvalds { 5541da177e4SLinus Torvalds struct sk_buff *skb = NULL; 55587a8a2aeSNikolay Borisov struct net *net = dev_net(in_dev->dev); 5561da177e4SLinus Torvalds int type; 5571da177e4SLinus Torvalds 5581da177e4SLinus Torvalds if (!pmc) { 5591d7138deSEric Dumazet rcu_read_lock(); 5601d7138deSEric Dumazet for_each_pmc_rcu(in_dev, pmc) { 5611da177e4SLinus Torvalds if (pmc->multiaddr == IGMP_ALL_HOSTS) 5621da177e4SLinus Torvalds continue; 563df2cf4a7SPhilip Downey if (ipv4_is_local_multicast(pmc->multiaddr) && 56487a8a2aeSNikolay Borisov !net->ipv4.sysctl_igmp_llm_reports) 565df2cf4a7SPhilip Downey continue; 5661da177e4SLinus Torvalds spin_lock_bh(&pmc->lock); 5671da177e4SLinus Torvalds if (pmc->sfcount[MCAST_EXCLUDE]) 5681da177e4SLinus Torvalds type = IGMPV3_MODE_IS_EXCLUDE; 5691da177e4SLinus Torvalds else 5701da177e4SLinus Torvalds type = IGMPV3_MODE_IS_INCLUDE; 5711da177e4SLinus Torvalds skb = add_grec(skb, pmc, type, 0, 0); 5721da177e4SLinus Torvalds spin_unlock_bh(&pmc->lock); 5731da177e4SLinus Torvalds } 5741d7138deSEric Dumazet rcu_read_unlock(); 5751da177e4SLinus Torvalds } else { 5761da177e4SLinus Torvalds spin_lock_bh(&pmc->lock); 5771da177e4SLinus Torvalds if (pmc->sfcount[MCAST_EXCLUDE]) 5781da177e4SLinus Torvalds type = IGMPV3_MODE_IS_EXCLUDE; 5791da177e4SLinus Torvalds else 5801da177e4SLinus Torvalds type = IGMPV3_MODE_IS_INCLUDE; 5811da177e4SLinus Torvalds skb = add_grec(skb, pmc, type, 0, 0); 5821da177e4SLinus Torvalds spin_unlock_bh(&pmc->lock); 5831da177e4SLinus Torvalds } 5841da177e4SLinus Torvalds if (!skb) 5851da177e4SLinus Torvalds return 0; 5861da177e4SLinus Torvalds return igmpv3_sendpack(skb); 5871da177e4SLinus Torvalds } 5881da177e4SLinus Torvalds 5891da177e4SLinus Torvalds /* 5901da177e4SLinus Torvalds * remove zero-count source records from a source filter list 5911da177e4SLinus Torvalds */ 5921da177e4SLinus Torvalds static void igmpv3_clear_zeros(struct ip_sf_list **ppsf) 5931da177e4SLinus Torvalds { 5941da177e4SLinus Torvalds struct ip_sf_list *psf_prev, *psf_next, *psf; 5951da177e4SLinus Torvalds 5961da177e4SLinus Torvalds psf_prev = NULL; 5971da177e4SLinus Torvalds for (psf = *ppsf; psf; psf = psf_next) { 5981da177e4SLinus Torvalds psf_next = psf->sf_next; 5991da177e4SLinus Torvalds if (psf->sf_crcount == 0) { 6001da177e4SLinus Torvalds if (psf_prev) 6011da177e4SLinus Torvalds psf_prev->sf_next = psf->sf_next; 6021da177e4SLinus Torvalds else 6031da177e4SLinus Torvalds *ppsf = psf->sf_next; 6041da177e4SLinus Torvalds kfree(psf); 6051da177e4SLinus Torvalds } else 6061da177e4SLinus Torvalds psf_prev = psf; 6071da177e4SLinus Torvalds } 6081da177e4SLinus Torvalds } 6091da177e4SLinus Torvalds 6101da177e4SLinus Torvalds static void igmpv3_send_cr(struct in_device *in_dev) 6111da177e4SLinus Torvalds { 6121da177e4SLinus Torvalds struct ip_mc_list *pmc, *pmc_prev, *pmc_next; 6131da177e4SLinus Torvalds struct sk_buff *skb = NULL; 6141da177e4SLinus Torvalds int type, dtype; 6151da177e4SLinus Torvalds 6161d7138deSEric Dumazet rcu_read_lock(); 6171da177e4SLinus Torvalds spin_lock_bh(&in_dev->mc_tomb_lock); 6181da177e4SLinus Torvalds 6191da177e4SLinus Torvalds /* deleted MCA's */ 6201da177e4SLinus Torvalds pmc_prev = NULL; 6211da177e4SLinus Torvalds for (pmc = in_dev->mc_tomb; pmc; pmc = pmc_next) { 6221da177e4SLinus Torvalds pmc_next = pmc->next; 6231da177e4SLinus Torvalds if (pmc->sfmode == MCAST_INCLUDE) { 6241da177e4SLinus Torvalds type = IGMPV3_BLOCK_OLD_SOURCES; 6251da177e4SLinus Torvalds dtype = IGMPV3_BLOCK_OLD_SOURCES; 6261da177e4SLinus Torvalds skb = add_grec(skb, pmc, type, 1, 0); 6271da177e4SLinus Torvalds skb = add_grec(skb, pmc, dtype, 1, 1); 6281da177e4SLinus Torvalds } 6291da177e4SLinus Torvalds if (pmc->crcount) { 6301da177e4SLinus Torvalds if (pmc->sfmode == MCAST_EXCLUDE) { 6311da177e4SLinus Torvalds type = IGMPV3_CHANGE_TO_INCLUDE; 6321da177e4SLinus Torvalds skb = add_grec(skb, pmc, type, 1, 0); 6331da177e4SLinus Torvalds } 634ad12583fSDavid L Stevens pmc->crcount--; 6351da177e4SLinus Torvalds if (pmc->crcount == 0) { 6361da177e4SLinus Torvalds igmpv3_clear_zeros(&pmc->tomb); 6371da177e4SLinus Torvalds igmpv3_clear_zeros(&pmc->sources); 6381da177e4SLinus Torvalds } 6391da177e4SLinus Torvalds } 6401da177e4SLinus Torvalds if (pmc->crcount == 0 && !pmc->tomb && !pmc->sources) { 6411da177e4SLinus Torvalds if (pmc_prev) 6421da177e4SLinus Torvalds pmc_prev->next = pmc_next; 6431da177e4SLinus Torvalds else 6441da177e4SLinus Torvalds in_dev->mc_tomb = pmc_next; 6451da177e4SLinus Torvalds in_dev_put(pmc->interface); 6461da177e4SLinus Torvalds kfree(pmc); 6471da177e4SLinus Torvalds } else 6481da177e4SLinus Torvalds pmc_prev = pmc; 6491da177e4SLinus Torvalds } 6501da177e4SLinus Torvalds spin_unlock_bh(&in_dev->mc_tomb_lock); 6511da177e4SLinus Torvalds 6521da177e4SLinus Torvalds /* change recs */ 6531d7138deSEric Dumazet for_each_pmc_rcu(in_dev, pmc) { 6541da177e4SLinus Torvalds spin_lock_bh(&pmc->lock); 6551da177e4SLinus Torvalds if (pmc->sfcount[MCAST_EXCLUDE]) { 6561da177e4SLinus Torvalds type = IGMPV3_BLOCK_OLD_SOURCES; 6571da177e4SLinus Torvalds dtype = IGMPV3_ALLOW_NEW_SOURCES; 6581da177e4SLinus Torvalds } else { 6591da177e4SLinus Torvalds type = IGMPV3_ALLOW_NEW_SOURCES; 6601da177e4SLinus Torvalds dtype = IGMPV3_BLOCK_OLD_SOURCES; 6611da177e4SLinus Torvalds } 6621da177e4SLinus Torvalds skb = add_grec(skb, pmc, type, 0, 0); 6631da177e4SLinus Torvalds skb = add_grec(skb, pmc, dtype, 0, 1); /* deleted sources */ 6641da177e4SLinus Torvalds 6651da177e4SLinus Torvalds /* filter mode changes */ 6661da177e4SLinus Torvalds if (pmc->crcount) { 6671da177e4SLinus Torvalds if (pmc->sfmode == MCAST_EXCLUDE) 6681da177e4SLinus Torvalds type = IGMPV3_CHANGE_TO_EXCLUDE; 6691da177e4SLinus Torvalds else 6701da177e4SLinus Torvalds type = IGMPV3_CHANGE_TO_INCLUDE; 6711da177e4SLinus Torvalds skb = add_grec(skb, pmc, type, 0, 0); 672ad12583fSDavid L Stevens pmc->crcount--; 6731da177e4SLinus Torvalds } 6741da177e4SLinus Torvalds spin_unlock_bh(&pmc->lock); 6751da177e4SLinus Torvalds } 6761d7138deSEric Dumazet rcu_read_unlock(); 6771da177e4SLinus Torvalds 6781da177e4SLinus Torvalds if (!skb) 6791da177e4SLinus Torvalds return; 6801da177e4SLinus Torvalds (void) igmpv3_sendpack(skb); 6811da177e4SLinus Torvalds } 6821da177e4SLinus Torvalds 6831da177e4SLinus Torvalds static int igmp_send_report(struct in_device *in_dev, struct ip_mc_list *pmc, 6841da177e4SLinus Torvalds int type) 6851da177e4SLinus Torvalds { 6861da177e4SLinus Torvalds struct sk_buff *skb; 6871da177e4SLinus Torvalds struct iphdr *iph; 6881da177e4SLinus Torvalds struct igmphdr *ih; 6891da177e4SLinus Torvalds struct rtable *rt; 6901da177e4SLinus Torvalds struct net_device *dev = in_dev->dev; 691877acedcSDaniel Lezcano struct net *net = dev_net(dev); 69263007727SAl Viro __be32 group = pmc ? pmc->multiaddr : 0; 69331e4543dSDavid S. Miller struct flowi4 fl4; 69463007727SAl Viro __be32 dst; 69566088243SHerbert Xu int hlen, tlen; 6961da177e4SLinus Torvalds 6971da177e4SLinus Torvalds if (type == IGMPV3_HOST_MEMBERSHIP_REPORT) 6981da177e4SLinus Torvalds return igmpv3_send_report(in_dev, pmc); 699df2cf4a7SPhilip Downey 70087a8a2aeSNikolay Borisov if (ipv4_is_local_multicast(group) && !net->ipv4.sysctl_igmp_llm_reports) 701df2cf4a7SPhilip Downey return 0; 702df2cf4a7SPhilip Downey 703df2cf4a7SPhilip Downey if (type == IGMP_HOST_LEAVE_MESSAGE) 7041da177e4SLinus Torvalds dst = IGMP_ALL_ROUTER; 7051da177e4SLinus Torvalds else 7061da177e4SLinus Torvalds dst = group; 7071da177e4SLinus Torvalds 70831e4543dSDavid S. Miller rt = ip_route_output_ports(net, &fl4, NULL, dst, 0, 70978fbfd8aSDavid S. Miller 0, 0, 71078fbfd8aSDavid S. Miller IPPROTO_IGMP, 0, dev->ifindex); 711b23dd4feSDavid S. Miller if (IS_ERR(rt)) 7121da177e4SLinus Torvalds return -1; 71378fbfd8aSDavid S. Miller 71466088243SHerbert Xu hlen = LL_RESERVED_SPACE(dev); 71566088243SHerbert Xu tlen = dev->needed_tailroom; 71666088243SHerbert Xu skb = alloc_skb(IGMP_SIZE + hlen + tlen, GFP_ATOMIC); 71751456b29SIan Morris if (!skb) { 7181da177e4SLinus Torvalds ip_rt_put(rt); 7191da177e4SLinus Torvalds return -1; 7201da177e4SLinus Torvalds } 7219d4a0314SHannes Frederic Sowa skb->priority = TC_PRIO_CONTROL; 7221da177e4SLinus Torvalds 723d8d1f30bSChangli Gao skb_dst_set(skb, &rt->dst); 7241da177e4SLinus Torvalds 72566088243SHerbert Xu skb_reserve(skb, hlen); 7261da177e4SLinus Torvalds 7277e28ecc2SArnaldo Carvalho de Melo skb_reset_network_header(skb); 728eddc9ec5SArnaldo Carvalho de Melo iph = ip_hdr(skb); 7297e28ecc2SArnaldo Carvalho de Melo skb_put(skb, sizeof(struct iphdr) + 4); 7301da177e4SLinus Torvalds 7311da177e4SLinus Torvalds iph->version = 4; 7321da177e4SLinus Torvalds iph->ihl = (sizeof(struct iphdr)+4)>>2; 7331da177e4SLinus Torvalds iph->tos = 0xc0; 7341da177e4SLinus Torvalds iph->frag_off = htons(IP_DF); 7351da177e4SLinus Torvalds iph->ttl = 1; 7361da177e4SLinus Torvalds iph->daddr = dst; 737492f64ceSDavid S. Miller iph->saddr = fl4.saddr; 7381da177e4SLinus Torvalds iph->protocol = IPPROTO_IGMP; 739b6a7719aSHannes Frederic Sowa ip_select_ident(net, skb, NULL); 7401da177e4SLinus Torvalds ((u8 *)&iph[1])[0] = IPOPT_RA; 7411da177e4SLinus Torvalds ((u8 *)&iph[1])[1] = 4; 7421da177e4SLinus Torvalds ((u8 *)&iph[1])[2] = 0; 7431da177e4SLinus Torvalds ((u8 *)&iph[1])[3] = 0; 7441da177e4SLinus Torvalds 7451da177e4SLinus Torvalds ih = (struct igmphdr *)skb_put(skb, sizeof(struct igmphdr)); 7461da177e4SLinus Torvalds ih->type = type; 7471da177e4SLinus Torvalds ih->code = 0; 7481da177e4SLinus Torvalds ih->csum = 0; 7491da177e4SLinus Torvalds ih->group = group; 7501da177e4SLinus Torvalds ih->csum = ip_compute_csum((void *)ih, sizeof(struct igmphdr)); 7511da177e4SLinus Torvalds 75233224b16SEric W. Biederman return ip_local_out(net, skb->sk, skb); 7531da177e4SLinus Torvalds } 7541da177e4SLinus Torvalds 7551da177e4SLinus Torvalds static void igmp_gq_timer_expire(unsigned long data) 7561da177e4SLinus Torvalds { 7571da177e4SLinus Torvalds struct in_device *in_dev = (struct in_device *)data; 7581da177e4SLinus Torvalds 7591da177e4SLinus Torvalds in_dev->mr_gq_running = 0; 7601da177e4SLinus Torvalds igmpv3_send_report(in_dev, NULL); 761e2401654SSalam Noureddine in_dev_put(in_dev); 7621da177e4SLinus Torvalds } 7631da177e4SLinus Torvalds 7641da177e4SLinus Torvalds static void igmp_ifc_timer_expire(unsigned long data) 7651da177e4SLinus Torvalds { 7661da177e4SLinus Torvalds struct in_device *in_dev = (struct in_device *)data; 7671da177e4SLinus Torvalds 7681da177e4SLinus Torvalds igmpv3_send_cr(in_dev); 7691da177e4SLinus Torvalds if (in_dev->mr_ifc_count) { 7701da177e4SLinus Torvalds in_dev->mr_ifc_count--; 771cab70040SWilliam Manley igmp_ifc_start_timer(in_dev, 772cab70040SWilliam Manley unsolicited_report_interval(in_dev)); 7731da177e4SLinus Torvalds } 774e2401654SSalam Noureddine in_dev_put(in_dev); 7751da177e4SLinus Torvalds } 7761da177e4SLinus Torvalds 7771da177e4SLinus Torvalds static void igmp_ifc_event(struct in_device *in_dev) 7781da177e4SLinus Torvalds { 779165094afSNikolay Borisov struct net *net = dev_net(in_dev->dev); 7801da177e4SLinus Torvalds if (IGMP_V1_SEEN(in_dev) || IGMP_V2_SEEN(in_dev)) 7811da177e4SLinus Torvalds return; 782165094afSNikolay Borisov in_dev->mr_ifc_count = in_dev->mr_qrv ?: net->ipv4.sysctl_igmp_qrv; 7831da177e4SLinus Torvalds igmp_ifc_start_timer(in_dev, 1); 7841da177e4SLinus Torvalds } 7851da177e4SLinus Torvalds 7861da177e4SLinus Torvalds 7871da177e4SLinus Torvalds static void igmp_timer_expire(unsigned long data) 7881da177e4SLinus Torvalds { 7891da177e4SLinus Torvalds struct ip_mc_list *im = (struct ip_mc_list *)data; 7901da177e4SLinus Torvalds struct in_device *in_dev = im->interface; 7911da177e4SLinus Torvalds 7921da177e4SLinus Torvalds spin_lock(&im->lock); 7931da177e4SLinus Torvalds im->tm_running = 0; 7941da177e4SLinus Torvalds 7951da177e4SLinus Torvalds if (im->unsolicit_count) { 7961da177e4SLinus Torvalds im->unsolicit_count--; 797cab70040SWilliam Manley igmp_start_timer(im, unsolicited_report_interval(in_dev)); 7981da177e4SLinus Torvalds } 7991da177e4SLinus Torvalds im->reporter = 1; 8001da177e4SLinus Torvalds spin_unlock(&im->lock); 8011da177e4SLinus Torvalds 8021da177e4SLinus Torvalds if (IGMP_V1_SEEN(in_dev)) 8031da177e4SLinus Torvalds igmp_send_report(in_dev, im, IGMP_HOST_MEMBERSHIP_REPORT); 8041da177e4SLinus Torvalds else if (IGMP_V2_SEEN(in_dev)) 8051da177e4SLinus Torvalds igmp_send_report(in_dev, im, IGMPV2_HOST_MEMBERSHIP_REPORT); 8061da177e4SLinus Torvalds else 8071da177e4SLinus Torvalds igmp_send_report(in_dev, im, IGMPV3_HOST_MEMBERSHIP_REPORT); 8081da177e4SLinus Torvalds 8091da177e4SLinus Torvalds ip_ma_put(im); 8101da177e4SLinus Torvalds } 8111da177e4SLinus Torvalds 812ad12583fSDavid L Stevens /* mark EXCLUDE-mode sources */ 813ea4d9e72SAl Viro static int igmp_xmarksources(struct ip_mc_list *pmc, int nsrcs, __be32 *srcs) 8141da177e4SLinus Torvalds { 8151da177e4SLinus Torvalds struct ip_sf_list *psf; 8161da177e4SLinus Torvalds int i, scount; 8171da177e4SLinus Torvalds 8181da177e4SLinus Torvalds scount = 0; 8191da177e4SLinus Torvalds for (psf = pmc->sources; psf; psf = psf->sf_next) { 8201da177e4SLinus Torvalds if (scount == nsrcs) 8211da177e4SLinus Torvalds break; 822ad12583fSDavid L Stevens for (i = 0; i < nsrcs; i++) { 823ad12583fSDavid L Stevens /* skip inactive filters */ 824e05c4ad3SYan, Zheng if (psf->sf_count[MCAST_INCLUDE] || 825ad12583fSDavid L Stevens pmc->sfcount[MCAST_EXCLUDE] != 826ad12583fSDavid L Stevens psf->sf_count[MCAST_EXCLUDE]) 827ce713ee5SRongQing.Li break; 828ad12583fSDavid L Stevens if (srcs[i] == psf->sf_inaddr) { 829ad12583fSDavid L Stevens scount++; 830ad12583fSDavid L Stevens break; 831ad12583fSDavid L Stevens } 832ad12583fSDavid L Stevens } 833ad12583fSDavid L Stevens } 834ad12583fSDavid L Stevens pmc->gsquery = 0; 835ad12583fSDavid L Stevens if (scount == nsrcs) /* all sources excluded */ 836ad12583fSDavid L Stevens return 0; 837ad12583fSDavid L Stevens return 1; 838ad12583fSDavid L Stevens } 839ad12583fSDavid L Stevens 840ea4d9e72SAl Viro static int igmp_marksources(struct ip_mc_list *pmc, int nsrcs, __be32 *srcs) 841ad12583fSDavid L Stevens { 842ad12583fSDavid L Stevens struct ip_sf_list *psf; 843ad12583fSDavid L Stevens int i, scount; 844ad12583fSDavid L Stevens 845ad12583fSDavid L Stevens if (pmc->sfmode == MCAST_EXCLUDE) 846ad12583fSDavid L Stevens return igmp_xmarksources(pmc, nsrcs, srcs); 847ad12583fSDavid L Stevens 848ad12583fSDavid L Stevens /* mark INCLUDE-mode sources */ 849ad12583fSDavid L Stevens scount = 0; 850ad12583fSDavid L Stevens for (psf = pmc->sources; psf; psf = psf->sf_next) { 851ad12583fSDavid L Stevens if (scount == nsrcs) 852ad12583fSDavid L Stevens break; 8531da177e4SLinus Torvalds for (i = 0; i < nsrcs; i++) 8541da177e4SLinus Torvalds if (srcs[i] == psf->sf_inaddr) { 8551da177e4SLinus Torvalds psf->sf_gsresp = 1; 8561da177e4SLinus Torvalds scount++; 8571da177e4SLinus Torvalds break; 8581da177e4SLinus Torvalds } 8591da177e4SLinus Torvalds } 860ad12583fSDavid L Stevens if (!scount) { 861ad12583fSDavid L Stevens pmc->gsquery = 0; 862ad12583fSDavid L Stevens return 0; 863ad12583fSDavid L Stevens } 864ad12583fSDavid L Stevens pmc->gsquery = 1; 865ad12583fSDavid L Stevens return 1; 8661da177e4SLinus Torvalds } 8671da177e4SLinus Torvalds 868d679c532SEric Dumazet /* return true if packet was dropped */ 869d679c532SEric Dumazet static bool igmp_heard_report(struct in_device *in_dev, __be32 group) 8701da177e4SLinus Torvalds { 8711da177e4SLinus Torvalds struct ip_mc_list *im; 87287a8a2aeSNikolay Borisov struct net *net = dev_net(in_dev->dev); 8731da177e4SLinus Torvalds 8741da177e4SLinus Torvalds /* Timers are only set for non-local groups */ 8751da177e4SLinus Torvalds 8761da177e4SLinus Torvalds if (group == IGMP_ALL_HOSTS) 877d679c532SEric Dumazet return false; 87887a8a2aeSNikolay Borisov if (ipv4_is_local_multicast(group) && !net->ipv4.sysctl_igmp_llm_reports) 879df2cf4a7SPhilip Downey return false; 8801da177e4SLinus Torvalds 8811d7138deSEric Dumazet rcu_read_lock(); 8821d7138deSEric Dumazet for_each_pmc_rcu(in_dev, im) { 8831da177e4SLinus Torvalds if (im->multiaddr == group) { 8841da177e4SLinus Torvalds igmp_stop_timer(im); 8851da177e4SLinus Torvalds break; 8861da177e4SLinus Torvalds } 8871da177e4SLinus Torvalds } 8881d7138deSEric Dumazet rcu_read_unlock(); 889d679c532SEric Dumazet return false; 8901da177e4SLinus Torvalds } 8911da177e4SLinus Torvalds 892d679c532SEric Dumazet /* return true if packet was dropped */ 893d679c532SEric Dumazet static bool igmp_heard_query(struct in_device *in_dev, struct sk_buff *skb, 8941da177e4SLinus Torvalds int len) 8951da177e4SLinus Torvalds { 896d9edf9e2SArnaldo Carvalho de Melo struct igmphdr *ih = igmp_hdr(skb); 897d9edf9e2SArnaldo Carvalho de Melo struct igmpv3_query *ih3 = igmpv3_query_hdr(skb); 8981da177e4SLinus Torvalds struct ip_mc_list *im; 89963007727SAl Viro __be32 group = ih->group; 9001da177e4SLinus Torvalds int max_delay; 9011da177e4SLinus Torvalds int mark = 0; 90287a8a2aeSNikolay Borisov struct net *net = dev_net(in_dev->dev); 9031da177e4SLinus Torvalds 9041da177e4SLinus Torvalds 9055b7c8406SDavid Stevens if (len == 8) { 9061da177e4SLinus Torvalds if (ih->code == 0) { 9071da177e4SLinus Torvalds /* Alas, old v1 router presents here. */ 9081da177e4SLinus Torvalds 909436f7c20SFabian Frederick max_delay = IGMP_QUERY_RESPONSE_INTERVAL; 9101da177e4SLinus Torvalds in_dev->mr_v1_seen = jiffies + 911436f7c20SFabian Frederick IGMP_V1_ROUTER_PRESENT_TIMEOUT; 9121da177e4SLinus Torvalds group = 0; 9131da177e4SLinus Torvalds } else { 9141da177e4SLinus Torvalds /* v2 router present */ 9151da177e4SLinus Torvalds max_delay = ih->code*(HZ/IGMP_TIMER_SCALE); 9161da177e4SLinus Torvalds in_dev->mr_v2_seen = jiffies + 917436f7c20SFabian Frederick IGMP_V2_ROUTER_PRESENT_TIMEOUT; 9181da177e4SLinus Torvalds } 9191da177e4SLinus Torvalds /* cancel the interface change timer */ 9201da177e4SLinus Torvalds in_dev->mr_ifc_count = 0; 9211da177e4SLinus Torvalds if (del_timer(&in_dev->mr_ifc_timer)) 9221da177e4SLinus Torvalds __in_dev_put(in_dev); 9231da177e4SLinus Torvalds /* clear deleted report items */ 9241da177e4SLinus Torvalds igmpv3_clear_delrec(in_dev); 9251da177e4SLinus Torvalds } else if (len < 12) { 926d679c532SEric Dumazet return true; /* ignore bogus packet; freed by caller */ 9275b7c8406SDavid Stevens } else if (IGMP_V1_SEEN(in_dev)) { 9285b7c8406SDavid Stevens /* This is a v3 query with v1 queriers present */ 929436f7c20SFabian Frederick max_delay = IGMP_QUERY_RESPONSE_INTERVAL; 9305b7c8406SDavid Stevens group = 0; 9315b7c8406SDavid Stevens } else if (IGMP_V2_SEEN(in_dev)) { 9325b7c8406SDavid Stevens /* this is a v3 query with v2 queriers present; 9335b7c8406SDavid Stevens * Interpretation of the max_delay code is problematic here. 9345b7c8406SDavid Stevens * A real v2 host would use ih_code directly, while v3 has a 9355b7c8406SDavid Stevens * different encoding. We use the v3 encoding as more likely 9365b7c8406SDavid Stevens * to be intended in a v3 query. 9375b7c8406SDavid Stevens */ 9385b7c8406SDavid Stevens max_delay = IGMPV3_MRC(ih3->code)*(HZ/IGMP_TIMER_SCALE); 939a8c1f65cSBen Hutchings if (!max_delay) 940a8c1f65cSBen Hutchings max_delay = 1; /* can't mod w/ 0 */ 9411da177e4SLinus Torvalds } else { /* v3 */ 9421da177e4SLinus Torvalds if (!pskb_may_pull(skb, sizeof(struct igmpv3_query))) 943d679c532SEric Dumazet return true; 9441da177e4SLinus Torvalds 945d9edf9e2SArnaldo Carvalho de Melo ih3 = igmpv3_query_hdr(skb); 9461da177e4SLinus Torvalds if (ih3->nsrcs) { 9471da177e4SLinus Torvalds if (!pskb_may_pull(skb, sizeof(struct igmpv3_query) 94863007727SAl Viro + ntohs(ih3->nsrcs)*sizeof(__be32))) 949d679c532SEric Dumazet return true; 950d9edf9e2SArnaldo Carvalho de Melo ih3 = igmpv3_query_hdr(skb); 9511da177e4SLinus Torvalds } 9521da177e4SLinus Torvalds 9531da177e4SLinus Torvalds max_delay = IGMPV3_MRC(ih3->code)*(HZ/IGMP_TIMER_SCALE); 9541da177e4SLinus Torvalds if (!max_delay) 9551da177e4SLinus Torvalds max_delay = 1; /* can't mod w/ 0 */ 9561da177e4SLinus Torvalds in_dev->mr_maxdelay = max_delay; 9571da177e4SLinus Torvalds if (ih3->qrv) 9581da177e4SLinus Torvalds in_dev->mr_qrv = ih3->qrv; 9591da177e4SLinus Torvalds if (!group) { /* general query */ 9601da177e4SLinus Torvalds if (ih3->nsrcs) 961b47bd8d2SDaniel Borkmann return true; /* no sources allowed */ 9621da177e4SLinus Torvalds igmp_gq_start_timer(in_dev); 963d679c532SEric Dumazet return false; 9641da177e4SLinus Torvalds } 9651da177e4SLinus Torvalds /* mark sources to include, if group & source-specific */ 9661da177e4SLinus Torvalds mark = ih3->nsrcs != 0; 9671da177e4SLinus Torvalds } 9681da177e4SLinus Torvalds 9691da177e4SLinus Torvalds /* 9701da177e4SLinus Torvalds * - Start the timers in all of our membership records 9711da177e4SLinus Torvalds * that the query applies to for the interface on 9721da177e4SLinus Torvalds * which the query arrived excl. those that belong 9731da177e4SLinus Torvalds * to a "local" group (224.0.0.X) 9741da177e4SLinus Torvalds * - For timers already running check if they need to 9751da177e4SLinus Torvalds * be reset. 9761da177e4SLinus Torvalds * - Use the igmp->igmp_code field as the maximum 9771da177e4SLinus Torvalds * delay possible 9781da177e4SLinus Torvalds */ 9791d7138deSEric Dumazet rcu_read_lock(); 9801d7138deSEric Dumazet for_each_pmc_rcu(in_dev, im) { 981ad12583fSDavid L Stevens int changed; 982ad12583fSDavid L Stevens 9831da177e4SLinus Torvalds if (group && group != im->multiaddr) 9841da177e4SLinus Torvalds continue; 9851da177e4SLinus Torvalds if (im->multiaddr == IGMP_ALL_HOSTS) 9861da177e4SLinus Torvalds continue; 987df2cf4a7SPhilip Downey if (ipv4_is_local_multicast(im->multiaddr) && 98887a8a2aeSNikolay Borisov !net->ipv4.sysctl_igmp_llm_reports) 989df2cf4a7SPhilip Downey continue; 9901da177e4SLinus Torvalds spin_lock_bh(&im->lock); 9911da177e4SLinus Torvalds if (im->tm_running) 9921da177e4SLinus Torvalds im->gsquery = im->gsquery && mark; 9931da177e4SLinus Torvalds else 9941da177e4SLinus Torvalds im->gsquery = mark; 995ad12583fSDavid L Stevens changed = !im->gsquery || 9961da177e4SLinus Torvalds igmp_marksources(im, ntohs(ih3->nsrcs), ih3->srcs); 9971da177e4SLinus Torvalds spin_unlock_bh(&im->lock); 998ad12583fSDavid L Stevens if (changed) 9991da177e4SLinus Torvalds igmp_mod_timer(im, max_delay); 10001da177e4SLinus Torvalds } 10011d7138deSEric Dumazet rcu_read_unlock(); 1002d679c532SEric Dumazet return false; 10031da177e4SLinus Torvalds } 10041da177e4SLinus Torvalds 10059a57a9d2SEric Dumazet /* called in rcu_read_lock() section */ 10061da177e4SLinus Torvalds int igmp_rcv(struct sk_buff *skb) 10071da177e4SLinus Torvalds { 10081da177e4SLinus Torvalds /* This basically follows the spec line by line -- see RFC1112 */ 10091da177e4SLinus Torvalds struct igmphdr *ih; 10109a57a9d2SEric Dumazet struct in_device *in_dev = __in_dev_get_rcu(skb->dev); 10111da177e4SLinus Torvalds int len = skb->len; 1012d679c532SEric Dumazet bool dropped = true; 10131da177e4SLinus Torvalds 101451456b29SIan Morris if (!in_dev) 1015cd557bc1SDenis V. Lunev goto drop; 10161da177e4SLinus Torvalds 1017fb286bb2SHerbert Xu if (!pskb_may_pull(skb, sizeof(struct igmphdr))) 10189a57a9d2SEric Dumazet goto drop; 1019fb286bb2SHerbert Xu 1020de08dc1aSTom Herbert if (skb_checksum_simple_validate(skb)) 10219a57a9d2SEric Dumazet goto drop; 10221da177e4SLinus Torvalds 1023d9edf9e2SArnaldo Carvalho de Melo ih = igmp_hdr(skb); 10241da177e4SLinus Torvalds switch (ih->type) { 10251da177e4SLinus Torvalds case IGMP_HOST_MEMBERSHIP_QUERY: 1026d679c532SEric Dumazet dropped = igmp_heard_query(in_dev, skb, len); 10271da177e4SLinus Torvalds break; 10281da177e4SLinus Torvalds case IGMP_HOST_MEMBERSHIP_REPORT: 10291da177e4SLinus Torvalds case IGMPV2_HOST_MEMBERSHIP_REPORT: 10301da177e4SLinus Torvalds /* Is it our report looped back? */ 1031c7537967SDavid S. Miller if (rt_is_output_route(skb_rtable(skb))) 10321da177e4SLinus Torvalds break; 103324c69275SDavid Stevens /* don't rely on MC router hearing unicast reports */ 103424c69275SDavid Stevens if (skb->pkt_type == PACKET_MULTICAST || 103524c69275SDavid Stevens skb->pkt_type == PACKET_BROADCAST) 1036d679c532SEric Dumazet dropped = igmp_heard_report(in_dev, ih->group); 10371da177e4SLinus Torvalds break; 10381da177e4SLinus Torvalds case IGMP_PIM: 10391da177e4SLinus Torvalds #ifdef CONFIG_IP_PIMSM_V1 10401da177e4SLinus Torvalds return pim_rcv_v1(skb); 10411da177e4SLinus Torvalds #endif 1042c6b471e6SHerbert Xu case IGMPV3_HOST_MEMBERSHIP_REPORT: 10431da177e4SLinus Torvalds case IGMP_DVMRP: 10441da177e4SLinus Torvalds case IGMP_TRACE: 10451da177e4SLinus Torvalds case IGMP_HOST_LEAVE_MESSAGE: 10461da177e4SLinus Torvalds case IGMP_MTRACE: 10471da177e4SLinus Torvalds case IGMP_MTRACE_RESP: 10481da177e4SLinus Torvalds break; 10491da177e4SLinus Torvalds default: 1050dd1c1853SLinus Torvalds break; 10511da177e4SLinus Torvalds } 1052fb286bb2SHerbert Xu 1053cd557bc1SDenis V. Lunev drop: 1054d679c532SEric Dumazet if (dropped) 10551da177e4SLinus Torvalds kfree_skb(skb); 1056d679c532SEric Dumazet else 1057d679c532SEric Dumazet consume_skb(skb); 10581da177e4SLinus Torvalds return 0; 10591da177e4SLinus Torvalds } 10601da177e4SLinus Torvalds 10611da177e4SLinus Torvalds #endif 10621da177e4SLinus Torvalds 10631da177e4SLinus Torvalds 10641da177e4SLinus Torvalds /* 10651da177e4SLinus Torvalds * Add a filter to a device 10661da177e4SLinus Torvalds */ 10671da177e4SLinus Torvalds 106863007727SAl Viro static void ip_mc_filter_add(struct in_device *in_dev, __be32 addr) 10691da177e4SLinus Torvalds { 10701da177e4SLinus Torvalds char buf[MAX_ADDR_LEN]; 10711da177e4SLinus Torvalds struct net_device *dev = in_dev->dev; 10721da177e4SLinus Torvalds 10731da177e4SLinus Torvalds /* Checking for IFF_MULTICAST here is WRONG-WRONG-WRONG. 10741da177e4SLinus Torvalds We will get multicast token leakage, when IFF_MULTICAST 1075b81693d9SJiri Pirko is changed. This check should be done in ndo_set_rx_mode 10761da177e4SLinus Torvalds routine. Something sort of: 10771da177e4SLinus Torvalds if (dev->mc_list && dev->flags&IFF_MULTICAST) { do it; } 10781da177e4SLinus Torvalds --ANK 10791da177e4SLinus Torvalds */ 10801da177e4SLinus Torvalds if (arp_mc_map(addr, buf, dev, 0) == 0) 108122bedad3SJiri Pirko dev_mc_add(dev, buf); 10821da177e4SLinus Torvalds } 10831da177e4SLinus Torvalds 10841da177e4SLinus Torvalds /* 10851da177e4SLinus Torvalds * Remove a filter from a device 10861da177e4SLinus Torvalds */ 10871da177e4SLinus Torvalds 108863007727SAl Viro static void ip_mc_filter_del(struct in_device *in_dev, __be32 addr) 10891da177e4SLinus Torvalds { 10901da177e4SLinus Torvalds char buf[MAX_ADDR_LEN]; 10911da177e4SLinus Torvalds struct net_device *dev = in_dev->dev; 10921da177e4SLinus Torvalds 10931da177e4SLinus Torvalds if (arp_mc_map(addr, buf, dev, 0) == 0) 109422bedad3SJiri Pirko dev_mc_del(dev, buf); 10951da177e4SLinus Torvalds } 10961da177e4SLinus Torvalds 10971da177e4SLinus Torvalds #ifdef CONFIG_IP_MULTICAST 10981da177e4SLinus Torvalds /* 10991da177e4SLinus Torvalds * deleted ip_mc_list manipulation 11001da177e4SLinus Torvalds */ 11011da177e4SLinus Torvalds static void igmpv3_add_delrec(struct in_device *in_dev, struct ip_mc_list *im) 11021da177e4SLinus Torvalds { 11031da177e4SLinus Torvalds struct ip_mc_list *pmc; 1104165094afSNikolay Borisov struct net *net = dev_net(in_dev->dev); 11051da177e4SLinus Torvalds 11061da177e4SLinus Torvalds /* this is an "ip_mc_list" for convenience; only the fields below 11071da177e4SLinus Torvalds * are actually used. In particular, the refcnt and users are not 11081da177e4SLinus Torvalds * used for management of the delete list. Using the same structure 11091da177e4SLinus Torvalds * for deleted items allows change reports to use common code with 11101da177e4SLinus Torvalds * non-deleted or query-response MCA's. 11111da177e4SLinus Torvalds */ 11120da974f4SPanagiotis Issaris pmc = kzalloc(sizeof(*pmc), GFP_KERNEL); 11131da177e4SLinus Torvalds if (!pmc) 11141da177e4SLinus Torvalds return; 11151da177e4SLinus Torvalds spin_lock_bh(&im->lock); 11161da177e4SLinus Torvalds pmc->interface = im->interface; 11171da177e4SLinus Torvalds in_dev_hold(in_dev); 11181da177e4SLinus Torvalds pmc->multiaddr = im->multiaddr; 1119165094afSNikolay Borisov pmc->crcount = in_dev->mr_qrv ?: net->ipv4.sysctl_igmp_qrv; 11201da177e4SLinus Torvalds pmc->sfmode = im->sfmode; 11211da177e4SLinus Torvalds if (pmc->sfmode == MCAST_INCLUDE) { 11221da177e4SLinus Torvalds struct ip_sf_list *psf; 11231da177e4SLinus Torvalds 11241da177e4SLinus Torvalds pmc->tomb = im->tomb; 11251da177e4SLinus Torvalds pmc->sources = im->sources; 11261da177e4SLinus Torvalds im->tomb = im->sources = NULL; 11271da177e4SLinus Torvalds for (psf = pmc->sources; psf; psf = psf->sf_next) 11281da177e4SLinus Torvalds psf->sf_crcount = pmc->crcount; 11291da177e4SLinus Torvalds } 11301da177e4SLinus Torvalds spin_unlock_bh(&im->lock); 11311da177e4SLinus Torvalds 11321da177e4SLinus Torvalds spin_lock_bh(&in_dev->mc_tomb_lock); 11331da177e4SLinus Torvalds pmc->next = in_dev->mc_tomb; 11341da177e4SLinus Torvalds in_dev->mc_tomb = pmc; 11351da177e4SLinus Torvalds spin_unlock_bh(&in_dev->mc_tomb_lock); 11361da177e4SLinus Torvalds } 11371da177e4SLinus Torvalds 113824803f38SHangbin Liu /* 113924803f38SHangbin Liu * restore ip_mc_list deleted records 114024803f38SHangbin Liu */ 114124803f38SHangbin Liu static void igmpv3_del_delrec(struct in_device *in_dev, struct ip_mc_list *im) 11421da177e4SLinus Torvalds { 11431da177e4SLinus Torvalds struct ip_mc_list *pmc, *pmc_prev; 114424803f38SHangbin Liu struct ip_sf_list *psf; 114524803f38SHangbin Liu struct net *net = dev_net(in_dev->dev); 114624803f38SHangbin Liu __be32 multiaddr = im->multiaddr; 11471da177e4SLinus Torvalds 11481da177e4SLinus Torvalds spin_lock_bh(&in_dev->mc_tomb_lock); 11491da177e4SLinus Torvalds pmc_prev = NULL; 11501da177e4SLinus Torvalds for (pmc = in_dev->mc_tomb; pmc; pmc = pmc->next) { 11511da177e4SLinus Torvalds if (pmc->multiaddr == multiaddr) 11521da177e4SLinus Torvalds break; 11531da177e4SLinus Torvalds pmc_prev = pmc; 11541da177e4SLinus Torvalds } 11551da177e4SLinus Torvalds if (pmc) { 11561da177e4SLinus Torvalds if (pmc_prev) 11571da177e4SLinus Torvalds pmc_prev->next = pmc->next; 11581da177e4SLinus Torvalds else 11591da177e4SLinus Torvalds in_dev->mc_tomb = pmc->next; 11601da177e4SLinus Torvalds } 11611da177e4SLinus Torvalds spin_unlock_bh(&in_dev->mc_tomb_lock); 116224803f38SHangbin Liu 116324803f38SHangbin Liu spin_lock_bh(&im->lock); 11641da177e4SLinus Torvalds if (pmc) { 116524803f38SHangbin Liu im->interface = pmc->interface; 116624803f38SHangbin Liu im->crcount = in_dev->mr_qrv ?: net->ipv4.sysctl_igmp_qrv; 116724803f38SHangbin Liu im->sfmode = pmc->sfmode; 116824803f38SHangbin Liu if (pmc->sfmode == MCAST_INCLUDE) { 116924803f38SHangbin Liu im->tomb = pmc->tomb; 117024803f38SHangbin Liu im->sources = pmc->sources; 117124803f38SHangbin Liu for (psf = im->sources; psf; psf = psf->sf_next) 117224803f38SHangbin Liu psf->sf_crcount = im->crcount; 11731da177e4SLinus Torvalds } 11741da177e4SLinus Torvalds in_dev_put(pmc->interface); 11759c8bb163SHangbin Liu kfree(pmc); 11761da177e4SLinus Torvalds } 117724803f38SHangbin Liu spin_unlock_bh(&im->lock); 11781da177e4SLinus Torvalds } 11791da177e4SLinus Torvalds 118024803f38SHangbin Liu /* 118124803f38SHangbin Liu * flush ip_mc_list deleted records 118224803f38SHangbin Liu */ 11831da177e4SLinus Torvalds static void igmpv3_clear_delrec(struct in_device *in_dev) 11841da177e4SLinus Torvalds { 11851da177e4SLinus Torvalds struct ip_mc_list *pmc, *nextpmc; 11861da177e4SLinus Torvalds 11871da177e4SLinus Torvalds spin_lock_bh(&in_dev->mc_tomb_lock); 11881da177e4SLinus Torvalds pmc = in_dev->mc_tomb; 11891da177e4SLinus Torvalds in_dev->mc_tomb = NULL; 11901da177e4SLinus Torvalds spin_unlock_bh(&in_dev->mc_tomb_lock); 11911da177e4SLinus Torvalds 11921da177e4SLinus Torvalds for (; pmc; pmc = nextpmc) { 11931da177e4SLinus Torvalds nextpmc = pmc->next; 11941da177e4SLinus Torvalds ip_mc_clear_src(pmc); 11951da177e4SLinus Torvalds in_dev_put(pmc->interface); 11961da177e4SLinus Torvalds kfree(pmc); 11971da177e4SLinus Torvalds } 11981da177e4SLinus Torvalds /* clear dead sources, too */ 11991d7138deSEric Dumazet rcu_read_lock(); 12001d7138deSEric Dumazet for_each_pmc_rcu(in_dev, pmc) { 12011da177e4SLinus Torvalds struct ip_sf_list *psf, *psf_next; 12021da177e4SLinus Torvalds 12031da177e4SLinus Torvalds spin_lock_bh(&pmc->lock); 12041da177e4SLinus Torvalds psf = pmc->tomb; 12051da177e4SLinus Torvalds pmc->tomb = NULL; 12061da177e4SLinus Torvalds spin_unlock_bh(&pmc->lock); 12071da177e4SLinus Torvalds for (; psf; psf = psf_next) { 12081da177e4SLinus Torvalds psf_next = psf->sf_next; 12091da177e4SLinus Torvalds kfree(psf); 12101da177e4SLinus Torvalds } 12111da177e4SLinus Torvalds } 12121d7138deSEric Dumazet rcu_read_unlock(); 12131da177e4SLinus Torvalds } 12141da177e4SLinus Torvalds #endif 12151da177e4SLinus Torvalds 12161da177e4SLinus Torvalds static void igmp_group_dropped(struct ip_mc_list *im) 12171da177e4SLinus Torvalds { 12181da177e4SLinus Torvalds struct in_device *in_dev = im->interface; 12191da177e4SLinus Torvalds #ifdef CONFIG_IP_MULTICAST 122087a8a2aeSNikolay Borisov struct net *net = dev_net(in_dev->dev); 12211da177e4SLinus Torvalds int reporter; 12221da177e4SLinus Torvalds #endif 12231da177e4SLinus Torvalds 12241da177e4SLinus Torvalds if (im->loaded) { 12251da177e4SLinus Torvalds im->loaded = 0; 12261da177e4SLinus Torvalds ip_mc_filter_del(in_dev, im->multiaddr); 12271da177e4SLinus Torvalds } 12281da177e4SLinus Torvalds 12291da177e4SLinus Torvalds #ifdef CONFIG_IP_MULTICAST 12301da177e4SLinus Torvalds if (im->multiaddr == IGMP_ALL_HOSTS) 12311da177e4SLinus Torvalds return; 123287a8a2aeSNikolay Borisov if (ipv4_is_local_multicast(im->multiaddr) && !net->ipv4.sysctl_igmp_llm_reports) 1233df2cf4a7SPhilip Downey return; 12341da177e4SLinus Torvalds 12351da177e4SLinus Torvalds reporter = im->reporter; 12361da177e4SLinus Torvalds igmp_stop_timer(im); 12371da177e4SLinus Torvalds 12381da177e4SLinus Torvalds if (!in_dev->dead) { 12391da177e4SLinus Torvalds if (IGMP_V1_SEEN(in_dev)) 124024cf3af3SVeaceslav Falico return; 12411da177e4SLinus Torvalds if (IGMP_V2_SEEN(in_dev)) { 12421da177e4SLinus Torvalds if (reporter) 12431da177e4SLinus Torvalds igmp_send_report(in_dev, im, IGMP_HOST_LEAVE_MESSAGE); 124424cf3af3SVeaceslav Falico return; 12451da177e4SLinus Torvalds } 12461da177e4SLinus Torvalds /* IGMPv3 */ 12471da177e4SLinus Torvalds igmpv3_add_delrec(in_dev, im); 12481da177e4SLinus Torvalds 12491da177e4SLinus Torvalds igmp_ifc_event(in_dev); 12501da177e4SLinus Torvalds } 12511da177e4SLinus Torvalds #endif 12521da177e4SLinus Torvalds } 12531da177e4SLinus Torvalds 12541da177e4SLinus Torvalds static void igmp_group_added(struct ip_mc_list *im) 12551da177e4SLinus Torvalds { 12561da177e4SLinus Torvalds struct in_device *in_dev = im->interface; 1257dcd87999SNikolay Borisov #ifdef CONFIG_IP_MULTICAST 125887a8a2aeSNikolay Borisov struct net *net = dev_net(in_dev->dev); 1259dcd87999SNikolay Borisov #endif 12601da177e4SLinus Torvalds 12611da177e4SLinus Torvalds if (im->loaded == 0) { 12621da177e4SLinus Torvalds im->loaded = 1; 12631da177e4SLinus Torvalds ip_mc_filter_add(in_dev, im->multiaddr); 12641da177e4SLinus Torvalds } 12651da177e4SLinus Torvalds 12661da177e4SLinus Torvalds #ifdef CONFIG_IP_MULTICAST 12671da177e4SLinus Torvalds if (im->multiaddr == IGMP_ALL_HOSTS) 12681da177e4SLinus Torvalds return; 126987a8a2aeSNikolay Borisov if (ipv4_is_local_multicast(im->multiaddr) && !net->ipv4.sysctl_igmp_llm_reports) 1270df2cf4a7SPhilip Downey return; 12711da177e4SLinus Torvalds 12721da177e4SLinus Torvalds if (in_dev->dead) 12731da177e4SLinus Torvalds return; 12741da177e4SLinus Torvalds if (IGMP_V1_SEEN(in_dev) || IGMP_V2_SEEN(in_dev)) { 12751da177e4SLinus Torvalds spin_lock_bh(&im->lock); 1276436f7c20SFabian Frederick igmp_start_timer(im, IGMP_INITIAL_REPORT_DELAY); 12771da177e4SLinus Torvalds spin_unlock_bh(&im->lock); 12781da177e4SLinus Torvalds return; 12791da177e4SLinus Torvalds } 12801da177e4SLinus Torvalds /* else, v3 */ 12811da177e4SLinus Torvalds 1282165094afSNikolay Borisov im->crcount = in_dev->mr_qrv ?: net->ipv4.sysctl_igmp_qrv; 12831da177e4SLinus Torvalds igmp_ifc_event(in_dev); 12841da177e4SLinus Torvalds #endif 12851da177e4SLinus Torvalds } 12861da177e4SLinus Torvalds 12871da177e4SLinus Torvalds 12881da177e4SLinus Torvalds /* 12891da177e4SLinus Torvalds * Multicast list managers 12901da177e4SLinus Torvalds */ 12911da177e4SLinus Torvalds 1292e9897071SEric Dumazet static u32 ip_mc_hash(const struct ip_mc_list *im) 1293e9897071SEric Dumazet { 1294c70eba74SEric Dumazet return hash_32((__force u32)im->multiaddr, MC_HASH_SZ_LOG); 1295e9897071SEric Dumazet } 1296e9897071SEric Dumazet 1297e9897071SEric Dumazet static void ip_mc_hash_add(struct in_device *in_dev, 1298e9897071SEric Dumazet struct ip_mc_list *im) 1299e9897071SEric Dumazet { 1300e9897071SEric Dumazet struct ip_mc_list __rcu **mc_hash; 1301e9897071SEric Dumazet u32 hash; 1302e9897071SEric Dumazet 1303e9897071SEric Dumazet mc_hash = rtnl_dereference(in_dev->mc_hash); 1304e9897071SEric Dumazet if (mc_hash) { 1305e9897071SEric Dumazet hash = ip_mc_hash(im); 1306c70eba74SEric Dumazet im->next_hash = mc_hash[hash]; 1307e9897071SEric Dumazet rcu_assign_pointer(mc_hash[hash], im); 1308e9897071SEric Dumazet return; 1309e9897071SEric Dumazet } 1310e9897071SEric Dumazet 1311e9897071SEric Dumazet /* do not use a hash table for small number of items */ 1312e9897071SEric Dumazet if (in_dev->mc_count < 4) 1313e9897071SEric Dumazet return; 1314e9897071SEric Dumazet 1315e9897071SEric Dumazet mc_hash = kzalloc(sizeof(struct ip_mc_list *) << MC_HASH_SZ_LOG, 1316e9897071SEric Dumazet GFP_KERNEL); 1317e9897071SEric Dumazet if (!mc_hash) 1318e9897071SEric Dumazet return; 1319e9897071SEric Dumazet 1320e9897071SEric Dumazet for_each_pmc_rtnl(in_dev, im) { 1321e9897071SEric Dumazet hash = ip_mc_hash(im); 1322c70eba74SEric Dumazet im->next_hash = mc_hash[hash]; 1323e9897071SEric Dumazet RCU_INIT_POINTER(mc_hash[hash], im); 1324e9897071SEric Dumazet } 1325e9897071SEric Dumazet 1326e9897071SEric Dumazet rcu_assign_pointer(in_dev->mc_hash, mc_hash); 1327e9897071SEric Dumazet } 1328e9897071SEric Dumazet 1329e9897071SEric Dumazet static void ip_mc_hash_remove(struct in_device *in_dev, 1330e9897071SEric Dumazet struct ip_mc_list *im) 1331e9897071SEric Dumazet { 1332e9897071SEric Dumazet struct ip_mc_list __rcu **mc_hash = rtnl_dereference(in_dev->mc_hash); 1333e9897071SEric Dumazet struct ip_mc_list *aux; 1334e9897071SEric Dumazet 1335e9897071SEric Dumazet if (!mc_hash) 1336e9897071SEric Dumazet return; 1337e9897071SEric Dumazet mc_hash += ip_mc_hash(im); 1338e9897071SEric Dumazet while ((aux = rtnl_dereference(*mc_hash)) != im) 1339e9897071SEric Dumazet mc_hash = &aux->next_hash; 1340e9897071SEric Dumazet *mc_hash = im->next_hash; 1341e9897071SEric Dumazet } 1342e9897071SEric Dumazet 13431da177e4SLinus Torvalds 13441da177e4SLinus Torvalds /* 13451da177e4SLinus Torvalds * A socket has joined a multicast group on device dev. 13461da177e4SLinus Torvalds */ 13471da177e4SLinus Torvalds 13488f935bbdSAl Viro void ip_mc_inc_group(struct in_device *in_dev, __be32 addr) 13491da177e4SLinus Torvalds { 13501da177e4SLinus Torvalds struct ip_mc_list *im; 1351dcd87999SNikolay Borisov #ifdef CONFIG_IP_MULTICAST 1352165094afSNikolay Borisov struct net *net = dev_net(in_dev->dev); 1353dcd87999SNikolay Borisov #endif 13541da177e4SLinus Torvalds 13551da177e4SLinus Torvalds ASSERT_RTNL(); 13561da177e4SLinus Torvalds 13571d7138deSEric Dumazet for_each_pmc_rtnl(in_dev, im) { 13581da177e4SLinus Torvalds if (im->multiaddr == addr) { 13591da177e4SLinus Torvalds im->users++; 13601da177e4SLinus Torvalds ip_mc_add_src(in_dev, &addr, MCAST_EXCLUDE, 0, NULL, 0); 13611da177e4SLinus Torvalds goto out; 13621da177e4SLinus Torvalds } 13631da177e4SLinus Torvalds } 13641da177e4SLinus Torvalds 13651d7138deSEric Dumazet im = kzalloc(sizeof(*im), GFP_KERNEL); 13661da177e4SLinus Torvalds if (!im) 13671da177e4SLinus Torvalds goto out; 13681da177e4SLinus Torvalds 13691da177e4SLinus Torvalds im->users = 1; 13701da177e4SLinus Torvalds im->interface = in_dev; 13711da177e4SLinus Torvalds in_dev_hold(in_dev); 13721da177e4SLinus Torvalds im->multiaddr = addr; 13731da177e4SLinus Torvalds /* initial mode is (EX, empty) */ 13741da177e4SLinus Torvalds im->sfmode = MCAST_EXCLUDE; 13751da177e4SLinus Torvalds im->sfcount[MCAST_EXCLUDE] = 1; 13761da177e4SLinus Torvalds atomic_set(&im->refcnt, 1); 13771da177e4SLinus Torvalds spin_lock_init(&im->lock); 13781da177e4SLinus Torvalds #ifdef CONFIG_IP_MULTICAST 1379179542a5SHimangi Saraogi setup_timer(&im->timer, igmp_timer_expire, (unsigned long)im); 1380165094afSNikolay Borisov im->unsolicit_count = net->ipv4.sysctl_igmp_qrv; 13811da177e4SLinus Torvalds #endif 13821d7138deSEric Dumazet 13831d7138deSEric Dumazet im->next_rcu = in_dev->mc_list; 1384b8bae41eSRami Rosen in_dev->mc_count++; 1385cf778b00SEric Dumazet rcu_assign_pointer(in_dev->mc_list, im); 13861d7138deSEric Dumazet 1387e9897071SEric Dumazet ip_mc_hash_add(in_dev, im); 1388e9897071SEric Dumazet 13891da177e4SLinus Torvalds #ifdef CONFIG_IP_MULTICAST 139024803f38SHangbin Liu igmpv3_del_delrec(in_dev, im); 13911da177e4SLinus Torvalds #endif 13921da177e4SLinus Torvalds igmp_group_added(im); 13931da177e4SLinus Torvalds if (!in_dev->dead) 13941da177e4SLinus Torvalds ip_rt_multicast_event(in_dev); 13951da177e4SLinus Torvalds out: 13961da177e4SLinus Torvalds return; 13971da177e4SLinus Torvalds } 13984bc2f18bSEric Dumazet EXPORT_SYMBOL(ip_mc_inc_group); 13991da177e4SLinus Torvalds 14009afd85c9SLinus Lüssing static int ip_mc_check_iphdr(struct sk_buff *skb) 14019afd85c9SLinus Lüssing { 14029afd85c9SLinus Lüssing const struct iphdr *iph; 14039afd85c9SLinus Lüssing unsigned int len; 14049afd85c9SLinus Lüssing unsigned int offset = skb_network_offset(skb) + sizeof(*iph); 14059afd85c9SLinus Lüssing 14069afd85c9SLinus Lüssing if (!pskb_may_pull(skb, offset)) 14079afd85c9SLinus Lüssing return -EINVAL; 14089afd85c9SLinus Lüssing 14099afd85c9SLinus Lüssing iph = ip_hdr(skb); 14109afd85c9SLinus Lüssing 14119afd85c9SLinus Lüssing if (iph->version != 4 || ip_hdrlen(skb) < sizeof(*iph)) 14129afd85c9SLinus Lüssing return -EINVAL; 14139afd85c9SLinus Lüssing 14149afd85c9SLinus Lüssing offset += ip_hdrlen(skb) - sizeof(*iph); 14159afd85c9SLinus Lüssing 14169afd85c9SLinus Lüssing if (!pskb_may_pull(skb, offset)) 14179afd85c9SLinus Lüssing return -EINVAL; 14189afd85c9SLinus Lüssing 14199afd85c9SLinus Lüssing iph = ip_hdr(skb); 14209afd85c9SLinus Lüssing 14219afd85c9SLinus Lüssing if (unlikely(ip_fast_csum((u8 *)iph, iph->ihl))) 14229afd85c9SLinus Lüssing return -EINVAL; 14239afd85c9SLinus Lüssing 14249afd85c9SLinus Lüssing len = skb_network_offset(skb) + ntohs(iph->tot_len); 14259afd85c9SLinus Lüssing if (skb->len < len || len < offset) 14269afd85c9SLinus Lüssing return -EINVAL; 14279afd85c9SLinus Lüssing 14289afd85c9SLinus Lüssing skb_set_transport_header(skb, offset); 14299afd85c9SLinus Lüssing 14309afd85c9SLinus Lüssing return 0; 14319afd85c9SLinus Lüssing } 14329afd85c9SLinus Lüssing 14339afd85c9SLinus Lüssing static int ip_mc_check_igmp_reportv3(struct sk_buff *skb) 14349afd85c9SLinus Lüssing { 14359afd85c9SLinus Lüssing unsigned int len = skb_transport_offset(skb); 14369afd85c9SLinus Lüssing 14379afd85c9SLinus Lüssing len += sizeof(struct igmpv3_report); 14389afd85c9SLinus Lüssing 14399afd85c9SLinus Lüssing return pskb_may_pull(skb, len) ? 0 : -EINVAL; 14409afd85c9SLinus Lüssing } 14419afd85c9SLinus Lüssing 14429afd85c9SLinus Lüssing static int ip_mc_check_igmp_query(struct sk_buff *skb) 14439afd85c9SLinus Lüssing { 14449afd85c9SLinus Lüssing unsigned int len = skb_transport_offset(skb); 14459afd85c9SLinus Lüssing 14469afd85c9SLinus Lüssing len += sizeof(struct igmphdr); 14479afd85c9SLinus Lüssing if (skb->len < len) 14489afd85c9SLinus Lüssing return -EINVAL; 14499afd85c9SLinus Lüssing 14509afd85c9SLinus Lüssing /* IGMPv{1,2}? */ 14519afd85c9SLinus Lüssing if (skb->len != len) { 14529afd85c9SLinus Lüssing /* or IGMPv3? */ 14539afd85c9SLinus Lüssing len += sizeof(struct igmpv3_query) - sizeof(struct igmphdr); 14549afd85c9SLinus Lüssing if (skb->len < len || !pskb_may_pull(skb, len)) 14559afd85c9SLinus Lüssing return -EINVAL; 14569afd85c9SLinus Lüssing } 14579afd85c9SLinus Lüssing 14589afd85c9SLinus Lüssing /* RFC2236+RFC3376 (IGMPv2+IGMPv3) require the multicast link layer 14599afd85c9SLinus Lüssing * all-systems destination addresses (224.0.0.1) for general queries 14609afd85c9SLinus Lüssing */ 14619afd85c9SLinus Lüssing if (!igmp_hdr(skb)->group && 14629afd85c9SLinus Lüssing ip_hdr(skb)->daddr != htonl(INADDR_ALLHOSTS_GROUP)) 14639afd85c9SLinus Lüssing return -EINVAL; 14649afd85c9SLinus Lüssing 14659afd85c9SLinus Lüssing return 0; 14669afd85c9SLinus Lüssing } 14679afd85c9SLinus Lüssing 14689afd85c9SLinus Lüssing static int ip_mc_check_igmp_msg(struct sk_buff *skb) 14699afd85c9SLinus Lüssing { 14709afd85c9SLinus Lüssing switch (igmp_hdr(skb)->type) { 14719afd85c9SLinus Lüssing case IGMP_HOST_LEAVE_MESSAGE: 14729afd85c9SLinus Lüssing case IGMP_HOST_MEMBERSHIP_REPORT: 14739afd85c9SLinus Lüssing case IGMPV2_HOST_MEMBERSHIP_REPORT: 14749afd85c9SLinus Lüssing /* fall through */ 14759afd85c9SLinus Lüssing return 0; 14769afd85c9SLinus Lüssing case IGMPV3_HOST_MEMBERSHIP_REPORT: 14779afd85c9SLinus Lüssing return ip_mc_check_igmp_reportv3(skb); 14789afd85c9SLinus Lüssing case IGMP_HOST_MEMBERSHIP_QUERY: 14799afd85c9SLinus Lüssing return ip_mc_check_igmp_query(skb); 14809afd85c9SLinus Lüssing default: 14819afd85c9SLinus Lüssing return -ENOMSG; 14829afd85c9SLinus Lüssing } 14839afd85c9SLinus Lüssing } 14849afd85c9SLinus Lüssing 14859afd85c9SLinus Lüssing static inline __sum16 ip_mc_validate_checksum(struct sk_buff *skb) 14869afd85c9SLinus Lüssing { 14879afd85c9SLinus Lüssing return skb_checksum_simple_validate(skb); 14889afd85c9SLinus Lüssing } 14899afd85c9SLinus Lüssing 14909afd85c9SLinus Lüssing static int __ip_mc_check_igmp(struct sk_buff *skb, struct sk_buff **skb_trimmed) 14919afd85c9SLinus Lüssing 14929afd85c9SLinus Lüssing { 14939afd85c9SLinus Lüssing struct sk_buff *skb_chk; 14949afd85c9SLinus Lüssing unsigned int transport_len; 14959afd85c9SLinus Lüssing unsigned int len = skb_transport_offset(skb) + sizeof(struct igmphdr); 1496a516993fSLinus Lüssing int ret = -EINVAL; 14979afd85c9SLinus Lüssing 14989afd85c9SLinus Lüssing transport_len = ntohs(ip_hdr(skb)->tot_len) - ip_hdrlen(skb); 14999afd85c9SLinus Lüssing 15009afd85c9SLinus Lüssing skb_chk = skb_checksum_trimmed(skb, transport_len, 15019afd85c9SLinus Lüssing ip_mc_validate_checksum); 15029afd85c9SLinus Lüssing if (!skb_chk) 1503a516993fSLinus Lüssing goto err; 15049afd85c9SLinus Lüssing 1505a516993fSLinus Lüssing if (!pskb_may_pull(skb_chk, len)) 1506a516993fSLinus Lüssing goto err; 15079afd85c9SLinus Lüssing 15089afd85c9SLinus Lüssing ret = ip_mc_check_igmp_msg(skb_chk); 1509a516993fSLinus Lüssing if (ret) 1510a516993fSLinus Lüssing goto err; 15119afd85c9SLinus Lüssing 15129afd85c9SLinus Lüssing if (skb_trimmed) 15139afd85c9SLinus Lüssing *skb_trimmed = skb_chk; 1514a516993fSLinus Lüssing /* free now unneeded clone */ 1515a516993fSLinus Lüssing else if (skb_chk != skb) 15169afd85c9SLinus Lüssing kfree_skb(skb_chk); 15179afd85c9SLinus Lüssing 1518a516993fSLinus Lüssing ret = 0; 1519a516993fSLinus Lüssing 1520a516993fSLinus Lüssing err: 1521a516993fSLinus Lüssing if (ret && skb_chk && skb_chk != skb) 1522a516993fSLinus Lüssing kfree_skb(skb_chk); 1523a516993fSLinus Lüssing 1524a516993fSLinus Lüssing return ret; 15259afd85c9SLinus Lüssing } 15269afd85c9SLinus Lüssing 15279afd85c9SLinus Lüssing /** 15289afd85c9SLinus Lüssing * ip_mc_check_igmp - checks whether this is a sane IGMP packet 15299afd85c9SLinus Lüssing * @skb: the skb to validate 15309afd85c9SLinus Lüssing * @skb_trimmed: to store an skb pointer trimmed to IPv4 packet tail (optional) 15319afd85c9SLinus Lüssing * 15329afd85c9SLinus Lüssing * Checks whether an IPv4 packet is a valid IGMP packet. If so sets 1533a516993fSLinus Lüssing * skb transport header accordingly and returns zero. 15349afd85c9SLinus Lüssing * 15359afd85c9SLinus Lüssing * -EINVAL: A broken packet was detected, i.e. it violates some internet 15369afd85c9SLinus Lüssing * standard 15379afd85c9SLinus Lüssing * -ENOMSG: IP header validation succeeded but it is not an IGMP packet. 15389afd85c9SLinus Lüssing * -ENOMEM: A memory allocation failure happened. 15399afd85c9SLinus Lüssing * 15409afd85c9SLinus Lüssing * Optionally, an skb pointer might be provided via skb_trimmed (or set it 15419afd85c9SLinus Lüssing * to NULL): After parsing an IGMP packet successfully it will point to 15429afd85c9SLinus Lüssing * an skb which has its tail aligned to the IP packet end. This might 15439afd85c9SLinus Lüssing * either be the originally provided skb or a trimmed, cloned version if 15449afd85c9SLinus Lüssing * the skb frame had data beyond the IP packet. A cloned skb allows us 15459afd85c9SLinus Lüssing * to leave the original skb and its full frame unchanged (which might be 15469afd85c9SLinus Lüssing * desirable for layer 2 frame jugglers). 15479afd85c9SLinus Lüssing * 1548a516993fSLinus Lüssing * Caller needs to set the skb network header and free any returned skb if it 1549a516993fSLinus Lüssing * differs from the provided skb. 15509afd85c9SLinus Lüssing */ 15519afd85c9SLinus Lüssing int ip_mc_check_igmp(struct sk_buff *skb, struct sk_buff **skb_trimmed) 15529afd85c9SLinus Lüssing { 15539afd85c9SLinus Lüssing int ret = ip_mc_check_iphdr(skb); 15549afd85c9SLinus Lüssing 15559afd85c9SLinus Lüssing if (ret < 0) 15569afd85c9SLinus Lüssing return ret; 15579afd85c9SLinus Lüssing 15589afd85c9SLinus Lüssing if (ip_hdr(skb)->protocol != IPPROTO_IGMP) 15599afd85c9SLinus Lüssing return -ENOMSG; 15609afd85c9SLinus Lüssing 15619afd85c9SLinus Lüssing return __ip_mc_check_igmp(skb, skb_trimmed); 15629afd85c9SLinus Lüssing } 15639afd85c9SLinus Lüssing EXPORT_SYMBOL(ip_mc_check_igmp); 15649afd85c9SLinus Lüssing 15651da177e4SLinus Torvalds /* 15664aa5dee4SJiri Pirko * Resend IGMP JOIN report; used by netdev notifier. 1567a816c7c7SJay Vosburgh */ 15684aa5dee4SJiri Pirko static void ip_mc_rejoin_groups(struct in_device *in_dev) 1569a816c7c7SJay Vosburgh { 157008882669SGeert Uytterhoeven #ifdef CONFIG_IP_MULTICAST 1571866f3b25SEric Dumazet struct ip_mc_list *im; 1572866f3b25SEric Dumazet int type; 157387a8a2aeSNikolay Borisov struct net *net = dev_net(in_dev->dev); 1574a816c7c7SJay Vosburgh 15754aa5dee4SJiri Pirko ASSERT_RTNL(); 15764aa5dee4SJiri Pirko 15774aa5dee4SJiri Pirko for_each_pmc_rtnl(in_dev, im) { 1578a816c7c7SJay Vosburgh if (im->multiaddr == IGMP_ALL_HOSTS) 1579866f3b25SEric Dumazet continue; 1580df2cf4a7SPhilip Downey if (ipv4_is_local_multicast(im->multiaddr) && 158187a8a2aeSNikolay Borisov !net->ipv4.sysctl_igmp_llm_reports) 1582df2cf4a7SPhilip Downey continue; 1583a816c7c7SJay Vosburgh 1584e12b4539SFlavio Leitner /* a failover is happening and switches 1585866f3b25SEric Dumazet * must be notified immediately 1586866f3b25SEric Dumazet */ 1587e12b4539SFlavio Leitner if (IGMP_V1_SEEN(in_dev)) 1588866f3b25SEric Dumazet type = IGMP_HOST_MEMBERSHIP_REPORT; 1589e12b4539SFlavio Leitner else if (IGMP_V2_SEEN(in_dev)) 1590866f3b25SEric Dumazet type = IGMPV2_HOST_MEMBERSHIP_REPORT; 1591e12b4539SFlavio Leitner else 1592866f3b25SEric Dumazet type = IGMPV3_HOST_MEMBERSHIP_REPORT; 1593866f3b25SEric Dumazet igmp_send_report(in_dev, im, type); 1594866f3b25SEric Dumazet } 1595a816c7c7SJay Vosburgh #endif 1596a816c7c7SJay Vosburgh } 1597a816c7c7SJay Vosburgh 1598a816c7c7SJay Vosburgh /* 15991da177e4SLinus Torvalds * A socket has left a multicast group on device dev 16001da177e4SLinus Torvalds */ 16011da177e4SLinus Torvalds 16028f935bbdSAl Viro void ip_mc_dec_group(struct in_device *in_dev, __be32 addr) 16031da177e4SLinus Torvalds { 16041d7138deSEric Dumazet struct ip_mc_list *i; 16051d7138deSEric Dumazet struct ip_mc_list __rcu **ip; 16061da177e4SLinus Torvalds 16071da177e4SLinus Torvalds ASSERT_RTNL(); 16081da177e4SLinus Torvalds 16091d7138deSEric Dumazet for (ip = &in_dev->mc_list; 16101d7138deSEric Dumazet (i = rtnl_dereference(*ip)) != NULL; 16111d7138deSEric Dumazet ip = &i->next_rcu) { 16121da177e4SLinus Torvalds if (i->multiaddr == addr) { 16131da177e4SLinus Torvalds if (--i->users == 0) { 1614e9897071SEric Dumazet ip_mc_hash_remove(in_dev, i); 16151d7138deSEric Dumazet *ip = i->next_rcu; 1616b8bae41eSRami Rosen in_dev->mc_count--; 16171da177e4SLinus Torvalds igmp_group_dropped(i); 161824cf3af3SVeaceslav Falico ip_mc_clear_src(i); 16191da177e4SLinus Torvalds 16201da177e4SLinus Torvalds if (!in_dev->dead) 16211da177e4SLinus Torvalds ip_rt_multicast_event(in_dev); 16221da177e4SLinus Torvalds 16231da177e4SLinus Torvalds ip_ma_put(i); 16241da177e4SLinus Torvalds return; 16251da177e4SLinus Torvalds } 16261da177e4SLinus Torvalds break; 16271da177e4SLinus Torvalds } 16281da177e4SLinus Torvalds } 16291da177e4SLinus Torvalds } 16304bc2f18bSEric Dumazet EXPORT_SYMBOL(ip_mc_dec_group); 16311da177e4SLinus Torvalds 163275c78500SMoni Shoua /* Device changing type */ 163375c78500SMoni Shoua 163475c78500SMoni Shoua void ip_mc_unmap(struct in_device *in_dev) 163575c78500SMoni Shoua { 16361d7138deSEric Dumazet struct ip_mc_list *pmc; 163775c78500SMoni Shoua 163875c78500SMoni Shoua ASSERT_RTNL(); 163975c78500SMoni Shoua 16401d7138deSEric Dumazet for_each_pmc_rtnl(in_dev, pmc) 16411d7138deSEric Dumazet igmp_group_dropped(pmc); 164275c78500SMoni Shoua } 164375c78500SMoni Shoua 164475c78500SMoni Shoua void ip_mc_remap(struct in_device *in_dev) 164575c78500SMoni Shoua { 16461d7138deSEric Dumazet struct ip_mc_list *pmc; 164775c78500SMoni Shoua 164875c78500SMoni Shoua ASSERT_RTNL(); 164975c78500SMoni Shoua 165024803f38SHangbin Liu for_each_pmc_rtnl(in_dev, pmc) { 165124803f38SHangbin Liu #ifdef CONFIG_IP_MULTICAST 165224803f38SHangbin Liu igmpv3_del_delrec(in_dev, pmc); 165324803f38SHangbin Liu #endif 16541d7138deSEric Dumazet igmp_group_added(pmc); 165575c78500SMoni Shoua } 165624803f38SHangbin Liu } 165775c78500SMoni Shoua 16581da177e4SLinus Torvalds /* Device going down */ 16591da177e4SLinus Torvalds 16601da177e4SLinus Torvalds void ip_mc_down(struct in_device *in_dev) 16611da177e4SLinus Torvalds { 16621d7138deSEric Dumazet struct ip_mc_list *pmc; 16631da177e4SLinus Torvalds 16641da177e4SLinus Torvalds ASSERT_RTNL(); 16651da177e4SLinus Torvalds 16661d7138deSEric Dumazet for_each_pmc_rtnl(in_dev, pmc) 16671d7138deSEric Dumazet igmp_group_dropped(pmc); 16681da177e4SLinus Torvalds 16691da177e4SLinus Torvalds #ifdef CONFIG_IP_MULTICAST 16701da177e4SLinus Torvalds in_dev->mr_ifc_count = 0; 16711da177e4SLinus Torvalds if (del_timer(&in_dev->mr_ifc_timer)) 16721da177e4SLinus Torvalds __in_dev_put(in_dev); 16731da177e4SLinus Torvalds in_dev->mr_gq_running = 0; 16741da177e4SLinus Torvalds if (del_timer(&in_dev->mr_gq_timer)) 16751da177e4SLinus Torvalds __in_dev_put(in_dev); 16761da177e4SLinus Torvalds #endif 16771da177e4SLinus Torvalds 16781da177e4SLinus Torvalds ip_mc_dec_group(in_dev, IGMP_ALL_HOSTS); 16791da177e4SLinus Torvalds } 16801da177e4SLinus Torvalds 16811da177e4SLinus Torvalds void ip_mc_init_dev(struct in_device *in_dev) 16821da177e4SLinus Torvalds { 1683dcd87999SNikolay Borisov #ifdef CONFIG_IP_MULTICAST 1684165094afSNikolay Borisov struct net *net = dev_net(in_dev->dev); 1685dcd87999SNikolay Borisov #endif 16861da177e4SLinus Torvalds ASSERT_RTNL(); 16871da177e4SLinus Torvalds 16881da177e4SLinus Torvalds #ifdef CONFIG_IP_MULTICAST 1689b24b8a24SPavel Emelyanov setup_timer(&in_dev->mr_gq_timer, igmp_gq_timer_expire, 1690b24b8a24SPavel Emelyanov (unsigned long)in_dev); 1691b24b8a24SPavel Emelyanov setup_timer(&in_dev->mr_ifc_timer, igmp_ifc_timer_expire, 1692b24b8a24SPavel Emelyanov (unsigned long)in_dev); 1693165094afSNikolay Borisov in_dev->mr_qrv = net->ipv4.sysctl_igmp_qrv; 16941da177e4SLinus Torvalds #endif 16951da177e4SLinus Torvalds 16961da177e4SLinus Torvalds spin_lock_init(&in_dev->mc_tomb_lock); 16971da177e4SLinus Torvalds } 16981da177e4SLinus Torvalds 16991da177e4SLinus Torvalds /* Device going up */ 17001da177e4SLinus Torvalds 17011da177e4SLinus Torvalds void ip_mc_up(struct in_device *in_dev) 17021da177e4SLinus Torvalds { 17031d7138deSEric Dumazet struct ip_mc_list *pmc; 1704dcd87999SNikolay Borisov #ifdef CONFIG_IP_MULTICAST 1705165094afSNikolay Borisov struct net *net = dev_net(in_dev->dev); 1706dcd87999SNikolay Borisov #endif 17071da177e4SLinus Torvalds 17081da177e4SLinus Torvalds ASSERT_RTNL(); 17091da177e4SLinus Torvalds 1710a9fe8e29SHannes Frederic Sowa #ifdef CONFIG_IP_MULTICAST 1711165094afSNikolay Borisov in_dev->mr_qrv = net->ipv4.sysctl_igmp_qrv; 1712a9fe8e29SHannes Frederic Sowa #endif 17131da177e4SLinus Torvalds ip_mc_inc_group(in_dev, IGMP_ALL_HOSTS); 17141da177e4SLinus Torvalds 171524803f38SHangbin Liu for_each_pmc_rtnl(in_dev, pmc) { 171624803f38SHangbin Liu #ifdef CONFIG_IP_MULTICAST 171724803f38SHangbin Liu igmpv3_del_delrec(in_dev, pmc); 171824803f38SHangbin Liu #endif 17191d7138deSEric Dumazet igmp_group_added(pmc); 17201da177e4SLinus Torvalds } 172124803f38SHangbin Liu } 17221da177e4SLinus Torvalds 17231da177e4SLinus Torvalds /* 17241da177e4SLinus Torvalds * Device is about to be destroyed: clean up. 17251da177e4SLinus Torvalds */ 17261da177e4SLinus Torvalds 17271da177e4SLinus Torvalds void ip_mc_destroy_dev(struct in_device *in_dev) 17281da177e4SLinus Torvalds { 17291da177e4SLinus Torvalds struct ip_mc_list *i; 17301da177e4SLinus Torvalds 17311da177e4SLinus Torvalds ASSERT_RTNL(); 17321da177e4SLinus Torvalds 17331da177e4SLinus Torvalds /* Deactivate timers */ 17341da177e4SLinus Torvalds ip_mc_down(in_dev); 173524803f38SHangbin Liu #ifdef CONFIG_IP_MULTICAST 173624803f38SHangbin Liu igmpv3_clear_delrec(in_dev); 173724803f38SHangbin Liu #endif 17381da177e4SLinus Torvalds 17391d7138deSEric Dumazet while ((i = rtnl_dereference(in_dev->mc_list)) != NULL) { 17401d7138deSEric Dumazet in_dev->mc_list = i->next_rcu; 1741b8bae41eSRami Rosen in_dev->mc_count--; 17421da177e4SLinus Torvalds ip_ma_put(i); 17431da177e4SLinus Torvalds } 17441da177e4SLinus Torvalds } 17451da177e4SLinus Torvalds 17469e917dcaSEric Dumazet /* RTNL is locked */ 1747877acedcSDaniel Lezcano static struct in_device *ip_mc_find_dev(struct net *net, struct ip_mreqn *imr) 17481da177e4SLinus Torvalds { 17491da177e4SLinus Torvalds struct net_device *dev = NULL; 17501da177e4SLinus Torvalds struct in_device *idev = NULL; 17511da177e4SLinus Torvalds 17521da177e4SLinus Torvalds if (imr->imr_ifindex) { 1753877acedcSDaniel Lezcano idev = inetdev_by_index(net, imr->imr_ifindex); 17541da177e4SLinus Torvalds return idev; 17551da177e4SLinus Torvalds } 17561da177e4SLinus Torvalds if (imr->imr_address.s_addr) { 17579e917dcaSEric Dumazet dev = __ip_dev_find(net, imr->imr_address.s_addr, false); 17581da177e4SLinus Torvalds if (!dev) 17591da177e4SLinus Torvalds return NULL; 17601da177e4SLinus Torvalds } 17611da177e4SLinus Torvalds 1762b23dd4feSDavid S. Miller if (!dev) { 176378fbfd8aSDavid S. Miller struct rtable *rt = ip_route_output(net, 176478fbfd8aSDavid S. Miller imr->imr_multiaddr.s_addr, 176578fbfd8aSDavid S. Miller 0, 0, 0); 1766b23dd4feSDavid S. Miller if (!IS_ERR(rt)) { 1767d8d1f30bSChangli Gao dev = rt->dst.dev; 17681da177e4SLinus Torvalds ip_rt_put(rt); 17691da177e4SLinus Torvalds } 1770b23dd4feSDavid S. Miller } 17711da177e4SLinus Torvalds if (dev) { 17721da177e4SLinus Torvalds imr->imr_ifindex = dev->ifindex; 1773e5ed6399SHerbert Xu idev = __in_dev_get_rtnl(dev); 17741da177e4SLinus Torvalds } 17751da177e4SLinus Torvalds return idev; 17761da177e4SLinus Torvalds } 17771da177e4SLinus Torvalds 17781da177e4SLinus Torvalds /* 17791da177e4SLinus Torvalds * Join a socket to a group 17801da177e4SLinus Torvalds */ 17811da177e4SLinus Torvalds 17821da177e4SLinus Torvalds static int ip_mc_del1_src(struct ip_mc_list *pmc, int sfmode, 17838f935bbdSAl Viro __be32 *psfsrc) 17841da177e4SLinus Torvalds { 17851da177e4SLinus Torvalds struct ip_sf_list *psf, *psf_prev; 17861da177e4SLinus Torvalds int rv = 0; 17871da177e4SLinus Torvalds 17881da177e4SLinus Torvalds psf_prev = NULL; 17891da177e4SLinus Torvalds for (psf = pmc->sources; psf; psf = psf->sf_next) { 17901da177e4SLinus Torvalds if (psf->sf_inaddr == *psfsrc) 17911da177e4SLinus Torvalds break; 17921da177e4SLinus Torvalds psf_prev = psf; 17931da177e4SLinus Torvalds } 17941da177e4SLinus Torvalds if (!psf || psf->sf_count[sfmode] == 0) { 17951da177e4SLinus Torvalds /* source filter not found, or count wrong => bug */ 17961da177e4SLinus Torvalds return -ESRCH; 17971da177e4SLinus Torvalds } 17981da177e4SLinus Torvalds psf->sf_count[sfmode]--; 17991da177e4SLinus Torvalds if (psf->sf_count[sfmode] == 0) { 18001da177e4SLinus Torvalds ip_rt_multicast_event(pmc->interface); 18011da177e4SLinus Torvalds } 18021da177e4SLinus Torvalds if (!psf->sf_count[MCAST_INCLUDE] && !psf->sf_count[MCAST_EXCLUDE]) { 18031da177e4SLinus Torvalds #ifdef CONFIG_IP_MULTICAST 18041da177e4SLinus Torvalds struct in_device *in_dev = pmc->interface; 1805165094afSNikolay Borisov struct net *net = dev_net(in_dev->dev); 18061da177e4SLinus Torvalds #endif 18071da177e4SLinus Torvalds 18081da177e4SLinus Torvalds /* no more filters for this source */ 18091da177e4SLinus Torvalds if (psf_prev) 18101da177e4SLinus Torvalds psf_prev->sf_next = psf->sf_next; 18111da177e4SLinus Torvalds else 18121da177e4SLinus Torvalds pmc->sources = psf->sf_next; 18131da177e4SLinus Torvalds #ifdef CONFIG_IP_MULTICAST 18141da177e4SLinus Torvalds if (psf->sf_oldin && 18151da177e4SLinus Torvalds !IGMP_V1_SEEN(in_dev) && !IGMP_V2_SEEN(in_dev)) { 1816165094afSNikolay Borisov psf->sf_crcount = in_dev->mr_qrv ?: net->ipv4.sysctl_igmp_qrv; 18171da177e4SLinus Torvalds psf->sf_next = pmc->tomb; 18181da177e4SLinus Torvalds pmc->tomb = psf; 18191da177e4SLinus Torvalds rv = 1; 18201da177e4SLinus Torvalds } else 18211da177e4SLinus Torvalds #endif 18221da177e4SLinus Torvalds kfree(psf); 18231da177e4SLinus Torvalds } 18241da177e4SLinus Torvalds return rv; 18251da177e4SLinus Torvalds } 18261da177e4SLinus Torvalds 18271da177e4SLinus Torvalds #ifndef CONFIG_IP_MULTICAST 18281da177e4SLinus Torvalds #define igmp_ifc_event(x) do { } while (0) 18291da177e4SLinus Torvalds #endif 18301da177e4SLinus Torvalds 18318f935bbdSAl Viro static int ip_mc_del_src(struct in_device *in_dev, __be32 *pmca, int sfmode, 18328f935bbdSAl Viro int sfcount, __be32 *psfsrc, int delta) 18331da177e4SLinus Torvalds { 18341da177e4SLinus Torvalds struct ip_mc_list *pmc; 18351da177e4SLinus Torvalds int changerec = 0; 18361da177e4SLinus Torvalds int i, err; 18371da177e4SLinus Torvalds 18381da177e4SLinus Torvalds if (!in_dev) 18391da177e4SLinus Torvalds return -ENODEV; 18401d7138deSEric Dumazet rcu_read_lock(); 18411d7138deSEric Dumazet for_each_pmc_rcu(in_dev, pmc) { 18421da177e4SLinus Torvalds if (*pmca == pmc->multiaddr) 18431da177e4SLinus Torvalds break; 18441da177e4SLinus Torvalds } 18451da177e4SLinus Torvalds if (!pmc) { 18461da177e4SLinus Torvalds /* MCA not found?? bug */ 18471d7138deSEric Dumazet rcu_read_unlock(); 18481da177e4SLinus Torvalds return -ESRCH; 18491da177e4SLinus Torvalds } 18501da177e4SLinus Torvalds spin_lock_bh(&pmc->lock); 18511d7138deSEric Dumazet rcu_read_unlock(); 18521da177e4SLinus Torvalds #ifdef CONFIG_IP_MULTICAST 18531da177e4SLinus Torvalds sf_markstate(pmc); 18541da177e4SLinus Torvalds #endif 18551da177e4SLinus Torvalds if (!delta) { 18561da177e4SLinus Torvalds err = -EINVAL; 18571da177e4SLinus Torvalds if (!pmc->sfcount[sfmode]) 18581da177e4SLinus Torvalds goto out_unlock; 18591da177e4SLinus Torvalds pmc->sfcount[sfmode]--; 18601da177e4SLinus Torvalds } 18611da177e4SLinus Torvalds err = 0; 18621da177e4SLinus Torvalds for (i = 0; i < sfcount; i++) { 18631da177e4SLinus Torvalds int rv = ip_mc_del1_src(pmc, sfmode, &psfsrc[i]); 18641da177e4SLinus Torvalds 18651da177e4SLinus Torvalds changerec |= rv > 0; 18661da177e4SLinus Torvalds if (!err && rv < 0) 18671da177e4SLinus Torvalds err = rv; 18681da177e4SLinus Torvalds } 18691da177e4SLinus Torvalds if (pmc->sfmode == MCAST_EXCLUDE && 18701da177e4SLinus Torvalds pmc->sfcount[MCAST_EXCLUDE] == 0 && 18711da177e4SLinus Torvalds pmc->sfcount[MCAST_INCLUDE]) { 18721da177e4SLinus Torvalds #ifdef CONFIG_IP_MULTICAST 18731da177e4SLinus Torvalds struct ip_sf_list *psf; 1874165094afSNikolay Borisov struct net *net = dev_net(in_dev->dev); 18751da177e4SLinus Torvalds #endif 18761da177e4SLinus Torvalds 18771da177e4SLinus Torvalds /* filter mode change */ 18781da177e4SLinus Torvalds pmc->sfmode = MCAST_INCLUDE; 18791da177e4SLinus Torvalds #ifdef CONFIG_IP_MULTICAST 1880165094afSNikolay Borisov pmc->crcount = in_dev->mr_qrv ?: net->ipv4.sysctl_igmp_qrv; 18811da177e4SLinus Torvalds in_dev->mr_ifc_count = pmc->crcount; 18821da177e4SLinus Torvalds for (psf = pmc->sources; psf; psf = psf->sf_next) 18831da177e4SLinus Torvalds psf->sf_crcount = 0; 18841da177e4SLinus Torvalds igmp_ifc_event(pmc->interface); 18851da177e4SLinus Torvalds } else if (sf_setstate(pmc) || changerec) { 18861da177e4SLinus Torvalds igmp_ifc_event(pmc->interface); 18871da177e4SLinus Torvalds #endif 18881da177e4SLinus Torvalds } 18891da177e4SLinus Torvalds out_unlock: 18901da177e4SLinus Torvalds spin_unlock_bh(&pmc->lock); 18911da177e4SLinus Torvalds return err; 18921da177e4SLinus Torvalds } 18931da177e4SLinus Torvalds 18941da177e4SLinus Torvalds /* 18951da177e4SLinus Torvalds * Add multicast single-source filter to the interface list 18961da177e4SLinus Torvalds */ 18971da177e4SLinus Torvalds static int ip_mc_add1_src(struct ip_mc_list *pmc, int sfmode, 18985eb81e89SJun Zhao __be32 *psfsrc) 18991da177e4SLinus Torvalds { 19001da177e4SLinus Torvalds struct ip_sf_list *psf, *psf_prev; 19011da177e4SLinus Torvalds 19021da177e4SLinus Torvalds psf_prev = NULL; 19031da177e4SLinus Torvalds for (psf = pmc->sources; psf; psf = psf->sf_next) { 19041da177e4SLinus Torvalds if (psf->sf_inaddr == *psfsrc) 19051da177e4SLinus Torvalds break; 19061da177e4SLinus Torvalds psf_prev = psf; 19071da177e4SLinus Torvalds } 19081da177e4SLinus Torvalds if (!psf) { 19090da974f4SPanagiotis Issaris psf = kzalloc(sizeof(*psf), GFP_ATOMIC); 19101da177e4SLinus Torvalds if (!psf) 19111da177e4SLinus Torvalds return -ENOBUFS; 19121da177e4SLinus Torvalds psf->sf_inaddr = *psfsrc; 19131da177e4SLinus Torvalds if (psf_prev) { 19141da177e4SLinus Torvalds psf_prev->sf_next = psf; 19151da177e4SLinus Torvalds } else 19161da177e4SLinus Torvalds pmc->sources = psf; 19171da177e4SLinus Torvalds } 19181da177e4SLinus Torvalds psf->sf_count[sfmode]++; 19191da177e4SLinus Torvalds if (psf->sf_count[sfmode] == 1) { 19201da177e4SLinus Torvalds ip_rt_multicast_event(pmc->interface); 19211da177e4SLinus Torvalds } 19221da177e4SLinus Torvalds return 0; 19231da177e4SLinus Torvalds } 19241da177e4SLinus Torvalds 19251da177e4SLinus Torvalds #ifdef CONFIG_IP_MULTICAST 19261da177e4SLinus Torvalds static void sf_markstate(struct ip_mc_list *pmc) 19271da177e4SLinus Torvalds { 19281da177e4SLinus Torvalds struct ip_sf_list *psf; 19291da177e4SLinus Torvalds int mca_xcount = pmc->sfcount[MCAST_EXCLUDE]; 19301da177e4SLinus Torvalds 19311da177e4SLinus Torvalds for (psf = pmc->sources; psf; psf = psf->sf_next) 19321da177e4SLinus Torvalds if (pmc->sfcount[MCAST_EXCLUDE]) { 19331da177e4SLinus Torvalds psf->sf_oldin = mca_xcount == 19341da177e4SLinus Torvalds psf->sf_count[MCAST_EXCLUDE] && 19351da177e4SLinus Torvalds !psf->sf_count[MCAST_INCLUDE]; 19361da177e4SLinus Torvalds } else 19371da177e4SLinus Torvalds psf->sf_oldin = psf->sf_count[MCAST_INCLUDE] != 0; 19381da177e4SLinus Torvalds } 19391da177e4SLinus Torvalds 19401da177e4SLinus Torvalds static int sf_setstate(struct ip_mc_list *pmc) 19411da177e4SLinus Torvalds { 1942ad12583fSDavid L Stevens struct ip_sf_list *psf, *dpsf; 19431da177e4SLinus Torvalds int mca_xcount = pmc->sfcount[MCAST_EXCLUDE]; 19441da177e4SLinus Torvalds int qrv = pmc->interface->mr_qrv; 19451da177e4SLinus Torvalds int new_in, rv; 19461da177e4SLinus Torvalds 19471da177e4SLinus Torvalds rv = 0; 19481da177e4SLinus Torvalds for (psf = pmc->sources; psf; psf = psf->sf_next) { 19491da177e4SLinus Torvalds if (pmc->sfcount[MCAST_EXCLUDE]) { 19501da177e4SLinus Torvalds new_in = mca_xcount == psf->sf_count[MCAST_EXCLUDE] && 19511da177e4SLinus Torvalds !psf->sf_count[MCAST_INCLUDE]; 19521da177e4SLinus Torvalds } else 19531da177e4SLinus Torvalds new_in = psf->sf_count[MCAST_INCLUDE] != 0; 1954ad12583fSDavid L Stevens if (new_in) { 1955ad12583fSDavid L Stevens if (!psf->sf_oldin) { 195676edc605SAl Viro struct ip_sf_list *prev = NULL; 1957ad12583fSDavid L Stevens 1958ad12583fSDavid L Stevens for (dpsf = pmc->tomb; dpsf; dpsf = dpsf->sf_next) { 1959ad12583fSDavid L Stevens if (dpsf->sf_inaddr == psf->sf_inaddr) 1960ad12583fSDavid L Stevens break; 1961ad12583fSDavid L Stevens prev = dpsf; 1962ad12583fSDavid L Stevens } 1963ad12583fSDavid L Stevens if (dpsf) { 1964ad12583fSDavid L Stevens if (prev) 1965ad12583fSDavid L Stevens prev->sf_next = dpsf->sf_next; 1966ad12583fSDavid L Stevens else 1967ad12583fSDavid L Stevens pmc->tomb = dpsf->sf_next; 1968ad12583fSDavid L Stevens kfree(dpsf); 1969ad12583fSDavid L Stevens } 19701da177e4SLinus Torvalds psf->sf_crcount = qrv; 19711da177e4SLinus Torvalds rv++; 19721da177e4SLinus Torvalds } 1973ad12583fSDavid L Stevens } else if (psf->sf_oldin) { 1974ad12583fSDavid L Stevens 1975ad12583fSDavid L Stevens psf->sf_crcount = 0; 1976ad12583fSDavid L Stevens /* 1977ad12583fSDavid L Stevens * add or update "delete" records if an active filter 1978ad12583fSDavid L Stevens * is now inactive 1979ad12583fSDavid L Stevens */ 1980ad12583fSDavid L Stevens for (dpsf = pmc->tomb; dpsf; dpsf = dpsf->sf_next) 1981ad12583fSDavid L Stevens if (dpsf->sf_inaddr == psf->sf_inaddr) 1982ad12583fSDavid L Stevens break; 1983ad12583fSDavid L Stevens if (!dpsf) { 19843ed37a6fSJoe Perches dpsf = kmalloc(sizeof(*dpsf), GFP_ATOMIC); 1985ad12583fSDavid L Stevens if (!dpsf) 1986ad12583fSDavid L Stevens continue; 1987ad12583fSDavid L Stevens *dpsf = *psf; 1988ad12583fSDavid L Stevens /* pmc->lock held by callers */ 1989ad12583fSDavid L Stevens dpsf->sf_next = pmc->tomb; 1990ad12583fSDavid L Stevens pmc->tomb = dpsf; 1991ad12583fSDavid L Stevens } 1992ad12583fSDavid L Stevens dpsf->sf_crcount = qrv; 1993ad12583fSDavid L Stevens rv++; 1994ad12583fSDavid L Stevens } 19951da177e4SLinus Torvalds } 19961da177e4SLinus Torvalds return rv; 19971da177e4SLinus Torvalds } 19981da177e4SLinus Torvalds #endif 19991da177e4SLinus Torvalds 20001da177e4SLinus Torvalds /* 20011da177e4SLinus Torvalds * Add multicast source filter list to the interface list 20021da177e4SLinus Torvalds */ 20038f935bbdSAl Viro static int ip_mc_add_src(struct in_device *in_dev, __be32 *pmca, int sfmode, 20048f935bbdSAl Viro int sfcount, __be32 *psfsrc, int delta) 20051da177e4SLinus Torvalds { 20061da177e4SLinus Torvalds struct ip_mc_list *pmc; 20071da177e4SLinus Torvalds int isexclude; 20081da177e4SLinus Torvalds int i, err; 20091da177e4SLinus Torvalds 20101da177e4SLinus Torvalds if (!in_dev) 20111da177e4SLinus Torvalds return -ENODEV; 20121d7138deSEric Dumazet rcu_read_lock(); 20131d7138deSEric Dumazet for_each_pmc_rcu(in_dev, pmc) { 20141da177e4SLinus Torvalds if (*pmca == pmc->multiaddr) 20151da177e4SLinus Torvalds break; 20161da177e4SLinus Torvalds } 20171da177e4SLinus Torvalds if (!pmc) { 20181da177e4SLinus Torvalds /* MCA not found?? bug */ 20191d7138deSEric Dumazet rcu_read_unlock(); 20201da177e4SLinus Torvalds return -ESRCH; 20211da177e4SLinus Torvalds } 20221da177e4SLinus Torvalds spin_lock_bh(&pmc->lock); 20231d7138deSEric Dumazet rcu_read_unlock(); 20241da177e4SLinus Torvalds 20251da177e4SLinus Torvalds #ifdef CONFIG_IP_MULTICAST 20261da177e4SLinus Torvalds sf_markstate(pmc); 20271da177e4SLinus Torvalds #endif 20281da177e4SLinus Torvalds isexclude = pmc->sfmode == MCAST_EXCLUDE; 20291da177e4SLinus Torvalds if (!delta) 20301da177e4SLinus Torvalds pmc->sfcount[sfmode]++; 20311da177e4SLinus Torvalds err = 0; 20321da177e4SLinus Torvalds for (i = 0; i < sfcount; i++) { 20335eb81e89SJun Zhao err = ip_mc_add1_src(pmc, sfmode, &psfsrc[i]); 20341da177e4SLinus Torvalds if (err) 20351da177e4SLinus Torvalds break; 20361da177e4SLinus Torvalds } 20371da177e4SLinus Torvalds if (err) { 20381da177e4SLinus Torvalds int j; 20391da177e4SLinus Torvalds 2040685f94e6SJun Zhao if (!delta) 20411da177e4SLinus Torvalds pmc->sfcount[sfmode]--; 20421da177e4SLinus Torvalds for (j = 0; j < i; j++) 2043a1889c0dSJulia Lawall (void) ip_mc_del1_src(pmc, sfmode, &psfsrc[j]); 20441da177e4SLinus Torvalds } else if (isexclude != (pmc->sfcount[MCAST_EXCLUDE] != 0)) { 20451da177e4SLinus Torvalds #ifdef CONFIG_IP_MULTICAST 20461da177e4SLinus Torvalds struct ip_sf_list *psf; 2047165094afSNikolay Borisov struct net *net = dev_net(pmc->interface->dev); 2048cfcabdccSStephen Hemminger in_dev = pmc->interface; 20491da177e4SLinus Torvalds #endif 20501da177e4SLinus Torvalds 20511da177e4SLinus Torvalds /* filter mode change */ 20521da177e4SLinus Torvalds if (pmc->sfcount[MCAST_EXCLUDE]) 20531da177e4SLinus Torvalds pmc->sfmode = MCAST_EXCLUDE; 20541da177e4SLinus Torvalds else if (pmc->sfcount[MCAST_INCLUDE]) 20551da177e4SLinus Torvalds pmc->sfmode = MCAST_INCLUDE; 20561da177e4SLinus Torvalds #ifdef CONFIG_IP_MULTICAST 20571da177e4SLinus Torvalds /* else no filters; keep old mode for reports */ 20581da177e4SLinus Torvalds 2059165094afSNikolay Borisov pmc->crcount = in_dev->mr_qrv ?: net->ipv4.sysctl_igmp_qrv; 20601da177e4SLinus Torvalds in_dev->mr_ifc_count = pmc->crcount; 20611da177e4SLinus Torvalds for (psf = pmc->sources; psf; psf = psf->sf_next) 20621da177e4SLinus Torvalds psf->sf_crcount = 0; 20631da177e4SLinus Torvalds igmp_ifc_event(in_dev); 20641da177e4SLinus Torvalds } else if (sf_setstate(pmc)) { 20651da177e4SLinus Torvalds igmp_ifc_event(in_dev); 20661da177e4SLinus Torvalds #endif 20671da177e4SLinus Torvalds } 20681da177e4SLinus Torvalds spin_unlock_bh(&pmc->lock); 20691da177e4SLinus Torvalds return err; 20701da177e4SLinus Torvalds } 20711da177e4SLinus Torvalds 20721da177e4SLinus Torvalds static void ip_mc_clear_src(struct ip_mc_list *pmc) 20731da177e4SLinus Torvalds { 2074*c38b7d32SWANG Cong struct ip_sf_list *psf, *nextpsf, *tomb, *sources; 20751da177e4SLinus Torvalds 2076*c38b7d32SWANG Cong spin_lock_bh(&pmc->lock); 2077*c38b7d32SWANG Cong tomb = pmc->tomb; 20781da177e4SLinus Torvalds pmc->tomb = NULL; 2079*c38b7d32SWANG Cong sources = pmc->sources; 20801da177e4SLinus Torvalds pmc->sources = NULL; 20811da177e4SLinus Torvalds pmc->sfmode = MCAST_EXCLUDE; 2082de9daad9SDenis Lukianov pmc->sfcount[MCAST_INCLUDE] = 0; 20831da177e4SLinus Torvalds pmc->sfcount[MCAST_EXCLUDE] = 1; 2084*c38b7d32SWANG Cong spin_unlock_bh(&pmc->lock); 2085*c38b7d32SWANG Cong 2086*c38b7d32SWANG Cong for (psf = tomb; psf; psf = nextpsf) { 2087*c38b7d32SWANG Cong nextpsf = psf->sf_next; 2088*c38b7d32SWANG Cong kfree(psf); 2089*c38b7d32SWANG Cong } 2090*c38b7d32SWANG Cong for (psf = sources; psf; psf = nextpsf) { 2091*c38b7d32SWANG Cong nextpsf = psf->sf_next; 2092*c38b7d32SWANG Cong kfree(psf); 2093*c38b7d32SWANG Cong } 20941da177e4SLinus Torvalds } 20951da177e4SLinus Torvalds 209654ff9ef3SMarcelo Ricardo Leitner /* Join a multicast group 209754ff9ef3SMarcelo Ricardo Leitner */ 209854ff9ef3SMarcelo Ricardo Leitner 209954ff9ef3SMarcelo Ricardo Leitner int ip_mc_join_group(struct sock *sk, struct ip_mreqn *imr) 21001da177e4SLinus Torvalds { 21018f935bbdSAl Viro __be32 addr = imr->imr_multiaddr.s_addr; 2102959d10f6SEric Dumazet struct ip_mc_socklist *iml, *i; 21031da177e4SLinus Torvalds struct in_device *in_dev; 21041da177e4SLinus Torvalds struct inet_sock *inet = inet_sk(sk); 2105877acedcSDaniel Lezcano struct net *net = sock_net(sk); 2106ca9b907dSDavid L Stevens int ifindex; 21071da177e4SLinus Torvalds int count = 0; 2108959d10f6SEric Dumazet int err; 2109959d10f6SEric Dumazet 2110959d10f6SEric Dumazet ASSERT_RTNL(); 21111da177e4SLinus Torvalds 2112f97c1e0cSJoe Perches if (!ipv4_is_multicast(addr)) 21131da177e4SLinus Torvalds return -EINVAL; 21141da177e4SLinus Torvalds 2115877acedcSDaniel Lezcano in_dev = ip_mc_find_dev(net, imr); 21161da177e4SLinus Torvalds 21171da177e4SLinus Torvalds if (!in_dev) { 21181da177e4SLinus Torvalds err = -ENODEV; 21191da177e4SLinus Torvalds goto done; 21201da177e4SLinus Torvalds } 21211da177e4SLinus Torvalds 21221da177e4SLinus Torvalds err = -EADDRINUSE; 2123ca9b907dSDavid L Stevens ifindex = imr->imr_ifindex; 21241d7138deSEric Dumazet for_each_pmc_rtnl(inet, i) { 2125ca9b907dSDavid L Stevens if (i->multi.imr_multiaddr.s_addr == addr && 2126ca9b907dSDavid L Stevens i->multi.imr_ifindex == ifindex) 21271da177e4SLinus Torvalds goto done; 21281da177e4SLinus Torvalds count++; 21291da177e4SLinus Torvalds } 21301da177e4SLinus Torvalds err = -ENOBUFS; 2131815c5270SNikolay Borisov if (count >= net->ipv4.sysctl_igmp_max_memberships) 21321da177e4SLinus Torvalds goto done; 21338b3a7005SKris Katterjohn iml = sock_kmalloc(sk, sizeof(*iml), GFP_KERNEL); 213451456b29SIan Morris if (!iml) 2135ca9b907dSDavid L Stevens goto done; 2136ca9b907dSDavid L Stevens 21371da177e4SLinus Torvalds memcpy(&iml->multi, imr, sizeof(*imr)); 21381d7138deSEric Dumazet iml->next_rcu = inet->mc_list; 21391da177e4SLinus Torvalds iml->sflist = NULL; 21401da177e4SLinus Torvalds iml->sfmode = MCAST_EXCLUDE; 2141cf778b00SEric Dumazet rcu_assign_pointer(inet->mc_list, iml); 21421da177e4SLinus Torvalds ip_mc_inc_group(in_dev, addr); 21431da177e4SLinus Torvalds err = 0; 21441da177e4SLinus Torvalds done: 21451da177e4SLinus Torvalds return err; 21461da177e4SLinus Torvalds } 21474bc2f18bSEric Dumazet EXPORT_SYMBOL(ip_mc_join_group); 21481da177e4SLinus Torvalds 21491da177e4SLinus Torvalds static int ip_mc_leave_src(struct sock *sk, struct ip_mc_socklist *iml, 21501da177e4SLinus Torvalds struct in_device *in_dev) 21511da177e4SLinus Torvalds { 21521d7138deSEric Dumazet struct ip_sf_socklist *psf = rtnl_dereference(iml->sflist); 21531da177e4SLinus Torvalds int err; 21541da177e4SLinus Torvalds 215551456b29SIan Morris if (!psf) { 21561da177e4SLinus Torvalds /* any-source empty exclude case */ 21571da177e4SLinus Torvalds return ip_mc_del_src(in_dev, &iml->multi.imr_multiaddr.s_addr, 21581da177e4SLinus Torvalds iml->sfmode, 0, NULL, 0); 21591da177e4SLinus Torvalds } 21601da177e4SLinus Torvalds err = ip_mc_del_src(in_dev, &iml->multi.imr_multiaddr.s_addr, 2161c85bb41eSFlavio Leitner iml->sfmode, psf->sl_count, psf->sl_addr, 0); 2162a9b3cd7fSStephen Hemminger RCU_INIT_POINTER(iml->sflist, NULL); 2163c85bb41eSFlavio Leitner /* decrease mem now to avoid the memleak warning */ 2164c85bb41eSFlavio Leitner atomic_sub(IP_SFLSIZE(psf->sl_max), &sk->sk_omem_alloc); 21657519cce4SLai Jiangshan kfree_rcu(psf, rcu); 21661da177e4SLinus Torvalds return err; 21671da177e4SLinus Torvalds } 21681da177e4SLinus Torvalds 216954ff9ef3SMarcelo Ricardo Leitner int ip_mc_leave_group(struct sock *sk, struct ip_mreqn *imr) 21701da177e4SLinus Torvalds { 21711da177e4SLinus Torvalds struct inet_sock *inet = inet_sk(sk); 21721d7138deSEric Dumazet struct ip_mc_socklist *iml; 21731d7138deSEric Dumazet struct ip_mc_socklist __rcu **imlp; 217484b42baeSDavid L Stevens struct in_device *in_dev; 2175877acedcSDaniel Lezcano struct net *net = sock_net(sk); 21768f935bbdSAl Viro __be32 group = imr->imr_multiaddr.s_addr; 217784b42baeSDavid L Stevens u32 ifindex; 2178acd6e00bSDavid L Stevens int ret = -EADDRNOTAVAIL; 21791da177e4SLinus Torvalds 2180959d10f6SEric Dumazet ASSERT_RTNL(); 2181959d10f6SEric Dumazet 2182877acedcSDaniel Lezcano in_dev = ip_mc_find_dev(net, imr); 21834eba7bb1SAndrew Lunn if (!imr->imr_ifindex && !imr->imr_address.s_addr && !in_dev) { 218452ad353aSdingtianhong ret = -ENODEV; 218552ad353aSdingtianhong goto out; 218652ad353aSdingtianhong } 218784b42baeSDavid L Stevens ifindex = imr->imr_ifindex; 21881d7138deSEric Dumazet for (imlp = &inet->mc_list; 21891d7138deSEric Dumazet (iml = rtnl_dereference(*imlp)) != NULL; 21901d7138deSEric Dumazet imlp = &iml->next_rcu) { 2191acd6e00bSDavid L Stevens if (iml->multi.imr_multiaddr.s_addr != group) 2192acd6e00bSDavid L Stevens continue; 2193acd6e00bSDavid L Stevens if (ifindex) { 2194acd6e00bSDavid L Stevens if (iml->multi.imr_ifindex != ifindex) 2195acd6e00bSDavid L Stevens continue; 2196acd6e00bSDavid L Stevens } else if (imr->imr_address.s_addr && imr->imr_address.s_addr != 2197acd6e00bSDavid L Stevens iml->multi.imr_address.s_addr) 2198acd6e00bSDavid L Stevens continue; 2199acd6e00bSDavid L Stevens 22001da177e4SLinus Torvalds (void) ip_mc_leave_src(sk, iml, in_dev); 22011da177e4SLinus Torvalds 22021d7138deSEric Dumazet *imlp = iml->next_rcu; 22031da177e4SLinus Torvalds 22044eba7bb1SAndrew Lunn if (in_dev) 220584b42baeSDavid L Stevens ip_mc_dec_group(in_dev, group); 2206959d10f6SEric Dumazet 2207c85bb41eSFlavio Leitner /* decrease mem now to avoid the memleak warning */ 2208c85bb41eSFlavio Leitner atomic_sub(sizeof(*iml), &sk->sk_omem_alloc); 220910d50e74SLai Jiangshan kfree_rcu(iml, rcu); 22101da177e4SLinus Torvalds return 0; 22111da177e4SLinus Torvalds } 221252ad353aSdingtianhong out: 2213959d10f6SEric Dumazet return ret; 2214959d10f6SEric Dumazet } 2215193ba924Sstephen hemminger EXPORT_SYMBOL(ip_mc_leave_group); 22161da177e4SLinus Torvalds 22171da177e4SLinus Torvalds int ip_mc_source(int add, int omode, struct sock *sk, struct 22181da177e4SLinus Torvalds ip_mreq_source *mreqs, int ifindex) 22191da177e4SLinus Torvalds { 22201da177e4SLinus Torvalds int err; 22211da177e4SLinus Torvalds struct ip_mreqn imr; 222263007727SAl Viro __be32 addr = mreqs->imr_multiaddr; 22231da177e4SLinus Torvalds struct ip_mc_socklist *pmc; 22241da177e4SLinus Torvalds struct in_device *in_dev = NULL; 22251da177e4SLinus Torvalds struct inet_sock *inet = inet_sk(sk); 22261da177e4SLinus Torvalds struct ip_sf_socklist *psl; 2227877acedcSDaniel Lezcano struct net *net = sock_net(sk); 22288cdaaa15SDavid L Stevens int leavegroup = 0; 22291da177e4SLinus Torvalds int i, j, rv; 22301da177e4SLinus Torvalds 2231f97c1e0cSJoe Perches if (!ipv4_is_multicast(addr)) 22321da177e4SLinus Torvalds return -EINVAL; 22331da177e4SLinus Torvalds 223454ff9ef3SMarcelo Ricardo Leitner ASSERT_RTNL(); 22351da177e4SLinus Torvalds 22361da177e4SLinus Torvalds imr.imr_multiaddr.s_addr = mreqs->imr_multiaddr; 22371da177e4SLinus Torvalds imr.imr_address.s_addr = mreqs->imr_interface; 22381da177e4SLinus Torvalds imr.imr_ifindex = ifindex; 2239877acedcSDaniel Lezcano in_dev = ip_mc_find_dev(net, &imr); 22401da177e4SLinus Torvalds 22411da177e4SLinus Torvalds if (!in_dev) { 22421da177e4SLinus Torvalds err = -ENODEV; 22431da177e4SLinus Torvalds goto done; 22441da177e4SLinus Torvalds } 22451da177e4SLinus Torvalds err = -EADDRNOTAVAIL; 22461da177e4SLinus Torvalds 22471d7138deSEric Dumazet for_each_pmc_rtnl(inet, pmc) { 2248f64f9e71SJoe Perches if ((pmc->multi.imr_multiaddr.s_addr == 2249f64f9e71SJoe Perches imr.imr_multiaddr.s_addr) && 2250f64f9e71SJoe Perches (pmc->multi.imr_ifindex == imr.imr_ifindex)) 22511da177e4SLinus Torvalds break; 22521da177e4SLinus Torvalds } 2253917f2f10SDavid L Stevens if (!pmc) { /* must have a prior join */ 2254917f2f10SDavid L Stevens err = -EINVAL; 22551da177e4SLinus Torvalds goto done; 2256917f2f10SDavid L Stevens } 22571da177e4SLinus Torvalds /* if a source filter was set, must be the same mode as before */ 22581da177e4SLinus Torvalds if (pmc->sflist) { 2259917f2f10SDavid L Stevens if (pmc->sfmode != omode) { 2260917f2f10SDavid L Stevens err = -EINVAL; 22611da177e4SLinus Torvalds goto done; 2262917f2f10SDavid L Stevens } 22631da177e4SLinus Torvalds } else if (pmc->sfmode != omode) { 22641da177e4SLinus Torvalds /* allow mode switches for empty-set filters */ 22651da177e4SLinus Torvalds ip_mc_add_src(in_dev, &mreqs->imr_multiaddr, omode, 0, NULL, 0); 22661da177e4SLinus Torvalds ip_mc_del_src(in_dev, &mreqs->imr_multiaddr, pmc->sfmode, 0, 22671da177e4SLinus Torvalds NULL, 0); 22681da177e4SLinus Torvalds pmc->sfmode = omode; 22691da177e4SLinus Torvalds } 22701da177e4SLinus Torvalds 22711d7138deSEric Dumazet psl = rtnl_dereference(pmc->sflist); 22721da177e4SLinus Torvalds if (!add) { 22731da177e4SLinus Torvalds if (!psl) 2274917f2f10SDavid L Stevens goto done; /* err = -EADDRNOTAVAIL */ 22751da177e4SLinus Torvalds rv = !0; 22761da177e4SLinus Torvalds for (i = 0; i < psl->sl_count; i++) { 22771da177e4SLinus Torvalds rv = memcmp(&psl->sl_addr[i], &mreqs->imr_sourceaddr, 227863007727SAl Viro sizeof(__be32)); 22791da177e4SLinus Torvalds if (rv == 0) 22801da177e4SLinus Torvalds break; 22811da177e4SLinus Torvalds } 22821da177e4SLinus Torvalds if (rv) /* source not found */ 2283917f2f10SDavid L Stevens goto done; /* err = -EADDRNOTAVAIL */ 22841da177e4SLinus Torvalds 22858cdaaa15SDavid L Stevens /* special case - (INCLUDE, empty) == LEAVE_GROUP */ 22868cdaaa15SDavid L Stevens if (psl->sl_count == 1 && omode == MCAST_INCLUDE) { 22878cdaaa15SDavid L Stevens leavegroup = 1; 22888cdaaa15SDavid L Stevens goto done; 22898cdaaa15SDavid L Stevens } 22908cdaaa15SDavid L Stevens 22911da177e4SLinus Torvalds /* update the interface filter */ 22921da177e4SLinus Torvalds ip_mc_del_src(in_dev, &mreqs->imr_multiaddr, omode, 1, 22931da177e4SLinus Torvalds &mreqs->imr_sourceaddr, 1); 22941da177e4SLinus Torvalds 22951da177e4SLinus Torvalds for (j = i+1; j < psl->sl_count; j++) 22961da177e4SLinus Torvalds psl->sl_addr[j-1] = psl->sl_addr[j]; 22971da177e4SLinus Torvalds psl->sl_count--; 22981da177e4SLinus Torvalds err = 0; 22991da177e4SLinus Torvalds goto done; 23001da177e4SLinus Torvalds } 23011da177e4SLinus Torvalds /* else, add a new source to the filter */ 23021da177e4SLinus Torvalds 2303166b6b2dSNikolay Borisov if (psl && psl->sl_count >= net->ipv4.sysctl_igmp_max_msf) { 23041da177e4SLinus Torvalds err = -ENOBUFS; 23051da177e4SLinus Torvalds goto done; 23061da177e4SLinus Torvalds } 23071da177e4SLinus Torvalds if (!psl || psl->sl_count == psl->sl_max) { 23081da177e4SLinus Torvalds struct ip_sf_socklist *newpsl; 23091da177e4SLinus Torvalds int count = IP_SFBLOCK; 23101da177e4SLinus Torvalds 23111da177e4SLinus Torvalds if (psl) 23121da177e4SLinus Torvalds count += psl->sl_max; 23138b3a7005SKris Katterjohn newpsl = sock_kmalloc(sk, IP_SFLSIZE(count), GFP_KERNEL); 23141da177e4SLinus Torvalds if (!newpsl) { 23151da177e4SLinus Torvalds err = -ENOBUFS; 23161da177e4SLinus Torvalds goto done; 23171da177e4SLinus Torvalds } 23181da177e4SLinus Torvalds newpsl->sl_max = count; 23191da177e4SLinus Torvalds newpsl->sl_count = count - IP_SFBLOCK; 23201da177e4SLinus Torvalds if (psl) { 23211da177e4SLinus Torvalds for (i = 0; i < psl->sl_count; i++) 23221da177e4SLinus Torvalds newpsl->sl_addr[i] = psl->sl_addr[i]; 2323c85bb41eSFlavio Leitner /* decrease mem now to avoid the memleak warning */ 2324c85bb41eSFlavio Leitner atomic_sub(IP_SFLSIZE(psl->sl_max), &sk->sk_omem_alloc); 23257519cce4SLai Jiangshan kfree_rcu(psl, rcu); 23261da177e4SLinus Torvalds } 2327cf778b00SEric Dumazet rcu_assign_pointer(pmc->sflist, newpsl); 2328c85bb41eSFlavio Leitner psl = newpsl; 23291da177e4SLinus Torvalds } 23301da177e4SLinus Torvalds rv = 1; /* > 0 for insert logic below if sl_count is 0 */ 23311da177e4SLinus Torvalds for (i = 0; i < psl->sl_count; i++) { 23321da177e4SLinus Torvalds rv = memcmp(&psl->sl_addr[i], &mreqs->imr_sourceaddr, 233363007727SAl Viro sizeof(__be32)); 23341da177e4SLinus Torvalds if (rv == 0) 23351da177e4SLinus Torvalds break; 23361da177e4SLinus Torvalds } 23371da177e4SLinus Torvalds if (rv == 0) /* address already there is an error */ 23381da177e4SLinus Torvalds goto done; 23391da177e4SLinus Torvalds for (j = psl->sl_count-1; j >= i; j--) 23401da177e4SLinus Torvalds psl->sl_addr[j+1] = psl->sl_addr[j]; 23411da177e4SLinus Torvalds psl->sl_addr[i] = mreqs->imr_sourceaddr; 23421da177e4SLinus Torvalds psl->sl_count++; 23431da177e4SLinus Torvalds err = 0; 23441da177e4SLinus Torvalds /* update the interface list */ 23451da177e4SLinus Torvalds ip_mc_add_src(in_dev, &mreqs->imr_multiaddr, omode, 1, 23461da177e4SLinus Torvalds &mreqs->imr_sourceaddr, 1); 23471da177e4SLinus Torvalds done: 23488cdaaa15SDavid L Stevens if (leavegroup) 234954ff9ef3SMarcelo Ricardo Leitner err = ip_mc_leave_group(sk, &imr); 23501da177e4SLinus Torvalds return err; 23511da177e4SLinus Torvalds } 23521da177e4SLinus Torvalds 23531da177e4SLinus Torvalds int ip_mc_msfilter(struct sock *sk, struct ip_msfilter *msf, int ifindex) 23541da177e4SLinus Torvalds { 23559951f036SDavid L Stevens int err = 0; 23561da177e4SLinus Torvalds struct ip_mreqn imr; 235763007727SAl Viro __be32 addr = msf->imsf_multiaddr; 23581da177e4SLinus Torvalds struct ip_mc_socklist *pmc; 23591da177e4SLinus Torvalds struct in_device *in_dev; 23601da177e4SLinus Torvalds struct inet_sock *inet = inet_sk(sk); 23611da177e4SLinus Torvalds struct ip_sf_socklist *newpsl, *psl; 2362877acedcSDaniel Lezcano struct net *net = sock_net(sk); 23639951f036SDavid L Stevens int leavegroup = 0; 23641da177e4SLinus Torvalds 2365f97c1e0cSJoe Perches if (!ipv4_is_multicast(addr)) 23661da177e4SLinus Torvalds return -EINVAL; 23671da177e4SLinus Torvalds if (msf->imsf_fmode != MCAST_INCLUDE && 23681da177e4SLinus Torvalds msf->imsf_fmode != MCAST_EXCLUDE) 23691da177e4SLinus Torvalds return -EINVAL; 23701da177e4SLinus Torvalds 237154ff9ef3SMarcelo Ricardo Leitner ASSERT_RTNL(); 23721da177e4SLinus Torvalds 23731da177e4SLinus Torvalds imr.imr_multiaddr.s_addr = msf->imsf_multiaddr; 23741da177e4SLinus Torvalds imr.imr_address.s_addr = msf->imsf_interface; 23751da177e4SLinus Torvalds imr.imr_ifindex = ifindex; 2376877acedcSDaniel Lezcano in_dev = ip_mc_find_dev(net, &imr); 23771da177e4SLinus Torvalds 23781da177e4SLinus Torvalds if (!in_dev) { 23791da177e4SLinus Torvalds err = -ENODEV; 23801da177e4SLinus Torvalds goto done; 23811da177e4SLinus Torvalds } 23821da177e4SLinus Torvalds 23839951f036SDavid L Stevens /* special case - (INCLUDE, empty) == LEAVE_GROUP */ 23849951f036SDavid L Stevens if (msf->imsf_fmode == MCAST_INCLUDE && msf->imsf_numsrc == 0) { 23859951f036SDavid L Stevens leavegroup = 1; 23869951f036SDavid L Stevens goto done; 23879951f036SDavid L Stevens } 23889951f036SDavid L Stevens 23891d7138deSEric Dumazet for_each_pmc_rtnl(inet, pmc) { 23901da177e4SLinus Torvalds if (pmc->multi.imr_multiaddr.s_addr == msf->imsf_multiaddr && 23911da177e4SLinus Torvalds pmc->multi.imr_ifindex == imr.imr_ifindex) 23921da177e4SLinus Torvalds break; 23931da177e4SLinus Torvalds } 2394917f2f10SDavid L Stevens if (!pmc) { /* must have a prior join */ 2395917f2f10SDavid L Stevens err = -EINVAL; 23961da177e4SLinus Torvalds goto done; 2397917f2f10SDavid L Stevens } 23981da177e4SLinus Torvalds if (msf->imsf_numsrc) { 23998b3a7005SKris Katterjohn newpsl = sock_kmalloc(sk, IP_SFLSIZE(msf->imsf_numsrc), 24008b3a7005SKris Katterjohn GFP_KERNEL); 24011da177e4SLinus Torvalds if (!newpsl) { 24021da177e4SLinus Torvalds err = -ENOBUFS; 24031da177e4SLinus Torvalds goto done; 24041da177e4SLinus Torvalds } 24051da177e4SLinus Torvalds newpsl->sl_max = newpsl->sl_count = msf->imsf_numsrc; 24061da177e4SLinus Torvalds memcpy(newpsl->sl_addr, msf->imsf_slist, 24071da177e4SLinus Torvalds msf->imsf_numsrc * sizeof(msf->imsf_slist[0])); 24081da177e4SLinus Torvalds err = ip_mc_add_src(in_dev, &msf->imsf_multiaddr, 24091da177e4SLinus Torvalds msf->imsf_fmode, newpsl->sl_count, newpsl->sl_addr, 0); 24101da177e4SLinus Torvalds if (err) { 24111da177e4SLinus Torvalds sock_kfree_s(sk, newpsl, IP_SFLSIZE(newpsl->sl_max)); 24121da177e4SLinus Torvalds goto done; 24131da177e4SLinus Torvalds } 24148713dbf0SYan Zheng } else { 24151da177e4SLinus Torvalds newpsl = NULL; 24168713dbf0SYan Zheng (void) ip_mc_add_src(in_dev, &msf->imsf_multiaddr, 24178713dbf0SYan Zheng msf->imsf_fmode, 0, NULL, 0); 24188713dbf0SYan Zheng } 24191d7138deSEric Dumazet psl = rtnl_dereference(pmc->sflist); 24201da177e4SLinus Torvalds if (psl) { 24211da177e4SLinus Torvalds (void) ip_mc_del_src(in_dev, &msf->imsf_multiaddr, pmc->sfmode, 24221da177e4SLinus Torvalds psl->sl_count, psl->sl_addr, 0); 2423c85bb41eSFlavio Leitner /* decrease mem now to avoid the memleak warning */ 2424c85bb41eSFlavio Leitner atomic_sub(IP_SFLSIZE(psl->sl_max), &sk->sk_omem_alloc); 24257519cce4SLai Jiangshan kfree_rcu(psl, rcu); 24261da177e4SLinus Torvalds } else 24271da177e4SLinus Torvalds (void) ip_mc_del_src(in_dev, &msf->imsf_multiaddr, pmc->sfmode, 24281da177e4SLinus Torvalds 0, NULL, 0); 2429cf778b00SEric Dumazet rcu_assign_pointer(pmc->sflist, newpsl); 24301da177e4SLinus Torvalds pmc->sfmode = msf->imsf_fmode; 2431917f2f10SDavid L Stevens err = 0; 24321da177e4SLinus Torvalds done: 24339951f036SDavid L Stevens if (leavegroup) 24349951f036SDavid L Stevens err = ip_mc_leave_group(sk, &imr); 24351da177e4SLinus Torvalds return err; 24361da177e4SLinus Torvalds } 24371da177e4SLinus Torvalds 24381da177e4SLinus Torvalds int ip_mc_msfget(struct sock *sk, struct ip_msfilter *msf, 24391da177e4SLinus Torvalds struct ip_msfilter __user *optval, int __user *optlen) 24401da177e4SLinus Torvalds { 24411da177e4SLinus Torvalds int err, len, count, copycount; 24421da177e4SLinus Torvalds struct ip_mreqn imr; 244363007727SAl Viro __be32 addr = msf->imsf_multiaddr; 24441da177e4SLinus Torvalds struct ip_mc_socklist *pmc; 24451da177e4SLinus Torvalds struct in_device *in_dev; 24461da177e4SLinus Torvalds struct inet_sock *inet = inet_sk(sk); 24471da177e4SLinus Torvalds struct ip_sf_socklist *psl; 2448877acedcSDaniel Lezcano struct net *net = sock_net(sk); 24491da177e4SLinus Torvalds 245087e9f031SWANG Cong ASSERT_RTNL(); 245187e9f031SWANG Cong 2452f97c1e0cSJoe Perches if (!ipv4_is_multicast(addr)) 24531da177e4SLinus Torvalds return -EINVAL; 24541da177e4SLinus Torvalds 24551da177e4SLinus Torvalds imr.imr_multiaddr.s_addr = msf->imsf_multiaddr; 24561da177e4SLinus Torvalds imr.imr_address.s_addr = msf->imsf_interface; 24571da177e4SLinus Torvalds imr.imr_ifindex = 0; 2458877acedcSDaniel Lezcano in_dev = ip_mc_find_dev(net, &imr); 24591da177e4SLinus Torvalds 24601da177e4SLinus Torvalds if (!in_dev) { 24611da177e4SLinus Torvalds err = -ENODEV; 24621da177e4SLinus Torvalds goto done; 24631da177e4SLinus Torvalds } 24641da177e4SLinus Torvalds err = -EADDRNOTAVAIL; 24651da177e4SLinus Torvalds 24661d7138deSEric Dumazet for_each_pmc_rtnl(inet, pmc) { 24671da177e4SLinus Torvalds if (pmc->multi.imr_multiaddr.s_addr == msf->imsf_multiaddr && 24681da177e4SLinus Torvalds pmc->multi.imr_ifindex == imr.imr_ifindex) 24691da177e4SLinus Torvalds break; 24701da177e4SLinus Torvalds } 24711da177e4SLinus Torvalds if (!pmc) /* must have a prior join */ 24721da177e4SLinus Torvalds goto done; 24731da177e4SLinus Torvalds msf->imsf_fmode = pmc->sfmode; 24741d7138deSEric Dumazet psl = rtnl_dereference(pmc->sflist); 24751da177e4SLinus Torvalds if (!psl) { 24761da177e4SLinus Torvalds len = 0; 24771da177e4SLinus Torvalds count = 0; 24781da177e4SLinus Torvalds } else { 24791da177e4SLinus Torvalds count = psl->sl_count; 24801da177e4SLinus Torvalds } 24811da177e4SLinus Torvalds copycount = count < msf->imsf_numsrc ? count : msf->imsf_numsrc; 24821da177e4SLinus Torvalds len = copycount * sizeof(psl->sl_addr[0]); 24831da177e4SLinus Torvalds msf->imsf_numsrc = count; 24841da177e4SLinus Torvalds if (put_user(IP_MSFILTER_SIZE(copycount), optlen) || 24851da177e4SLinus Torvalds copy_to_user(optval, msf, IP_MSFILTER_SIZE(0))) { 24861da177e4SLinus Torvalds return -EFAULT; 24871da177e4SLinus Torvalds } 24881da177e4SLinus Torvalds if (len && 24891da177e4SLinus Torvalds copy_to_user(&optval->imsf_slist[0], psl->sl_addr, len)) 24901da177e4SLinus Torvalds return -EFAULT; 24911da177e4SLinus Torvalds return 0; 24921da177e4SLinus Torvalds done: 24931da177e4SLinus Torvalds return err; 24941da177e4SLinus Torvalds } 24951da177e4SLinus Torvalds 24961da177e4SLinus Torvalds int ip_mc_gsfget(struct sock *sk, struct group_filter *gsf, 24971da177e4SLinus Torvalds struct group_filter __user *optval, int __user *optlen) 24981da177e4SLinus Torvalds { 24991da177e4SLinus Torvalds int err, i, count, copycount; 25001da177e4SLinus Torvalds struct sockaddr_in *psin; 250163007727SAl Viro __be32 addr; 25021da177e4SLinus Torvalds struct ip_mc_socklist *pmc; 25031da177e4SLinus Torvalds struct inet_sock *inet = inet_sk(sk); 25041da177e4SLinus Torvalds struct ip_sf_socklist *psl; 25051da177e4SLinus Torvalds 250687e9f031SWANG Cong ASSERT_RTNL(); 250787e9f031SWANG Cong 25081da177e4SLinus Torvalds psin = (struct sockaddr_in *)&gsf->gf_group; 25091da177e4SLinus Torvalds if (psin->sin_family != AF_INET) 25101da177e4SLinus Torvalds return -EINVAL; 25111da177e4SLinus Torvalds addr = psin->sin_addr.s_addr; 2512f97c1e0cSJoe Perches if (!ipv4_is_multicast(addr)) 25131da177e4SLinus Torvalds return -EINVAL; 25141da177e4SLinus Torvalds 25151da177e4SLinus Torvalds err = -EADDRNOTAVAIL; 25161da177e4SLinus Torvalds 25171d7138deSEric Dumazet for_each_pmc_rtnl(inet, pmc) { 25181da177e4SLinus Torvalds if (pmc->multi.imr_multiaddr.s_addr == addr && 25191da177e4SLinus Torvalds pmc->multi.imr_ifindex == gsf->gf_interface) 25201da177e4SLinus Torvalds break; 25211da177e4SLinus Torvalds } 25221da177e4SLinus Torvalds if (!pmc) /* must have a prior join */ 25231da177e4SLinus Torvalds goto done; 25241da177e4SLinus Torvalds gsf->gf_fmode = pmc->sfmode; 25251d7138deSEric Dumazet psl = rtnl_dereference(pmc->sflist); 25261da177e4SLinus Torvalds count = psl ? psl->sl_count : 0; 25271da177e4SLinus Torvalds copycount = count < gsf->gf_numsrc ? count : gsf->gf_numsrc; 25281da177e4SLinus Torvalds gsf->gf_numsrc = count; 25291da177e4SLinus Torvalds if (put_user(GROUP_FILTER_SIZE(copycount), optlen) || 25301da177e4SLinus Torvalds copy_to_user(optval, gsf, GROUP_FILTER_SIZE(0))) { 25311da177e4SLinus Torvalds return -EFAULT; 25321da177e4SLinus Torvalds } 25331da177e4SLinus Torvalds for (i = 0; i < copycount; i++) { 25341da177e4SLinus Torvalds struct sockaddr_storage ss; 25351da177e4SLinus Torvalds 25361da177e4SLinus Torvalds psin = (struct sockaddr_in *)&ss; 25371da177e4SLinus Torvalds memset(&ss, 0, sizeof(ss)); 25381da177e4SLinus Torvalds psin->sin_family = AF_INET; 25391da177e4SLinus Torvalds psin->sin_addr.s_addr = psl->sl_addr[i]; 25401da177e4SLinus Torvalds if (copy_to_user(&optval->gf_slist[i], &ss, sizeof(ss))) 25411da177e4SLinus Torvalds return -EFAULT; 25421da177e4SLinus Torvalds } 25431da177e4SLinus Torvalds return 0; 25441da177e4SLinus Torvalds done: 25451da177e4SLinus Torvalds return err; 25461da177e4SLinus Torvalds } 25471da177e4SLinus Torvalds 25481da177e4SLinus Torvalds /* 25491da177e4SLinus Torvalds * check if a multicast source filter allows delivery for a given <src,dst,intf> 25501da177e4SLinus Torvalds */ 2551c0cda068SAl Viro int ip_mc_sf_allow(struct sock *sk, __be32 loc_addr, __be32 rmt_addr, int dif) 25521da177e4SLinus Torvalds { 25531da177e4SLinus Torvalds struct inet_sock *inet = inet_sk(sk); 25541da177e4SLinus Torvalds struct ip_mc_socklist *pmc; 25551da177e4SLinus Torvalds struct ip_sf_socklist *psl; 25561da177e4SLinus Torvalds int i; 2557c85bb41eSFlavio Leitner int ret; 25581da177e4SLinus Torvalds 2559c85bb41eSFlavio Leitner ret = 1; 2560f97c1e0cSJoe Perches if (!ipv4_is_multicast(loc_addr)) 2561c85bb41eSFlavio Leitner goto out; 25621da177e4SLinus Torvalds 2563c85bb41eSFlavio Leitner rcu_read_lock(); 25641d7138deSEric Dumazet for_each_pmc_rcu(inet, pmc) { 25651da177e4SLinus Torvalds if (pmc->multi.imr_multiaddr.s_addr == loc_addr && 25661da177e4SLinus Torvalds pmc->multi.imr_ifindex == dif) 25671da177e4SLinus Torvalds break; 25681da177e4SLinus Torvalds } 2569c85bb41eSFlavio Leitner ret = inet->mc_all; 25701da177e4SLinus Torvalds if (!pmc) 2571c85bb41eSFlavio Leitner goto unlock; 25721d7138deSEric Dumazet psl = rcu_dereference(pmc->sflist); 2573c85bb41eSFlavio Leitner ret = (pmc->sfmode == MCAST_EXCLUDE); 25741da177e4SLinus Torvalds if (!psl) 2575c85bb41eSFlavio Leitner goto unlock; 25761da177e4SLinus Torvalds 25771da177e4SLinus Torvalds for (i = 0; i < psl->sl_count; i++) { 25781da177e4SLinus Torvalds if (psl->sl_addr[i] == rmt_addr) 25791da177e4SLinus Torvalds break; 25801da177e4SLinus Torvalds } 2581c85bb41eSFlavio Leitner ret = 0; 25821da177e4SLinus Torvalds if (pmc->sfmode == MCAST_INCLUDE && i >= psl->sl_count) 2583c85bb41eSFlavio Leitner goto unlock; 25841da177e4SLinus Torvalds if (pmc->sfmode == MCAST_EXCLUDE && i < psl->sl_count) 2585c85bb41eSFlavio Leitner goto unlock; 2586c85bb41eSFlavio Leitner ret = 1; 2587c85bb41eSFlavio Leitner unlock: 2588c85bb41eSFlavio Leitner rcu_read_unlock(); 2589c85bb41eSFlavio Leitner out: 2590c85bb41eSFlavio Leitner return ret; 25911da177e4SLinus Torvalds } 25921da177e4SLinus Torvalds 25931da177e4SLinus Torvalds /* 25941da177e4SLinus Torvalds * A socket is closing. 25951da177e4SLinus Torvalds */ 25961da177e4SLinus Torvalds 25971da177e4SLinus Torvalds void ip_mc_drop_socket(struct sock *sk) 25981da177e4SLinus Torvalds { 25991da177e4SLinus Torvalds struct inet_sock *inet = inet_sk(sk); 26001da177e4SLinus Torvalds struct ip_mc_socklist *iml; 2601877acedcSDaniel Lezcano struct net *net = sock_net(sk); 26021da177e4SLinus Torvalds 260351456b29SIan Morris if (!inet->mc_list) 26041da177e4SLinus Torvalds return; 26051da177e4SLinus Torvalds 26061da177e4SLinus Torvalds rtnl_lock(); 26071d7138deSEric Dumazet while ((iml = rtnl_dereference(inet->mc_list)) != NULL) { 26081da177e4SLinus Torvalds struct in_device *in_dev; 26091da177e4SLinus Torvalds 26101d7138deSEric Dumazet inet->mc_list = iml->next_rcu; 2611877acedcSDaniel Lezcano in_dev = inetdev_by_index(net, iml->multi.imr_ifindex); 26121da177e4SLinus Torvalds (void) ip_mc_leave_src(sk, iml, in_dev); 261300db4124SIan Morris if (in_dev) 26141da177e4SLinus Torvalds ip_mc_dec_group(in_dev, iml->multi.imr_multiaddr.s_addr); 2615c85bb41eSFlavio Leitner /* decrease mem now to avoid the memleak warning */ 2616c85bb41eSFlavio Leitner atomic_sub(sizeof(*iml), &sk->sk_omem_alloc); 261710d50e74SLai Jiangshan kfree_rcu(iml, rcu); 26181da177e4SLinus Torvalds } 26191da177e4SLinus Torvalds rtnl_unlock(); 26201da177e4SLinus Torvalds } 26211da177e4SLinus Torvalds 2622dbdd9a52SDavid S. Miller /* called with rcu_read_lock() */ 26232094acbbSAlexander Duyck int ip_check_mc_rcu(struct in_device *in_dev, __be32 mc_addr, __be32 src_addr, u8 proto) 26241da177e4SLinus Torvalds { 26251da177e4SLinus Torvalds struct ip_mc_list *im; 2626e9897071SEric Dumazet struct ip_mc_list __rcu **mc_hash; 26271da177e4SLinus Torvalds struct ip_sf_list *psf; 26281da177e4SLinus Torvalds int rv = 0; 26291da177e4SLinus Torvalds 2630e9897071SEric Dumazet mc_hash = rcu_dereference(in_dev->mc_hash); 2631e9897071SEric Dumazet if (mc_hash) { 2632c70eba74SEric Dumazet u32 hash = hash_32((__force u32)mc_addr, MC_HASH_SZ_LOG); 2633e9897071SEric Dumazet 2634e9897071SEric Dumazet for (im = rcu_dereference(mc_hash[hash]); 2635e9897071SEric Dumazet im != NULL; 2636e9897071SEric Dumazet im = rcu_dereference(im->next_hash)) { 2637e9897071SEric Dumazet if (im->multiaddr == mc_addr) 2638e9897071SEric Dumazet break; 2639e9897071SEric Dumazet } 2640e9897071SEric Dumazet } else { 26411d7138deSEric Dumazet for_each_pmc_rcu(in_dev, im) { 26421da177e4SLinus Torvalds if (im->multiaddr == mc_addr) 26431da177e4SLinus Torvalds break; 26441da177e4SLinus Torvalds } 2645e9897071SEric Dumazet } 26461da177e4SLinus Torvalds if (im && proto == IPPROTO_IGMP) { 26471da177e4SLinus Torvalds rv = 1; 26481da177e4SLinus Torvalds } else if (im) { 26491da177e4SLinus Torvalds if (src_addr) { 26501da177e4SLinus Torvalds for (psf = im->sources; psf; psf = psf->sf_next) { 26511da177e4SLinus Torvalds if (psf->sf_inaddr == src_addr) 26521da177e4SLinus Torvalds break; 26531da177e4SLinus Torvalds } 26541da177e4SLinus Torvalds if (psf) 26551da177e4SLinus Torvalds rv = psf->sf_count[MCAST_INCLUDE] || 26561da177e4SLinus Torvalds psf->sf_count[MCAST_EXCLUDE] != 26571da177e4SLinus Torvalds im->sfcount[MCAST_EXCLUDE]; 26581da177e4SLinus Torvalds else 26591da177e4SLinus Torvalds rv = im->sfcount[MCAST_EXCLUDE] != 0; 26601da177e4SLinus Torvalds } else 26611da177e4SLinus Torvalds rv = 1; /* unspecified source; tentatively allow */ 26621da177e4SLinus Torvalds } 26631da177e4SLinus Torvalds return rv; 26641da177e4SLinus Torvalds } 26651da177e4SLinus Torvalds 26661da177e4SLinus Torvalds #if defined(CONFIG_PROC_FS) 26671da177e4SLinus Torvalds struct igmp_mc_iter_state { 26687091e728SAlexey Dobriyan struct seq_net_private p; 26691da177e4SLinus Torvalds struct net_device *dev; 26701da177e4SLinus Torvalds struct in_device *in_dev; 26711da177e4SLinus Torvalds }; 26721da177e4SLinus Torvalds 26731da177e4SLinus Torvalds #define igmp_mc_seq_private(seq) ((struct igmp_mc_iter_state *)(seq)->private) 26741da177e4SLinus Torvalds 26751da177e4SLinus Torvalds static inline struct ip_mc_list *igmp_mc_get_first(struct seq_file *seq) 26761da177e4SLinus Torvalds { 26777091e728SAlexey Dobriyan struct net *net = seq_file_net(seq); 26781da177e4SLinus Torvalds struct ip_mc_list *im = NULL; 26791da177e4SLinus Torvalds struct igmp_mc_iter_state *state = igmp_mc_seq_private(seq); 26801da177e4SLinus Torvalds 26817562f876SPavel Emelianov state->in_dev = NULL; 268261fbab77Sstephen hemminger for_each_netdev_rcu(net, state->dev) { 26831da177e4SLinus Torvalds struct in_device *in_dev; 26846baff150SEric Dumazet 26856baff150SEric Dumazet in_dev = __in_dev_get_rcu(state->dev); 26861da177e4SLinus Torvalds if (!in_dev) 26871da177e4SLinus Torvalds continue; 26881d7138deSEric Dumazet im = rcu_dereference(in_dev->mc_list); 26891da177e4SLinus Torvalds if (im) { 26901da177e4SLinus Torvalds state->in_dev = in_dev; 26911da177e4SLinus Torvalds break; 26921da177e4SLinus Torvalds } 26931da177e4SLinus Torvalds } 26941da177e4SLinus Torvalds return im; 26951da177e4SLinus Torvalds } 26961da177e4SLinus Torvalds 26971da177e4SLinus Torvalds static struct ip_mc_list *igmp_mc_get_next(struct seq_file *seq, struct ip_mc_list *im) 26981da177e4SLinus Torvalds { 26991da177e4SLinus Torvalds struct igmp_mc_iter_state *state = igmp_mc_seq_private(seq); 27006baff150SEric Dumazet 27011d7138deSEric Dumazet im = rcu_dereference(im->next_rcu); 27021d7138deSEric Dumazet while (!im) { 27036baff150SEric Dumazet state->dev = next_net_device_rcu(state->dev); 27041da177e4SLinus Torvalds if (!state->dev) { 27051da177e4SLinus Torvalds state->in_dev = NULL; 27061da177e4SLinus Torvalds break; 27071da177e4SLinus Torvalds } 27086baff150SEric Dumazet state->in_dev = __in_dev_get_rcu(state->dev); 27091da177e4SLinus Torvalds if (!state->in_dev) 27101da177e4SLinus Torvalds continue; 27111d7138deSEric Dumazet im = rcu_dereference(state->in_dev->mc_list); 27121da177e4SLinus Torvalds } 27131da177e4SLinus Torvalds return im; 27141da177e4SLinus Torvalds } 27151da177e4SLinus Torvalds 27161da177e4SLinus Torvalds static struct ip_mc_list *igmp_mc_get_idx(struct seq_file *seq, loff_t pos) 27171da177e4SLinus Torvalds { 27181da177e4SLinus Torvalds struct ip_mc_list *im = igmp_mc_get_first(seq); 27191da177e4SLinus Torvalds if (im) 27201da177e4SLinus Torvalds while (pos && (im = igmp_mc_get_next(seq, im)) != NULL) 27211da177e4SLinus Torvalds --pos; 27221da177e4SLinus Torvalds return pos ? NULL : im; 27231da177e4SLinus Torvalds } 27241da177e4SLinus Torvalds 27251da177e4SLinus Torvalds static void *igmp_mc_seq_start(struct seq_file *seq, loff_t *pos) 272661fbab77Sstephen hemminger __acquires(rcu) 27271da177e4SLinus Torvalds { 272861fbab77Sstephen hemminger rcu_read_lock(); 27291da177e4SLinus Torvalds return *pos ? igmp_mc_get_idx(seq, *pos - 1) : SEQ_START_TOKEN; 27301da177e4SLinus Torvalds } 27311da177e4SLinus Torvalds 27321da177e4SLinus Torvalds static void *igmp_mc_seq_next(struct seq_file *seq, void *v, loff_t *pos) 27331da177e4SLinus Torvalds { 27341da177e4SLinus Torvalds struct ip_mc_list *im; 27351da177e4SLinus Torvalds if (v == SEQ_START_TOKEN) 27361da177e4SLinus Torvalds im = igmp_mc_get_first(seq); 27371da177e4SLinus Torvalds else 27381da177e4SLinus Torvalds im = igmp_mc_get_next(seq, v); 27391da177e4SLinus Torvalds ++*pos; 27401da177e4SLinus Torvalds return im; 27411da177e4SLinus Torvalds } 27421da177e4SLinus Torvalds 27431da177e4SLinus Torvalds static void igmp_mc_seq_stop(struct seq_file *seq, void *v) 274461fbab77Sstephen hemminger __releases(rcu) 27451da177e4SLinus Torvalds { 27461da177e4SLinus Torvalds struct igmp_mc_iter_state *state = igmp_mc_seq_private(seq); 27471d7138deSEric Dumazet 27481da177e4SLinus Torvalds state->in_dev = NULL; 27491da177e4SLinus Torvalds state->dev = NULL; 275061fbab77Sstephen hemminger rcu_read_unlock(); 27511da177e4SLinus Torvalds } 27521da177e4SLinus Torvalds 27531da177e4SLinus Torvalds static int igmp_mc_seq_show(struct seq_file *seq, void *v) 27541da177e4SLinus Torvalds { 27551da177e4SLinus Torvalds if (v == SEQ_START_TOKEN) 27561da177e4SLinus Torvalds seq_puts(seq, 27571da177e4SLinus Torvalds "Idx\tDevice : Count Querier\tGroup Users Timer\tReporter\n"); 27581da177e4SLinus Torvalds else { 27591da177e4SLinus Torvalds struct ip_mc_list *im = (struct ip_mc_list *)v; 27601da177e4SLinus Torvalds struct igmp_mc_iter_state *state = igmp_mc_seq_private(seq); 27611da177e4SLinus Torvalds char *querier; 2762a399a805SEric Dumazet long delta; 2763a399a805SEric Dumazet 27641da177e4SLinus Torvalds #ifdef CONFIG_IP_MULTICAST 27651da177e4SLinus Torvalds querier = IGMP_V1_SEEN(state->in_dev) ? "V1" : 27661da177e4SLinus Torvalds IGMP_V2_SEEN(state->in_dev) ? "V2" : 27671da177e4SLinus Torvalds "V3"; 27681da177e4SLinus Torvalds #else 27691da177e4SLinus Torvalds querier = "NONE"; 27701da177e4SLinus Torvalds #endif 27711da177e4SLinus Torvalds 2772e6b68883SAndreea-Cristina Bernat if (rcu_access_pointer(state->in_dev->mc_list) == im) { 27731da177e4SLinus Torvalds seq_printf(seq, "%d\t%-10s: %5d %7s\n", 2774b8bae41eSRami Rosen state->dev->ifindex, state->dev->name, state->in_dev->mc_count, querier); 27751da177e4SLinus Torvalds } 27761da177e4SLinus Torvalds 2777a399a805SEric Dumazet delta = im->timer.expires - jiffies; 27781da177e4SLinus Torvalds seq_printf(seq, 2779338fcf98SAlexey Dobriyan "\t\t\t\t%08X %5d %d:%08lX\t\t%d\n", 27801da177e4SLinus Torvalds im->multiaddr, im->users, 2781a399a805SEric Dumazet im->tm_running, 2782a399a805SEric Dumazet im->tm_running ? jiffies_delta_to_clock_t(delta) : 0, 27831da177e4SLinus Torvalds im->reporter); 27841da177e4SLinus Torvalds } 27851da177e4SLinus Torvalds return 0; 27861da177e4SLinus Torvalds } 27871da177e4SLinus Torvalds 2788f690808eSStephen Hemminger static const struct seq_operations igmp_mc_seq_ops = { 27891da177e4SLinus Torvalds .start = igmp_mc_seq_start, 27901da177e4SLinus Torvalds .next = igmp_mc_seq_next, 27911da177e4SLinus Torvalds .stop = igmp_mc_seq_stop, 27921da177e4SLinus Torvalds .show = igmp_mc_seq_show, 27931da177e4SLinus Torvalds }; 27941da177e4SLinus Torvalds 27951da177e4SLinus Torvalds static int igmp_mc_seq_open(struct inode *inode, struct file *file) 27961da177e4SLinus Torvalds { 27977091e728SAlexey Dobriyan return seq_open_net(inode, file, &igmp_mc_seq_ops, 2798cf7732e4SPavel Emelyanov sizeof(struct igmp_mc_iter_state)); 27991da177e4SLinus Torvalds } 28001da177e4SLinus Torvalds 28019a32144eSArjan van de Ven static const struct file_operations igmp_mc_seq_fops = { 28021da177e4SLinus Torvalds .owner = THIS_MODULE, 28031da177e4SLinus Torvalds .open = igmp_mc_seq_open, 28041da177e4SLinus Torvalds .read = seq_read, 28051da177e4SLinus Torvalds .llseek = seq_lseek, 28067091e728SAlexey Dobriyan .release = seq_release_net, 28071da177e4SLinus Torvalds }; 28081da177e4SLinus Torvalds 28091da177e4SLinus Torvalds struct igmp_mcf_iter_state { 28107091e728SAlexey Dobriyan struct seq_net_private p; 28111da177e4SLinus Torvalds struct net_device *dev; 28121da177e4SLinus Torvalds struct in_device *idev; 28131da177e4SLinus Torvalds struct ip_mc_list *im; 28141da177e4SLinus Torvalds }; 28151da177e4SLinus Torvalds 28161da177e4SLinus Torvalds #define igmp_mcf_seq_private(seq) ((struct igmp_mcf_iter_state *)(seq)->private) 28171da177e4SLinus Torvalds 28181da177e4SLinus Torvalds static inline struct ip_sf_list *igmp_mcf_get_first(struct seq_file *seq) 28191da177e4SLinus Torvalds { 28207091e728SAlexey Dobriyan struct net *net = seq_file_net(seq); 28211da177e4SLinus Torvalds struct ip_sf_list *psf = NULL; 28221da177e4SLinus Torvalds struct ip_mc_list *im = NULL; 28231da177e4SLinus Torvalds struct igmp_mcf_iter_state *state = igmp_mcf_seq_private(seq); 28241da177e4SLinus Torvalds 28257562f876SPavel Emelianov state->idev = NULL; 28267562f876SPavel Emelianov state->im = NULL; 282761fbab77Sstephen hemminger for_each_netdev_rcu(net, state->dev) { 28281da177e4SLinus Torvalds struct in_device *idev; 28296baff150SEric Dumazet idev = __in_dev_get_rcu(state->dev); 283051456b29SIan Morris if (unlikely(!idev)) 28311da177e4SLinus Torvalds continue; 28321d7138deSEric Dumazet im = rcu_dereference(idev->mc_list); 283300db4124SIan Morris if (likely(im)) { 28341da177e4SLinus Torvalds spin_lock_bh(&im->lock); 28351da177e4SLinus Torvalds psf = im->sources; 283600db4124SIan Morris if (likely(psf)) { 28371da177e4SLinus Torvalds state->im = im; 28381da177e4SLinus Torvalds state->idev = idev; 28391da177e4SLinus Torvalds break; 28401da177e4SLinus Torvalds } 28411da177e4SLinus Torvalds spin_unlock_bh(&im->lock); 28421da177e4SLinus Torvalds } 28431da177e4SLinus Torvalds } 28441da177e4SLinus Torvalds return psf; 28451da177e4SLinus Torvalds } 28461da177e4SLinus Torvalds 28471da177e4SLinus Torvalds static struct ip_sf_list *igmp_mcf_get_next(struct seq_file *seq, struct ip_sf_list *psf) 28481da177e4SLinus Torvalds { 28491da177e4SLinus Torvalds struct igmp_mcf_iter_state *state = igmp_mcf_seq_private(seq); 28501da177e4SLinus Torvalds 28511da177e4SLinus Torvalds psf = psf->sf_next; 28521da177e4SLinus Torvalds while (!psf) { 28531da177e4SLinus Torvalds spin_unlock_bh(&state->im->lock); 28541da177e4SLinus Torvalds state->im = state->im->next; 28551da177e4SLinus Torvalds while (!state->im) { 28566baff150SEric Dumazet state->dev = next_net_device_rcu(state->dev); 28571da177e4SLinus Torvalds if (!state->dev) { 28581da177e4SLinus Torvalds state->idev = NULL; 28591da177e4SLinus Torvalds goto out; 28601da177e4SLinus Torvalds } 28616baff150SEric Dumazet state->idev = __in_dev_get_rcu(state->dev); 28621da177e4SLinus Torvalds if (!state->idev) 28631da177e4SLinus Torvalds continue; 28641d7138deSEric Dumazet state->im = rcu_dereference(state->idev->mc_list); 28651da177e4SLinus Torvalds } 28661da177e4SLinus Torvalds if (!state->im) 28671da177e4SLinus Torvalds break; 28681da177e4SLinus Torvalds spin_lock_bh(&state->im->lock); 28691da177e4SLinus Torvalds psf = state->im->sources; 28701da177e4SLinus Torvalds } 28711da177e4SLinus Torvalds out: 28721da177e4SLinus Torvalds return psf; 28731da177e4SLinus Torvalds } 28741da177e4SLinus Torvalds 28751da177e4SLinus Torvalds static struct ip_sf_list *igmp_mcf_get_idx(struct seq_file *seq, loff_t pos) 28761da177e4SLinus Torvalds { 28771da177e4SLinus Torvalds struct ip_sf_list *psf = igmp_mcf_get_first(seq); 28781da177e4SLinus Torvalds if (psf) 28791da177e4SLinus Torvalds while (pos && (psf = igmp_mcf_get_next(seq, psf)) != NULL) 28801da177e4SLinus Torvalds --pos; 28811da177e4SLinus Torvalds return pos ? NULL : psf; 28821da177e4SLinus Torvalds } 28831da177e4SLinus Torvalds 28841da177e4SLinus Torvalds static void *igmp_mcf_seq_start(struct seq_file *seq, loff_t *pos) 288561fbab77Sstephen hemminger __acquires(rcu) 28861da177e4SLinus Torvalds { 288761fbab77Sstephen hemminger rcu_read_lock(); 28881da177e4SLinus Torvalds return *pos ? igmp_mcf_get_idx(seq, *pos - 1) : SEQ_START_TOKEN; 28891da177e4SLinus Torvalds } 28901da177e4SLinus Torvalds 28911da177e4SLinus Torvalds static void *igmp_mcf_seq_next(struct seq_file *seq, void *v, loff_t *pos) 28921da177e4SLinus Torvalds { 28931da177e4SLinus Torvalds struct ip_sf_list *psf; 28941da177e4SLinus Torvalds if (v == SEQ_START_TOKEN) 28951da177e4SLinus Torvalds psf = igmp_mcf_get_first(seq); 28961da177e4SLinus Torvalds else 28971da177e4SLinus Torvalds psf = igmp_mcf_get_next(seq, v); 28981da177e4SLinus Torvalds ++*pos; 28991da177e4SLinus Torvalds return psf; 29001da177e4SLinus Torvalds } 29011da177e4SLinus Torvalds 29021da177e4SLinus Torvalds static void igmp_mcf_seq_stop(struct seq_file *seq, void *v) 290361fbab77Sstephen hemminger __releases(rcu) 29041da177e4SLinus Torvalds { 29051da177e4SLinus Torvalds struct igmp_mcf_iter_state *state = igmp_mcf_seq_private(seq); 290600db4124SIan Morris if (likely(state->im)) { 29071da177e4SLinus Torvalds spin_unlock_bh(&state->im->lock); 29081da177e4SLinus Torvalds state->im = NULL; 29091da177e4SLinus Torvalds } 29101da177e4SLinus Torvalds state->idev = NULL; 29111da177e4SLinus Torvalds state->dev = NULL; 291261fbab77Sstephen hemminger rcu_read_unlock(); 29131da177e4SLinus Torvalds } 29141da177e4SLinus Torvalds 29151da177e4SLinus Torvalds static int igmp_mcf_seq_show(struct seq_file *seq, void *v) 29161da177e4SLinus Torvalds { 29171da177e4SLinus Torvalds struct ip_sf_list *psf = (struct ip_sf_list *)v; 29181da177e4SLinus Torvalds struct igmp_mcf_iter_state *state = igmp_mcf_seq_private(seq); 29191da177e4SLinus Torvalds 29201da177e4SLinus Torvalds if (v == SEQ_START_TOKEN) { 29211744bea1SJoe Perches seq_puts(seq, "Idx Device MCA SRC INC EXC\n"); 29221da177e4SLinus Torvalds } else { 29231da177e4SLinus Torvalds seq_printf(seq, 29241da177e4SLinus Torvalds "%3d %6.6s 0x%08x " 29251da177e4SLinus Torvalds "0x%08x %6lu %6lu\n", 29261da177e4SLinus Torvalds state->dev->ifindex, state->dev->name, 29271da177e4SLinus Torvalds ntohl(state->im->multiaddr), 29281da177e4SLinus Torvalds ntohl(psf->sf_inaddr), 29291da177e4SLinus Torvalds psf->sf_count[MCAST_INCLUDE], 29301da177e4SLinus Torvalds psf->sf_count[MCAST_EXCLUDE]); 29311da177e4SLinus Torvalds } 29321da177e4SLinus Torvalds return 0; 29331da177e4SLinus Torvalds } 29341da177e4SLinus Torvalds 2935f690808eSStephen Hemminger static const struct seq_operations igmp_mcf_seq_ops = { 29361da177e4SLinus Torvalds .start = igmp_mcf_seq_start, 29371da177e4SLinus Torvalds .next = igmp_mcf_seq_next, 29381da177e4SLinus Torvalds .stop = igmp_mcf_seq_stop, 29391da177e4SLinus Torvalds .show = igmp_mcf_seq_show, 29401da177e4SLinus Torvalds }; 29411da177e4SLinus Torvalds 29421da177e4SLinus Torvalds static int igmp_mcf_seq_open(struct inode *inode, struct file *file) 29431da177e4SLinus Torvalds { 29447091e728SAlexey Dobriyan return seq_open_net(inode, file, &igmp_mcf_seq_ops, 2945cf7732e4SPavel Emelyanov sizeof(struct igmp_mcf_iter_state)); 29461da177e4SLinus Torvalds } 29471da177e4SLinus Torvalds 29489a32144eSArjan van de Ven static const struct file_operations igmp_mcf_seq_fops = { 29491da177e4SLinus Torvalds .owner = THIS_MODULE, 29501da177e4SLinus Torvalds .open = igmp_mcf_seq_open, 29511da177e4SLinus Torvalds .read = seq_read, 29521da177e4SLinus Torvalds .llseek = seq_lseek, 29537091e728SAlexey Dobriyan .release = seq_release_net, 29547091e728SAlexey Dobriyan }; 29557091e728SAlexey Dobriyan 29562c8c1e72SAlexey Dobriyan static int __net_init igmp_net_init(struct net *net) 29577091e728SAlexey Dobriyan { 29587091e728SAlexey Dobriyan struct proc_dir_entry *pde; 295993a714d6SMadhu Challa int err; 29607091e728SAlexey Dobriyan 2961d4beaa66SGao feng pde = proc_create("igmp", S_IRUGO, net->proc_net, &igmp_mc_seq_fops); 29627091e728SAlexey Dobriyan if (!pde) 29637091e728SAlexey Dobriyan goto out_igmp; 2964d4beaa66SGao feng pde = proc_create("mcfilter", S_IRUGO, net->proc_net, 2965d4beaa66SGao feng &igmp_mcf_seq_fops); 29667091e728SAlexey Dobriyan if (!pde) 29677091e728SAlexey Dobriyan goto out_mcfilter; 296893a714d6SMadhu Challa err = inet_ctl_sock_create(&net->ipv4.mc_autojoin_sk, AF_INET, 296993a714d6SMadhu Challa SOCK_DGRAM, 0, net); 297093a714d6SMadhu Challa if (err < 0) { 297193a714d6SMadhu Challa pr_err("Failed to initialize the IGMP autojoin socket (err %d)\n", 297293a714d6SMadhu Challa err); 297393a714d6SMadhu Challa goto out_sock; 297493a714d6SMadhu Challa } 297593a714d6SMadhu Challa 2976dcd87999SNikolay Borisov /* Sysctl initialization */ 2977dcd87999SNikolay Borisov net->ipv4.sysctl_igmp_max_memberships = 20; 2978dcd87999SNikolay Borisov net->ipv4.sysctl_igmp_max_msf = 10; 2979dcd87999SNikolay Borisov /* IGMP reports for link-local multicast groups are enabled by default */ 2980dcd87999SNikolay Borisov net->ipv4.sysctl_igmp_llm_reports = 1; 2981dcd87999SNikolay Borisov net->ipv4.sysctl_igmp_qrv = 2; 29827091e728SAlexey Dobriyan return 0; 29837091e728SAlexey Dobriyan 298493a714d6SMadhu Challa out_sock: 298593a714d6SMadhu Challa remove_proc_entry("mcfilter", net->proc_net); 29867091e728SAlexey Dobriyan out_mcfilter: 2987ece31ffdSGao feng remove_proc_entry("igmp", net->proc_net); 29887091e728SAlexey Dobriyan out_igmp: 29897091e728SAlexey Dobriyan return -ENOMEM; 29907091e728SAlexey Dobriyan } 29917091e728SAlexey Dobriyan 29922c8c1e72SAlexey Dobriyan static void __net_exit igmp_net_exit(struct net *net) 29937091e728SAlexey Dobriyan { 2994ece31ffdSGao feng remove_proc_entry("mcfilter", net->proc_net); 2995ece31ffdSGao feng remove_proc_entry("igmp", net->proc_net); 299693a714d6SMadhu Challa inet_ctl_sock_destroy(net->ipv4.mc_autojoin_sk); 29977091e728SAlexey Dobriyan } 29987091e728SAlexey Dobriyan 29997091e728SAlexey Dobriyan static struct pernet_operations igmp_net_ops = { 30007091e728SAlexey Dobriyan .init = igmp_net_init, 30017091e728SAlexey Dobriyan .exit = igmp_net_exit, 30021da177e4SLinus Torvalds }; 300372c1d3bdSWANG Cong #endif 30041da177e4SLinus Torvalds 30054aa5dee4SJiri Pirko static int igmp_netdev_event(struct notifier_block *this, 30064aa5dee4SJiri Pirko unsigned long event, void *ptr) 30074aa5dee4SJiri Pirko { 30084aa5dee4SJiri Pirko struct net_device *dev = netdev_notifier_info_to_dev(ptr); 30094aa5dee4SJiri Pirko struct in_device *in_dev; 30104aa5dee4SJiri Pirko 30114aa5dee4SJiri Pirko switch (event) { 30124aa5dee4SJiri Pirko case NETDEV_RESEND_IGMP: 30134aa5dee4SJiri Pirko in_dev = __in_dev_get_rtnl(dev); 30144aa5dee4SJiri Pirko if (in_dev) 30154aa5dee4SJiri Pirko ip_mc_rejoin_groups(in_dev); 30164aa5dee4SJiri Pirko break; 30174aa5dee4SJiri Pirko default: 30184aa5dee4SJiri Pirko break; 30194aa5dee4SJiri Pirko } 30204aa5dee4SJiri Pirko return NOTIFY_DONE; 30214aa5dee4SJiri Pirko } 30224aa5dee4SJiri Pirko 30234aa5dee4SJiri Pirko static struct notifier_block igmp_notifier = { 30244aa5dee4SJiri Pirko .notifier_call = igmp_netdev_event, 30254aa5dee4SJiri Pirko }; 30264aa5dee4SJiri Pirko 302772c1d3bdSWANG Cong int __init igmp_mc_init(void) 30281da177e4SLinus Torvalds { 302972c1d3bdSWANG Cong #if defined(CONFIG_PROC_FS) 30304aa5dee4SJiri Pirko int err; 30314aa5dee4SJiri Pirko 30324aa5dee4SJiri Pirko err = register_pernet_subsys(&igmp_net_ops); 30334aa5dee4SJiri Pirko if (err) 30344aa5dee4SJiri Pirko return err; 30354aa5dee4SJiri Pirko err = register_netdevice_notifier(&igmp_notifier); 30364aa5dee4SJiri Pirko if (err) 30374aa5dee4SJiri Pirko goto reg_notif_fail; 30384aa5dee4SJiri Pirko return 0; 30394aa5dee4SJiri Pirko 30404aa5dee4SJiri Pirko reg_notif_fail: 30414aa5dee4SJiri Pirko unregister_pernet_subsys(&igmp_net_ops); 30424aa5dee4SJiri Pirko return err; 304372c1d3bdSWANG Cong #else 304472c1d3bdSWANG Cong return register_netdevice_notifier(&igmp_notifier); 30451da177e4SLinus Torvalds #endif 304672c1d3bdSWANG Cong } 3047