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> 751da177e4SLinus Torvalds #include <asm/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> 1001da177e4SLinus Torvalds #include <linux/netfilter_ipv4.h> 1011da177e4SLinus Torvalds #ifdef CONFIG_IP_MROUTE 1021da177e4SLinus Torvalds #include <linux/mroute.h> 1031da177e4SLinus Torvalds #endif 1041da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS 1051da177e4SLinus Torvalds #include <linux/proc_fs.h> 1061da177e4SLinus Torvalds #include <linux/seq_file.h> 1071da177e4SLinus Torvalds #endif 1081da177e4SLinus Torvalds 1091da177e4SLinus Torvalds #define IP_MAX_MEMBERSHIPS 20 1101da177e4SLinus Torvalds #define IP_MAX_MSF 10 1111da177e4SLinus Torvalds 1121da177e4SLinus Torvalds #ifdef CONFIG_IP_MULTICAST 1131da177e4SLinus Torvalds /* Parameter names and values are taken from igmp-v2-06 draft */ 1141da177e4SLinus Torvalds 1151da177e4SLinus Torvalds #define IGMP_V1_Router_Present_Timeout (400*HZ) 1161da177e4SLinus Torvalds #define IGMP_V2_Router_Present_Timeout (400*HZ) 117*cab70040SWilliam Manley #define IGMP_V2_Unsolicited_Report_Interval (10*HZ) 118*cab70040SWilliam Manley #define IGMP_V3_Unsolicited_Report_Interval (1*HZ) 1191da177e4SLinus Torvalds #define IGMP_Query_Response_Interval (10*HZ) 1201da177e4SLinus Torvalds #define IGMP_Unsolicited_Report_Count 2 1211da177e4SLinus Torvalds 1221da177e4SLinus Torvalds 1231da177e4SLinus Torvalds #define IGMP_Initial_Report_Delay (1) 1241da177e4SLinus Torvalds 1251da177e4SLinus Torvalds /* IGMP_Initial_Report_Delay is not from IGMP specs! 1261da177e4SLinus Torvalds * IGMP specs require to report membership immediately after 1271da177e4SLinus Torvalds * joining a group, but we delay the first report by a 1281da177e4SLinus Torvalds * small interval. It seems more natural and still does not 1291da177e4SLinus Torvalds * contradict to specs provided this delay is small enough. 1301da177e4SLinus Torvalds */ 1311da177e4SLinus Torvalds 13242f811b8SHerbert Xu #define IGMP_V1_SEEN(in_dev) \ 133c346dca1SYOSHIFUJI Hideaki (IPV4_DEVCONF_ALL(dev_net(in_dev->dev), FORCE_IGMP_VERSION) == 1 || \ 13442f811b8SHerbert Xu IN_DEV_CONF_GET((in_dev), FORCE_IGMP_VERSION) == 1 || \ 1351da177e4SLinus Torvalds ((in_dev)->mr_v1_seen && \ 1361da177e4SLinus Torvalds time_before(jiffies, (in_dev)->mr_v1_seen))) 13742f811b8SHerbert Xu #define IGMP_V2_SEEN(in_dev) \ 138c346dca1SYOSHIFUJI Hideaki (IPV4_DEVCONF_ALL(dev_net(in_dev->dev), FORCE_IGMP_VERSION) == 2 || \ 13942f811b8SHerbert Xu IN_DEV_CONF_GET((in_dev), FORCE_IGMP_VERSION) == 2 || \ 1401da177e4SLinus Torvalds ((in_dev)->mr_v2_seen && \ 1411da177e4SLinus Torvalds time_before(jiffies, (in_dev)->mr_v2_seen))) 1421da177e4SLinus Torvalds 143*cab70040SWilliam Manley static int unsolicited_report_interval(struct in_device *in_dev) 144*cab70040SWilliam Manley { 145*cab70040SWilliam Manley if (IGMP_V1_SEEN(in_dev) || IGMP_V2_SEEN(in_dev)) 146*cab70040SWilliam Manley return IGMP_V2_Unsolicited_Report_Interval; 147*cab70040SWilliam Manley else /* v3 */ 148*cab70040SWilliam Manley return IGMP_V3_Unsolicited_Report_Interval; 149*cab70040SWilliam Manley } 150*cab70040SWilliam Manley 1511da177e4SLinus Torvalds static void igmpv3_add_delrec(struct in_device *in_dev, struct ip_mc_list *im); 15263007727SAl Viro static void igmpv3_del_delrec(struct in_device *in_dev, __be32 multiaddr); 1531da177e4SLinus Torvalds static void igmpv3_clear_delrec(struct in_device *in_dev); 1541da177e4SLinus Torvalds static int sf_setstate(struct ip_mc_list *pmc); 1551da177e4SLinus Torvalds static void sf_markstate(struct ip_mc_list *pmc); 1561da177e4SLinus Torvalds #endif 1571da177e4SLinus Torvalds static void ip_mc_clear_src(struct ip_mc_list *pmc); 1588f935bbdSAl Viro static int ip_mc_add_src(struct in_device *in_dev, __be32 *pmca, int sfmode, 1598f935bbdSAl Viro int sfcount, __be32 *psfsrc, int delta); 1601da177e4SLinus Torvalds 1611da177e4SLinus Torvalds static void ip_ma_put(struct ip_mc_list *im) 1621da177e4SLinus Torvalds { 1631da177e4SLinus Torvalds if (atomic_dec_and_test(&im->refcnt)) { 1641da177e4SLinus Torvalds in_dev_put(im->interface); 16542ea299dSLai Jiangshan kfree_rcu(im, rcu); 1661da177e4SLinus Torvalds } 1671da177e4SLinus Torvalds } 1681da177e4SLinus Torvalds 169d9aa9380SDavid S. Miller #define for_each_pmc_rcu(in_dev, pmc) \ 170d9aa9380SDavid S. Miller for (pmc = rcu_dereference(in_dev->mc_list); \ 171d9aa9380SDavid S. Miller pmc != NULL; \ 172d9aa9380SDavid S. Miller pmc = rcu_dereference(pmc->next_rcu)) 173d9aa9380SDavid S. Miller 174d9aa9380SDavid S. Miller #define for_each_pmc_rtnl(in_dev, pmc) \ 175d9aa9380SDavid S. Miller for (pmc = rtnl_dereference(in_dev->mc_list); \ 176d9aa9380SDavid S. Miller pmc != NULL; \ 177d9aa9380SDavid S. Miller pmc = rtnl_dereference(pmc->next_rcu)) 178d9aa9380SDavid S. Miller 1791da177e4SLinus Torvalds #ifdef CONFIG_IP_MULTICAST 1801da177e4SLinus Torvalds 1811da177e4SLinus Torvalds /* 1821da177e4SLinus Torvalds * Timer management 1831da177e4SLinus Torvalds */ 1841da177e4SLinus Torvalds 1851d7138deSEric Dumazet static void igmp_stop_timer(struct ip_mc_list *im) 1861da177e4SLinus Torvalds { 1871da177e4SLinus Torvalds spin_lock_bh(&im->lock); 1881da177e4SLinus Torvalds if (del_timer(&im->timer)) 1891da177e4SLinus Torvalds atomic_dec(&im->refcnt); 1901da177e4SLinus Torvalds im->tm_running = 0; 1911da177e4SLinus Torvalds im->reporter = 0; 1921da177e4SLinus Torvalds im->unsolicit_count = 0; 1931da177e4SLinus Torvalds spin_unlock_bh(&im->lock); 1941da177e4SLinus Torvalds } 1951da177e4SLinus Torvalds 1961da177e4SLinus Torvalds /* It must be called with locked im->lock */ 1971da177e4SLinus Torvalds static void igmp_start_timer(struct ip_mc_list *im, int max_delay) 1981da177e4SLinus Torvalds { 1991da177e4SLinus Torvalds int tv = net_random() % max_delay; 2001da177e4SLinus Torvalds 2011da177e4SLinus Torvalds im->tm_running = 1; 2021da177e4SLinus Torvalds if (!mod_timer(&im->timer, jiffies+tv+2)) 2031da177e4SLinus Torvalds atomic_inc(&im->refcnt); 2041da177e4SLinus Torvalds } 2051da177e4SLinus Torvalds 2061da177e4SLinus Torvalds static void igmp_gq_start_timer(struct in_device *in_dev) 2071da177e4SLinus Torvalds { 2081da177e4SLinus Torvalds int tv = net_random() % in_dev->mr_maxdelay; 2091da177e4SLinus Torvalds 2101da177e4SLinus Torvalds in_dev->mr_gq_running = 1; 2111da177e4SLinus Torvalds if (!mod_timer(&in_dev->mr_gq_timer, jiffies+tv+2)) 2121da177e4SLinus Torvalds in_dev_hold(in_dev); 2131da177e4SLinus Torvalds } 2141da177e4SLinus Torvalds 2151da177e4SLinus Torvalds static void igmp_ifc_start_timer(struct in_device *in_dev, int delay) 2161da177e4SLinus Torvalds { 2171da177e4SLinus Torvalds int tv = net_random() % delay; 2181da177e4SLinus Torvalds 2191da177e4SLinus Torvalds if (!mod_timer(&in_dev->mr_ifc_timer, jiffies+tv+2)) 2201da177e4SLinus Torvalds in_dev_hold(in_dev); 2211da177e4SLinus Torvalds } 2221da177e4SLinus Torvalds 2231da177e4SLinus Torvalds static void igmp_mod_timer(struct ip_mc_list *im, int max_delay) 2241da177e4SLinus Torvalds { 2251da177e4SLinus Torvalds spin_lock_bh(&im->lock); 2261da177e4SLinus Torvalds im->unsolicit_count = 0; 2271da177e4SLinus Torvalds if (del_timer(&im->timer)) { 2281da177e4SLinus Torvalds if ((long)(im->timer.expires-jiffies) < max_delay) { 2291da177e4SLinus Torvalds add_timer(&im->timer); 2301da177e4SLinus Torvalds im->tm_running = 1; 2311da177e4SLinus Torvalds spin_unlock_bh(&im->lock); 2321da177e4SLinus Torvalds return; 2331da177e4SLinus Torvalds } 2341da177e4SLinus Torvalds atomic_dec(&im->refcnt); 2351da177e4SLinus Torvalds } 2361da177e4SLinus Torvalds igmp_start_timer(im, max_delay); 2371da177e4SLinus Torvalds spin_unlock_bh(&im->lock); 2381da177e4SLinus Torvalds } 2391da177e4SLinus Torvalds 2401da177e4SLinus Torvalds 2411da177e4SLinus Torvalds /* 2421da177e4SLinus Torvalds * Send an IGMP report. 2431da177e4SLinus Torvalds */ 2441da177e4SLinus Torvalds 2451da177e4SLinus Torvalds #define IGMP_SIZE (sizeof(struct igmphdr)+sizeof(struct iphdr)+4) 2461da177e4SLinus Torvalds 2471da177e4SLinus Torvalds 2481da177e4SLinus Torvalds static int is_in(struct ip_mc_list *pmc, struct ip_sf_list *psf, int type, 2491da177e4SLinus Torvalds int gdeleted, int sdeleted) 2501da177e4SLinus Torvalds { 2511da177e4SLinus Torvalds switch (type) { 2521da177e4SLinus Torvalds case IGMPV3_MODE_IS_INCLUDE: 2531da177e4SLinus Torvalds case IGMPV3_MODE_IS_EXCLUDE: 2541da177e4SLinus Torvalds if (gdeleted || sdeleted) 2551da177e4SLinus Torvalds return 0; 256ad12583fSDavid L Stevens if (!(pmc->gsquery && !psf->sf_gsresp)) { 257ad12583fSDavid L Stevens if (pmc->sfmode == MCAST_INCLUDE) 258ad12583fSDavid L Stevens return 1; 259ad12583fSDavid L Stevens /* don't include if this source is excluded 260ad12583fSDavid L Stevens * in all filters 261ad12583fSDavid L Stevens */ 262ad12583fSDavid L Stevens if (psf->sf_count[MCAST_INCLUDE]) 263ad12583fSDavid L Stevens return type == IGMPV3_MODE_IS_INCLUDE; 264ad12583fSDavid L Stevens return pmc->sfcount[MCAST_EXCLUDE] == 265ad12583fSDavid L Stevens psf->sf_count[MCAST_EXCLUDE]; 266ad12583fSDavid L Stevens } 267ad12583fSDavid L Stevens return 0; 2681da177e4SLinus Torvalds case IGMPV3_CHANGE_TO_INCLUDE: 2691da177e4SLinus Torvalds if (gdeleted || sdeleted) 2701da177e4SLinus Torvalds return 0; 2711da177e4SLinus Torvalds return psf->sf_count[MCAST_INCLUDE] != 0; 2721da177e4SLinus Torvalds case IGMPV3_CHANGE_TO_EXCLUDE: 2731da177e4SLinus Torvalds if (gdeleted || sdeleted) 2741da177e4SLinus Torvalds return 0; 2751da177e4SLinus Torvalds if (pmc->sfcount[MCAST_EXCLUDE] == 0 || 2761da177e4SLinus Torvalds psf->sf_count[MCAST_INCLUDE]) 2771da177e4SLinus Torvalds return 0; 2781da177e4SLinus Torvalds return pmc->sfcount[MCAST_EXCLUDE] == 2791da177e4SLinus Torvalds psf->sf_count[MCAST_EXCLUDE]; 2801da177e4SLinus Torvalds case IGMPV3_ALLOW_NEW_SOURCES: 2811da177e4SLinus Torvalds if (gdeleted || !psf->sf_crcount) 2821da177e4SLinus Torvalds return 0; 2831da177e4SLinus Torvalds return (pmc->sfmode == MCAST_INCLUDE) ^ sdeleted; 2841da177e4SLinus Torvalds case IGMPV3_BLOCK_OLD_SOURCES: 2851da177e4SLinus Torvalds if (pmc->sfmode == MCAST_INCLUDE) 2861da177e4SLinus Torvalds return gdeleted || (psf->sf_crcount && sdeleted); 2871da177e4SLinus Torvalds return psf->sf_crcount && !gdeleted && !sdeleted; 2881da177e4SLinus Torvalds } 2891da177e4SLinus Torvalds return 0; 2901da177e4SLinus Torvalds } 2911da177e4SLinus Torvalds 2921da177e4SLinus Torvalds static int 2931da177e4SLinus Torvalds igmp_scount(struct ip_mc_list *pmc, int type, int gdeleted, int sdeleted) 2941da177e4SLinus Torvalds { 2951da177e4SLinus Torvalds struct ip_sf_list *psf; 2961da177e4SLinus Torvalds int scount = 0; 2971da177e4SLinus Torvalds 2981da177e4SLinus Torvalds for (psf=pmc->sources; psf; psf=psf->sf_next) { 2991da177e4SLinus Torvalds if (!is_in(pmc, psf, type, gdeleted, sdeleted)) 3001da177e4SLinus Torvalds continue; 3011da177e4SLinus Torvalds scount++; 3021da177e4SLinus Torvalds } 3031da177e4SLinus Torvalds return scount; 3041da177e4SLinus Torvalds } 3051da177e4SLinus Torvalds 30657e1ab6eSEric Dumazet #define igmp_skb_size(skb) (*(unsigned int *)((skb)->cb)) 30757e1ab6eSEric Dumazet 3081da177e4SLinus Torvalds static struct sk_buff *igmpv3_newpack(struct net_device *dev, int size) 3091da177e4SLinus Torvalds { 3101da177e4SLinus Torvalds struct sk_buff *skb; 3111da177e4SLinus Torvalds struct rtable *rt; 3121da177e4SLinus Torvalds struct iphdr *pip; 3131da177e4SLinus Torvalds struct igmpv3_report *pig; 314877acedcSDaniel Lezcano struct net *net = dev_net(dev); 31531e4543dSDavid S. Miller struct flowi4 fl4; 31666088243SHerbert Xu int hlen = LL_RESERVED_SPACE(dev); 31766088243SHerbert Xu int tlen = dev->needed_tailroom; 3181da177e4SLinus Torvalds 31957e1ab6eSEric Dumazet while (1) { 32066088243SHerbert Xu skb = alloc_skb(size + hlen + tlen, 32157e1ab6eSEric Dumazet GFP_ATOMIC | __GFP_NOWARN); 32257e1ab6eSEric Dumazet if (skb) 32357e1ab6eSEric Dumazet break; 32457e1ab6eSEric Dumazet size >>= 1; 32557e1ab6eSEric Dumazet if (size < 256) 3261da177e4SLinus Torvalds return NULL; 32757e1ab6eSEric Dumazet } 3289d4a0314SHannes Frederic Sowa skb->priority = TC_PRIO_CONTROL; 32957e1ab6eSEric Dumazet igmp_skb_size(skb) = size; 3301da177e4SLinus Torvalds 33131e4543dSDavid S. Miller rt = ip_route_output_ports(net, &fl4, NULL, IGMPV3_ALL_MCR, 0, 33278fbfd8aSDavid S. Miller 0, 0, 33378fbfd8aSDavid S. Miller IPPROTO_IGMP, 0, dev->ifindex); 334b23dd4feSDavid S. Miller if (IS_ERR(rt)) { 3351da177e4SLinus Torvalds kfree_skb(skb); 3361da177e4SLinus Torvalds return NULL; 3371da177e4SLinus Torvalds } 3381da177e4SLinus Torvalds 339d8d1f30bSChangli Gao skb_dst_set(skb, &rt->dst); 3401da177e4SLinus Torvalds skb->dev = dev; 3411da177e4SLinus Torvalds 34266088243SHerbert Xu skb_reserve(skb, hlen); 3431da177e4SLinus Torvalds 3447e28ecc2SArnaldo Carvalho de Melo skb_reset_network_header(skb); 345eddc9ec5SArnaldo Carvalho de Melo pip = ip_hdr(skb); 3467e28ecc2SArnaldo Carvalho de Melo skb_put(skb, sizeof(struct iphdr) + 4); 3471da177e4SLinus Torvalds 3481da177e4SLinus Torvalds pip->version = 4; 3491da177e4SLinus Torvalds pip->ihl = (sizeof(struct iphdr)+4)>>2; 3501da177e4SLinus Torvalds pip->tos = 0xc0; 3511da177e4SLinus Torvalds pip->frag_off = htons(IP_DF); 3521da177e4SLinus Torvalds pip->ttl = 1; 353492f64ceSDavid S. Miller pip->daddr = fl4.daddr; 354492f64ceSDavid S. Miller pip->saddr = fl4.saddr; 3551da177e4SLinus Torvalds pip->protocol = IPPROTO_IGMP; 3561da177e4SLinus Torvalds pip->tot_len = 0; /* filled in later */ 357d8d1f30bSChangli Gao ip_select_ident(pip, &rt->dst, NULL); 3581da177e4SLinus Torvalds ((u8 *)&pip[1])[0] = IPOPT_RA; 3591da177e4SLinus Torvalds ((u8 *)&pip[1])[1] = 4; 3601da177e4SLinus Torvalds ((u8 *)&pip[1])[2] = 0; 3611da177e4SLinus Torvalds ((u8 *)&pip[1])[3] = 0; 3621da177e4SLinus Torvalds 363b0e380b1SArnaldo Carvalho de Melo skb->transport_header = skb->network_header + sizeof(struct iphdr) + 4; 364d10ba34bSArnaldo Carvalho de Melo skb_put(skb, sizeof(*pig)); 365d9edf9e2SArnaldo Carvalho de Melo pig = igmpv3_report_hdr(skb); 3661da177e4SLinus Torvalds pig->type = IGMPV3_HOST_MEMBERSHIP_REPORT; 3671da177e4SLinus Torvalds pig->resv1 = 0; 3681da177e4SLinus Torvalds pig->csum = 0; 3691da177e4SLinus Torvalds pig->resv2 = 0; 3701da177e4SLinus Torvalds pig->ngrec = 0; 3711da177e4SLinus Torvalds return skb; 3721da177e4SLinus Torvalds } 3731da177e4SLinus Torvalds 3741da177e4SLinus Torvalds static int igmpv3_sendpack(struct sk_buff *skb) 3751da177e4SLinus Torvalds { 376d9edf9e2SArnaldo Carvalho de Melo struct igmphdr *pig = igmp_hdr(skb); 377f7c0c2aeSSimon Horman const int igmplen = skb_tail_pointer(skb) - skb_transport_header(skb); 3781da177e4SLinus Torvalds 379d9edf9e2SArnaldo Carvalho de Melo pig->csum = ip_compute_csum(igmp_hdr(skb), igmplen); 3801da177e4SLinus Torvalds 381c439cb2eSHerbert Xu return ip_local_out(skb); 3821da177e4SLinus Torvalds } 3831da177e4SLinus Torvalds 3841da177e4SLinus Torvalds static int grec_size(struct ip_mc_list *pmc, int type, int gdel, int sdel) 3851da177e4SLinus Torvalds { 3861da177e4SLinus Torvalds return sizeof(struct igmpv3_grec) + 4*igmp_scount(pmc, type, gdel, sdel); 3871da177e4SLinus Torvalds } 3881da177e4SLinus Torvalds 3891da177e4SLinus Torvalds static struct sk_buff *add_grhead(struct sk_buff *skb, struct ip_mc_list *pmc, 3901da177e4SLinus Torvalds int type, struct igmpv3_grec **ppgr) 3911da177e4SLinus Torvalds { 3921da177e4SLinus Torvalds struct net_device *dev = pmc->interface->dev; 3931da177e4SLinus Torvalds struct igmpv3_report *pih; 3941da177e4SLinus Torvalds struct igmpv3_grec *pgr; 3951da177e4SLinus Torvalds 3961da177e4SLinus Torvalds if (!skb) 3971da177e4SLinus Torvalds skb = igmpv3_newpack(dev, dev->mtu); 3981da177e4SLinus Torvalds if (!skb) 3991da177e4SLinus Torvalds return NULL; 4001da177e4SLinus Torvalds pgr = (struct igmpv3_grec *)skb_put(skb, sizeof(struct igmpv3_grec)); 4011da177e4SLinus Torvalds pgr->grec_type = type; 4021da177e4SLinus Torvalds pgr->grec_auxwords = 0; 4031da177e4SLinus Torvalds pgr->grec_nsrcs = 0; 4041da177e4SLinus Torvalds pgr->grec_mca = pmc->multiaddr; 405d9edf9e2SArnaldo Carvalho de Melo pih = igmpv3_report_hdr(skb); 4061da177e4SLinus Torvalds pih->ngrec = htons(ntohs(pih->ngrec)+1); 4071da177e4SLinus Torvalds *ppgr = pgr; 4081da177e4SLinus Torvalds return skb; 4091da177e4SLinus Torvalds } 4101da177e4SLinus Torvalds 41157e1ab6eSEric Dumazet #define AVAILABLE(skb) ((skb) ? ((skb)->dev ? igmp_skb_size(skb) - (skb)->len : \ 4121da177e4SLinus Torvalds skb_tailroom(skb)) : 0) 4131da177e4SLinus Torvalds 4141da177e4SLinus Torvalds static struct sk_buff *add_grec(struct sk_buff *skb, struct ip_mc_list *pmc, 4151da177e4SLinus Torvalds int type, int gdeleted, int sdeleted) 4161da177e4SLinus Torvalds { 4171da177e4SLinus Torvalds struct net_device *dev = pmc->interface->dev; 4181da177e4SLinus Torvalds struct igmpv3_report *pih; 4191da177e4SLinus Torvalds struct igmpv3_grec *pgr = NULL; 4201da177e4SLinus Torvalds struct ip_sf_list *psf, *psf_next, *psf_prev, **psf_list; 421ad12583fSDavid L Stevens int scount, stotal, first, isquery, truncate; 4221da177e4SLinus Torvalds 4231da177e4SLinus Torvalds if (pmc->multiaddr == IGMP_ALL_HOSTS) 4241da177e4SLinus Torvalds return skb; 4251da177e4SLinus Torvalds 4261da177e4SLinus Torvalds isquery = type == IGMPV3_MODE_IS_INCLUDE || 4271da177e4SLinus Torvalds type == IGMPV3_MODE_IS_EXCLUDE; 4281da177e4SLinus Torvalds truncate = type == IGMPV3_MODE_IS_EXCLUDE || 4291da177e4SLinus Torvalds type == IGMPV3_CHANGE_TO_EXCLUDE; 4301da177e4SLinus Torvalds 431ad12583fSDavid L Stevens stotal = scount = 0; 432ad12583fSDavid L Stevens 4331da177e4SLinus Torvalds psf_list = sdeleted ? &pmc->tomb : &pmc->sources; 4341da177e4SLinus Torvalds 435ad12583fSDavid L Stevens if (!*psf_list) 436ad12583fSDavid L Stevens goto empty_source; 437ad12583fSDavid L Stevens 438d9edf9e2SArnaldo Carvalho de Melo pih = skb ? igmpv3_report_hdr(skb) : NULL; 4391da177e4SLinus Torvalds 4401da177e4SLinus Torvalds /* EX and TO_EX get a fresh packet, if needed */ 4411da177e4SLinus Torvalds if (truncate) { 4421da177e4SLinus Torvalds if (pih && pih->ngrec && 4431da177e4SLinus Torvalds AVAILABLE(skb) < grec_size(pmc, type, gdeleted, sdeleted)) { 4441da177e4SLinus Torvalds if (skb) 4451da177e4SLinus Torvalds igmpv3_sendpack(skb); 4461da177e4SLinus Torvalds skb = igmpv3_newpack(dev, dev->mtu); 4471da177e4SLinus Torvalds } 4481da177e4SLinus Torvalds } 4491da177e4SLinus Torvalds first = 1; 4501da177e4SLinus Torvalds psf_prev = NULL; 4511da177e4SLinus Torvalds for (psf=*psf_list; psf; psf=psf_next) { 452ea4d9e72SAl Viro __be32 *psrc; 4531da177e4SLinus Torvalds 4541da177e4SLinus Torvalds psf_next = psf->sf_next; 4551da177e4SLinus Torvalds 4561da177e4SLinus Torvalds if (!is_in(pmc, psf, type, gdeleted, sdeleted)) { 4571da177e4SLinus Torvalds psf_prev = psf; 4581da177e4SLinus Torvalds continue; 4591da177e4SLinus Torvalds } 4601da177e4SLinus Torvalds 4611da177e4SLinus Torvalds /* clear marks on query responses */ 4621da177e4SLinus Torvalds if (isquery) 4631da177e4SLinus Torvalds psf->sf_gsresp = 0; 4641da177e4SLinus Torvalds 46563007727SAl Viro if (AVAILABLE(skb) < sizeof(__be32) + 4661da177e4SLinus Torvalds first*sizeof(struct igmpv3_grec)) { 4671da177e4SLinus Torvalds if (truncate && !first) 4681da177e4SLinus Torvalds break; /* truncate these */ 4691da177e4SLinus Torvalds if (pgr) 4701da177e4SLinus Torvalds pgr->grec_nsrcs = htons(scount); 4711da177e4SLinus Torvalds if (skb) 4721da177e4SLinus Torvalds igmpv3_sendpack(skb); 4731da177e4SLinus Torvalds skb = igmpv3_newpack(dev, dev->mtu); 4741da177e4SLinus Torvalds first = 1; 4751da177e4SLinus Torvalds scount = 0; 4761da177e4SLinus Torvalds } 4771da177e4SLinus Torvalds if (first) { 4781da177e4SLinus Torvalds skb = add_grhead(skb, pmc, type, &pgr); 4791da177e4SLinus Torvalds first = 0; 4801da177e4SLinus Torvalds } 481cc63f70bSAlexey Dobriyan if (!skb) 482cc63f70bSAlexey Dobriyan return NULL; 48363007727SAl Viro psrc = (__be32 *)skb_put(skb, sizeof(__be32)); 4841da177e4SLinus Torvalds *psrc = psf->sf_inaddr; 485ad12583fSDavid L Stevens scount++; stotal++; 4861da177e4SLinus Torvalds if ((type == IGMPV3_ALLOW_NEW_SOURCES || 4871da177e4SLinus Torvalds type == IGMPV3_BLOCK_OLD_SOURCES) && psf->sf_crcount) { 4881da177e4SLinus Torvalds psf->sf_crcount--; 4891da177e4SLinus Torvalds if ((sdeleted || gdeleted) && psf->sf_crcount == 0) { 4901da177e4SLinus Torvalds if (psf_prev) 4911da177e4SLinus Torvalds psf_prev->sf_next = psf->sf_next; 4921da177e4SLinus Torvalds else 4931da177e4SLinus Torvalds *psf_list = psf->sf_next; 4941da177e4SLinus Torvalds kfree(psf); 4951da177e4SLinus Torvalds continue; 4961da177e4SLinus Torvalds } 4971da177e4SLinus Torvalds } 4981da177e4SLinus Torvalds psf_prev = psf; 4991da177e4SLinus Torvalds } 500ad12583fSDavid L Stevens 501ad12583fSDavid L Stevens empty_source: 502ad12583fSDavid L Stevens if (!stotal) { 503ad12583fSDavid L Stevens if (type == IGMPV3_ALLOW_NEW_SOURCES || 504ad12583fSDavid L Stevens type == IGMPV3_BLOCK_OLD_SOURCES) 505ad12583fSDavid L Stevens return skb; 506ad12583fSDavid L Stevens if (pmc->crcount || isquery) { 507ad12583fSDavid L Stevens /* make sure we have room for group header */ 508ad12583fSDavid L Stevens if (skb && AVAILABLE(skb)<sizeof(struct igmpv3_grec)) { 509ad12583fSDavid L Stevens igmpv3_sendpack(skb); 510ad12583fSDavid L Stevens skb = NULL; /* add_grhead will get a new one */ 511ad12583fSDavid L Stevens } 512ad12583fSDavid L Stevens skb = add_grhead(skb, pmc, type, &pgr); 513ad12583fSDavid L Stevens } 514ad12583fSDavid L Stevens } 5151da177e4SLinus Torvalds if (pgr) 5161da177e4SLinus Torvalds pgr->grec_nsrcs = htons(scount); 5171da177e4SLinus Torvalds 5181da177e4SLinus Torvalds if (isquery) 5191da177e4SLinus Torvalds pmc->gsquery = 0; /* clear query state on report */ 5201da177e4SLinus Torvalds return skb; 5211da177e4SLinus Torvalds } 5221da177e4SLinus Torvalds 5231da177e4SLinus Torvalds static int igmpv3_send_report(struct in_device *in_dev, struct ip_mc_list *pmc) 5241da177e4SLinus Torvalds { 5251da177e4SLinus Torvalds struct sk_buff *skb = NULL; 5261da177e4SLinus Torvalds int type; 5271da177e4SLinus Torvalds 5281da177e4SLinus Torvalds if (!pmc) { 5291d7138deSEric Dumazet rcu_read_lock(); 5301d7138deSEric Dumazet for_each_pmc_rcu(in_dev, pmc) { 5311da177e4SLinus Torvalds if (pmc->multiaddr == IGMP_ALL_HOSTS) 5321da177e4SLinus Torvalds continue; 5331da177e4SLinus Torvalds spin_lock_bh(&pmc->lock); 5341da177e4SLinus Torvalds if (pmc->sfcount[MCAST_EXCLUDE]) 5351da177e4SLinus Torvalds type = IGMPV3_MODE_IS_EXCLUDE; 5361da177e4SLinus Torvalds else 5371da177e4SLinus Torvalds type = IGMPV3_MODE_IS_INCLUDE; 5381da177e4SLinus Torvalds skb = add_grec(skb, pmc, type, 0, 0); 5391da177e4SLinus Torvalds spin_unlock_bh(&pmc->lock); 5401da177e4SLinus Torvalds } 5411d7138deSEric Dumazet rcu_read_unlock(); 5421da177e4SLinus Torvalds } else { 5431da177e4SLinus Torvalds spin_lock_bh(&pmc->lock); 5441da177e4SLinus Torvalds if (pmc->sfcount[MCAST_EXCLUDE]) 5451da177e4SLinus Torvalds type = IGMPV3_MODE_IS_EXCLUDE; 5461da177e4SLinus Torvalds else 5471da177e4SLinus Torvalds type = IGMPV3_MODE_IS_INCLUDE; 5481da177e4SLinus Torvalds skb = add_grec(skb, pmc, type, 0, 0); 5491da177e4SLinus Torvalds spin_unlock_bh(&pmc->lock); 5501da177e4SLinus Torvalds } 5511da177e4SLinus Torvalds if (!skb) 5521da177e4SLinus Torvalds return 0; 5531da177e4SLinus Torvalds return igmpv3_sendpack(skb); 5541da177e4SLinus Torvalds } 5551da177e4SLinus Torvalds 5561da177e4SLinus Torvalds /* 5571da177e4SLinus Torvalds * remove zero-count source records from a source filter list 5581da177e4SLinus Torvalds */ 5591da177e4SLinus Torvalds static void igmpv3_clear_zeros(struct ip_sf_list **ppsf) 5601da177e4SLinus Torvalds { 5611da177e4SLinus Torvalds struct ip_sf_list *psf_prev, *psf_next, *psf; 5621da177e4SLinus Torvalds 5631da177e4SLinus Torvalds psf_prev = NULL; 5641da177e4SLinus Torvalds for (psf=*ppsf; psf; psf = psf_next) { 5651da177e4SLinus Torvalds psf_next = psf->sf_next; 5661da177e4SLinus Torvalds if (psf->sf_crcount == 0) { 5671da177e4SLinus Torvalds if (psf_prev) 5681da177e4SLinus Torvalds psf_prev->sf_next = psf->sf_next; 5691da177e4SLinus Torvalds else 5701da177e4SLinus Torvalds *ppsf = psf->sf_next; 5711da177e4SLinus Torvalds kfree(psf); 5721da177e4SLinus Torvalds } else 5731da177e4SLinus Torvalds psf_prev = psf; 5741da177e4SLinus Torvalds } 5751da177e4SLinus Torvalds } 5761da177e4SLinus Torvalds 5771da177e4SLinus Torvalds static void igmpv3_send_cr(struct in_device *in_dev) 5781da177e4SLinus Torvalds { 5791da177e4SLinus Torvalds struct ip_mc_list *pmc, *pmc_prev, *pmc_next; 5801da177e4SLinus Torvalds struct sk_buff *skb = NULL; 5811da177e4SLinus Torvalds int type, dtype; 5821da177e4SLinus Torvalds 5831d7138deSEric Dumazet rcu_read_lock(); 5841da177e4SLinus Torvalds spin_lock_bh(&in_dev->mc_tomb_lock); 5851da177e4SLinus Torvalds 5861da177e4SLinus Torvalds /* deleted MCA's */ 5871da177e4SLinus Torvalds pmc_prev = NULL; 5881da177e4SLinus Torvalds for (pmc=in_dev->mc_tomb; pmc; pmc=pmc_next) { 5891da177e4SLinus Torvalds pmc_next = pmc->next; 5901da177e4SLinus Torvalds if (pmc->sfmode == MCAST_INCLUDE) { 5911da177e4SLinus Torvalds type = IGMPV3_BLOCK_OLD_SOURCES; 5921da177e4SLinus Torvalds dtype = IGMPV3_BLOCK_OLD_SOURCES; 5931da177e4SLinus Torvalds skb = add_grec(skb, pmc, type, 1, 0); 5941da177e4SLinus Torvalds skb = add_grec(skb, pmc, dtype, 1, 1); 5951da177e4SLinus Torvalds } 5961da177e4SLinus Torvalds if (pmc->crcount) { 5971da177e4SLinus Torvalds if (pmc->sfmode == MCAST_EXCLUDE) { 5981da177e4SLinus Torvalds type = IGMPV3_CHANGE_TO_INCLUDE; 5991da177e4SLinus Torvalds skb = add_grec(skb, pmc, type, 1, 0); 6001da177e4SLinus Torvalds } 601ad12583fSDavid L Stevens pmc->crcount--; 6021da177e4SLinus Torvalds if (pmc->crcount == 0) { 6031da177e4SLinus Torvalds igmpv3_clear_zeros(&pmc->tomb); 6041da177e4SLinus Torvalds igmpv3_clear_zeros(&pmc->sources); 6051da177e4SLinus Torvalds } 6061da177e4SLinus Torvalds } 6071da177e4SLinus Torvalds if (pmc->crcount == 0 && !pmc->tomb && !pmc->sources) { 6081da177e4SLinus Torvalds if (pmc_prev) 6091da177e4SLinus Torvalds pmc_prev->next = pmc_next; 6101da177e4SLinus Torvalds else 6111da177e4SLinus Torvalds in_dev->mc_tomb = pmc_next; 6121da177e4SLinus Torvalds in_dev_put(pmc->interface); 6131da177e4SLinus Torvalds kfree(pmc); 6141da177e4SLinus Torvalds } else 6151da177e4SLinus Torvalds pmc_prev = pmc; 6161da177e4SLinus Torvalds } 6171da177e4SLinus Torvalds spin_unlock_bh(&in_dev->mc_tomb_lock); 6181da177e4SLinus Torvalds 6191da177e4SLinus Torvalds /* change recs */ 6201d7138deSEric Dumazet for_each_pmc_rcu(in_dev, pmc) { 6211da177e4SLinus Torvalds spin_lock_bh(&pmc->lock); 6221da177e4SLinus Torvalds if (pmc->sfcount[MCAST_EXCLUDE]) { 6231da177e4SLinus Torvalds type = IGMPV3_BLOCK_OLD_SOURCES; 6241da177e4SLinus Torvalds dtype = IGMPV3_ALLOW_NEW_SOURCES; 6251da177e4SLinus Torvalds } else { 6261da177e4SLinus Torvalds type = IGMPV3_ALLOW_NEW_SOURCES; 6271da177e4SLinus Torvalds dtype = IGMPV3_BLOCK_OLD_SOURCES; 6281da177e4SLinus Torvalds } 6291da177e4SLinus Torvalds skb = add_grec(skb, pmc, type, 0, 0); 6301da177e4SLinus Torvalds skb = add_grec(skb, pmc, dtype, 0, 1); /* deleted sources */ 6311da177e4SLinus Torvalds 6321da177e4SLinus Torvalds /* filter mode changes */ 6331da177e4SLinus Torvalds if (pmc->crcount) { 6341da177e4SLinus Torvalds if (pmc->sfmode == MCAST_EXCLUDE) 6351da177e4SLinus Torvalds type = IGMPV3_CHANGE_TO_EXCLUDE; 6361da177e4SLinus Torvalds else 6371da177e4SLinus Torvalds type = IGMPV3_CHANGE_TO_INCLUDE; 6381da177e4SLinus Torvalds skb = add_grec(skb, pmc, type, 0, 0); 639ad12583fSDavid L Stevens pmc->crcount--; 6401da177e4SLinus Torvalds } 6411da177e4SLinus Torvalds spin_unlock_bh(&pmc->lock); 6421da177e4SLinus Torvalds } 6431d7138deSEric Dumazet rcu_read_unlock(); 6441da177e4SLinus Torvalds 6451da177e4SLinus Torvalds if (!skb) 6461da177e4SLinus Torvalds return; 6471da177e4SLinus Torvalds (void) igmpv3_sendpack(skb); 6481da177e4SLinus Torvalds } 6491da177e4SLinus Torvalds 6501da177e4SLinus Torvalds static int igmp_send_report(struct in_device *in_dev, struct ip_mc_list *pmc, 6511da177e4SLinus Torvalds int type) 6521da177e4SLinus Torvalds { 6531da177e4SLinus Torvalds struct sk_buff *skb; 6541da177e4SLinus Torvalds struct iphdr *iph; 6551da177e4SLinus Torvalds struct igmphdr *ih; 6561da177e4SLinus Torvalds struct rtable *rt; 6571da177e4SLinus Torvalds struct net_device *dev = in_dev->dev; 658877acedcSDaniel Lezcano struct net *net = dev_net(dev); 65963007727SAl Viro __be32 group = pmc ? pmc->multiaddr : 0; 66031e4543dSDavid S. Miller struct flowi4 fl4; 66163007727SAl Viro __be32 dst; 66266088243SHerbert Xu int hlen, tlen; 6631da177e4SLinus Torvalds 6641da177e4SLinus Torvalds if (type == IGMPV3_HOST_MEMBERSHIP_REPORT) 6651da177e4SLinus Torvalds return igmpv3_send_report(in_dev, pmc); 6661da177e4SLinus Torvalds else if (type == IGMP_HOST_LEAVE_MESSAGE) 6671da177e4SLinus Torvalds dst = IGMP_ALL_ROUTER; 6681da177e4SLinus Torvalds else 6691da177e4SLinus Torvalds dst = group; 6701da177e4SLinus Torvalds 67131e4543dSDavid S. Miller rt = ip_route_output_ports(net, &fl4, NULL, dst, 0, 67278fbfd8aSDavid S. Miller 0, 0, 67378fbfd8aSDavid S. Miller IPPROTO_IGMP, 0, dev->ifindex); 674b23dd4feSDavid S. Miller if (IS_ERR(rt)) 6751da177e4SLinus Torvalds return -1; 67678fbfd8aSDavid S. Miller 67766088243SHerbert Xu hlen = LL_RESERVED_SPACE(dev); 67866088243SHerbert Xu tlen = dev->needed_tailroom; 67966088243SHerbert Xu skb = alloc_skb(IGMP_SIZE + hlen + tlen, GFP_ATOMIC); 6801da177e4SLinus Torvalds if (skb == NULL) { 6811da177e4SLinus Torvalds ip_rt_put(rt); 6821da177e4SLinus Torvalds return -1; 6831da177e4SLinus Torvalds } 6849d4a0314SHannes Frederic Sowa skb->priority = TC_PRIO_CONTROL; 6851da177e4SLinus Torvalds 686d8d1f30bSChangli Gao skb_dst_set(skb, &rt->dst); 6871da177e4SLinus Torvalds 68866088243SHerbert Xu skb_reserve(skb, hlen); 6891da177e4SLinus Torvalds 6907e28ecc2SArnaldo Carvalho de Melo skb_reset_network_header(skb); 691eddc9ec5SArnaldo Carvalho de Melo iph = ip_hdr(skb); 6927e28ecc2SArnaldo Carvalho de Melo skb_put(skb, sizeof(struct iphdr) + 4); 6931da177e4SLinus Torvalds 6941da177e4SLinus Torvalds iph->version = 4; 6951da177e4SLinus Torvalds iph->ihl = (sizeof(struct iphdr)+4)>>2; 6961da177e4SLinus Torvalds iph->tos = 0xc0; 6971da177e4SLinus Torvalds iph->frag_off = htons(IP_DF); 6981da177e4SLinus Torvalds iph->ttl = 1; 6991da177e4SLinus Torvalds iph->daddr = dst; 700492f64ceSDavid S. Miller iph->saddr = fl4.saddr; 7011da177e4SLinus Torvalds iph->protocol = IPPROTO_IGMP; 702d8d1f30bSChangli Gao ip_select_ident(iph, &rt->dst, NULL); 7031da177e4SLinus Torvalds ((u8 *)&iph[1])[0] = IPOPT_RA; 7041da177e4SLinus Torvalds ((u8 *)&iph[1])[1] = 4; 7051da177e4SLinus Torvalds ((u8 *)&iph[1])[2] = 0; 7061da177e4SLinus Torvalds ((u8 *)&iph[1])[3] = 0; 7071da177e4SLinus Torvalds 7081da177e4SLinus Torvalds ih = (struct igmphdr *)skb_put(skb, sizeof(struct igmphdr)); 7091da177e4SLinus Torvalds ih->type = type; 7101da177e4SLinus Torvalds ih->code = 0; 7111da177e4SLinus Torvalds ih->csum = 0; 7121da177e4SLinus Torvalds ih->group = group; 7131da177e4SLinus Torvalds ih->csum = ip_compute_csum((void *)ih, sizeof(struct igmphdr)); 7141da177e4SLinus Torvalds 715c439cb2eSHerbert Xu return ip_local_out(skb); 7161da177e4SLinus Torvalds } 7171da177e4SLinus Torvalds 7181da177e4SLinus Torvalds static void igmp_gq_timer_expire(unsigned long data) 7191da177e4SLinus Torvalds { 7201da177e4SLinus Torvalds struct in_device *in_dev = (struct in_device *)data; 7211da177e4SLinus Torvalds 7221da177e4SLinus Torvalds in_dev->mr_gq_running = 0; 7231da177e4SLinus Torvalds igmpv3_send_report(in_dev, NULL); 7241da177e4SLinus Torvalds __in_dev_put(in_dev); 7251da177e4SLinus Torvalds } 7261da177e4SLinus Torvalds 7271da177e4SLinus Torvalds static void igmp_ifc_timer_expire(unsigned long data) 7281da177e4SLinus Torvalds { 7291da177e4SLinus Torvalds struct in_device *in_dev = (struct in_device *)data; 7301da177e4SLinus Torvalds 7311da177e4SLinus Torvalds igmpv3_send_cr(in_dev); 7321da177e4SLinus Torvalds if (in_dev->mr_ifc_count) { 7331da177e4SLinus Torvalds in_dev->mr_ifc_count--; 734*cab70040SWilliam Manley igmp_ifc_start_timer(in_dev, 735*cab70040SWilliam Manley unsolicited_report_interval(in_dev)); 7361da177e4SLinus Torvalds } 7371da177e4SLinus Torvalds __in_dev_put(in_dev); 7381da177e4SLinus Torvalds } 7391da177e4SLinus Torvalds 7401da177e4SLinus Torvalds static void igmp_ifc_event(struct in_device *in_dev) 7411da177e4SLinus Torvalds { 7421da177e4SLinus Torvalds if (IGMP_V1_SEEN(in_dev) || IGMP_V2_SEEN(in_dev)) 7431da177e4SLinus Torvalds return; 7441da177e4SLinus Torvalds in_dev->mr_ifc_count = in_dev->mr_qrv ? in_dev->mr_qrv : 7451da177e4SLinus Torvalds IGMP_Unsolicited_Report_Count; 7461da177e4SLinus Torvalds igmp_ifc_start_timer(in_dev, 1); 7471da177e4SLinus Torvalds } 7481da177e4SLinus Torvalds 7491da177e4SLinus Torvalds 7501da177e4SLinus Torvalds static void igmp_timer_expire(unsigned long data) 7511da177e4SLinus Torvalds { 7521da177e4SLinus Torvalds struct ip_mc_list *im=(struct ip_mc_list *)data; 7531da177e4SLinus Torvalds struct in_device *in_dev = im->interface; 7541da177e4SLinus Torvalds 7551da177e4SLinus Torvalds spin_lock(&im->lock); 7561da177e4SLinus Torvalds im->tm_running = 0; 7571da177e4SLinus Torvalds 7581da177e4SLinus Torvalds if (im->unsolicit_count) { 7591da177e4SLinus Torvalds im->unsolicit_count--; 760*cab70040SWilliam Manley igmp_start_timer(im, unsolicited_report_interval(in_dev)); 7611da177e4SLinus Torvalds } 7621da177e4SLinus Torvalds im->reporter = 1; 7631da177e4SLinus Torvalds spin_unlock(&im->lock); 7641da177e4SLinus Torvalds 7651da177e4SLinus Torvalds if (IGMP_V1_SEEN(in_dev)) 7661da177e4SLinus Torvalds igmp_send_report(in_dev, im, IGMP_HOST_MEMBERSHIP_REPORT); 7671da177e4SLinus Torvalds else if (IGMP_V2_SEEN(in_dev)) 7681da177e4SLinus Torvalds igmp_send_report(in_dev, im, IGMPV2_HOST_MEMBERSHIP_REPORT); 7691da177e4SLinus Torvalds else 7701da177e4SLinus Torvalds igmp_send_report(in_dev, im, IGMPV3_HOST_MEMBERSHIP_REPORT); 7711da177e4SLinus Torvalds 7721da177e4SLinus Torvalds ip_ma_put(im); 7731da177e4SLinus Torvalds } 7741da177e4SLinus Torvalds 775ad12583fSDavid L Stevens /* mark EXCLUDE-mode sources */ 776ea4d9e72SAl Viro static int igmp_xmarksources(struct ip_mc_list *pmc, int nsrcs, __be32 *srcs) 7771da177e4SLinus Torvalds { 7781da177e4SLinus Torvalds struct ip_sf_list *psf; 7791da177e4SLinus Torvalds int i, scount; 7801da177e4SLinus Torvalds 7811da177e4SLinus Torvalds scount = 0; 7821da177e4SLinus Torvalds for (psf=pmc->sources; psf; psf=psf->sf_next) { 7831da177e4SLinus Torvalds if (scount == nsrcs) 7841da177e4SLinus Torvalds break; 785ad12583fSDavid L Stevens for (i=0; i<nsrcs; i++) { 786ad12583fSDavid L Stevens /* skip inactive filters */ 787e05c4ad3SYan, Zheng if (psf->sf_count[MCAST_INCLUDE] || 788ad12583fSDavid L Stevens pmc->sfcount[MCAST_EXCLUDE] != 789ad12583fSDavid L Stevens psf->sf_count[MCAST_EXCLUDE]) 790ce713ee5SRongQing.Li break; 791ad12583fSDavid L Stevens if (srcs[i] == psf->sf_inaddr) { 792ad12583fSDavid L Stevens scount++; 793ad12583fSDavid L Stevens break; 794ad12583fSDavid L Stevens } 795ad12583fSDavid L Stevens } 796ad12583fSDavid L Stevens } 797ad12583fSDavid L Stevens pmc->gsquery = 0; 798ad12583fSDavid L Stevens if (scount == nsrcs) /* all sources excluded */ 799ad12583fSDavid L Stevens return 0; 800ad12583fSDavid L Stevens return 1; 801ad12583fSDavid L Stevens } 802ad12583fSDavid L Stevens 803ea4d9e72SAl Viro static int igmp_marksources(struct ip_mc_list *pmc, int nsrcs, __be32 *srcs) 804ad12583fSDavid L Stevens { 805ad12583fSDavid L Stevens struct ip_sf_list *psf; 806ad12583fSDavid L Stevens int i, scount; 807ad12583fSDavid L Stevens 808ad12583fSDavid L Stevens if (pmc->sfmode == MCAST_EXCLUDE) 809ad12583fSDavid L Stevens return igmp_xmarksources(pmc, nsrcs, srcs); 810ad12583fSDavid L Stevens 811ad12583fSDavid L Stevens /* mark INCLUDE-mode sources */ 812ad12583fSDavid L Stevens scount = 0; 813ad12583fSDavid L Stevens for (psf=pmc->sources; psf; psf=psf->sf_next) { 814ad12583fSDavid L Stevens if (scount == nsrcs) 815ad12583fSDavid L Stevens break; 8161da177e4SLinus Torvalds for (i=0; i<nsrcs; i++) 8171da177e4SLinus Torvalds if (srcs[i] == psf->sf_inaddr) { 8181da177e4SLinus Torvalds psf->sf_gsresp = 1; 8191da177e4SLinus Torvalds scount++; 8201da177e4SLinus Torvalds break; 8211da177e4SLinus Torvalds } 8221da177e4SLinus Torvalds } 823ad12583fSDavid L Stevens if (!scount) { 824ad12583fSDavid L Stevens pmc->gsquery = 0; 825ad12583fSDavid L Stevens return 0; 826ad12583fSDavid L Stevens } 827ad12583fSDavid L Stevens pmc->gsquery = 1; 828ad12583fSDavid L Stevens return 1; 8291da177e4SLinus Torvalds } 8301da177e4SLinus Torvalds 831d679c532SEric Dumazet /* return true if packet was dropped */ 832d679c532SEric Dumazet static bool igmp_heard_report(struct in_device *in_dev, __be32 group) 8331da177e4SLinus Torvalds { 8341da177e4SLinus Torvalds struct ip_mc_list *im; 8351da177e4SLinus Torvalds 8361da177e4SLinus Torvalds /* Timers are only set for non-local groups */ 8371da177e4SLinus Torvalds 8381da177e4SLinus Torvalds if (group == IGMP_ALL_HOSTS) 839d679c532SEric Dumazet return false; 8401da177e4SLinus Torvalds 8411d7138deSEric Dumazet rcu_read_lock(); 8421d7138deSEric Dumazet for_each_pmc_rcu(in_dev, im) { 8431da177e4SLinus Torvalds if (im->multiaddr == group) { 8441da177e4SLinus Torvalds igmp_stop_timer(im); 8451da177e4SLinus Torvalds break; 8461da177e4SLinus Torvalds } 8471da177e4SLinus Torvalds } 8481d7138deSEric Dumazet rcu_read_unlock(); 849d679c532SEric Dumazet return false; 8501da177e4SLinus Torvalds } 8511da177e4SLinus Torvalds 852d679c532SEric Dumazet /* return true if packet was dropped */ 853d679c532SEric Dumazet static bool igmp_heard_query(struct in_device *in_dev, struct sk_buff *skb, 8541da177e4SLinus Torvalds int len) 8551da177e4SLinus Torvalds { 856d9edf9e2SArnaldo Carvalho de Melo struct igmphdr *ih = igmp_hdr(skb); 857d9edf9e2SArnaldo Carvalho de Melo struct igmpv3_query *ih3 = igmpv3_query_hdr(skb); 8581da177e4SLinus Torvalds struct ip_mc_list *im; 85963007727SAl Viro __be32 group = ih->group; 8601da177e4SLinus Torvalds int max_delay; 8611da177e4SLinus Torvalds int mark = 0; 8621da177e4SLinus Torvalds 8631da177e4SLinus Torvalds 8645b7c8406SDavid Stevens if (len == 8) { 8651da177e4SLinus Torvalds if (ih->code == 0) { 8661da177e4SLinus Torvalds /* Alas, old v1 router presents here. */ 8671da177e4SLinus Torvalds 8681da177e4SLinus Torvalds max_delay = IGMP_Query_Response_Interval; 8691da177e4SLinus Torvalds in_dev->mr_v1_seen = jiffies + 8701da177e4SLinus Torvalds IGMP_V1_Router_Present_Timeout; 8711da177e4SLinus Torvalds group = 0; 8721da177e4SLinus Torvalds } else { 8731da177e4SLinus Torvalds /* v2 router present */ 8741da177e4SLinus Torvalds max_delay = ih->code*(HZ/IGMP_TIMER_SCALE); 8751da177e4SLinus Torvalds in_dev->mr_v2_seen = jiffies + 8761da177e4SLinus Torvalds IGMP_V2_Router_Present_Timeout; 8771da177e4SLinus Torvalds } 8781da177e4SLinus Torvalds /* cancel the interface change timer */ 8791da177e4SLinus Torvalds in_dev->mr_ifc_count = 0; 8801da177e4SLinus Torvalds if (del_timer(&in_dev->mr_ifc_timer)) 8811da177e4SLinus Torvalds __in_dev_put(in_dev); 8821da177e4SLinus Torvalds /* clear deleted report items */ 8831da177e4SLinus Torvalds igmpv3_clear_delrec(in_dev); 8841da177e4SLinus Torvalds } else if (len < 12) { 885d679c532SEric Dumazet return true; /* ignore bogus packet; freed by caller */ 8865b7c8406SDavid Stevens } else if (IGMP_V1_SEEN(in_dev)) { 8875b7c8406SDavid Stevens /* This is a v3 query with v1 queriers present */ 8885b7c8406SDavid Stevens max_delay = IGMP_Query_Response_Interval; 8895b7c8406SDavid Stevens group = 0; 8905b7c8406SDavid Stevens } else if (IGMP_V2_SEEN(in_dev)) { 8915b7c8406SDavid Stevens /* this is a v3 query with v2 queriers present; 8925b7c8406SDavid Stevens * Interpretation of the max_delay code is problematic here. 8935b7c8406SDavid Stevens * A real v2 host would use ih_code directly, while v3 has a 8945b7c8406SDavid Stevens * different encoding. We use the v3 encoding as more likely 8955b7c8406SDavid Stevens * to be intended in a v3 query. 8965b7c8406SDavid Stevens */ 8975b7c8406SDavid Stevens max_delay = IGMPV3_MRC(ih3->code)*(HZ/IGMP_TIMER_SCALE); 898a8c1f65cSBen Hutchings if (!max_delay) 899a8c1f65cSBen Hutchings max_delay = 1; /* can't mod w/ 0 */ 9001da177e4SLinus Torvalds } else { /* v3 */ 9011da177e4SLinus Torvalds if (!pskb_may_pull(skb, sizeof(struct igmpv3_query))) 902d679c532SEric Dumazet return true; 9031da177e4SLinus Torvalds 904d9edf9e2SArnaldo Carvalho de Melo ih3 = igmpv3_query_hdr(skb); 9051da177e4SLinus Torvalds if (ih3->nsrcs) { 9061da177e4SLinus Torvalds if (!pskb_may_pull(skb, sizeof(struct igmpv3_query) 90763007727SAl Viro + ntohs(ih3->nsrcs)*sizeof(__be32))) 908d679c532SEric Dumazet return true; 909d9edf9e2SArnaldo Carvalho de Melo ih3 = igmpv3_query_hdr(skb); 9101da177e4SLinus Torvalds } 9111da177e4SLinus Torvalds 9121da177e4SLinus Torvalds max_delay = IGMPV3_MRC(ih3->code)*(HZ/IGMP_TIMER_SCALE); 9131da177e4SLinus Torvalds if (!max_delay) 9141da177e4SLinus Torvalds max_delay = 1; /* can't mod w/ 0 */ 9151da177e4SLinus Torvalds in_dev->mr_maxdelay = max_delay; 9161da177e4SLinus Torvalds if (ih3->qrv) 9171da177e4SLinus Torvalds in_dev->mr_qrv = ih3->qrv; 9181da177e4SLinus Torvalds if (!group) { /* general query */ 9191da177e4SLinus Torvalds if (ih3->nsrcs) 920d679c532SEric Dumazet return false; /* no sources allowed */ 9211da177e4SLinus Torvalds igmp_gq_start_timer(in_dev); 922d679c532SEric Dumazet return false; 9231da177e4SLinus Torvalds } 9241da177e4SLinus Torvalds /* mark sources to include, if group & source-specific */ 9251da177e4SLinus Torvalds mark = ih3->nsrcs != 0; 9261da177e4SLinus Torvalds } 9271da177e4SLinus Torvalds 9281da177e4SLinus Torvalds /* 9291da177e4SLinus Torvalds * - Start the timers in all of our membership records 9301da177e4SLinus Torvalds * that the query applies to for the interface on 9311da177e4SLinus Torvalds * which the query arrived excl. those that belong 9321da177e4SLinus Torvalds * to a "local" group (224.0.0.X) 9331da177e4SLinus Torvalds * - For timers already running check if they need to 9341da177e4SLinus Torvalds * be reset. 9351da177e4SLinus Torvalds * - Use the igmp->igmp_code field as the maximum 9361da177e4SLinus Torvalds * delay possible 9371da177e4SLinus Torvalds */ 9381d7138deSEric Dumazet rcu_read_lock(); 9391d7138deSEric Dumazet for_each_pmc_rcu(in_dev, im) { 940ad12583fSDavid L Stevens int changed; 941ad12583fSDavid L Stevens 9421da177e4SLinus Torvalds if (group && group != im->multiaddr) 9431da177e4SLinus Torvalds continue; 9441da177e4SLinus Torvalds if (im->multiaddr == IGMP_ALL_HOSTS) 9451da177e4SLinus Torvalds continue; 9461da177e4SLinus Torvalds spin_lock_bh(&im->lock); 9471da177e4SLinus Torvalds if (im->tm_running) 9481da177e4SLinus Torvalds im->gsquery = im->gsquery && mark; 9491da177e4SLinus Torvalds else 9501da177e4SLinus Torvalds im->gsquery = mark; 951ad12583fSDavid L Stevens changed = !im->gsquery || 9521da177e4SLinus Torvalds igmp_marksources(im, ntohs(ih3->nsrcs), ih3->srcs); 9531da177e4SLinus Torvalds spin_unlock_bh(&im->lock); 954ad12583fSDavid L Stevens if (changed) 9551da177e4SLinus Torvalds igmp_mod_timer(im, max_delay); 9561da177e4SLinus Torvalds } 9571d7138deSEric Dumazet rcu_read_unlock(); 958d679c532SEric Dumazet return false; 9591da177e4SLinus Torvalds } 9601da177e4SLinus Torvalds 9619a57a9d2SEric Dumazet /* called in rcu_read_lock() section */ 9621da177e4SLinus Torvalds int igmp_rcv(struct sk_buff *skb) 9631da177e4SLinus Torvalds { 9641da177e4SLinus Torvalds /* This basically follows the spec line by line -- see RFC1112 */ 9651da177e4SLinus Torvalds struct igmphdr *ih; 9669a57a9d2SEric Dumazet struct in_device *in_dev = __in_dev_get_rcu(skb->dev); 9671da177e4SLinus Torvalds int len = skb->len; 968d679c532SEric Dumazet bool dropped = true; 9691da177e4SLinus Torvalds 970cd557bc1SDenis V. Lunev if (in_dev == NULL) 971cd557bc1SDenis V. Lunev goto drop; 9721da177e4SLinus Torvalds 973fb286bb2SHerbert Xu if (!pskb_may_pull(skb, sizeof(struct igmphdr))) 9749a57a9d2SEric Dumazet goto drop; 975fb286bb2SHerbert Xu 976fb286bb2SHerbert Xu switch (skb->ip_summed) { 97784fa7933SPatrick McHardy case CHECKSUM_COMPLETE: 978d3bc23e7SAl Viro if (!csum_fold(skb->csum)) 979fb286bb2SHerbert Xu break; 980fb286bb2SHerbert Xu /* fall through */ 981fb286bb2SHerbert Xu case CHECKSUM_NONE: 982fb286bb2SHerbert Xu skb->csum = 0; 983fb286bb2SHerbert Xu if (__skb_checksum_complete(skb)) 9849a57a9d2SEric Dumazet goto drop; 9851da177e4SLinus Torvalds } 9861da177e4SLinus Torvalds 987d9edf9e2SArnaldo Carvalho de Melo ih = igmp_hdr(skb); 9881da177e4SLinus Torvalds switch (ih->type) { 9891da177e4SLinus Torvalds case IGMP_HOST_MEMBERSHIP_QUERY: 990d679c532SEric Dumazet dropped = igmp_heard_query(in_dev, skb, len); 9911da177e4SLinus Torvalds break; 9921da177e4SLinus Torvalds case IGMP_HOST_MEMBERSHIP_REPORT: 9931da177e4SLinus Torvalds case IGMPV2_HOST_MEMBERSHIP_REPORT: 9941da177e4SLinus Torvalds /* Is it our report looped back? */ 995c7537967SDavid S. Miller if (rt_is_output_route(skb_rtable(skb))) 9961da177e4SLinus Torvalds break; 99724c69275SDavid Stevens /* don't rely on MC router hearing unicast reports */ 99824c69275SDavid Stevens if (skb->pkt_type == PACKET_MULTICAST || 99924c69275SDavid Stevens skb->pkt_type == PACKET_BROADCAST) 1000d679c532SEric Dumazet dropped = igmp_heard_report(in_dev, ih->group); 10011da177e4SLinus Torvalds break; 10021da177e4SLinus Torvalds case IGMP_PIM: 10031da177e4SLinus Torvalds #ifdef CONFIG_IP_PIMSM_V1 10041da177e4SLinus Torvalds return pim_rcv_v1(skb); 10051da177e4SLinus Torvalds #endif 1006c6b471e6SHerbert Xu case IGMPV3_HOST_MEMBERSHIP_REPORT: 10071da177e4SLinus Torvalds case IGMP_DVMRP: 10081da177e4SLinus Torvalds case IGMP_TRACE: 10091da177e4SLinus Torvalds case IGMP_HOST_LEAVE_MESSAGE: 10101da177e4SLinus Torvalds case IGMP_MTRACE: 10111da177e4SLinus Torvalds case IGMP_MTRACE_RESP: 10121da177e4SLinus Torvalds break; 10131da177e4SLinus Torvalds default: 1014dd1c1853SLinus Torvalds break; 10151da177e4SLinus Torvalds } 1016fb286bb2SHerbert Xu 1017cd557bc1SDenis V. Lunev drop: 1018d679c532SEric Dumazet if (dropped) 10191da177e4SLinus Torvalds kfree_skb(skb); 1020d679c532SEric Dumazet else 1021d679c532SEric Dumazet consume_skb(skb); 10221da177e4SLinus Torvalds return 0; 10231da177e4SLinus Torvalds } 10241da177e4SLinus Torvalds 10251da177e4SLinus Torvalds #endif 10261da177e4SLinus Torvalds 10271da177e4SLinus Torvalds 10281da177e4SLinus Torvalds /* 10291da177e4SLinus Torvalds * Add a filter to a device 10301da177e4SLinus Torvalds */ 10311da177e4SLinus Torvalds 103263007727SAl Viro static void ip_mc_filter_add(struct in_device *in_dev, __be32 addr) 10331da177e4SLinus Torvalds { 10341da177e4SLinus Torvalds char buf[MAX_ADDR_LEN]; 10351da177e4SLinus Torvalds struct net_device *dev = in_dev->dev; 10361da177e4SLinus Torvalds 10371da177e4SLinus Torvalds /* Checking for IFF_MULTICAST here is WRONG-WRONG-WRONG. 10381da177e4SLinus Torvalds We will get multicast token leakage, when IFF_MULTICAST 1039b81693d9SJiri Pirko is changed. This check should be done in ndo_set_rx_mode 10401da177e4SLinus Torvalds routine. Something sort of: 10411da177e4SLinus Torvalds if (dev->mc_list && dev->flags&IFF_MULTICAST) { do it; } 10421da177e4SLinus Torvalds --ANK 10431da177e4SLinus Torvalds */ 10441da177e4SLinus Torvalds if (arp_mc_map(addr, buf, dev, 0) == 0) 104522bedad3SJiri Pirko dev_mc_add(dev, buf); 10461da177e4SLinus Torvalds } 10471da177e4SLinus Torvalds 10481da177e4SLinus Torvalds /* 10491da177e4SLinus Torvalds * Remove a filter from a device 10501da177e4SLinus Torvalds */ 10511da177e4SLinus Torvalds 105263007727SAl Viro static void ip_mc_filter_del(struct in_device *in_dev, __be32 addr) 10531da177e4SLinus Torvalds { 10541da177e4SLinus Torvalds char buf[MAX_ADDR_LEN]; 10551da177e4SLinus Torvalds struct net_device *dev = in_dev->dev; 10561da177e4SLinus Torvalds 10571da177e4SLinus Torvalds if (arp_mc_map(addr, buf, dev, 0) == 0) 105822bedad3SJiri Pirko dev_mc_del(dev, buf); 10591da177e4SLinus Torvalds } 10601da177e4SLinus Torvalds 10611da177e4SLinus Torvalds #ifdef CONFIG_IP_MULTICAST 10621da177e4SLinus Torvalds /* 10631da177e4SLinus Torvalds * deleted ip_mc_list manipulation 10641da177e4SLinus Torvalds */ 10651da177e4SLinus Torvalds static void igmpv3_add_delrec(struct in_device *in_dev, struct ip_mc_list *im) 10661da177e4SLinus Torvalds { 10671da177e4SLinus Torvalds struct ip_mc_list *pmc; 10681da177e4SLinus Torvalds 10691da177e4SLinus Torvalds /* this is an "ip_mc_list" for convenience; only the fields below 10701da177e4SLinus Torvalds * are actually used. In particular, the refcnt and users are not 10711da177e4SLinus Torvalds * used for management of the delete list. Using the same structure 10721da177e4SLinus Torvalds * for deleted items allows change reports to use common code with 10731da177e4SLinus Torvalds * non-deleted or query-response MCA's. 10741da177e4SLinus Torvalds */ 10750da974f4SPanagiotis Issaris pmc = kzalloc(sizeof(*pmc), GFP_KERNEL); 10761da177e4SLinus Torvalds if (!pmc) 10771da177e4SLinus Torvalds return; 10781da177e4SLinus Torvalds spin_lock_bh(&im->lock); 10791da177e4SLinus Torvalds pmc->interface = im->interface; 10801da177e4SLinus Torvalds in_dev_hold(in_dev); 10811da177e4SLinus Torvalds pmc->multiaddr = im->multiaddr; 10821da177e4SLinus Torvalds pmc->crcount = in_dev->mr_qrv ? in_dev->mr_qrv : 10831da177e4SLinus Torvalds IGMP_Unsolicited_Report_Count; 10841da177e4SLinus Torvalds pmc->sfmode = im->sfmode; 10851da177e4SLinus Torvalds if (pmc->sfmode == MCAST_INCLUDE) { 10861da177e4SLinus Torvalds struct ip_sf_list *psf; 10871da177e4SLinus Torvalds 10881da177e4SLinus Torvalds pmc->tomb = im->tomb; 10891da177e4SLinus Torvalds pmc->sources = im->sources; 10901da177e4SLinus Torvalds im->tomb = im->sources = NULL; 10911da177e4SLinus Torvalds for (psf=pmc->sources; psf; psf=psf->sf_next) 10921da177e4SLinus Torvalds psf->sf_crcount = pmc->crcount; 10931da177e4SLinus Torvalds } 10941da177e4SLinus Torvalds spin_unlock_bh(&im->lock); 10951da177e4SLinus Torvalds 10961da177e4SLinus Torvalds spin_lock_bh(&in_dev->mc_tomb_lock); 10971da177e4SLinus Torvalds pmc->next = in_dev->mc_tomb; 10981da177e4SLinus Torvalds in_dev->mc_tomb = pmc; 10991da177e4SLinus Torvalds spin_unlock_bh(&in_dev->mc_tomb_lock); 11001da177e4SLinus Torvalds } 11011da177e4SLinus Torvalds 110263007727SAl Viro static void igmpv3_del_delrec(struct in_device *in_dev, __be32 multiaddr) 11031da177e4SLinus Torvalds { 11041da177e4SLinus Torvalds struct ip_mc_list *pmc, *pmc_prev; 11051da177e4SLinus Torvalds struct ip_sf_list *psf, *psf_next; 11061da177e4SLinus Torvalds 11071da177e4SLinus Torvalds spin_lock_bh(&in_dev->mc_tomb_lock); 11081da177e4SLinus Torvalds pmc_prev = NULL; 11091da177e4SLinus Torvalds for (pmc=in_dev->mc_tomb; pmc; pmc=pmc->next) { 11101da177e4SLinus Torvalds if (pmc->multiaddr == multiaddr) 11111da177e4SLinus Torvalds break; 11121da177e4SLinus Torvalds pmc_prev = pmc; 11131da177e4SLinus Torvalds } 11141da177e4SLinus Torvalds if (pmc) { 11151da177e4SLinus Torvalds if (pmc_prev) 11161da177e4SLinus Torvalds pmc_prev->next = pmc->next; 11171da177e4SLinus Torvalds else 11181da177e4SLinus Torvalds in_dev->mc_tomb = pmc->next; 11191da177e4SLinus Torvalds } 11201da177e4SLinus Torvalds spin_unlock_bh(&in_dev->mc_tomb_lock); 11211da177e4SLinus Torvalds if (pmc) { 11221da177e4SLinus Torvalds for (psf=pmc->tomb; psf; psf=psf_next) { 11231da177e4SLinus Torvalds psf_next = psf->sf_next; 11241da177e4SLinus Torvalds kfree(psf); 11251da177e4SLinus Torvalds } 11261da177e4SLinus Torvalds in_dev_put(pmc->interface); 11271da177e4SLinus Torvalds kfree(pmc); 11281da177e4SLinus Torvalds } 11291da177e4SLinus Torvalds } 11301da177e4SLinus Torvalds 11311da177e4SLinus Torvalds static void igmpv3_clear_delrec(struct in_device *in_dev) 11321da177e4SLinus Torvalds { 11331da177e4SLinus Torvalds struct ip_mc_list *pmc, *nextpmc; 11341da177e4SLinus Torvalds 11351da177e4SLinus Torvalds spin_lock_bh(&in_dev->mc_tomb_lock); 11361da177e4SLinus Torvalds pmc = in_dev->mc_tomb; 11371da177e4SLinus Torvalds in_dev->mc_tomb = NULL; 11381da177e4SLinus Torvalds spin_unlock_bh(&in_dev->mc_tomb_lock); 11391da177e4SLinus Torvalds 11401da177e4SLinus Torvalds for (; pmc; pmc = nextpmc) { 11411da177e4SLinus Torvalds nextpmc = pmc->next; 11421da177e4SLinus Torvalds ip_mc_clear_src(pmc); 11431da177e4SLinus Torvalds in_dev_put(pmc->interface); 11441da177e4SLinus Torvalds kfree(pmc); 11451da177e4SLinus Torvalds } 11461da177e4SLinus Torvalds /* clear dead sources, too */ 11471d7138deSEric Dumazet rcu_read_lock(); 11481d7138deSEric Dumazet for_each_pmc_rcu(in_dev, pmc) { 11491da177e4SLinus Torvalds struct ip_sf_list *psf, *psf_next; 11501da177e4SLinus Torvalds 11511da177e4SLinus Torvalds spin_lock_bh(&pmc->lock); 11521da177e4SLinus Torvalds psf = pmc->tomb; 11531da177e4SLinus Torvalds pmc->tomb = NULL; 11541da177e4SLinus Torvalds spin_unlock_bh(&pmc->lock); 11551da177e4SLinus Torvalds for (; psf; psf=psf_next) { 11561da177e4SLinus Torvalds psf_next = psf->sf_next; 11571da177e4SLinus Torvalds kfree(psf); 11581da177e4SLinus Torvalds } 11591da177e4SLinus Torvalds } 11601d7138deSEric Dumazet rcu_read_unlock(); 11611da177e4SLinus Torvalds } 11621da177e4SLinus Torvalds #endif 11631da177e4SLinus Torvalds 11641da177e4SLinus Torvalds static void igmp_group_dropped(struct ip_mc_list *im) 11651da177e4SLinus Torvalds { 11661da177e4SLinus Torvalds struct in_device *in_dev = im->interface; 11671da177e4SLinus Torvalds #ifdef CONFIG_IP_MULTICAST 11681da177e4SLinus Torvalds int reporter; 11691da177e4SLinus Torvalds #endif 11701da177e4SLinus Torvalds 11711da177e4SLinus Torvalds if (im->loaded) { 11721da177e4SLinus Torvalds im->loaded = 0; 11731da177e4SLinus Torvalds ip_mc_filter_del(in_dev, im->multiaddr); 11741da177e4SLinus Torvalds } 11751da177e4SLinus Torvalds 11761da177e4SLinus Torvalds #ifdef CONFIG_IP_MULTICAST 11771da177e4SLinus Torvalds if (im->multiaddr == IGMP_ALL_HOSTS) 11781da177e4SLinus Torvalds return; 11791da177e4SLinus Torvalds 11801da177e4SLinus Torvalds reporter = im->reporter; 11811da177e4SLinus Torvalds igmp_stop_timer(im); 11821da177e4SLinus Torvalds 11831da177e4SLinus Torvalds if (!in_dev->dead) { 11841da177e4SLinus Torvalds if (IGMP_V1_SEEN(in_dev)) 118524cf3af3SVeaceslav Falico return; 11861da177e4SLinus Torvalds if (IGMP_V2_SEEN(in_dev)) { 11871da177e4SLinus Torvalds if (reporter) 11881da177e4SLinus Torvalds igmp_send_report(in_dev, im, IGMP_HOST_LEAVE_MESSAGE); 118924cf3af3SVeaceslav Falico return; 11901da177e4SLinus Torvalds } 11911da177e4SLinus Torvalds /* IGMPv3 */ 11921da177e4SLinus Torvalds igmpv3_add_delrec(in_dev, im); 11931da177e4SLinus Torvalds 11941da177e4SLinus Torvalds igmp_ifc_event(in_dev); 11951da177e4SLinus Torvalds } 11961da177e4SLinus Torvalds #endif 11971da177e4SLinus Torvalds } 11981da177e4SLinus Torvalds 11991da177e4SLinus Torvalds static void igmp_group_added(struct ip_mc_list *im) 12001da177e4SLinus Torvalds { 12011da177e4SLinus Torvalds struct in_device *in_dev = im->interface; 12021da177e4SLinus Torvalds 12031da177e4SLinus Torvalds if (im->loaded == 0) { 12041da177e4SLinus Torvalds im->loaded = 1; 12051da177e4SLinus Torvalds ip_mc_filter_add(in_dev, im->multiaddr); 12061da177e4SLinus Torvalds } 12071da177e4SLinus Torvalds 12081da177e4SLinus Torvalds #ifdef CONFIG_IP_MULTICAST 12091da177e4SLinus Torvalds if (im->multiaddr == IGMP_ALL_HOSTS) 12101da177e4SLinus Torvalds return; 12111da177e4SLinus Torvalds 12121da177e4SLinus Torvalds if (in_dev->dead) 12131da177e4SLinus Torvalds return; 12141da177e4SLinus Torvalds if (IGMP_V1_SEEN(in_dev) || IGMP_V2_SEEN(in_dev)) { 12151da177e4SLinus Torvalds spin_lock_bh(&im->lock); 12161da177e4SLinus Torvalds igmp_start_timer(im, IGMP_Initial_Report_Delay); 12171da177e4SLinus Torvalds spin_unlock_bh(&im->lock); 12181da177e4SLinus Torvalds return; 12191da177e4SLinus Torvalds } 12201da177e4SLinus Torvalds /* else, v3 */ 12211da177e4SLinus Torvalds 12221da177e4SLinus Torvalds im->crcount = in_dev->mr_qrv ? in_dev->mr_qrv : 12231da177e4SLinus Torvalds IGMP_Unsolicited_Report_Count; 12241da177e4SLinus Torvalds igmp_ifc_event(in_dev); 12251da177e4SLinus Torvalds #endif 12261da177e4SLinus Torvalds } 12271da177e4SLinus Torvalds 12281da177e4SLinus Torvalds 12291da177e4SLinus Torvalds /* 12301da177e4SLinus Torvalds * Multicast list managers 12311da177e4SLinus Torvalds */ 12321da177e4SLinus Torvalds 1233e9897071SEric Dumazet static u32 ip_mc_hash(const struct ip_mc_list *im) 1234e9897071SEric Dumazet { 1235c70eba74SEric Dumazet return hash_32((__force u32)im->multiaddr, MC_HASH_SZ_LOG); 1236e9897071SEric Dumazet } 1237e9897071SEric Dumazet 1238e9897071SEric Dumazet static void ip_mc_hash_add(struct in_device *in_dev, 1239e9897071SEric Dumazet struct ip_mc_list *im) 1240e9897071SEric Dumazet { 1241e9897071SEric Dumazet struct ip_mc_list __rcu **mc_hash; 1242e9897071SEric Dumazet u32 hash; 1243e9897071SEric Dumazet 1244e9897071SEric Dumazet mc_hash = rtnl_dereference(in_dev->mc_hash); 1245e9897071SEric Dumazet if (mc_hash) { 1246e9897071SEric Dumazet hash = ip_mc_hash(im); 1247c70eba74SEric Dumazet im->next_hash = mc_hash[hash]; 1248e9897071SEric Dumazet rcu_assign_pointer(mc_hash[hash], im); 1249e9897071SEric Dumazet return; 1250e9897071SEric Dumazet } 1251e9897071SEric Dumazet 1252e9897071SEric Dumazet /* do not use a hash table for small number of items */ 1253e9897071SEric Dumazet if (in_dev->mc_count < 4) 1254e9897071SEric Dumazet return; 1255e9897071SEric Dumazet 1256e9897071SEric Dumazet mc_hash = kzalloc(sizeof(struct ip_mc_list *) << MC_HASH_SZ_LOG, 1257e9897071SEric Dumazet GFP_KERNEL); 1258e9897071SEric Dumazet if (!mc_hash) 1259e9897071SEric Dumazet return; 1260e9897071SEric Dumazet 1261e9897071SEric Dumazet for_each_pmc_rtnl(in_dev, im) { 1262e9897071SEric Dumazet hash = ip_mc_hash(im); 1263c70eba74SEric Dumazet im->next_hash = mc_hash[hash]; 1264e9897071SEric Dumazet RCU_INIT_POINTER(mc_hash[hash], im); 1265e9897071SEric Dumazet } 1266e9897071SEric Dumazet 1267e9897071SEric Dumazet rcu_assign_pointer(in_dev->mc_hash, mc_hash); 1268e9897071SEric Dumazet } 1269e9897071SEric Dumazet 1270e9897071SEric Dumazet static void ip_mc_hash_remove(struct in_device *in_dev, 1271e9897071SEric Dumazet struct ip_mc_list *im) 1272e9897071SEric Dumazet { 1273e9897071SEric Dumazet struct ip_mc_list __rcu **mc_hash = rtnl_dereference(in_dev->mc_hash); 1274e9897071SEric Dumazet struct ip_mc_list *aux; 1275e9897071SEric Dumazet 1276e9897071SEric Dumazet if (!mc_hash) 1277e9897071SEric Dumazet return; 1278e9897071SEric Dumazet mc_hash += ip_mc_hash(im); 1279e9897071SEric Dumazet while ((aux = rtnl_dereference(*mc_hash)) != im) 1280e9897071SEric Dumazet mc_hash = &aux->next_hash; 1281e9897071SEric Dumazet *mc_hash = im->next_hash; 1282e9897071SEric Dumazet } 1283e9897071SEric Dumazet 12841da177e4SLinus Torvalds 12851da177e4SLinus Torvalds /* 12861da177e4SLinus Torvalds * A socket has joined a multicast group on device dev. 12871da177e4SLinus Torvalds */ 12881da177e4SLinus Torvalds 12898f935bbdSAl Viro void ip_mc_inc_group(struct in_device *in_dev, __be32 addr) 12901da177e4SLinus Torvalds { 12911da177e4SLinus Torvalds struct ip_mc_list *im; 12921da177e4SLinus Torvalds 12931da177e4SLinus Torvalds ASSERT_RTNL(); 12941da177e4SLinus Torvalds 12951d7138deSEric Dumazet for_each_pmc_rtnl(in_dev, im) { 12961da177e4SLinus Torvalds if (im->multiaddr == addr) { 12971da177e4SLinus Torvalds im->users++; 12981da177e4SLinus Torvalds ip_mc_add_src(in_dev, &addr, MCAST_EXCLUDE, 0, NULL, 0); 12991da177e4SLinus Torvalds goto out; 13001da177e4SLinus Torvalds } 13011da177e4SLinus Torvalds } 13021da177e4SLinus Torvalds 13031d7138deSEric Dumazet im = kzalloc(sizeof(*im), GFP_KERNEL); 13041da177e4SLinus Torvalds if (!im) 13051da177e4SLinus Torvalds goto out; 13061da177e4SLinus Torvalds 13071da177e4SLinus Torvalds im->users = 1; 13081da177e4SLinus Torvalds im->interface = in_dev; 13091da177e4SLinus Torvalds in_dev_hold(in_dev); 13101da177e4SLinus Torvalds im->multiaddr = addr; 13111da177e4SLinus Torvalds /* initial mode is (EX, empty) */ 13121da177e4SLinus Torvalds im->sfmode = MCAST_EXCLUDE; 13131da177e4SLinus Torvalds im->sfcount[MCAST_EXCLUDE] = 1; 13141da177e4SLinus Torvalds atomic_set(&im->refcnt, 1); 13151da177e4SLinus Torvalds spin_lock_init(&im->lock); 13161da177e4SLinus Torvalds #ifdef CONFIG_IP_MULTICAST 1317b24b8a24SPavel Emelyanov setup_timer(&im->timer, &igmp_timer_expire, (unsigned long)im); 13181da177e4SLinus Torvalds im->unsolicit_count = IGMP_Unsolicited_Report_Count; 13191da177e4SLinus Torvalds #endif 13201d7138deSEric Dumazet 13211d7138deSEric Dumazet im->next_rcu = in_dev->mc_list; 1322b8bae41eSRami Rosen in_dev->mc_count++; 1323cf778b00SEric Dumazet rcu_assign_pointer(in_dev->mc_list, im); 13241d7138deSEric Dumazet 1325e9897071SEric Dumazet ip_mc_hash_add(in_dev, im); 1326e9897071SEric Dumazet 13271da177e4SLinus Torvalds #ifdef CONFIG_IP_MULTICAST 13281da177e4SLinus Torvalds igmpv3_del_delrec(in_dev, im->multiaddr); 13291da177e4SLinus Torvalds #endif 13301da177e4SLinus Torvalds igmp_group_added(im); 13311da177e4SLinus Torvalds if (!in_dev->dead) 13321da177e4SLinus Torvalds ip_rt_multicast_event(in_dev); 13331da177e4SLinus Torvalds out: 13341da177e4SLinus Torvalds return; 13351da177e4SLinus Torvalds } 13364bc2f18bSEric Dumazet EXPORT_SYMBOL(ip_mc_inc_group); 13371da177e4SLinus Torvalds 13381da177e4SLinus Torvalds /* 13394aa5dee4SJiri Pirko * Resend IGMP JOIN report; used by netdev notifier. 1340a816c7c7SJay Vosburgh */ 13414aa5dee4SJiri Pirko static void ip_mc_rejoin_groups(struct in_device *in_dev) 1342a816c7c7SJay Vosburgh { 134308882669SGeert Uytterhoeven #ifdef CONFIG_IP_MULTICAST 1344866f3b25SEric Dumazet struct ip_mc_list *im; 1345866f3b25SEric Dumazet int type; 1346a816c7c7SJay Vosburgh 13474aa5dee4SJiri Pirko ASSERT_RTNL(); 13484aa5dee4SJiri Pirko 13494aa5dee4SJiri Pirko for_each_pmc_rtnl(in_dev, im) { 1350a816c7c7SJay Vosburgh if (im->multiaddr == IGMP_ALL_HOSTS) 1351866f3b25SEric Dumazet continue; 1352a816c7c7SJay Vosburgh 1353e12b4539SFlavio Leitner /* a failover is happening and switches 1354866f3b25SEric Dumazet * must be notified immediately 1355866f3b25SEric Dumazet */ 1356e12b4539SFlavio Leitner if (IGMP_V1_SEEN(in_dev)) 1357866f3b25SEric Dumazet type = IGMP_HOST_MEMBERSHIP_REPORT; 1358e12b4539SFlavio Leitner else if (IGMP_V2_SEEN(in_dev)) 1359866f3b25SEric Dumazet type = IGMPV2_HOST_MEMBERSHIP_REPORT; 1360e12b4539SFlavio Leitner else 1361866f3b25SEric Dumazet type = IGMPV3_HOST_MEMBERSHIP_REPORT; 1362866f3b25SEric Dumazet igmp_send_report(in_dev, im, type); 1363866f3b25SEric Dumazet } 1364a816c7c7SJay Vosburgh #endif 1365a816c7c7SJay Vosburgh } 1366a816c7c7SJay Vosburgh 1367a816c7c7SJay Vosburgh /* 13681da177e4SLinus Torvalds * A socket has left a multicast group on device dev 13691da177e4SLinus Torvalds */ 13701da177e4SLinus Torvalds 13718f935bbdSAl Viro void ip_mc_dec_group(struct in_device *in_dev, __be32 addr) 13721da177e4SLinus Torvalds { 13731d7138deSEric Dumazet struct ip_mc_list *i; 13741d7138deSEric Dumazet struct ip_mc_list __rcu **ip; 13751da177e4SLinus Torvalds 13761da177e4SLinus Torvalds ASSERT_RTNL(); 13771da177e4SLinus Torvalds 13781d7138deSEric Dumazet for (ip = &in_dev->mc_list; 13791d7138deSEric Dumazet (i = rtnl_dereference(*ip)) != NULL; 13801d7138deSEric Dumazet ip = &i->next_rcu) { 13811da177e4SLinus Torvalds if (i->multiaddr == addr) { 13821da177e4SLinus Torvalds if (--i->users == 0) { 1383e9897071SEric Dumazet ip_mc_hash_remove(in_dev, i); 13841d7138deSEric Dumazet *ip = i->next_rcu; 1385b8bae41eSRami Rosen in_dev->mc_count--; 13861da177e4SLinus Torvalds igmp_group_dropped(i); 138724cf3af3SVeaceslav Falico ip_mc_clear_src(i); 13881da177e4SLinus Torvalds 13891da177e4SLinus Torvalds if (!in_dev->dead) 13901da177e4SLinus Torvalds ip_rt_multicast_event(in_dev); 13911da177e4SLinus Torvalds 13921da177e4SLinus Torvalds ip_ma_put(i); 13931da177e4SLinus Torvalds return; 13941da177e4SLinus Torvalds } 13951da177e4SLinus Torvalds break; 13961da177e4SLinus Torvalds } 13971da177e4SLinus Torvalds } 13981da177e4SLinus Torvalds } 13994bc2f18bSEric Dumazet EXPORT_SYMBOL(ip_mc_dec_group); 14001da177e4SLinus Torvalds 140175c78500SMoni Shoua /* Device changing type */ 140275c78500SMoni Shoua 140375c78500SMoni Shoua void ip_mc_unmap(struct in_device *in_dev) 140475c78500SMoni Shoua { 14051d7138deSEric Dumazet struct ip_mc_list *pmc; 140675c78500SMoni Shoua 140775c78500SMoni Shoua ASSERT_RTNL(); 140875c78500SMoni Shoua 14091d7138deSEric Dumazet for_each_pmc_rtnl(in_dev, pmc) 14101d7138deSEric Dumazet igmp_group_dropped(pmc); 141175c78500SMoni Shoua } 141275c78500SMoni Shoua 141375c78500SMoni Shoua void ip_mc_remap(struct in_device *in_dev) 141475c78500SMoni Shoua { 14151d7138deSEric Dumazet struct ip_mc_list *pmc; 141675c78500SMoni Shoua 141775c78500SMoni Shoua ASSERT_RTNL(); 141875c78500SMoni Shoua 14191d7138deSEric Dumazet for_each_pmc_rtnl(in_dev, pmc) 14201d7138deSEric Dumazet igmp_group_added(pmc); 142175c78500SMoni Shoua } 142275c78500SMoni Shoua 14231da177e4SLinus Torvalds /* Device going down */ 14241da177e4SLinus Torvalds 14251da177e4SLinus Torvalds void ip_mc_down(struct in_device *in_dev) 14261da177e4SLinus Torvalds { 14271d7138deSEric Dumazet struct ip_mc_list *pmc; 14281da177e4SLinus Torvalds 14291da177e4SLinus Torvalds ASSERT_RTNL(); 14301da177e4SLinus Torvalds 14311d7138deSEric Dumazet for_each_pmc_rtnl(in_dev, pmc) 14321d7138deSEric Dumazet igmp_group_dropped(pmc); 14331da177e4SLinus Torvalds 14341da177e4SLinus Torvalds #ifdef CONFIG_IP_MULTICAST 14351da177e4SLinus Torvalds in_dev->mr_ifc_count = 0; 14361da177e4SLinus Torvalds if (del_timer(&in_dev->mr_ifc_timer)) 14371da177e4SLinus Torvalds __in_dev_put(in_dev); 14381da177e4SLinus Torvalds in_dev->mr_gq_running = 0; 14391da177e4SLinus Torvalds if (del_timer(&in_dev->mr_gq_timer)) 14401da177e4SLinus Torvalds __in_dev_put(in_dev); 14411da177e4SLinus Torvalds igmpv3_clear_delrec(in_dev); 14421da177e4SLinus Torvalds #endif 14431da177e4SLinus Torvalds 14441da177e4SLinus Torvalds ip_mc_dec_group(in_dev, IGMP_ALL_HOSTS); 14451da177e4SLinus Torvalds } 14461da177e4SLinus Torvalds 14471da177e4SLinus Torvalds void ip_mc_init_dev(struct in_device *in_dev) 14481da177e4SLinus Torvalds { 14491da177e4SLinus Torvalds ASSERT_RTNL(); 14501da177e4SLinus Torvalds 14511da177e4SLinus Torvalds #ifdef CONFIG_IP_MULTICAST 1452b24b8a24SPavel Emelyanov setup_timer(&in_dev->mr_gq_timer, igmp_gq_timer_expire, 1453b24b8a24SPavel Emelyanov (unsigned long)in_dev); 1454b24b8a24SPavel Emelyanov setup_timer(&in_dev->mr_ifc_timer, igmp_ifc_timer_expire, 1455b24b8a24SPavel Emelyanov (unsigned long)in_dev); 14561da177e4SLinus Torvalds in_dev->mr_qrv = IGMP_Unsolicited_Report_Count; 14571da177e4SLinus Torvalds #endif 14581da177e4SLinus Torvalds 14591da177e4SLinus Torvalds spin_lock_init(&in_dev->mc_tomb_lock); 14601da177e4SLinus Torvalds } 14611da177e4SLinus Torvalds 14621da177e4SLinus Torvalds /* Device going up */ 14631da177e4SLinus Torvalds 14641da177e4SLinus Torvalds void ip_mc_up(struct in_device *in_dev) 14651da177e4SLinus Torvalds { 14661d7138deSEric Dumazet struct ip_mc_list *pmc; 14671da177e4SLinus Torvalds 14681da177e4SLinus Torvalds ASSERT_RTNL(); 14691da177e4SLinus Torvalds 14701da177e4SLinus Torvalds ip_mc_inc_group(in_dev, IGMP_ALL_HOSTS); 14711da177e4SLinus Torvalds 14721d7138deSEric Dumazet for_each_pmc_rtnl(in_dev, pmc) 14731d7138deSEric Dumazet igmp_group_added(pmc); 14741da177e4SLinus Torvalds } 14751da177e4SLinus Torvalds 14761da177e4SLinus Torvalds /* 14771da177e4SLinus Torvalds * Device is about to be destroyed: clean up. 14781da177e4SLinus Torvalds */ 14791da177e4SLinus Torvalds 14801da177e4SLinus Torvalds void ip_mc_destroy_dev(struct in_device *in_dev) 14811da177e4SLinus Torvalds { 14821da177e4SLinus Torvalds struct ip_mc_list *i; 14831da177e4SLinus Torvalds 14841da177e4SLinus Torvalds ASSERT_RTNL(); 14851da177e4SLinus Torvalds 14861da177e4SLinus Torvalds /* Deactivate timers */ 14871da177e4SLinus Torvalds ip_mc_down(in_dev); 14881da177e4SLinus Torvalds 14891d7138deSEric Dumazet while ((i = rtnl_dereference(in_dev->mc_list)) != NULL) { 14901d7138deSEric Dumazet in_dev->mc_list = i->next_rcu; 1491b8bae41eSRami Rosen in_dev->mc_count--; 14921d7138deSEric Dumazet 149324cf3af3SVeaceslav Falico /* We've dropped the groups in ip_mc_down already */ 149424cf3af3SVeaceslav Falico ip_mc_clear_src(i); 14951da177e4SLinus Torvalds ip_ma_put(i); 14961da177e4SLinus Torvalds } 14971da177e4SLinus Torvalds } 14981da177e4SLinus Torvalds 14999e917dcaSEric Dumazet /* RTNL is locked */ 1500877acedcSDaniel Lezcano static struct in_device *ip_mc_find_dev(struct net *net, struct ip_mreqn *imr) 15011da177e4SLinus Torvalds { 15021da177e4SLinus Torvalds struct net_device *dev = NULL; 15031da177e4SLinus Torvalds struct in_device *idev = NULL; 15041da177e4SLinus Torvalds 15051da177e4SLinus Torvalds if (imr->imr_ifindex) { 1506877acedcSDaniel Lezcano idev = inetdev_by_index(net, imr->imr_ifindex); 15071da177e4SLinus Torvalds return idev; 15081da177e4SLinus Torvalds } 15091da177e4SLinus Torvalds if (imr->imr_address.s_addr) { 15109e917dcaSEric Dumazet dev = __ip_dev_find(net, imr->imr_address.s_addr, false); 15111da177e4SLinus Torvalds if (!dev) 15121da177e4SLinus Torvalds return NULL; 15131da177e4SLinus Torvalds } 15141da177e4SLinus Torvalds 1515b23dd4feSDavid S. Miller if (!dev) { 151678fbfd8aSDavid S. Miller struct rtable *rt = ip_route_output(net, 151778fbfd8aSDavid S. Miller imr->imr_multiaddr.s_addr, 151878fbfd8aSDavid S. Miller 0, 0, 0); 1519b23dd4feSDavid S. Miller if (!IS_ERR(rt)) { 1520d8d1f30bSChangli Gao dev = rt->dst.dev; 15211da177e4SLinus Torvalds ip_rt_put(rt); 15221da177e4SLinus Torvalds } 1523b23dd4feSDavid S. Miller } 15241da177e4SLinus Torvalds if (dev) { 15251da177e4SLinus Torvalds imr->imr_ifindex = dev->ifindex; 1526e5ed6399SHerbert Xu idev = __in_dev_get_rtnl(dev); 15271da177e4SLinus Torvalds } 15281da177e4SLinus Torvalds return idev; 15291da177e4SLinus Torvalds } 15301da177e4SLinus Torvalds 15311da177e4SLinus Torvalds /* 15321da177e4SLinus Torvalds * Join a socket to a group 15331da177e4SLinus Torvalds */ 1534ab32ea5dSBrian Haley int sysctl_igmp_max_memberships __read_mostly = IP_MAX_MEMBERSHIPS; 1535ab32ea5dSBrian Haley int sysctl_igmp_max_msf __read_mostly = IP_MAX_MSF; 15361da177e4SLinus Torvalds 15371da177e4SLinus Torvalds 15381da177e4SLinus Torvalds static int ip_mc_del1_src(struct ip_mc_list *pmc, int sfmode, 15398f935bbdSAl Viro __be32 *psfsrc) 15401da177e4SLinus Torvalds { 15411da177e4SLinus Torvalds struct ip_sf_list *psf, *psf_prev; 15421da177e4SLinus Torvalds int rv = 0; 15431da177e4SLinus Torvalds 15441da177e4SLinus Torvalds psf_prev = NULL; 15451da177e4SLinus Torvalds for (psf=pmc->sources; psf; psf=psf->sf_next) { 15461da177e4SLinus Torvalds if (psf->sf_inaddr == *psfsrc) 15471da177e4SLinus Torvalds break; 15481da177e4SLinus Torvalds psf_prev = psf; 15491da177e4SLinus Torvalds } 15501da177e4SLinus Torvalds if (!psf || psf->sf_count[sfmode] == 0) { 15511da177e4SLinus Torvalds /* source filter not found, or count wrong => bug */ 15521da177e4SLinus Torvalds return -ESRCH; 15531da177e4SLinus Torvalds } 15541da177e4SLinus Torvalds psf->sf_count[sfmode]--; 15551da177e4SLinus Torvalds if (psf->sf_count[sfmode] == 0) { 15561da177e4SLinus Torvalds ip_rt_multicast_event(pmc->interface); 15571da177e4SLinus Torvalds } 15581da177e4SLinus Torvalds if (!psf->sf_count[MCAST_INCLUDE] && !psf->sf_count[MCAST_EXCLUDE]) { 15591da177e4SLinus Torvalds #ifdef CONFIG_IP_MULTICAST 15601da177e4SLinus Torvalds struct in_device *in_dev = pmc->interface; 15611da177e4SLinus Torvalds #endif 15621da177e4SLinus Torvalds 15631da177e4SLinus Torvalds /* no more filters for this source */ 15641da177e4SLinus Torvalds if (psf_prev) 15651da177e4SLinus Torvalds psf_prev->sf_next = psf->sf_next; 15661da177e4SLinus Torvalds else 15671da177e4SLinus Torvalds pmc->sources = psf->sf_next; 15681da177e4SLinus Torvalds #ifdef CONFIG_IP_MULTICAST 15691da177e4SLinus Torvalds if (psf->sf_oldin && 15701da177e4SLinus Torvalds !IGMP_V1_SEEN(in_dev) && !IGMP_V2_SEEN(in_dev)) { 15711da177e4SLinus Torvalds psf->sf_crcount = in_dev->mr_qrv ? in_dev->mr_qrv : 15721da177e4SLinus Torvalds IGMP_Unsolicited_Report_Count; 15731da177e4SLinus Torvalds psf->sf_next = pmc->tomb; 15741da177e4SLinus Torvalds pmc->tomb = psf; 15751da177e4SLinus Torvalds rv = 1; 15761da177e4SLinus Torvalds } else 15771da177e4SLinus Torvalds #endif 15781da177e4SLinus Torvalds kfree(psf); 15791da177e4SLinus Torvalds } 15801da177e4SLinus Torvalds return rv; 15811da177e4SLinus Torvalds } 15821da177e4SLinus Torvalds 15831da177e4SLinus Torvalds #ifndef CONFIG_IP_MULTICAST 15841da177e4SLinus Torvalds #define igmp_ifc_event(x) do { } while (0) 15851da177e4SLinus Torvalds #endif 15861da177e4SLinus Torvalds 15878f935bbdSAl Viro static int ip_mc_del_src(struct in_device *in_dev, __be32 *pmca, int sfmode, 15888f935bbdSAl Viro int sfcount, __be32 *psfsrc, int delta) 15891da177e4SLinus Torvalds { 15901da177e4SLinus Torvalds struct ip_mc_list *pmc; 15911da177e4SLinus Torvalds int changerec = 0; 15921da177e4SLinus Torvalds int i, err; 15931da177e4SLinus Torvalds 15941da177e4SLinus Torvalds if (!in_dev) 15951da177e4SLinus Torvalds return -ENODEV; 15961d7138deSEric Dumazet rcu_read_lock(); 15971d7138deSEric Dumazet for_each_pmc_rcu(in_dev, pmc) { 15981da177e4SLinus Torvalds if (*pmca == pmc->multiaddr) 15991da177e4SLinus Torvalds break; 16001da177e4SLinus Torvalds } 16011da177e4SLinus Torvalds if (!pmc) { 16021da177e4SLinus Torvalds /* MCA not found?? bug */ 16031d7138deSEric Dumazet rcu_read_unlock(); 16041da177e4SLinus Torvalds return -ESRCH; 16051da177e4SLinus Torvalds } 16061da177e4SLinus Torvalds spin_lock_bh(&pmc->lock); 16071d7138deSEric Dumazet rcu_read_unlock(); 16081da177e4SLinus Torvalds #ifdef CONFIG_IP_MULTICAST 16091da177e4SLinus Torvalds sf_markstate(pmc); 16101da177e4SLinus Torvalds #endif 16111da177e4SLinus Torvalds if (!delta) { 16121da177e4SLinus Torvalds err = -EINVAL; 16131da177e4SLinus Torvalds if (!pmc->sfcount[sfmode]) 16141da177e4SLinus Torvalds goto out_unlock; 16151da177e4SLinus Torvalds pmc->sfcount[sfmode]--; 16161da177e4SLinus Torvalds } 16171da177e4SLinus Torvalds err = 0; 16181da177e4SLinus Torvalds for (i=0; i<sfcount; i++) { 16191da177e4SLinus Torvalds int rv = ip_mc_del1_src(pmc, sfmode, &psfsrc[i]); 16201da177e4SLinus Torvalds 16211da177e4SLinus Torvalds changerec |= rv > 0; 16221da177e4SLinus Torvalds if (!err && rv < 0) 16231da177e4SLinus Torvalds err = rv; 16241da177e4SLinus Torvalds } 16251da177e4SLinus Torvalds if (pmc->sfmode == MCAST_EXCLUDE && 16261da177e4SLinus Torvalds pmc->sfcount[MCAST_EXCLUDE] == 0 && 16271da177e4SLinus Torvalds pmc->sfcount[MCAST_INCLUDE]) { 16281da177e4SLinus Torvalds #ifdef CONFIG_IP_MULTICAST 16291da177e4SLinus Torvalds struct ip_sf_list *psf; 16301da177e4SLinus Torvalds #endif 16311da177e4SLinus Torvalds 16321da177e4SLinus Torvalds /* filter mode change */ 16331da177e4SLinus Torvalds pmc->sfmode = MCAST_INCLUDE; 16341da177e4SLinus Torvalds #ifdef CONFIG_IP_MULTICAST 16351da177e4SLinus Torvalds pmc->crcount = in_dev->mr_qrv ? in_dev->mr_qrv : 16361da177e4SLinus Torvalds IGMP_Unsolicited_Report_Count; 16371da177e4SLinus Torvalds in_dev->mr_ifc_count = pmc->crcount; 16381da177e4SLinus Torvalds for (psf=pmc->sources; psf; psf = psf->sf_next) 16391da177e4SLinus Torvalds psf->sf_crcount = 0; 16401da177e4SLinus Torvalds igmp_ifc_event(pmc->interface); 16411da177e4SLinus Torvalds } else if (sf_setstate(pmc) || changerec) { 16421da177e4SLinus Torvalds igmp_ifc_event(pmc->interface); 16431da177e4SLinus Torvalds #endif 16441da177e4SLinus Torvalds } 16451da177e4SLinus Torvalds out_unlock: 16461da177e4SLinus Torvalds spin_unlock_bh(&pmc->lock); 16471da177e4SLinus Torvalds return err; 16481da177e4SLinus Torvalds } 16491da177e4SLinus Torvalds 16501da177e4SLinus Torvalds /* 16511da177e4SLinus Torvalds * Add multicast single-source filter to the interface list 16521da177e4SLinus Torvalds */ 16531da177e4SLinus Torvalds static int ip_mc_add1_src(struct ip_mc_list *pmc, int sfmode, 16545eb81e89SJun Zhao __be32 *psfsrc) 16551da177e4SLinus Torvalds { 16561da177e4SLinus Torvalds struct ip_sf_list *psf, *psf_prev; 16571da177e4SLinus Torvalds 16581da177e4SLinus Torvalds psf_prev = NULL; 16591da177e4SLinus Torvalds for (psf=pmc->sources; psf; psf=psf->sf_next) { 16601da177e4SLinus Torvalds if (psf->sf_inaddr == *psfsrc) 16611da177e4SLinus Torvalds break; 16621da177e4SLinus Torvalds psf_prev = psf; 16631da177e4SLinus Torvalds } 16641da177e4SLinus Torvalds if (!psf) { 16650da974f4SPanagiotis Issaris psf = kzalloc(sizeof(*psf), GFP_ATOMIC); 16661da177e4SLinus Torvalds if (!psf) 16671da177e4SLinus Torvalds return -ENOBUFS; 16681da177e4SLinus Torvalds psf->sf_inaddr = *psfsrc; 16691da177e4SLinus Torvalds if (psf_prev) { 16701da177e4SLinus Torvalds psf_prev->sf_next = psf; 16711da177e4SLinus Torvalds } else 16721da177e4SLinus Torvalds pmc->sources = psf; 16731da177e4SLinus Torvalds } 16741da177e4SLinus Torvalds psf->sf_count[sfmode]++; 16751da177e4SLinus Torvalds if (psf->sf_count[sfmode] == 1) { 16761da177e4SLinus Torvalds ip_rt_multicast_event(pmc->interface); 16771da177e4SLinus Torvalds } 16781da177e4SLinus Torvalds return 0; 16791da177e4SLinus Torvalds } 16801da177e4SLinus Torvalds 16811da177e4SLinus Torvalds #ifdef CONFIG_IP_MULTICAST 16821da177e4SLinus Torvalds static void sf_markstate(struct ip_mc_list *pmc) 16831da177e4SLinus Torvalds { 16841da177e4SLinus Torvalds struct ip_sf_list *psf; 16851da177e4SLinus Torvalds int mca_xcount = pmc->sfcount[MCAST_EXCLUDE]; 16861da177e4SLinus Torvalds 16871da177e4SLinus Torvalds for (psf=pmc->sources; psf; psf=psf->sf_next) 16881da177e4SLinus Torvalds if (pmc->sfcount[MCAST_EXCLUDE]) { 16891da177e4SLinus Torvalds psf->sf_oldin = mca_xcount == 16901da177e4SLinus Torvalds psf->sf_count[MCAST_EXCLUDE] && 16911da177e4SLinus Torvalds !psf->sf_count[MCAST_INCLUDE]; 16921da177e4SLinus Torvalds } else 16931da177e4SLinus Torvalds psf->sf_oldin = psf->sf_count[MCAST_INCLUDE] != 0; 16941da177e4SLinus Torvalds } 16951da177e4SLinus Torvalds 16961da177e4SLinus Torvalds static int sf_setstate(struct ip_mc_list *pmc) 16971da177e4SLinus Torvalds { 1698ad12583fSDavid L Stevens struct ip_sf_list *psf, *dpsf; 16991da177e4SLinus Torvalds int mca_xcount = pmc->sfcount[MCAST_EXCLUDE]; 17001da177e4SLinus Torvalds int qrv = pmc->interface->mr_qrv; 17011da177e4SLinus Torvalds int new_in, rv; 17021da177e4SLinus Torvalds 17031da177e4SLinus Torvalds rv = 0; 17041da177e4SLinus Torvalds for (psf=pmc->sources; psf; psf=psf->sf_next) { 17051da177e4SLinus Torvalds if (pmc->sfcount[MCAST_EXCLUDE]) { 17061da177e4SLinus Torvalds new_in = mca_xcount == psf->sf_count[MCAST_EXCLUDE] && 17071da177e4SLinus Torvalds !psf->sf_count[MCAST_INCLUDE]; 17081da177e4SLinus Torvalds } else 17091da177e4SLinus Torvalds new_in = psf->sf_count[MCAST_INCLUDE] != 0; 1710ad12583fSDavid L Stevens if (new_in) { 1711ad12583fSDavid L Stevens if (!psf->sf_oldin) { 171276edc605SAl Viro struct ip_sf_list *prev = NULL; 1713ad12583fSDavid L Stevens 1714ad12583fSDavid L Stevens for (dpsf=pmc->tomb; dpsf; dpsf=dpsf->sf_next) { 1715ad12583fSDavid L Stevens if (dpsf->sf_inaddr == psf->sf_inaddr) 1716ad12583fSDavid L Stevens break; 1717ad12583fSDavid L Stevens prev = dpsf; 1718ad12583fSDavid L Stevens } 1719ad12583fSDavid L Stevens if (dpsf) { 1720ad12583fSDavid L Stevens if (prev) 1721ad12583fSDavid L Stevens prev->sf_next = dpsf->sf_next; 1722ad12583fSDavid L Stevens else 1723ad12583fSDavid L Stevens pmc->tomb = dpsf->sf_next; 1724ad12583fSDavid L Stevens kfree(dpsf); 1725ad12583fSDavid L Stevens } 17261da177e4SLinus Torvalds psf->sf_crcount = qrv; 17271da177e4SLinus Torvalds rv++; 17281da177e4SLinus Torvalds } 1729ad12583fSDavid L Stevens } else if (psf->sf_oldin) { 1730ad12583fSDavid L Stevens 1731ad12583fSDavid L Stevens psf->sf_crcount = 0; 1732ad12583fSDavid L Stevens /* 1733ad12583fSDavid L Stevens * add or update "delete" records if an active filter 1734ad12583fSDavid L Stevens * is now inactive 1735ad12583fSDavid L Stevens */ 1736ad12583fSDavid L Stevens for (dpsf=pmc->tomb; dpsf; dpsf=dpsf->sf_next) 1737ad12583fSDavid L Stevens if (dpsf->sf_inaddr == psf->sf_inaddr) 1738ad12583fSDavid L Stevens break; 1739ad12583fSDavid L Stevens if (!dpsf) { 17403ed37a6fSJoe Perches dpsf = kmalloc(sizeof(*dpsf), GFP_ATOMIC); 1741ad12583fSDavid L Stevens if (!dpsf) 1742ad12583fSDavid L Stevens continue; 1743ad12583fSDavid L Stevens *dpsf = *psf; 1744ad12583fSDavid L Stevens /* pmc->lock held by callers */ 1745ad12583fSDavid L Stevens dpsf->sf_next = pmc->tomb; 1746ad12583fSDavid L Stevens pmc->tomb = dpsf; 1747ad12583fSDavid L Stevens } 1748ad12583fSDavid L Stevens dpsf->sf_crcount = qrv; 1749ad12583fSDavid L Stevens rv++; 1750ad12583fSDavid L Stevens } 17511da177e4SLinus Torvalds } 17521da177e4SLinus Torvalds return rv; 17531da177e4SLinus Torvalds } 17541da177e4SLinus Torvalds #endif 17551da177e4SLinus Torvalds 17561da177e4SLinus Torvalds /* 17571da177e4SLinus Torvalds * Add multicast source filter list to the interface list 17581da177e4SLinus Torvalds */ 17598f935bbdSAl Viro static int ip_mc_add_src(struct in_device *in_dev, __be32 *pmca, int sfmode, 17608f935bbdSAl Viro int sfcount, __be32 *psfsrc, int delta) 17611da177e4SLinus Torvalds { 17621da177e4SLinus Torvalds struct ip_mc_list *pmc; 17631da177e4SLinus Torvalds int isexclude; 17641da177e4SLinus Torvalds int i, err; 17651da177e4SLinus Torvalds 17661da177e4SLinus Torvalds if (!in_dev) 17671da177e4SLinus Torvalds return -ENODEV; 17681d7138deSEric Dumazet rcu_read_lock(); 17691d7138deSEric Dumazet for_each_pmc_rcu(in_dev, pmc) { 17701da177e4SLinus Torvalds if (*pmca == pmc->multiaddr) 17711da177e4SLinus Torvalds break; 17721da177e4SLinus Torvalds } 17731da177e4SLinus Torvalds if (!pmc) { 17741da177e4SLinus Torvalds /* MCA not found?? bug */ 17751d7138deSEric Dumazet rcu_read_unlock(); 17761da177e4SLinus Torvalds return -ESRCH; 17771da177e4SLinus Torvalds } 17781da177e4SLinus Torvalds spin_lock_bh(&pmc->lock); 17791d7138deSEric Dumazet rcu_read_unlock(); 17801da177e4SLinus Torvalds 17811da177e4SLinus Torvalds #ifdef CONFIG_IP_MULTICAST 17821da177e4SLinus Torvalds sf_markstate(pmc); 17831da177e4SLinus Torvalds #endif 17841da177e4SLinus Torvalds isexclude = pmc->sfmode == MCAST_EXCLUDE; 17851da177e4SLinus Torvalds if (!delta) 17861da177e4SLinus Torvalds pmc->sfcount[sfmode]++; 17871da177e4SLinus Torvalds err = 0; 17881da177e4SLinus Torvalds for (i=0; i<sfcount; i++) { 17895eb81e89SJun Zhao err = ip_mc_add1_src(pmc, sfmode, &psfsrc[i]); 17901da177e4SLinus Torvalds if (err) 17911da177e4SLinus Torvalds break; 17921da177e4SLinus Torvalds } 17931da177e4SLinus Torvalds if (err) { 17941da177e4SLinus Torvalds int j; 17951da177e4SLinus Torvalds 1796685f94e6SJun Zhao if (!delta) 17971da177e4SLinus Torvalds pmc->sfcount[sfmode]--; 17981da177e4SLinus Torvalds for (j=0; j<i; j++) 1799a1889c0dSJulia Lawall (void) ip_mc_del1_src(pmc, sfmode, &psfsrc[j]); 18001da177e4SLinus Torvalds } else if (isexclude != (pmc->sfcount[MCAST_EXCLUDE] != 0)) { 18011da177e4SLinus Torvalds #ifdef CONFIG_IP_MULTICAST 18021da177e4SLinus Torvalds struct ip_sf_list *psf; 1803cfcabdccSStephen Hemminger in_dev = pmc->interface; 18041da177e4SLinus Torvalds #endif 18051da177e4SLinus Torvalds 18061da177e4SLinus Torvalds /* filter mode change */ 18071da177e4SLinus Torvalds if (pmc->sfcount[MCAST_EXCLUDE]) 18081da177e4SLinus Torvalds pmc->sfmode = MCAST_EXCLUDE; 18091da177e4SLinus Torvalds else if (pmc->sfcount[MCAST_INCLUDE]) 18101da177e4SLinus Torvalds pmc->sfmode = MCAST_INCLUDE; 18111da177e4SLinus Torvalds #ifdef CONFIG_IP_MULTICAST 18121da177e4SLinus Torvalds /* else no filters; keep old mode for reports */ 18131da177e4SLinus Torvalds 18141da177e4SLinus Torvalds pmc->crcount = in_dev->mr_qrv ? in_dev->mr_qrv : 18151da177e4SLinus Torvalds IGMP_Unsolicited_Report_Count; 18161da177e4SLinus Torvalds in_dev->mr_ifc_count = pmc->crcount; 18171da177e4SLinus Torvalds for (psf=pmc->sources; psf; psf = psf->sf_next) 18181da177e4SLinus Torvalds psf->sf_crcount = 0; 18191da177e4SLinus Torvalds igmp_ifc_event(in_dev); 18201da177e4SLinus Torvalds } else if (sf_setstate(pmc)) { 18211da177e4SLinus Torvalds igmp_ifc_event(in_dev); 18221da177e4SLinus Torvalds #endif 18231da177e4SLinus Torvalds } 18241da177e4SLinus Torvalds spin_unlock_bh(&pmc->lock); 18251da177e4SLinus Torvalds return err; 18261da177e4SLinus Torvalds } 18271da177e4SLinus Torvalds 18281da177e4SLinus Torvalds static void ip_mc_clear_src(struct ip_mc_list *pmc) 18291da177e4SLinus Torvalds { 18301da177e4SLinus Torvalds struct ip_sf_list *psf, *nextpsf; 18311da177e4SLinus Torvalds 18321da177e4SLinus Torvalds for (psf=pmc->tomb; psf; psf=nextpsf) { 18331da177e4SLinus Torvalds nextpsf = psf->sf_next; 18341da177e4SLinus Torvalds kfree(psf); 18351da177e4SLinus Torvalds } 18361da177e4SLinus Torvalds pmc->tomb = NULL; 18371da177e4SLinus Torvalds for (psf=pmc->sources; psf; psf=nextpsf) { 18381da177e4SLinus Torvalds nextpsf = psf->sf_next; 18391da177e4SLinus Torvalds kfree(psf); 18401da177e4SLinus Torvalds } 18411da177e4SLinus Torvalds pmc->sources = NULL; 18421da177e4SLinus Torvalds pmc->sfmode = MCAST_EXCLUDE; 1843de9daad9SDenis Lukianov pmc->sfcount[MCAST_INCLUDE] = 0; 18441da177e4SLinus Torvalds pmc->sfcount[MCAST_EXCLUDE] = 1; 18451da177e4SLinus Torvalds } 18461da177e4SLinus Torvalds 18471da177e4SLinus Torvalds 18481da177e4SLinus Torvalds /* 18491da177e4SLinus Torvalds * Join a multicast group 18501da177e4SLinus Torvalds */ 18511da177e4SLinus Torvalds int ip_mc_join_group(struct sock *sk , struct ip_mreqn *imr) 18521da177e4SLinus Torvalds { 18531da177e4SLinus Torvalds int err; 18548f935bbdSAl Viro __be32 addr = imr->imr_multiaddr.s_addr; 1855ca9b907dSDavid L Stevens struct ip_mc_socklist *iml = NULL, *i; 18561da177e4SLinus Torvalds struct in_device *in_dev; 18571da177e4SLinus Torvalds struct inet_sock *inet = inet_sk(sk); 1858877acedcSDaniel Lezcano struct net *net = sock_net(sk); 1859ca9b907dSDavid L Stevens int ifindex; 18601da177e4SLinus Torvalds int count = 0; 18611da177e4SLinus Torvalds 1862f97c1e0cSJoe Perches if (!ipv4_is_multicast(addr)) 18631da177e4SLinus Torvalds return -EINVAL; 18641da177e4SLinus Torvalds 18656756ae4bSStephen Hemminger rtnl_lock(); 18661da177e4SLinus Torvalds 1867877acedcSDaniel Lezcano in_dev = ip_mc_find_dev(net, imr); 18681da177e4SLinus Torvalds 18691da177e4SLinus Torvalds if (!in_dev) { 18701da177e4SLinus Torvalds iml = NULL; 18711da177e4SLinus Torvalds err = -ENODEV; 18721da177e4SLinus Torvalds goto done; 18731da177e4SLinus Torvalds } 18741da177e4SLinus Torvalds 18751da177e4SLinus Torvalds err = -EADDRINUSE; 1876ca9b907dSDavid L Stevens ifindex = imr->imr_ifindex; 18771d7138deSEric Dumazet for_each_pmc_rtnl(inet, i) { 1878ca9b907dSDavid L Stevens if (i->multi.imr_multiaddr.s_addr == addr && 1879ca9b907dSDavid L Stevens i->multi.imr_ifindex == ifindex) 18801da177e4SLinus Torvalds goto done; 18811da177e4SLinus Torvalds count++; 18821da177e4SLinus Torvalds } 18831da177e4SLinus Torvalds err = -ENOBUFS; 1884ca9b907dSDavid L Stevens if (count >= sysctl_igmp_max_memberships) 18851da177e4SLinus Torvalds goto done; 18868b3a7005SKris Katterjohn iml = sock_kmalloc(sk, sizeof(*iml), GFP_KERNEL); 1887ca9b907dSDavid L Stevens if (iml == NULL) 1888ca9b907dSDavid L Stevens goto done; 1889ca9b907dSDavid L Stevens 18901da177e4SLinus Torvalds memcpy(&iml->multi, imr, sizeof(*imr)); 18911d7138deSEric Dumazet iml->next_rcu = inet->mc_list; 18921da177e4SLinus Torvalds iml->sflist = NULL; 18931da177e4SLinus Torvalds iml->sfmode = MCAST_EXCLUDE; 1894cf778b00SEric Dumazet rcu_assign_pointer(inet->mc_list, iml); 18951da177e4SLinus Torvalds ip_mc_inc_group(in_dev, addr); 18961da177e4SLinus Torvalds err = 0; 18971da177e4SLinus Torvalds done: 18986756ae4bSStephen Hemminger rtnl_unlock(); 18991da177e4SLinus Torvalds return err; 19001da177e4SLinus Torvalds } 19014bc2f18bSEric Dumazet EXPORT_SYMBOL(ip_mc_join_group); 19021da177e4SLinus Torvalds 19031da177e4SLinus Torvalds static int ip_mc_leave_src(struct sock *sk, struct ip_mc_socklist *iml, 19041da177e4SLinus Torvalds struct in_device *in_dev) 19051da177e4SLinus Torvalds { 19061d7138deSEric Dumazet struct ip_sf_socklist *psf = rtnl_dereference(iml->sflist); 19071da177e4SLinus Torvalds int err; 19081da177e4SLinus Torvalds 1909c85bb41eSFlavio Leitner if (psf == NULL) { 19101da177e4SLinus Torvalds /* any-source empty exclude case */ 19111da177e4SLinus Torvalds return ip_mc_del_src(in_dev, &iml->multi.imr_multiaddr.s_addr, 19121da177e4SLinus Torvalds iml->sfmode, 0, NULL, 0); 19131da177e4SLinus Torvalds } 19141da177e4SLinus Torvalds err = ip_mc_del_src(in_dev, &iml->multi.imr_multiaddr.s_addr, 1915c85bb41eSFlavio Leitner iml->sfmode, psf->sl_count, psf->sl_addr, 0); 1916a9b3cd7fSStephen Hemminger RCU_INIT_POINTER(iml->sflist, NULL); 1917c85bb41eSFlavio Leitner /* decrease mem now to avoid the memleak warning */ 1918c85bb41eSFlavio Leitner atomic_sub(IP_SFLSIZE(psf->sl_max), &sk->sk_omem_alloc); 19197519cce4SLai Jiangshan kfree_rcu(psf, rcu); 19201da177e4SLinus Torvalds return err; 19211da177e4SLinus Torvalds } 19221da177e4SLinus Torvalds 19231da177e4SLinus Torvalds /* 19241da177e4SLinus Torvalds * Ask a socket to leave a group. 19251da177e4SLinus Torvalds */ 19261da177e4SLinus Torvalds 19271da177e4SLinus Torvalds int ip_mc_leave_group(struct sock *sk, struct ip_mreqn *imr) 19281da177e4SLinus Torvalds { 19291da177e4SLinus Torvalds struct inet_sock *inet = inet_sk(sk); 19301d7138deSEric Dumazet struct ip_mc_socklist *iml; 19311d7138deSEric Dumazet struct ip_mc_socklist __rcu **imlp; 193284b42baeSDavid L Stevens struct in_device *in_dev; 1933877acedcSDaniel Lezcano struct net *net = sock_net(sk); 19348f935bbdSAl Viro __be32 group = imr->imr_multiaddr.s_addr; 193584b42baeSDavid L Stevens u32 ifindex; 1936acd6e00bSDavid L Stevens int ret = -EADDRNOTAVAIL; 19371da177e4SLinus Torvalds 19381da177e4SLinus Torvalds rtnl_lock(); 1939877acedcSDaniel Lezcano in_dev = ip_mc_find_dev(net, imr); 194084b42baeSDavid L Stevens ifindex = imr->imr_ifindex; 19411d7138deSEric Dumazet for (imlp = &inet->mc_list; 19421d7138deSEric Dumazet (iml = rtnl_dereference(*imlp)) != NULL; 19431d7138deSEric Dumazet imlp = &iml->next_rcu) { 1944acd6e00bSDavid L Stevens if (iml->multi.imr_multiaddr.s_addr != group) 1945acd6e00bSDavid L Stevens continue; 1946acd6e00bSDavid L Stevens if (ifindex) { 1947acd6e00bSDavid L Stevens if (iml->multi.imr_ifindex != ifindex) 1948acd6e00bSDavid L Stevens continue; 1949acd6e00bSDavid L Stevens } else if (imr->imr_address.s_addr && imr->imr_address.s_addr != 1950acd6e00bSDavid L Stevens iml->multi.imr_address.s_addr) 1951acd6e00bSDavid L Stevens continue; 1952acd6e00bSDavid L Stevens 19531da177e4SLinus Torvalds (void) ip_mc_leave_src(sk, iml, in_dev); 19541da177e4SLinus Torvalds 19551d7138deSEric Dumazet *imlp = iml->next_rcu; 19561da177e4SLinus Torvalds 1957acd6e00bSDavid L Stevens if (in_dev) 195884b42baeSDavid L Stevens ip_mc_dec_group(in_dev, group); 19591da177e4SLinus Torvalds rtnl_unlock(); 1960c85bb41eSFlavio Leitner /* decrease mem now to avoid the memleak warning */ 1961c85bb41eSFlavio Leitner atomic_sub(sizeof(*iml), &sk->sk_omem_alloc); 196210d50e74SLai Jiangshan kfree_rcu(iml, rcu); 19631da177e4SLinus Torvalds return 0; 19641da177e4SLinus Torvalds } 1965acd6e00bSDavid L Stevens if (!in_dev) 1966acd6e00bSDavid L Stevens ret = -ENODEV; 19671da177e4SLinus Torvalds rtnl_unlock(); 1968acd6e00bSDavid L Stevens return ret; 19691da177e4SLinus Torvalds } 1970193ba924Sstephen hemminger EXPORT_SYMBOL(ip_mc_leave_group); 19711da177e4SLinus Torvalds 19721da177e4SLinus Torvalds int ip_mc_source(int add, int omode, struct sock *sk, struct 19731da177e4SLinus Torvalds ip_mreq_source *mreqs, int ifindex) 19741da177e4SLinus Torvalds { 19751da177e4SLinus Torvalds int err; 19761da177e4SLinus Torvalds struct ip_mreqn imr; 197763007727SAl Viro __be32 addr = mreqs->imr_multiaddr; 19781da177e4SLinus Torvalds struct ip_mc_socklist *pmc; 19791da177e4SLinus Torvalds struct in_device *in_dev = NULL; 19801da177e4SLinus Torvalds struct inet_sock *inet = inet_sk(sk); 19811da177e4SLinus Torvalds struct ip_sf_socklist *psl; 1982877acedcSDaniel Lezcano struct net *net = sock_net(sk); 19838cdaaa15SDavid L Stevens int leavegroup = 0; 19841da177e4SLinus Torvalds int i, j, rv; 19851da177e4SLinus Torvalds 1986f97c1e0cSJoe Perches if (!ipv4_is_multicast(addr)) 19871da177e4SLinus Torvalds return -EINVAL; 19881da177e4SLinus Torvalds 19896756ae4bSStephen Hemminger rtnl_lock(); 19901da177e4SLinus Torvalds 19911da177e4SLinus Torvalds imr.imr_multiaddr.s_addr = mreqs->imr_multiaddr; 19921da177e4SLinus Torvalds imr.imr_address.s_addr = mreqs->imr_interface; 19931da177e4SLinus Torvalds imr.imr_ifindex = ifindex; 1994877acedcSDaniel Lezcano in_dev = ip_mc_find_dev(net, &imr); 19951da177e4SLinus Torvalds 19961da177e4SLinus Torvalds if (!in_dev) { 19971da177e4SLinus Torvalds err = -ENODEV; 19981da177e4SLinus Torvalds goto done; 19991da177e4SLinus Torvalds } 20001da177e4SLinus Torvalds err = -EADDRNOTAVAIL; 20011da177e4SLinus Torvalds 20021d7138deSEric Dumazet for_each_pmc_rtnl(inet, pmc) { 2003f64f9e71SJoe Perches if ((pmc->multi.imr_multiaddr.s_addr == 2004f64f9e71SJoe Perches imr.imr_multiaddr.s_addr) && 2005f64f9e71SJoe Perches (pmc->multi.imr_ifindex == imr.imr_ifindex)) 20061da177e4SLinus Torvalds break; 20071da177e4SLinus Torvalds } 2008917f2f10SDavid L Stevens if (!pmc) { /* must have a prior join */ 2009917f2f10SDavid L Stevens err = -EINVAL; 20101da177e4SLinus Torvalds goto done; 2011917f2f10SDavid L Stevens } 20121da177e4SLinus Torvalds /* if a source filter was set, must be the same mode as before */ 20131da177e4SLinus Torvalds if (pmc->sflist) { 2014917f2f10SDavid L Stevens if (pmc->sfmode != omode) { 2015917f2f10SDavid L Stevens err = -EINVAL; 20161da177e4SLinus Torvalds goto done; 2017917f2f10SDavid L Stevens } 20181da177e4SLinus Torvalds } else if (pmc->sfmode != omode) { 20191da177e4SLinus Torvalds /* allow mode switches for empty-set filters */ 20201da177e4SLinus Torvalds ip_mc_add_src(in_dev, &mreqs->imr_multiaddr, omode, 0, NULL, 0); 20211da177e4SLinus Torvalds ip_mc_del_src(in_dev, &mreqs->imr_multiaddr, pmc->sfmode, 0, 20221da177e4SLinus Torvalds NULL, 0); 20231da177e4SLinus Torvalds pmc->sfmode = omode; 20241da177e4SLinus Torvalds } 20251da177e4SLinus Torvalds 20261d7138deSEric Dumazet psl = rtnl_dereference(pmc->sflist); 20271da177e4SLinus Torvalds if (!add) { 20281da177e4SLinus Torvalds if (!psl) 2029917f2f10SDavid L Stevens goto done; /* err = -EADDRNOTAVAIL */ 20301da177e4SLinus Torvalds rv = !0; 20311da177e4SLinus Torvalds for (i=0; i<psl->sl_count; i++) { 20321da177e4SLinus Torvalds rv = memcmp(&psl->sl_addr[i], &mreqs->imr_sourceaddr, 203363007727SAl Viro sizeof(__be32)); 20341da177e4SLinus Torvalds if (rv == 0) 20351da177e4SLinus Torvalds break; 20361da177e4SLinus Torvalds } 20371da177e4SLinus Torvalds if (rv) /* source not found */ 2038917f2f10SDavid L Stevens goto done; /* err = -EADDRNOTAVAIL */ 20391da177e4SLinus Torvalds 20408cdaaa15SDavid L Stevens /* special case - (INCLUDE, empty) == LEAVE_GROUP */ 20418cdaaa15SDavid L Stevens if (psl->sl_count == 1 && omode == MCAST_INCLUDE) { 20428cdaaa15SDavid L Stevens leavegroup = 1; 20438cdaaa15SDavid L Stevens goto done; 20448cdaaa15SDavid L Stevens } 20458cdaaa15SDavid L Stevens 20461da177e4SLinus Torvalds /* update the interface filter */ 20471da177e4SLinus Torvalds ip_mc_del_src(in_dev, &mreqs->imr_multiaddr, omode, 1, 20481da177e4SLinus Torvalds &mreqs->imr_sourceaddr, 1); 20491da177e4SLinus Torvalds 20501da177e4SLinus Torvalds for (j=i+1; j<psl->sl_count; j++) 20511da177e4SLinus Torvalds psl->sl_addr[j-1] = psl->sl_addr[j]; 20521da177e4SLinus Torvalds psl->sl_count--; 20531da177e4SLinus Torvalds err = 0; 20541da177e4SLinus Torvalds goto done; 20551da177e4SLinus Torvalds } 20561da177e4SLinus Torvalds /* else, add a new source to the filter */ 20571da177e4SLinus Torvalds 20581da177e4SLinus Torvalds if (psl && psl->sl_count >= sysctl_igmp_max_msf) { 20591da177e4SLinus Torvalds err = -ENOBUFS; 20601da177e4SLinus Torvalds goto done; 20611da177e4SLinus Torvalds } 20621da177e4SLinus Torvalds if (!psl || psl->sl_count == psl->sl_max) { 20631da177e4SLinus Torvalds struct ip_sf_socklist *newpsl; 20641da177e4SLinus Torvalds int count = IP_SFBLOCK; 20651da177e4SLinus Torvalds 20661da177e4SLinus Torvalds if (psl) 20671da177e4SLinus Torvalds count += psl->sl_max; 20688b3a7005SKris Katterjohn newpsl = sock_kmalloc(sk, IP_SFLSIZE(count), GFP_KERNEL); 20691da177e4SLinus Torvalds if (!newpsl) { 20701da177e4SLinus Torvalds err = -ENOBUFS; 20711da177e4SLinus Torvalds goto done; 20721da177e4SLinus Torvalds } 20731da177e4SLinus Torvalds newpsl->sl_max = count; 20741da177e4SLinus Torvalds newpsl->sl_count = count - IP_SFBLOCK; 20751da177e4SLinus Torvalds if (psl) { 20761da177e4SLinus Torvalds for (i=0; i<psl->sl_count; i++) 20771da177e4SLinus Torvalds newpsl->sl_addr[i] = psl->sl_addr[i]; 2078c85bb41eSFlavio Leitner /* decrease mem now to avoid the memleak warning */ 2079c85bb41eSFlavio Leitner atomic_sub(IP_SFLSIZE(psl->sl_max), &sk->sk_omem_alloc); 20807519cce4SLai Jiangshan kfree_rcu(psl, rcu); 20811da177e4SLinus Torvalds } 2082cf778b00SEric Dumazet rcu_assign_pointer(pmc->sflist, newpsl); 2083c85bb41eSFlavio Leitner psl = newpsl; 20841da177e4SLinus Torvalds } 20851da177e4SLinus Torvalds rv = 1; /* > 0 for insert logic below if sl_count is 0 */ 20861da177e4SLinus Torvalds for (i=0; i<psl->sl_count; i++) { 20871da177e4SLinus Torvalds rv = memcmp(&psl->sl_addr[i], &mreqs->imr_sourceaddr, 208863007727SAl Viro sizeof(__be32)); 20891da177e4SLinus Torvalds if (rv == 0) 20901da177e4SLinus Torvalds break; 20911da177e4SLinus Torvalds } 20921da177e4SLinus Torvalds if (rv == 0) /* address already there is an error */ 20931da177e4SLinus Torvalds goto done; 20941da177e4SLinus Torvalds for (j=psl->sl_count-1; j>=i; j--) 20951da177e4SLinus Torvalds psl->sl_addr[j+1] = psl->sl_addr[j]; 20961da177e4SLinus Torvalds psl->sl_addr[i] = mreqs->imr_sourceaddr; 20971da177e4SLinus Torvalds psl->sl_count++; 20981da177e4SLinus Torvalds err = 0; 20991da177e4SLinus Torvalds /* update the interface list */ 21001da177e4SLinus Torvalds ip_mc_add_src(in_dev, &mreqs->imr_multiaddr, omode, 1, 21011da177e4SLinus Torvalds &mreqs->imr_sourceaddr, 1); 21021da177e4SLinus Torvalds done: 21036756ae4bSStephen Hemminger rtnl_unlock(); 21048cdaaa15SDavid L Stevens if (leavegroup) 21058cdaaa15SDavid L Stevens return ip_mc_leave_group(sk, &imr); 21061da177e4SLinus Torvalds return err; 21071da177e4SLinus Torvalds } 21081da177e4SLinus Torvalds 21091da177e4SLinus Torvalds int ip_mc_msfilter(struct sock *sk, struct ip_msfilter *msf, int ifindex) 21101da177e4SLinus Torvalds { 21119951f036SDavid L Stevens int err = 0; 21121da177e4SLinus Torvalds struct ip_mreqn imr; 211363007727SAl Viro __be32 addr = msf->imsf_multiaddr; 21141da177e4SLinus Torvalds struct ip_mc_socklist *pmc; 21151da177e4SLinus Torvalds struct in_device *in_dev; 21161da177e4SLinus Torvalds struct inet_sock *inet = inet_sk(sk); 21171da177e4SLinus Torvalds struct ip_sf_socklist *newpsl, *psl; 2118877acedcSDaniel Lezcano struct net *net = sock_net(sk); 21199951f036SDavid L Stevens int leavegroup = 0; 21201da177e4SLinus Torvalds 2121f97c1e0cSJoe Perches if (!ipv4_is_multicast(addr)) 21221da177e4SLinus Torvalds return -EINVAL; 21231da177e4SLinus Torvalds if (msf->imsf_fmode != MCAST_INCLUDE && 21241da177e4SLinus Torvalds msf->imsf_fmode != MCAST_EXCLUDE) 21251da177e4SLinus Torvalds return -EINVAL; 21261da177e4SLinus Torvalds 21276756ae4bSStephen Hemminger rtnl_lock(); 21281da177e4SLinus Torvalds 21291da177e4SLinus Torvalds imr.imr_multiaddr.s_addr = msf->imsf_multiaddr; 21301da177e4SLinus Torvalds imr.imr_address.s_addr = msf->imsf_interface; 21311da177e4SLinus Torvalds imr.imr_ifindex = ifindex; 2132877acedcSDaniel Lezcano in_dev = ip_mc_find_dev(net, &imr); 21331da177e4SLinus Torvalds 21341da177e4SLinus Torvalds if (!in_dev) { 21351da177e4SLinus Torvalds err = -ENODEV; 21361da177e4SLinus Torvalds goto done; 21371da177e4SLinus Torvalds } 21381da177e4SLinus Torvalds 21399951f036SDavid L Stevens /* special case - (INCLUDE, empty) == LEAVE_GROUP */ 21409951f036SDavid L Stevens if (msf->imsf_fmode == MCAST_INCLUDE && msf->imsf_numsrc == 0) { 21419951f036SDavid L Stevens leavegroup = 1; 21429951f036SDavid L Stevens goto done; 21439951f036SDavid L Stevens } 21449951f036SDavid L Stevens 21451d7138deSEric Dumazet for_each_pmc_rtnl(inet, pmc) { 21461da177e4SLinus Torvalds if (pmc->multi.imr_multiaddr.s_addr == msf->imsf_multiaddr && 21471da177e4SLinus Torvalds pmc->multi.imr_ifindex == imr.imr_ifindex) 21481da177e4SLinus Torvalds break; 21491da177e4SLinus Torvalds } 2150917f2f10SDavid L Stevens if (!pmc) { /* must have a prior join */ 2151917f2f10SDavid L Stevens err = -EINVAL; 21521da177e4SLinus Torvalds goto done; 2153917f2f10SDavid L Stevens } 21541da177e4SLinus Torvalds if (msf->imsf_numsrc) { 21558b3a7005SKris Katterjohn newpsl = sock_kmalloc(sk, IP_SFLSIZE(msf->imsf_numsrc), 21568b3a7005SKris Katterjohn GFP_KERNEL); 21571da177e4SLinus Torvalds if (!newpsl) { 21581da177e4SLinus Torvalds err = -ENOBUFS; 21591da177e4SLinus Torvalds goto done; 21601da177e4SLinus Torvalds } 21611da177e4SLinus Torvalds newpsl->sl_max = newpsl->sl_count = msf->imsf_numsrc; 21621da177e4SLinus Torvalds memcpy(newpsl->sl_addr, msf->imsf_slist, 21631da177e4SLinus Torvalds msf->imsf_numsrc * sizeof(msf->imsf_slist[0])); 21641da177e4SLinus Torvalds err = ip_mc_add_src(in_dev, &msf->imsf_multiaddr, 21651da177e4SLinus Torvalds msf->imsf_fmode, newpsl->sl_count, newpsl->sl_addr, 0); 21661da177e4SLinus Torvalds if (err) { 21671da177e4SLinus Torvalds sock_kfree_s(sk, newpsl, IP_SFLSIZE(newpsl->sl_max)); 21681da177e4SLinus Torvalds goto done; 21691da177e4SLinus Torvalds } 21708713dbf0SYan Zheng } else { 21711da177e4SLinus Torvalds newpsl = NULL; 21728713dbf0SYan Zheng (void) ip_mc_add_src(in_dev, &msf->imsf_multiaddr, 21738713dbf0SYan Zheng msf->imsf_fmode, 0, NULL, 0); 21748713dbf0SYan Zheng } 21751d7138deSEric Dumazet psl = rtnl_dereference(pmc->sflist); 21761da177e4SLinus Torvalds if (psl) { 21771da177e4SLinus Torvalds (void) ip_mc_del_src(in_dev, &msf->imsf_multiaddr, pmc->sfmode, 21781da177e4SLinus Torvalds psl->sl_count, psl->sl_addr, 0); 2179c85bb41eSFlavio Leitner /* decrease mem now to avoid the memleak warning */ 2180c85bb41eSFlavio Leitner atomic_sub(IP_SFLSIZE(psl->sl_max), &sk->sk_omem_alloc); 21817519cce4SLai Jiangshan kfree_rcu(psl, rcu); 21821da177e4SLinus Torvalds } else 21831da177e4SLinus Torvalds (void) ip_mc_del_src(in_dev, &msf->imsf_multiaddr, pmc->sfmode, 21841da177e4SLinus Torvalds 0, NULL, 0); 2185cf778b00SEric Dumazet rcu_assign_pointer(pmc->sflist, newpsl); 21861da177e4SLinus Torvalds pmc->sfmode = msf->imsf_fmode; 2187917f2f10SDavid L Stevens err = 0; 21881da177e4SLinus Torvalds done: 21896756ae4bSStephen Hemminger rtnl_unlock(); 21909951f036SDavid L Stevens if (leavegroup) 21919951f036SDavid L Stevens err = ip_mc_leave_group(sk, &imr); 21921da177e4SLinus Torvalds return err; 21931da177e4SLinus Torvalds } 21941da177e4SLinus Torvalds 21951da177e4SLinus Torvalds int ip_mc_msfget(struct sock *sk, struct ip_msfilter *msf, 21961da177e4SLinus Torvalds struct ip_msfilter __user *optval, int __user *optlen) 21971da177e4SLinus Torvalds { 21981da177e4SLinus Torvalds int err, len, count, copycount; 21991da177e4SLinus Torvalds struct ip_mreqn imr; 220063007727SAl Viro __be32 addr = msf->imsf_multiaddr; 22011da177e4SLinus Torvalds struct ip_mc_socklist *pmc; 22021da177e4SLinus Torvalds struct in_device *in_dev; 22031da177e4SLinus Torvalds struct inet_sock *inet = inet_sk(sk); 22041da177e4SLinus Torvalds struct ip_sf_socklist *psl; 2205877acedcSDaniel Lezcano struct net *net = sock_net(sk); 22061da177e4SLinus Torvalds 2207f97c1e0cSJoe Perches if (!ipv4_is_multicast(addr)) 22081da177e4SLinus Torvalds return -EINVAL; 22091da177e4SLinus Torvalds 22106756ae4bSStephen Hemminger rtnl_lock(); 22111da177e4SLinus Torvalds 22121da177e4SLinus Torvalds imr.imr_multiaddr.s_addr = msf->imsf_multiaddr; 22131da177e4SLinus Torvalds imr.imr_address.s_addr = msf->imsf_interface; 22141da177e4SLinus Torvalds imr.imr_ifindex = 0; 2215877acedcSDaniel Lezcano in_dev = ip_mc_find_dev(net, &imr); 22161da177e4SLinus Torvalds 22171da177e4SLinus Torvalds if (!in_dev) { 22181da177e4SLinus Torvalds err = -ENODEV; 22191da177e4SLinus Torvalds goto done; 22201da177e4SLinus Torvalds } 22211da177e4SLinus Torvalds err = -EADDRNOTAVAIL; 22221da177e4SLinus Torvalds 22231d7138deSEric Dumazet for_each_pmc_rtnl(inet, pmc) { 22241da177e4SLinus Torvalds if (pmc->multi.imr_multiaddr.s_addr == msf->imsf_multiaddr && 22251da177e4SLinus Torvalds pmc->multi.imr_ifindex == imr.imr_ifindex) 22261da177e4SLinus Torvalds break; 22271da177e4SLinus Torvalds } 22281da177e4SLinus Torvalds if (!pmc) /* must have a prior join */ 22291da177e4SLinus Torvalds goto done; 22301da177e4SLinus Torvalds msf->imsf_fmode = pmc->sfmode; 22311d7138deSEric Dumazet psl = rtnl_dereference(pmc->sflist); 22326756ae4bSStephen Hemminger rtnl_unlock(); 22331da177e4SLinus Torvalds if (!psl) { 22341da177e4SLinus Torvalds len = 0; 22351da177e4SLinus Torvalds count = 0; 22361da177e4SLinus Torvalds } else { 22371da177e4SLinus Torvalds count = psl->sl_count; 22381da177e4SLinus Torvalds } 22391da177e4SLinus Torvalds copycount = count < msf->imsf_numsrc ? count : msf->imsf_numsrc; 22401da177e4SLinus Torvalds len = copycount * sizeof(psl->sl_addr[0]); 22411da177e4SLinus Torvalds msf->imsf_numsrc = count; 22421da177e4SLinus Torvalds if (put_user(IP_MSFILTER_SIZE(copycount), optlen) || 22431da177e4SLinus Torvalds copy_to_user(optval, msf, IP_MSFILTER_SIZE(0))) { 22441da177e4SLinus Torvalds return -EFAULT; 22451da177e4SLinus Torvalds } 22461da177e4SLinus Torvalds if (len && 22471da177e4SLinus Torvalds copy_to_user(&optval->imsf_slist[0], psl->sl_addr, len)) 22481da177e4SLinus Torvalds return -EFAULT; 22491da177e4SLinus Torvalds return 0; 22501da177e4SLinus Torvalds done: 22516756ae4bSStephen Hemminger rtnl_unlock(); 22521da177e4SLinus Torvalds return err; 22531da177e4SLinus Torvalds } 22541da177e4SLinus Torvalds 22551da177e4SLinus Torvalds int ip_mc_gsfget(struct sock *sk, struct group_filter *gsf, 22561da177e4SLinus Torvalds struct group_filter __user *optval, int __user *optlen) 22571da177e4SLinus Torvalds { 22581da177e4SLinus Torvalds int err, i, count, copycount; 22591da177e4SLinus Torvalds struct sockaddr_in *psin; 226063007727SAl Viro __be32 addr; 22611da177e4SLinus Torvalds struct ip_mc_socklist *pmc; 22621da177e4SLinus Torvalds struct inet_sock *inet = inet_sk(sk); 22631da177e4SLinus Torvalds struct ip_sf_socklist *psl; 22641da177e4SLinus Torvalds 22651da177e4SLinus Torvalds psin = (struct sockaddr_in *)&gsf->gf_group; 22661da177e4SLinus Torvalds if (psin->sin_family != AF_INET) 22671da177e4SLinus Torvalds return -EINVAL; 22681da177e4SLinus Torvalds addr = psin->sin_addr.s_addr; 2269f97c1e0cSJoe Perches if (!ipv4_is_multicast(addr)) 22701da177e4SLinus Torvalds return -EINVAL; 22711da177e4SLinus Torvalds 22726756ae4bSStephen Hemminger rtnl_lock(); 22731da177e4SLinus Torvalds 22741da177e4SLinus Torvalds err = -EADDRNOTAVAIL; 22751da177e4SLinus Torvalds 22761d7138deSEric Dumazet for_each_pmc_rtnl(inet, pmc) { 22771da177e4SLinus Torvalds if (pmc->multi.imr_multiaddr.s_addr == addr && 22781da177e4SLinus Torvalds pmc->multi.imr_ifindex == gsf->gf_interface) 22791da177e4SLinus Torvalds break; 22801da177e4SLinus Torvalds } 22811da177e4SLinus Torvalds if (!pmc) /* must have a prior join */ 22821da177e4SLinus Torvalds goto done; 22831da177e4SLinus Torvalds gsf->gf_fmode = pmc->sfmode; 22841d7138deSEric Dumazet psl = rtnl_dereference(pmc->sflist); 22856756ae4bSStephen Hemminger rtnl_unlock(); 22861da177e4SLinus Torvalds count = psl ? psl->sl_count : 0; 22871da177e4SLinus Torvalds copycount = count < gsf->gf_numsrc ? count : gsf->gf_numsrc; 22881da177e4SLinus Torvalds gsf->gf_numsrc = count; 22891da177e4SLinus Torvalds if (put_user(GROUP_FILTER_SIZE(copycount), optlen) || 22901da177e4SLinus Torvalds copy_to_user(optval, gsf, GROUP_FILTER_SIZE(0))) { 22911da177e4SLinus Torvalds return -EFAULT; 22921da177e4SLinus Torvalds } 22931da177e4SLinus Torvalds for (i=0; i<copycount; i++) { 22941da177e4SLinus Torvalds struct sockaddr_storage ss; 22951da177e4SLinus Torvalds 22961da177e4SLinus Torvalds psin = (struct sockaddr_in *)&ss; 22971da177e4SLinus Torvalds memset(&ss, 0, sizeof(ss)); 22981da177e4SLinus Torvalds psin->sin_family = AF_INET; 22991da177e4SLinus Torvalds psin->sin_addr.s_addr = psl->sl_addr[i]; 23001da177e4SLinus Torvalds if (copy_to_user(&optval->gf_slist[i], &ss, sizeof(ss))) 23011da177e4SLinus Torvalds return -EFAULT; 23021da177e4SLinus Torvalds } 23031da177e4SLinus Torvalds return 0; 23041da177e4SLinus Torvalds done: 23056756ae4bSStephen Hemminger rtnl_unlock(); 23061da177e4SLinus Torvalds return err; 23071da177e4SLinus Torvalds } 23081da177e4SLinus Torvalds 23091da177e4SLinus Torvalds /* 23101da177e4SLinus Torvalds * check if a multicast source filter allows delivery for a given <src,dst,intf> 23111da177e4SLinus Torvalds */ 2312c0cda068SAl Viro int ip_mc_sf_allow(struct sock *sk, __be32 loc_addr, __be32 rmt_addr, int dif) 23131da177e4SLinus Torvalds { 23141da177e4SLinus Torvalds struct inet_sock *inet = inet_sk(sk); 23151da177e4SLinus Torvalds struct ip_mc_socklist *pmc; 23161da177e4SLinus Torvalds struct ip_sf_socklist *psl; 23171da177e4SLinus Torvalds int i; 2318c85bb41eSFlavio Leitner int ret; 23191da177e4SLinus Torvalds 2320c85bb41eSFlavio Leitner ret = 1; 2321f97c1e0cSJoe Perches if (!ipv4_is_multicast(loc_addr)) 2322c85bb41eSFlavio Leitner goto out; 23231da177e4SLinus Torvalds 2324c85bb41eSFlavio Leitner rcu_read_lock(); 23251d7138deSEric Dumazet for_each_pmc_rcu(inet, pmc) { 23261da177e4SLinus Torvalds if (pmc->multi.imr_multiaddr.s_addr == loc_addr && 23271da177e4SLinus Torvalds pmc->multi.imr_ifindex == dif) 23281da177e4SLinus Torvalds break; 23291da177e4SLinus Torvalds } 2330c85bb41eSFlavio Leitner ret = inet->mc_all; 23311da177e4SLinus Torvalds if (!pmc) 2332c85bb41eSFlavio Leitner goto unlock; 23331d7138deSEric Dumazet psl = rcu_dereference(pmc->sflist); 2334c85bb41eSFlavio Leitner ret = (pmc->sfmode == MCAST_EXCLUDE); 23351da177e4SLinus Torvalds if (!psl) 2336c85bb41eSFlavio Leitner goto unlock; 23371da177e4SLinus Torvalds 23381da177e4SLinus Torvalds for (i=0; i<psl->sl_count; i++) { 23391da177e4SLinus Torvalds if (psl->sl_addr[i] == rmt_addr) 23401da177e4SLinus Torvalds break; 23411da177e4SLinus Torvalds } 2342c85bb41eSFlavio Leitner ret = 0; 23431da177e4SLinus Torvalds if (pmc->sfmode == MCAST_INCLUDE && i >= psl->sl_count) 2344c85bb41eSFlavio Leitner goto unlock; 23451da177e4SLinus Torvalds if (pmc->sfmode == MCAST_EXCLUDE && i < psl->sl_count) 2346c85bb41eSFlavio Leitner goto unlock; 2347c85bb41eSFlavio Leitner ret = 1; 2348c85bb41eSFlavio Leitner unlock: 2349c85bb41eSFlavio Leitner rcu_read_unlock(); 2350c85bb41eSFlavio Leitner out: 2351c85bb41eSFlavio Leitner return ret; 23521da177e4SLinus Torvalds } 23531da177e4SLinus Torvalds 23541da177e4SLinus Torvalds /* 23551da177e4SLinus Torvalds * A socket is closing. 23561da177e4SLinus Torvalds */ 23571da177e4SLinus Torvalds 23581da177e4SLinus Torvalds void ip_mc_drop_socket(struct sock *sk) 23591da177e4SLinus Torvalds { 23601da177e4SLinus Torvalds struct inet_sock *inet = inet_sk(sk); 23611da177e4SLinus Torvalds struct ip_mc_socklist *iml; 2362877acedcSDaniel Lezcano struct net *net = sock_net(sk); 23631da177e4SLinus Torvalds 23641da177e4SLinus Torvalds if (inet->mc_list == NULL) 23651da177e4SLinus Torvalds return; 23661da177e4SLinus Torvalds 23671da177e4SLinus Torvalds rtnl_lock(); 23681d7138deSEric Dumazet while ((iml = rtnl_dereference(inet->mc_list)) != NULL) { 23691da177e4SLinus Torvalds struct in_device *in_dev; 23701da177e4SLinus Torvalds 23711d7138deSEric Dumazet inet->mc_list = iml->next_rcu; 2372877acedcSDaniel Lezcano in_dev = inetdev_by_index(net, iml->multi.imr_ifindex); 23731da177e4SLinus Torvalds (void) ip_mc_leave_src(sk, iml, in_dev); 237418943d29SEric Dumazet if (in_dev != NULL) 23751da177e4SLinus Torvalds ip_mc_dec_group(in_dev, iml->multi.imr_multiaddr.s_addr); 2376c85bb41eSFlavio Leitner /* decrease mem now to avoid the memleak warning */ 2377c85bb41eSFlavio Leitner atomic_sub(sizeof(*iml), &sk->sk_omem_alloc); 237810d50e74SLai Jiangshan kfree_rcu(iml, rcu); 23791da177e4SLinus Torvalds } 23801da177e4SLinus Torvalds rtnl_unlock(); 23811da177e4SLinus Torvalds } 23821da177e4SLinus Torvalds 2383dbdd9a52SDavid S. Miller /* called with rcu_read_lock() */ 2384dbdd9a52SDavid S. Miller int ip_check_mc_rcu(struct in_device *in_dev, __be32 mc_addr, __be32 src_addr, u16 proto) 23851da177e4SLinus Torvalds { 23861da177e4SLinus Torvalds struct ip_mc_list *im; 2387e9897071SEric Dumazet struct ip_mc_list __rcu **mc_hash; 23881da177e4SLinus Torvalds struct ip_sf_list *psf; 23891da177e4SLinus Torvalds int rv = 0; 23901da177e4SLinus Torvalds 2391e9897071SEric Dumazet mc_hash = rcu_dereference(in_dev->mc_hash); 2392e9897071SEric Dumazet if (mc_hash) { 2393c70eba74SEric Dumazet u32 hash = hash_32((__force u32)mc_addr, MC_HASH_SZ_LOG); 2394e9897071SEric Dumazet 2395e9897071SEric Dumazet for (im = rcu_dereference(mc_hash[hash]); 2396e9897071SEric Dumazet im != NULL; 2397e9897071SEric Dumazet im = rcu_dereference(im->next_hash)) { 2398e9897071SEric Dumazet if (im->multiaddr == mc_addr) 2399e9897071SEric Dumazet break; 2400e9897071SEric Dumazet } 2401e9897071SEric Dumazet } else { 24021d7138deSEric Dumazet for_each_pmc_rcu(in_dev, im) { 24031da177e4SLinus Torvalds if (im->multiaddr == mc_addr) 24041da177e4SLinus Torvalds break; 24051da177e4SLinus Torvalds } 2406e9897071SEric Dumazet } 24071da177e4SLinus Torvalds if (im && proto == IPPROTO_IGMP) { 24081da177e4SLinus Torvalds rv = 1; 24091da177e4SLinus Torvalds } else if (im) { 24101da177e4SLinus Torvalds if (src_addr) { 24111da177e4SLinus Torvalds for (psf=im->sources; psf; psf=psf->sf_next) { 24121da177e4SLinus Torvalds if (psf->sf_inaddr == src_addr) 24131da177e4SLinus Torvalds break; 24141da177e4SLinus Torvalds } 24151da177e4SLinus Torvalds if (psf) 24161da177e4SLinus Torvalds rv = psf->sf_count[MCAST_INCLUDE] || 24171da177e4SLinus Torvalds psf->sf_count[MCAST_EXCLUDE] != 24181da177e4SLinus Torvalds im->sfcount[MCAST_EXCLUDE]; 24191da177e4SLinus Torvalds else 24201da177e4SLinus Torvalds rv = im->sfcount[MCAST_EXCLUDE] != 0; 24211da177e4SLinus Torvalds } else 24221da177e4SLinus Torvalds rv = 1; /* unspecified source; tentatively allow */ 24231da177e4SLinus Torvalds } 24241da177e4SLinus Torvalds return rv; 24251da177e4SLinus Torvalds } 24261da177e4SLinus Torvalds 24271da177e4SLinus Torvalds #if defined(CONFIG_PROC_FS) 24281da177e4SLinus Torvalds struct igmp_mc_iter_state { 24297091e728SAlexey Dobriyan struct seq_net_private p; 24301da177e4SLinus Torvalds struct net_device *dev; 24311da177e4SLinus Torvalds struct in_device *in_dev; 24321da177e4SLinus Torvalds }; 24331da177e4SLinus Torvalds 24341da177e4SLinus Torvalds #define igmp_mc_seq_private(seq) ((struct igmp_mc_iter_state *)(seq)->private) 24351da177e4SLinus Torvalds 24361da177e4SLinus Torvalds static inline struct ip_mc_list *igmp_mc_get_first(struct seq_file *seq) 24371da177e4SLinus Torvalds { 24387091e728SAlexey Dobriyan struct net *net = seq_file_net(seq); 24391da177e4SLinus Torvalds struct ip_mc_list *im = NULL; 24401da177e4SLinus Torvalds struct igmp_mc_iter_state *state = igmp_mc_seq_private(seq); 24411da177e4SLinus Torvalds 24427562f876SPavel Emelianov state->in_dev = NULL; 244361fbab77Sstephen hemminger for_each_netdev_rcu(net, state->dev) { 24441da177e4SLinus Torvalds struct in_device *in_dev; 24456baff150SEric Dumazet 24466baff150SEric Dumazet in_dev = __in_dev_get_rcu(state->dev); 24471da177e4SLinus Torvalds if (!in_dev) 24481da177e4SLinus Torvalds continue; 24491d7138deSEric Dumazet im = rcu_dereference(in_dev->mc_list); 24501da177e4SLinus Torvalds if (im) { 24511da177e4SLinus Torvalds state->in_dev = in_dev; 24521da177e4SLinus Torvalds break; 24531da177e4SLinus Torvalds } 24541da177e4SLinus Torvalds } 24551da177e4SLinus Torvalds return im; 24561da177e4SLinus Torvalds } 24571da177e4SLinus Torvalds 24581da177e4SLinus Torvalds static struct ip_mc_list *igmp_mc_get_next(struct seq_file *seq, struct ip_mc_list *im) 24591da177e4SLinus Torvalds { 24601da177e4SLinus Torvalds struct igmp_mc_iter_state *state = igmp_mc_seq_private(seq); 24616baff150SEric Dumazet 24621d7138deSEric Dumazet im = rcu_dereference(im->next_rcu); 24631d7138deSEric Dumazet while (!im) { 24646baff150SEric Dumazet state->dev = next_net_device_rcu(state->dev); 24651da177e4SLinus Torvalds if (!state->dev) { 24661da177e4SLinus Torvalds state->in_dev = NULL; 24671da177e4SLinus Torvalds break; 24681da177e4SLinus Torvalds } 24696baff150SEric Dumazet state->in_dev = __in_dev_get_rcu(state->dev); 24701da177e4SLinus Torvalds if (!state->in_dev) 24711da177e4SLinus Torvalds continue; 24721d7138deSEric Dumazet im = rcu_dereference(state->in_dev->mc_list); 24731da177e4SLinus Torvalds } 24741da177e4SLinus Torvalds return im; 24751da177e4SLinus Torvalds } 24761da177e4SLinus Torvalds 24771da177e4SLinus Torvalds static struct ip_mc_list *igmp_mc_get_idx(struct seq_file *seq, loff_t pos) 24781da177e4SLinus Torvalds { 24791da177e4SLinus Torvalds struct ip_mc_list *im = igmp_mc_get_first(seq); 24801da177e4SLinus Torvalds if (im) 24811da177e4SLinus Torvalds while (pos && (im = igmp_mc_get_next(seq, im)) != NULL) 24821da177e4SLinus Torvalds --pos; 24831da177e4SLinus Torvalds return pos ? NULL : im; 24841da177e4SLinus Torvalds } 24851da177e4SLinus Torvalds 24861da177e4SLinus Torvalds static void *igmp_mc_seq_start(struct seq_file *seq, loff_t *pos) 248761fbab77Sstephen hemminger __acquires(rcu) 24881da177e4SLinus Torvalds { 248961fbab77Sstephen hemminger rcu_read_lock(); 24901da177e4SLinus Torvalds return *pos ? igmp_mc_get_idx(seq, *pos - 1) : SEQ_START_TOKEN; 24911da177e4SLinus Torvalds } 24921da177e4SLinus Torvalds 24931da177e4SLinus Torvalds static void *igmp_mc_seq_next(struct seq_file *seq, void *v, loff_t *pos) 24941da177e4SLinus Torvalds { 24951da177e4SLinus Torvalds struct ip_mc_list *im; 24961da177e4SLinus Torvalds if (v == SEQ_START_TOKEN) 24971da177e4SLinus Torvalds im = igmp_mc_get_first(seq); 24981da177e4SLinus Torvalds else 24991da177e4SLinus Torvalds im = igmp_mc_get_next(seq, v); 25001da177e4SLinus Torvalds ++*pos; 25011da177e4SLinus Torvalds return im; 25021da177e4SLinus Torvalds } 25031da177e4SLinus Torvalds 25041da177e4SLinus Torvalds static void igmp_mc_seq_stop(struct seq_file *seq, void *v) 250561fbab77Sstephen hemminger __releases(rcu) 25061da177e4SLinus Torvalds { 25071da177e4SLinus Torvalds struct igmp_mc_iter_state *state = igmp_mc_seq_private(seq); 25081d7138deSEric Dumazet 25091da177e4SLinus Torvalds state->in_dev = NULL; 25101da177e4SLinus Torvalds state->dev = NULL; 251161fbab77Sstephen hemminger rcu_read_unlock(); 25121da177e4SLinus Torvalds } 25131da177e4SLinus Torvalds 25141da177e4SLinus Torvalds static int igmp_mc_seq_show(struct seq_file *seq, void *v) 25151da177e4SLinus Torvalds { 25161da177e4SLinus Torvalds if (v == SEQ_START_TOKEN) 25171da177e4SLinus Torvalds seq_puts(seq, 25181da177e4SLinus Torvalds "Idx\tDevice : Count Querier\tGroup Users Timer\tReporter\n"); 25191da177e4SLinus Torvalds else { 25201da177e4SLinus Torvalds struct ip_mc_list *im = (struct ip_mc_list *)v; 25211da177e4SLinus Torvalds struct igmp_mc_iter_state *state = igmp_mc_seq_private(seq); 25221da177e4SLinus Torvalds char *querier; 2523a399a805SEric Dumazet long delta; 2524a399a805SEric Dumazet 25251da177e4SLinus Torvalds #ifdef CONFIG_IP_MULTICAST 25261da177e4SLinus Torvalds querier = IGMP_V1_SEEN(state->in_dev) ? "V1" : 25271da177e4SLinus Torvalds IGMP_V2_SEEN(state->in_dev) ? "V2" : 25281da177e4SLinus Torvalds "V3"; 25291da177e4SLinus Torvalds #else 25301da177e4SLinus Torvalds querier = "NONE"; 25311da177e4SLinus Torvalds #endif 25321da177e4SLinus Torvalds 25331d7138deSEric Dumazet if (rcu_dereference(state->in_dev->mc_list) == im) { 25341da177e4SLinus Torvalds seq_printf(seq, "%d\t%-10s: %5d %7s\n", 2535b8bae41eSRami Rosen state->dev->ifindex, state->dev->name, state->in_dev->mc_count, querier); 25361da177e4SLinus Torvalds } 25371da177e4SLinus Torvalds 2538a399a805SEric Dumazet delta = im->timer.expires - jiffies; 25391da177e4SLinus Torvalds seq_printf(seq, 2540338fcf98SAlexey Dobriyan "\t\t\t\t%08X %5d %d:%08lX\t\t%d\n", 25411da177e4SLinus Torvalds im->multiaddr, im->users, 2542a399a805SEric Dumazet im->tm_running, 2543a399a805SEric Dumazet im->tm_running ? jiffies_delta_to_clock_t(delta) : 0, 25441da177e4SLinus Torvalds im->reporter); 25451da177e4SLinus Torvalds } 25461da177e4SLinus Torvalds return 0; 25471da177e4SLinus Torvalds } 25481da177e4SLinus Torvalds 2549f690808eSStephen Hemminger static const struct seq_operations igmp_mc_seq_ops = { 25501da177e4SLinus Torvalds .start = igmp_mc_seq_start, 25511da177e4SLinus Torvalds .next = igmp_mc_seq_next, 25521da177e4SLinus Torvalds .stop = igmp_mc_seq_stop, 25531da177e4SLinus Torvalds .show = igmp_mc_seq_show, 25541da177e4SLinus Torvalds }; 25551da177e4SLinus Torvalds 25561da177e4SLinus Torvalds static int igmp_mc_seq_open(struct inode *inode, struct file *file) 25571da177e4SLinus Torvalds { 25587091e728SAlexey Dobriyan return seq_open_net(inode, file, &igmp_mc_seq_ops, 2559cf7732e4SPavel Emelyanov sizeof(struct igmp_mc_iter_state)); 25601da177e4SLinus Torvalds } 25611da177e4SLinus Torvalds 25629a32144eSArjan van de Ven static const struct file_operations igmp_mc_seq_fops = { 25631da177e4SLinus Torvalds .owner = THIS_MODULE, 25641da177e4SLinus Torvalds .open = igmp_mc_seq_open, 25651da177e4SLinus Torvalds .read = seq_read, 25661da177e4SLinus Torvalds .llseek = seq_lseek, 25677091e728SAlexey Dobriyan .release = seq_release_net, 25681da177e4SLinus Torvalds }; 25691da177e4SLinus Torvalds 25701da177e4SLinus Torvalds struct igmp_mcf_iter_state { 25717091e728SAlexey Dobriyan struct seq_net_private p; 25721da177e4SLinus Torvalds struct net_device *dev; 25731da177e4SLinus Torvalds struct in_device *idev; 25741da177e4SLinus Torvalds struct ip_mc_list *im; 25751da177e4SLinus Torvalds }; 25761da177e4SLinus Torvalds 25771da177e4SLinus Torvalds #define igmp_mcf_seq_private(seq) ((struct igmp_mcf_iter_state *)(seq)->private) 25781da177e4SLinus Torvalds 25791da177e4SLinus Torvalds static inline struct ip_sf_list *igmp_mcf_get_first(struct seq_file *seq) 25801da177e4SLinus Torvalds { 25817091e728SAlexey Dobriyan struct net *net = seq_file_net(seq); 25821da177e4SLinus Torvalds struct ip_sf_list *psf = NULL; 25831da177e4SLinus Torvalds struct ip_mc_list *im = NULL; 25841da177e4SLinus Torvalds struct igmp_mcf_iter_state *state = igmp_mcf_seq_private(seq); 25851da177e4SLinus Torvalds 25867562f876SPavel Emelianov state->idev = NULL; 25877562f876SPavel Emelianov state->im = NULL; 258861fbab77Sstephen hemminger for_each_netdev_rcu(net, state->dev) { 25891da177e4SLinus Torvalds struct in_device *idev; 25906baff150SEric Dumazet idev = __in_dev_get_rcu(state->dev); 25911da177e4SLinus Torvalds if (unlikely(idev == NULL)) 25921da177e4SLinus Torvalds continue; 25931d7138deSEric Dumazet im = rcu_dereference(idev->mc_list); 25941da177e4SLinus Torvalds if (likely(im != NULL)) { 25951da177e4SLinus Torvalds spin_lock_bh(&im->lock); 25961da177e4SLinus Torvalds psf = im->sources; 25971da177e4SLinus Torvalds if (likely(psf != NULL)) { 25981da177e4SLinus Torvalds state->im = im; 25991da177e4SLinus Torvalds state->idev = idev; 26001da177e4SLinus Torvalds break; 26011da177e4SLinus Torvalds } 26021da177e4SLinus Torvalds spin_unlock_bh(&im->lock); 26031da177e4SLinus Torvalds } 26041da177e4SLinus Torvalds } 26051da177e4SLinus Torvalds return psf; 26061da177e4SLinus Torvalds } 26071da177e4SLinus Torvalds 26081da177e4SLinus Torvalds static struct ip_sf_list *igmp_mcf_get_next(struct seq_file *seq, struct ip_sf_list *psf) 26091da177e4SLinus Torvalds { 26101da177e4SLinus Torvalds struct igmp_mcf_iter_state *state = igmp_mcf_seq_private(seq); 26111da177e4SLinus Torvalds 26121da177e4SLinus Torvalds psf = psf->sf_next; 26131da177e4SLinus Torvalds while (!psf) { 26141da177e4SLinus Torvalds spin_unlock_bh(&state->im->lock); 26151da177e4SLinus Torvalds state->im = state->im->next; 26161da177e4SLinus Torvalds while (!state->im) { 26176baff150SEric Dumazet state->dev = next_net_device_rcu(state->dev); 26181da177e4SLinus Torvalds if (!state->dev) { 26191da177e4SLinus Torvalds state->idev = NULL; 26201da177e4SLinus Torvalds goto out; 26211da177e4SLinus Torvalds } 26226baff150SEric Dumazet state->idev = __in_dev_get_rcu(state->dev); 26231da177e4SLinus Torvalds if (!state->idev) 26241da177e4SLinus Torvalds continue; 26251d7138deSEric Dumazet state->im = rcu_dereference(state->idev->mc_list); 26261da177e4SLinus Torvalds } 26271da177e4SLinus Torvalds if (!state->im) 26281da177e4SLinus Torvalds break; 26291da177e4SLinus Torvalds spin_lock_bh(&state->im->lock); 26301da177e4SLinus Torvalds psf = state->im->sources; 26311da177e4SLinus Torvalds } 26321da177e4SLinus Torvalds out: 26331da177e4SLinus Torvalds return psf; 26341da177e4SLinus Torvalds } 26351da177e4SLinus Torvalds 26361da177e4SLinus Torvalds static struct ip_sf_list *igmp_mcf_get_idx(struct seq_file *seq, loff_t pos) 26371da177e4SLinus Torvalds { 26381da177e4SLinus Torvalds struct ip_sf_list *psf = igmp_mcf_get_first(seq); 26391da177e4SLinus Torvalds if (psf) 26401da177e4SLinus Torvalds while (pos && (psf = igmp_mcf_get_next(seq, psf)) != NULL) 26411da177e4SLinus Torvalds --pos; 26421da177e4SLinus Torvalds return pos ? NULL : psf; 26431da177e4SLinus Torvalds } 26441da177e4SLinus Torvalds 26451da177e4SLinus Torvalds static void *igmp_mcf_seq_start(struct seq_file *seq, loff_t *pos) 264661fbab77Sstephen hemminger __acquires(rcu) 26471da177e4SLinus Torvalds { 264861fbab77Sstephen hemminger rcu_read_lock(); 26491da177e4SLinus Torvalds return *pos ? igmp_mcf_get_idx(seq, *pos - 1) : SEQ_START_TOKEN; 26501da177e4SLinus Torvalds } 26511da177e4SLinus Torvalds 26521da177e4SLinus Torvalds static void *igmp_mcf_seq_next(struct seq_file *seq, void *v, loff_t *pos) 26531da177e4SLinus Torvalds { 26541da177e4SLinus Torvalds struct ip_sf_list *psf; 26551da177e4SLinus Torvalds if (v == SEQ_START_TOKEN) 26561da177e4SLinus Torvalds psf = igmp_mcf_get_first(seq); 26571da177e4SLinus Torvalds else 26581da177e4SLinus Torvalds psf = igmp_mcf_get_next(seq, v); 26591da177e4SLinus Torvalds ++*pos; 26601da177e4SLinus Torvalds return psf; 26611da177e4SLinus Torvalds } 26621da177e4SLinus Torvalds 26631da177e4SLinus Torvalds static void igmp_mcf_seq_stop(struct seq_file *seq, void *v) 266461fbab77Sstephen hemminger __releases(rcu) 26651da177e4SLinus Torvalds { 26661da177e4SLinus Torvalds struct igmp_mcf_iter_state *state = igmp_mcf_seq_private(seq); 26671da177e4SLinus Torvalds if (likely(state->im != NULL)) { 26681da177e4SLinus Torvalds spin_unlock_bh(&state->im->lock); 26691da177e4SLinus Torvalds state->im = NULL; 26701da177e4SLinus Torvalds } 26711da177e4SLinus Torvalds state->idev = NULL; 26721da177e4SLinus Torvalds state->dev = NULL; 267361fbab77Sstephen hemminger rcu_read_unlock(); 26741da177e4SLinus Torvalds } 26751da177e4SLinus Torvalds 26761da177e4SLinus Torvalds static int igmp_mcf_seq_show(struct seq_file *seq, void *v) 26771da177e4SLinus Torvalds { 26781da177e4SLinus Torvalds struct ip_sf_list *psf = (struct ip_sf_list *)v; 26791da177e4SLinus Torvalds struct igmp_mcf_iter_state *state = igmp_mcf_seq_private(seq); 26801da177e4SLinus Torvalds 26811da177e4SLinus Torvalds if (v == SEQ_START_TOKEN) { 26821da177e4SLinus Torvalds seq_printf(seq, 26831da177e4SLinus Torvalds "%3s %6s " 26841da177e4SLinus Torvalds "%10s %10s %6s %6s\n", "Idx", 26851da177e4SLinus Torvalds "Device", "MCA", 26861da177e4SLinus Torvalds "SRC", "INC", "EXC"); 26871da177e4SLinus Torvalds } else { 26881da177e4SLinus Torvalds seq_printf(seq, 26891da177e4SLinus Torvalds "%3d %6.6s 0x%08x " 26901da177e4SLinus Torvalds "0x%08x %6lu %6lu\n", 26911da177e4SLinus Torvalds state->dev->ifindex, state->dev->name, 26921da177e4SLinus Torvalds ntohl(state->im->multiaddr), 26931da177e4SLinus Torvalds ntohl(psf->sf_inaddr), 26941da177e4SLinus Torvalds psf->sf_count[MCAST_INCLUDE], 26951da177e4SLinus Torvalds psf->sf_count[MCAST_EXCLUDE]); 26961da177e4SLinus Torvalds } 26971da177e4SLinus Torvalds return 0; 26981da177e4SLinus Torvalds } 26991da177e4SLinus Torvalds 2700f690808eSStephen Hemminger static const struct seq_operations igmp_mcf_seq_ops = { 27011da177e4SLinus Torvalds .start = igmp_mcf_seq_start, 27021da177e4SLinus Torvalds .next = igmp_mcf_seq_next, 27031da177e4SLinus Torvalds .stop = igmp_mcf_seq_stop, 27041da177e4SLinus Torvalds .show = igmp_mcf_seq_show, 27051da177e4SLinus Torvalds }; 27061da177e4SLinus Torvalds 27071da177e4SLinus Torvalds static int igmp_mcf_seq_open(struct inode *inode, struct file *file) 27081da177e4SLinus Torvalds { 27097091e728SAlexey Dobriyan return seq_open_net(inode, file, &igmp_mcf_seq_ops, 2710cf7732e4SPavel Emelyanov sizeof(struct igmp_mcf_iter_state)); 27111da177e4SLinus Torvalds } 27121da177e4SLinus Torvalds 27139a32144eSArjan van de Ven static const struct file_operations igmp_mcf_seq_fops = { 27141da177e4SLinus Torvalds .owner = THIS_MODULE, 27151da177e4SLinus Torvalds .open = igmp_mcf_seq_open, 27161da177e4SLinus Torvalds .read = seq_read, 27171da177e4SLinus Torvalds .llseek = seq_lseek, 27187091e728SAlexey Dobriyan .release = seq_release_net, 27197091e728SAlexey Dobriyan }; 27207091e728SAlexey Dobriyan 27212c8c1e72SAlexey Dobriyan static int __net_init igmp_net_init(struct net *net) 27227091e728SAlexey Dobriyan { 27237091e728SAlexey Dobriyan struct proc_dir_entry *pde; 27247091e728SAlexey Dobriyan 2725d4beaa66SGao feng pde = proc_create("igmp", S_IRUGO, net->proc_net, &igmp_mc_seq_fops); 27267091e728SAlexey Dobriyan if (!pde) 27277091e728SAlexey Dobriyan goto out_igmp; 2728d4beaa66SGao feng pde = proc_create("mcfilter", S_IRUGO, net->proc_net, 2729d4beaa66SGao feng &igmp_mcf_seq_fops); 27307091e728SAlexey Dobriyan if (!pde) 27317091e728SAlexey Dobriyan goto out_mcfilter; 27327091e728SAlexey Dobriyan return 0; 27337091e728SAlexey Dobriyan 27347091e728SAlexey Dobriyan out_mcfilter: 2735ece31ffdSGao feng remove_proc_entry("igmp", net->proc_net); 27367091e728SAlexey Dobriyan out_igmp: 27377091e728SAlexey Dobriyan return -ENOMEM; 27387091e728SAlexey Dobriyan } 27397091e728SAlexey Dobriyan 27402c8c1e72SAlexey Dobriyan static void __net_exit igmp_net_exit(struct net *net) 27417091e728SAlexey Dobriyan { 2742ece31ffdSGao feng remove_proc_entry("mcfilter", net->proc_net); 2743ece31ffdSGao feng remove_proc_entry("igmp", net->proc_net); 27447091e728SAlexey Dobriyan } 27457091e728SAlexey Dobriyan 27467091e728SAlexey Dobriyan static struct pernet_operations igmp_net_ops = { 27477091e728SAlexey Dobriyan .init = igmp_net_init, 27487091e728SAlexey Dobriyan .exit = igmp_net_exit, 27491da177e4SLinus Torvalds }; 27501da177e4SLinus Torvalds 27514aa5dee4SJiri Pirko static int igmp_netdev_event(struct notifier_block *this, 27524aa5dee4SJiri Pirko unsigned long event, void *ptr) 27534aa5dee4SJiri Pirko { 27544aa5dee4SJiri Pirko struct net_device *dev = netdev_notifier_info_to_dev(ptr); 27554aa5dee4SJiri Pirko struct in_device *in_dev; 27564aa5dee4SJiri Pirko 27574aa5dee4SJiri Pirko switch (event) { 27584aa5dee4SJiri Pirko case NETDEV_RESEND_IGMP: 27594aa5dee4SJiri Pirko in_dev = __in_dev_get_rtnl(dev); 27604aa5dee4SJiri Pirko if (in_dev) 27614aa5dee4SJiri Pirko ip_mc_rejoin_groups(in_dev); 27624aa5dee4SJiri Pirko break; 27634aa5dee4SJiri Pirko default: 27644aa5dee4SJiri Pirko break; 27654aa5dee4SJiri Pirko } 27664aa5dee4SJiri Pirko return NOTIFY_DONE; 27674aa5dee4SJiri Pirko } 27684aa5dee4SJiri Pirko 27694aa5dee4SJiri Pirko static struct notifier_block igmp_notifier = { 27704aa5dee4SJiri Pirko .notifier_call = igmp_netdev_event, 27714aa5dee4SJiri Pirko }; 27724aa5dee4SJiri Pirko 27731da177e4SLinus Torvalds int __init igmp_mc_proc_init(void) 27741da177e4SLinus Torvalds { 27754aa5dee4SJiri Pirko int err; 27764aa5dee4SJiri Pirko 27774aa5dee4SJiri Pirko err = register_pernet_subsys(&igmp_net_ops); 27784aa5dee4SJiri Pirko if (err) 27794aa5dee4SJiri Pirko return err; 27804aa5dee4SJiri Pirko err = register_netdevice_notifier(&igmp_notifier); 27814aa5dee4SJiri Pirko if (err) 27824aa5dee4SJiri Pirko goto reg_notif_fail; 27834aa5dee4SJiri Pirko return 0; 27844aa5dee4SJiri Pirko 27854aa5dee4SJiri Pirko reg_notif_fail: 27864aa5dee4SJiri Pirko unregister_pernet_subsys(&igmp_net_ops); 27874aa5dee4SJiri Pirko return err; 27881da177e4SLinus Torvalds } 27891da177e4SLinus Torvalds #endif 2790