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 * Version: $Id: igmp.c,v 1.47 2002/02/01 22:01:03 davem Exp $ 121da177e4SLinus Torvalds * 131da177e4SLinus Torvalds * Authors: 141da177e4SLinus Torvalds * Alan Cox <Alan.Cox@linux.org> 151da177e4SLinus Torvalds * 161da177e4SLinus Torvalds * This program is free software; you can redistribute it and/or 171da177e4SLinus Torvalds * modify it under the terms of the GNU General Public License 181da177e4SLinus Torvalds * as published by the Free Software Foundation; either version 191da177e4SLinus Torvalds * 2 of the License, or (at your option) any later version. 201da177e4SLinus Torvalds * 211da177e4SLinus Torvalds * Fixes: 221da177e4SLinus Torvalds * 231da177e4SLinus Torvalds * Alan Cox : Added lots of __inline__ to optimise 241da177e4SLinus Torvalds * the memory usage of all the tiny little 251da177e4SLinus Torvalds * functions. 261da177e4SLinus Torvalds * Alan Cox : Dumped the header building experiment. 271da177e4SLinus Torvalds * Alan Cox : Minor tweaks ready for multicast routing 281da177e4SLinus Torvalds * and extended IGMP protocol. 291da177e4SLinus Torvalds * Alan Cox : Removed a load of inline directives. Gcc 2.5.8 301da177e4SLinus Torvalds * writes utterly bogus code otherwise (sigh) 311da177e4SLinus Torvalds * fixed IGMP loopback to behave in the manner 321da177e4SLinus Torvalds * desired by mrouted, fixed the fact it has been 331da177e4SLinus Torvalds * broken since 1.3.6 and cleaned up a few minor 341da177e4SLinus Torvalds * points. 351da177e4SLinus Torvalds * 361da177e4SLinus Torvalds * Chih-Jen Chang : Tried to revise IGMP to Version 2 371da177e4SLinus Torvalds * Tsu-Sheng Tsao E-mail: chihjenc@scf.usc.edu and tsusheng@scf.usc.edu 381da177e4SLinus Torvalds * The enhancements are mainly based on Steve Deering's 391da177e4SLinus Torvalds * ipmulti-3.5 source code. 401da177e4SLinus Torvalds * Chih-Jen Chang : Added the igmp_get_mrouter_info and 411da177e4SLinus Torvalds * Tsu-Sheng Tsao igmp_set_mrouter_info to keep track of 421da177e4SLinus Torvalds * the mrouted version on that device. 431da177e4SLinus Torvalds * Chih-Jen Chang : Added the max_resp_time parameter to 441da177e4SLinus Torvalds * Tsu-Sheng Tsao igmp_heard_query(). Using this parameter 451da177e4SLinus Torvalds * to identify the multicast router version 461da177e4SLinus Torvalds * and do what the IGMP version 2 specified. 471da177e4SLinus Torvalds * Chih-Jen Chang : Added a timer to revert to IGMP V2 router 481da177e4SLinus Torvalds * Tsu-Sheng Tsao if the specified time expired. 491da177e4SLinus Torvalds * Alan Cox : Stop IGMP from 0.0.0.0 being accepted. 501da177e4SLinus Torvalds * Alan Cox : Use GFP_ATOMIC in the right places. 511da177e4SLinus Torvalds * Christian Daudt : igmp timer wasn't set for local group 521da177e4SLinus Torvalds * memberships but was being deleted, 531da177e4SLinus Torvalds * which caused a "del_timer() called 541da177e4SLinus Torvalds * from %p with timer not initialized\n" 551da177e4SLinus Torvalds * message (960131). 561da177e4SLinus Torvalds * Christian Daudt : removed del_timer from 571da177e4SLinus Torvalds * igmp_timer_expire function (960205). 581da177e4SLinus Torvalds * Christian Daudt : igmp_heard_report now only calls 591da177e4SLinus Torvalds * igmp_timer_expire if tm->running is 601da177e4SLinus Torvalds * true (960216). 611da177e4SLinus Torvalds * Malcolm Beattie : ttl comparison wrong in igmp_rcv made 621da177e4SLinus Torvalds * igmp_heard_query never trigger. Expiry 631da177e4SLinus Torvalds * miscalculation fixed in igmp_heard_query 641da177e4SLinus Torvalds * and random() made to return unsigned to 651da177e4SLinus Torvalds * prevent negative expiry times. 661da177e4SLinus Torvalds * Alexey Kuznetsov: Wrong group leaving behaviour, backport 671da177e4SLinus Torvalds * fix from pending 2.1.x patches. 681da177e4SLinus Torvalds * Alan Cox: Forget to enable FDDI support earlier. 691da177e4SLinus Torvalds * Alexey Kuznetsov: Fixed leaving groups on device down. 701da177e4SLinus Torvalds * Alexey Kuznetsov: Accordance to igmp-v2-06 draft. 711da177e4SLinus Torvalds * David L Stevens: IGMPv3 support, with help from 721da177e4SLinus Torvalds * Vinay Kulkarni 731da177e4SLinus Torvalds */ 741da177e4SLinus Torvalds 751da177e4SLinus Torvalds #include <linux/config.h> 761da177e4SLinus Torvalds #include <linux/module.h> 771da177e4SLinus Torvalds #include <asm/uaccess.h> 781da177e4SLinus Torvalds #include <asm/system.h> 791da177e4SLinus Torvalds #include <linux/types.h> 801da177e4SLinus Torvalds #include <linux/kernel.h> 811da177e4SLinus Torvalds #include <linux/jiffies.h> 821da177e4SLinus Torvalds #include <linux/string.h> 831da177e4SLinus Torvalds #include <linux/socket.h> 841da177e4SLinus Torvalds #include <linux/sockios.h> 851da177e4SLinus Torvalds #include <linux/in.h> 861da177e4SLinus Torvalds #include <linux/inet.h> 871da177e4SLinus Torvalds #include <linux/netdevice.h> 881da177e4SLinus Torvalds #include <linux/skbuff.h> 891da177e4SLinus Torvalds #include <linux/inetdevice.h> 901da177e4SLinus Torvalds #include <linux/igmp.h> 911da177e4SLinus Torvalds #include <linux/if_arp.h> 921da177e4SLinus Torvalds #include <linux/rtnetlink.h> 931da177e4SLinus Torvalds #include <linux/times.h> 9414c85021SArnaldo Carvalho de Melo 9514c85021SArnaldo Carvalho de Melo #include <net/arp.h> 961da177e4SLinus Torvalds #include <net/ip.h> 971da177e4SLinus Torvalds #include <net/protocol.h> 981da177e4SLinus Torvalds #include <net/route.h> 991da177e4SLinus Torvalds #include <net/sock.h> 1001da177e4SLinus Torvalds #include <net/checksum.h> 1011da177e4SLinus Torvalds #include <linux/netfilter_ipv4.h> 1021da177e4SLinus Torvalds #ifdef CONFIG_IP_MROUTE 1031da177e4SLinus Torvalds #include <linux/mroute.h> 1041da177e4SLinus Torvalds #endif 1051da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS 1061da177e4SLinus Torvalds #include <linux/proc_fs.h> 1071da177e4SLinus Torvalds #include <linux/seq_file.h> 1081da177e4SLinus Torvalds #endif 1091da177e4SLinus Torvalds 1101da177e4SLinus Torvalds #define IP_MAX_MEMBERSHIPS 20 1111da177e4SLinus Torvalds #define IP_MAX_MSF 10 1121da177e4SLinus Torvalds 1131da177e4SLinus Torvalds #ifdef CONFIG_IP_MULTICAST 1141da177e4SLinus Torvalds /* Parameter names and values are taken from igmp-v2-06 draft */ 1151da177e4SLinus Torvalds 1161da177e4SLinus Torvalds #define IGMP_V1_Router_Present_Timeout (400*HZ) 1171da177e4SLinus Torvalds #define IGMP_V2_Router_Present_Timeout (400*HZ) 1181da177e4SLinus Torvalds #define IGMP_Unsolicited_Report_Interval (10*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 1321da177e4SLinus Torvalds #define IGMP_V1_SEEN(in_dev) (ipv4_devconf.force_igmp_version == 1 || \ 1331da177e4SLinus Torvalds (in_dev)->cnf.force_igmp_version == 1 || \ 1341da177e4SLinus Torvalds ((in_dev)->mr_v1_seen && \ 1351da177e4SLinus Torvalds time_before(jiffies, (in_dev)->mr_v1_seen))) 1361da177e4SLinus Torvalds #define IGMP_V2_SEEN(in_dev) (ipv4_devconf.force_igmp_version == 2 || \ 1371da177e4SLinus Torvalds (in_dev)->cnf.force_igmp_version == 2 || \ 1381da177e4SLinus Torvalds ((in_dev)->mr_v2_seen && \ 1391da177e4SLinus Torvalds time_before(jiffies, (in_dev)->mr_v2_seen))) 1401da177e4SLinus Torvalds 1411da177e4SLinus Torvalds static void igmpv3_add_delrec(struct in_device *in_dev, struct ip_mc_list *im); 1421da177e4SLinus Torvalds static void igmpv3_del_delrec(struct in_device *in_dev, __u32 multiaddr); 1431da177e4SLinus Torvalds static void igmpv3_clear_delrec(struct in_device *in_dev); 1441da177e4SLinus Torvalds static int sf_setstate(struct ip_mc_list *pmc); 1451da177e4SLinus Torvalds static void sf_markstate(struct ip_mc_list *pmc); 1461da177e4SLinus Torvalds #endif 1471da177e4SLinus Torvalds static void ip_mc_clear_src(struct ip_mc_list *pmc); 1481da177e4SLinus Torvalds static int ip_mc_add_src(struct in_device *in_dev, __u32 *pmca, int sfmode, 1491da177e4SLinus Torvalds int sfcount, __u32 *psfsrc, int delta); 1501da177e4SLinus Torvalds 1511da177e4SLinus Torvalds static void ip_ma_put(struct ip_mc_list *im) 1521da177e4SLinus Torvalds { 1531da177e4SLinus Torvalds if (atomic_dec_and_test(&im->refcnt)) { 1541da177e4SLinus Torvalds in_dev_put(im->interface); 1551da177e4SLinus Torvalds kfree(im); 1561da177e4SLinus Torvalds } 1571da177e4SLinus Torvalds } 1581da177e4SLinus Torvalds 1591da177e4SLinus Torvalds #ifdef CONFIG_IP_MULTICAST 1601da177e4SLinus Torvalds 1611da177e4SLinus Torvalds /* 1621da177e4SLinus Torvalds * Timer management 1631da177e4SLinus Torvalds */ 1641da177e4SLinus Torvalds 1651da177e4SLinus Torvalds static __inline__ void igmp_stop_timer(struct ip_mc_list *im) 1661da177e4SLinus Torvalds { 1671da177e4SLinus Torvalds spin_lock_bh(&im->lock); 1681da177e4SLinus Torvalds if (del_timer(&im->timer)) 1691da177e4SLinus Torvalds atomic_dec(&im->refcnt); 1701da177e4SLinus Torvalds im->tm_running=0; 1711da177e4SLinus Torvalds im->reporter = 0; 1721da177e4SLinus Torvalds im->unsolicit_count = 0; 1731da177e4SLinus Torvalds spin_unlock_bh(&im->lock); 1741da177e4SLinus Torvalds } 1751da177e4SLinus Torvalds 1761da177e4SLinus Torvalds /* It must be called with locked im->lock */ 1771da177e4SLinus Torvalds static void igmp_start_timer(struct ip_mc_list *im, int max_delay) 1781da177e4SLinus Torvalds { 1791da177e4SLinus Torvalds int tv=net_random() % max_delay; 1801da177e4SLinus Torvalds 1811da177e4SLinus Torvalds im->tm_running=1; 1821da177e4SLinus Torvalds if (!mod_timer(&im->timer, jiffies+tv+2)) 1831da177e4SLinus Torvalds atomic_inc(&im->refcnt); 1841da177e4SLinus Torvalds } 1851da177e4SLinus Torvalds 1861da177e4SLinus Torvalds static void igmp_gq_start_timer(struct in_device *in_dev) 1871da177e4SLinus Torvalds { 1881da177e4SLinus Torvalds int tv = net_random() % in_dev->mr_maxdelay; 1891da177e4SLinus Torvalds 1901da177e4SLinus Torvalds in_dev->mr_gq_running = 1; 1911da177e4SLinus Torvalds if (!mod_timer(&in_dev->mr_gq_timer, jiffies+tv+2)) 1921da177e4SLinus Torvalds in_dev_hold(in_dev); 1931da177e4SLinus Torvalds } 1941da177e4SLinus Torvalds 1951da177e4SLinus Torvalds static void igmp_ifc_start_timer(struct in_device *in_dev, int delay) 1961da177e4SLinus Torvalds { 1971da177e4SLinus Torvalds int tv = net_random() % delay; 1981da177e4SLinus Torvalds 1991da177e4SLinus Torvalds if (!mod_timer(&in_dev->mr_ifc_timer, jiffies+tv+2)) 2001da177e4SLinus Torvalds in_dev_hold(in_dev); 2011da177e4SLinus Torvalds } 2021da177e4SLinus Torvalds 2031da177e4SLinus Torvalds static void igmp_mod_timer(struct ip_mc_list *im, int max_delay) 2041da177e4SLinus Torvalds { 2051da177e4SLinus Torvalds spin_lock_bh(&im->lock); 2061da177e4SLinus Torvalds im->unsolicit_count = 0; 2071da177e4SLinus Torvalds if (del_timer(&im->timer)) { 2081da177e4SLinus Torvalds if ((long)(im->timer.expires-jiffies) < max_delay) { 2091da177e4SLinus Torvalds add_timer(&im->timer); 2101da177e4SLinus Torvalds im->tm_running=1; 2111da177e4SLinus Torvalds spin_unlock_bh(&im->lock); 2121da177e4SLinus Torvalds return; 2131da177e4SLinus Torvalds } 2141da177e4SLinus Torvalds atomic_dec(&im->refcnt); 2151da177e4SLinus Torvalds } 2161da177e4SLinus Torvalds igmp_start_timer(im, max_delay); 2171da177e4SLinus Torvalds spin_unlock_bh(&im->lock); 2181da177e4SLinus Torvalds } 2191da177e4SLinus Torvalds 2201da177e4SLinus Torvalds 2211da177e4SLinus Torvalds /* 2221da177e4SLinus Torvalds * Send an IGMP report. 2231da177e4SLinus Torvalds */ 2241da177e4SLinus Torvalds 2251da177e4SLinus Torvalds #define IGMP_SIZE (sizeof(struct igmphdr)+sizeof(struct iphdr)+4) 2261da177e4SLinus Torvalds 2271da177e4SLinus Torvalds 2281da177e4SLinus Torvalds static int is_in(struct ip_mc_list *pmc, struct ip_sf_list *psf, int type, 2291da177e4SLinus Torvalds int gdeleted, int sdeleted) 2301da177e4SLinus Torvalds { 2311da177e4SLinus Torvalds switch (type) { 2321da177e4SLinus Torvalds case IGMPV3_MODE_IS_INCLUDE: 2331da177e4SLinus Torvalds case IGMPV3_MODE_IS_EXCLUDE: 2341da177e4SLinus Torvalds if (gdeleted || sdeleted) 2351da177e4SLinus Torvalds return 0; 2361da177e4SLinus Torvalds return !(pmc->gsquery && !psf->sf_gsresp); 2371da177e4SLinus Torvalds case IGMPV3_CHANGE_TO_INCLUDE: 2381da177e4SLinus Torvalds if (gdeleted || sdeleted) 2391da177e4SLinus Torvalds return 0; 2401da177e4SLinus Torvalds return psf->sf_count[MCAST_INCLUDE] != 0; 2411da177e4SLinus Torvalds case IGMPV3_CHANGE_TO_EXCLUDE: 2421da177e4SLinus Torvalds if (gdeleted || sdeleted) 2431da177e4SLinus Torvalds return 0; 2441da177e4SLinus Torvalds if (pmc->sfcount[MCAST_EXCLUDE] == 0 || 2451da177e4SLinus Torvalds psf->sf_count[MCAST_INCLUDE]) 2461da177e4SLinus Torvalds return 0; 2471da177e4SLinus Torvalds return pmc->sfcount[MCAST_EXCLUDE] == 2481da177e4SLinus Torvalds psf->sf_count[MCAST_EXCLUDE]; 2491da177e4SLinus Torvalds case IGMPV3_ALLOW_NEW_SOURCES: 2501da177e4SLinus Torvalds if (gdeleted || !psf->sf_crcount) 2511da177e4SLinus Torvalds return 0; 2521da177e4SLinus Torvalds return (pmc->sfmode == MCAST_INCLUDE) ^ sdeleted; 2531da177e4SLinus Torvalds case IGMPV3_BLOCK_OLD_SOURCES: 2541da177e4SLinus Torvalds if (pmc->sfmode == MCAST_INCLUDE) 2551da177e4SLinus Torvalds return gdeleted || (psf->sf_crcount && sdeleted); 2561da177e4SLinus Torvalds return psf->sf_crcount && !gdeleted && !sdeleted; 2571da177e4SLinus Torvalds } 2581da177e4SLinus Torvalds return 0; 2591da177e4SLinus Torvalds } 2601da177e4SLinus Torvalds 2611da177e4SLinus Torvalds static int 2621da177e4SLinus Torvalds igmp_scount(struct ip_mc_list *pmc, int type, int gdeleted, int sdeleted) 2631da177e4SLinus Torvalds { 2641da177e4SLinus Torvalds struct ip_sf_list *psf; 2651da177e4SLinus Torvalds int scount = 0; 2661da177e4SLinus Torvalds 2671da177e4SLinus Torvalds for (psf=pmc->sources; psf; psf=psf->sf_next) { 2681da177e4SLinus Torvalds if (!is_in(pmc, psf, type, gdeleted, sdeleted)) 2691da177e4SLinus Torvalds continue; 2701da177e4SLinus Torvalds scount++; 2711da177e4SLinus Torvalds } 2721da177e4SLinus Torvalds return scount; 2731da177e4SLinus Torvalds } 2741da177e4SLinus Torvalds 2751da177e4SLinus Torvalds static struct sk_buff *igmpv3_newpack(struct net_device *dev, int size) 2761da177e4SLinus Torvalds { 2771da177e4SLinus Torvalds struct sk_buff *skb; 2781da177e4SLinus Torvalds struct rtable *rt; 2791da177e4SLinus Torvalds struct iphdr *pip; 2801da177e4SLinus Torvalds struct igmpv3_report *pig; 2811da177e4SLinus Torvalds 2821da177e4SLinus Torvalds skb = alloc_skb(size + LL_RESERVED_SPACE(dev), GFP_ATOMIC); 2831da177e4SLinus Torvalds if (skb == NULL) 2841da177e4SLinus Torvalds return NULL; 2851da177e4SLinus Torvalds 2861da177e4SLinus Torvalds { 2871da177e4SLinus Torvalds struct flowi fl = { .oif = dev->ifindex, 2881da177e4SLinus Torvalds .nl_u = { .ip4_u = { 2891da177e4SLinus Torvalds .daddr = IGMPV3_ALL_MCR } }, 2901da177e4SLinus Torvalds .proto = IPPROTO_IGMP }; 2911da177e4SLinus Torvalds if (ip_route_output_key(&rt, &fl)) { 2921da177e4SLinus Torvalds kfree_skb(skb); 2931da177e4SLinus Torvalds return NULL; 2941da177e4SLinus Torvalds } 2951da177e4SLinus Torvalds } 2961da177e4SLinus Torvalds if (rt->rt_src == 0) { 2971da177e4SLinus Torvalds kfree_skb(skb); 2981da177e4SLinus Torvalds ip_rt_put(rt); 2991da177e4SLinus Torvalds return NULL; 3001da177e4SLinus Torvalds } 3011da177e4SLinus Torvalds 3021da177e4SLinus Torvalds skb->dst = &rt->u.dst; 3031da177e4SLinus Torvalds skb->dev = dev; 3041da177e4SLinus Torvalds 3051da177e4SLinus Torvalds skb_reserve(skb, LL_RESERVED_SPACE(dev)); 3061da177e4SLinus Torvalds 3071da177e4SLinus Torvalds skb->nh.iph = pip =(struct iphdr *)skb_put(skb, sizeof(struct iphdr)+4); 3081da177e4SLinus Torvalds 3091da177e4SLinus Torvalds pip->version = 4; 3101da177e4SLinus Torvalds pip->ihl = (sizeof(struct iphdr)+4)>>2; 3111da177e4SLinus Torvalds pip->tos = 0xc0; 3121da177e4SLinus Torvalds pip->frag_off = htons(IP_DF); 3131da177e4SLinus Torvalds pip->ttl = 1; 3141da177e4SLinus Torvalds pip->daddr = rt->rt_dst; 3151da177e4SLinus Torvalds pip->saddr = rt->rt_src; 3161da177e4SLinus Torvalds pip->protocol = IPPROTO_IGMP; 3171da177e4SLinus Torvalds pip->tot_len = 0; /* filled in later */ 3181da177e4SLinus Torvalds ip_select_ident(pip, &rt->u.dst, NULL); 3191da177e4SLinus Torvalds ((u8*)&pip[1])[0] = IPOPT_RA; 3201da177e4SLinus Torvalds ((u8*)&pip[1])[1] = 4; 3211da177e4SLinus Torvalds ((u8*)&pip[1])[2] = 0; 3221da177e4SLinus Torvalds ((u8*)&pip[1])[3] = 0; 3231da177e4SLinus Torvalds 3241da177e4SLinus Torvalds pig =(struct igmpv3_report *)skb_put(skb, sizeof(*pig)); 3251da177e4SLinus Torvalds skb->h.igmph = (struct igmphdr *)pig; 3261da177e4SLinus Torvalds pig->type = IGMPV3_HOST_MEMBERSHIP_REPORT; 3271da177e4SLinus Torvalds pig->resv1 = 0; 3281da177e4SLinus Torvalds pig->csum = 0; 3291da177e4SLinus Torvalds pig->resv2 = 0; 3301da177e4SLinus Torvalds pig->ngrec = 0; 3311da177e4SLinus Torvalds return skb; 3321da177e4SLinus Torvalds } 3331da177e4SLinus Torvalds 3341da177e4SLinus Torvalds static int igmpv3_sendpack(struct sk_buff *skb) 3351da177e4SLinus Torvalds { 3361da177e4SLinus Torvalds struct iphdr *pip = skb->nh.iph; 3371da177e4SLinus Torvalds struct igmphdr *pig = skb->h.igmph; 3381da177e4SLinus Torvalds int iplen, igmplen; 3391da177e4SLinus Torvalds 3401da177e4SLinus Torvalds iplen = skb->tail - (unsigned char *)skb->nh.iph; 3411da177e4SLinus Torvalds pip->tot_len = htons(iplen); 3421da177e4SLinus Torvalds ip_send_check(pip); 3431da177e4SLinus Torvalds 3441da177e4SLinus Torvalds igmplen = skb->tail - (unsigned char *)skb->h.igmph; 3451da177e4SLinus Torvalds pig->csum = ip_compute_csum((void *)skb->h.igmph, igmplen); 3461da177e4SLinus Torvalds 3471da177e4SLinus Torvalds return NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, skb->dev, 3481da177e4SLinus Torvalds dst_output); 3491da177e4SLinus Torvalds } 3501da177e4SLinus Torvalds 3511da177e4SLinus Torvalds static int grec_size(struct ip_mc_list *pmc, int type, int gdel, int sdel) 3521da177e4SLinus Torvalds { 3531da177e4SLinus Torvalds return sizeof(struct igmpv3_grec) + 4*igmp_scount(pmc,type,gdel,sdel); 3541da177e4SLinus Torvalds } 3551da177e4SLinus Torvalds 3561da177e4SLinus Torvalds static struct sk_buff *add_grhead(struct sk_buff *skb, struct ip_mc_list *pmc, 3571da177e4SLinus Torvalds int type, struct igmpv3_grec **ppgr) 3581da177e4SLinus Torvalds { 3591da177e4SLinus Torvalds struct net_device *dev = pmc->interface->dev; 3601da177e4SLinus Torvalds struct igmpv3_report *pih; 3611da177e4SLinus Torvalds struct igmpv3_grec *pgr; 3621da177e4SLinus Torvalds 3631da177e4SLinus Torvalds if (!skb) 3641da177e4SLinus Torvalds skb = igmpv3_newpack(dev, dev->mtu); 3651da177e4SLinus Torvalds if (!skb) 3661da177e4SLinus Torvalds return NULL; 3671da177e4SLinus Torvalds pgr = (struct igmpv3_grec *)skb_put(skb, sizeof(struct igmpv3_grec)); 3681da177e4SLinus Torvalds pgr->grec_type = type; 3691da177e4SLinus Torvalds pgr->grec_auxwords = 0; 3701da177e4SLinus Torvalds pgr->grec_nsrcs = 0; 3711da177e4SLinus Torvalds pgr->grec_mca = pmc->multiaddr; 3721da177e4SLinus Torvalds pih = (struct igmpv3_report *)skb->h.igmph; 3731da177e4SLinus Torvalds pih->ngrec = htons(ntohs(pih->ngrec)+1); 3741da177e4SLinus Torvalds *ppgr = pgr; 3751da177e4SLinus Torvalds return skb; 3761da177e4SLinus Torvalds } 3771da177e4SLinus Torvalds 3781da177e4SLinus Torvalds #define AVAILABLE(skb) ((skb) ? ((skb)->dev ? (skb)->dev->mtu - (skb)->len : \ 3791da177e4SLinus Torvalds skb_tailroom(skb)) : 0) 3801da177e4SLinus Torvalds 3811da177e4SLinus Torvalds static struct sk_buff *add_grec(struct sk_buff *skb, struct ip_mc_list *pmc, 3821da177e4SLinus Torvalds int type, int gdeleted, int sdeleted) 3831da177e4SLinus Torvalds { 3841da177e4SLinus Torvalds struct net_device *dev = pmc->interface->dev; 3851da177e4SLinus Torvalds struct igmpv3_report *pih; 3861da177e4SLinus Torvalds struct igmpv3_grec *pgr = NULL; 3871da177e4SLinus Torvalds struct ip_sf_list *psf, *psf_next, *psf_prev, **psf_list; 3881da177e4SLinus Torvalds int scount, first, isquery, truncate; 3891da177e4SLinus Torvalds 3901da177e4SLinus Torvalds if (pmc->multiaddr == IGMP_ALL_HOSTS) 3911da177e4SLinus Torvalds return skb; 3921da177e4SLinus Torvalds 3931da177e4SLinus Torvalds isquery = type == IGMPV3_MODE_IS_INCLUDE || 3941da177e4SLinus Torvalds type == IGMPV3_MODE_IS_EXCLUDE; 3951da177e4SLinus Torvalds truncate = type == IGMPV3_MODE_IS_EXCLUDE || 3961da177e4SLinus Torvalds type == IGMPV3_CHANGE_TO_EXCLUDE; 3971da177e4SLinus Torvalds 3981da177e4SLinus Torvalds psf_list = sdeleted ? &pmc->tomb : &pmc->sources; 3991da177e4SLinus Torvalds 4001da177e4SLinus Torvalds if (!*psf_list) { 4011da177e4SLinus Torvalds if (type == IGMPV3_ALLOW_NEW_SOURCES || 4021da177e4SLinus Torvalds type == IGMPV3_BLOCK_OLD_SOURCES) 4031da177e4SLinus Torvalds return skb; 4041da177e4SLinus Torvalds if (pmc->crcount || isquery) { 4051da177e4SLinus Torvalds /* make sure we have room for group header and at 4061da177e4SLinus Torvalds * least one source. 4071da177e4SLinus Torvalds */ 4081da177e4SLinus Torvalds if (skb && AVAILABLE(skb) < sizeof(struct igmpv3_grec)+ 4091da177e4SLinus Torvalds sizeof(__u32)) { 4101da177e4SLinus Torvalds igmpv3_sendpack(skb); 4111da177e4SLinus Torvalds skb = NULL; /* add_grhead will get a new one */ 4121da177e4SLinus Torvalds } 4131da177e4SLinus Torvalds skb = add_grhead(skb, pmc, type, &pgr); 4141da177e4SLinus Torvalds } 4151da177e4SLinus Torvalds return skb; 4161da177e4SLinus Torvalds } 4171da177e4SLinus Torvalds pih = skb ? (struct igmpv3_report *)skb->h.igmph : NULL; 4181da177e4SLinus Torvalds 4191da177e4SLinus Torvalds /* EX and TO_EX get a fresh packet, if needed */ 4201da177e4SLinus Torvalds if (truncate) { 4211da177e4SLinus Torvalds if (pih && pih->ngrec && 4221da177e4SLinus Torvalds AVAILABLE(skb) < grec_size(pmc, type, gdeleted, sdeleted)) { 4231da177e4SLinus Torvalds if (skb) 4241da177e4SLinus Torvalds igmpv3_sendpack(skb); 4251da177e4SLinus Torvalds skb = igmpv3_newpack(dev, dev->mtu); 4261da177e4SLinus Torvalds } 4271da177e4SLinus Torvalds } 4281da177e4SLinus Torvalds first = 1; 4291da177e4SLinus Torvalds scount = 0; 4301da177e4SLinus Torvalds psf_prev = NULL; 4311da177e4SLinus Torvalds for (psf=*psf_list; psf; psf=psf_next) { 4321da177e4SLinus Torvalds u32 *psrc; 4331da177e4SLinus Torvalds 4341da177e4SLinus Torvalds psf_next = psf->sf_next; 4351da177e4SLinus Torvalds 4361da177e4SLinus Torvalds if (!is_in(pmc, psf, type, gdeleted, sdeleted)) { 4371da177e4SLinus Torvalds psf_prev = psf; 4381da177e4SLinus Torvalds continue; 4391da177e4SLinus Torvalds } 4401da177e4SLinus Torvalds 4411da177e4SLinus Torvalds /* clear marks on query responses */ 4421da177e4SLinus Torvalds if (isquery) 4431da177e4SLinus Torvalds psf->sf_gsresp = 0; 4441da177e4SLinus Torvalds 4451da177e4SLinus Torvalds if (AVAILABLE(skb) < sizeof(u32) + 4461da177e4SLinus Torvalds first*sizeof(struct igmpv3_grec)) { 4471da177e4SLinus Torvalds if (truncate && !first) 4481da177e4SLinus Torvalds break; /* truncate these */ 4491da177e4SLinus Torvalds if (pgr) 4501da177e4SLinus Torvalds pgr->grec_nsrcs = htons(scount); 4511da177e4SLinus Torvalds if (skb) 4521da177e4SLinus Torvalds igmpv3_sendpack(skb); 4531da177e4SLinus Torvalds skb = igmpv3_newpack(dev, dev->mtu); 4541da177e4SLinus Torvalds first = 1; 4551da177e4SLinus Torvalds scount = 0; 4561da177e4SLinus Torvalds } 4571da177e4SLinus Torvalds if (first) { 4581da177e4SLinus Torvalds skb = add_grhead(skb, pmc, type, &pgr); 4591da177e4SLinus Torvalds first = 0; 4601da177e4SLinus Torvalds } 4611da177e4SLinus Torvalds psrc = (u32 *)skb_put(skb, sizeof(u32)); 4621da177e4SLinus Torvalds *psrc = psf->sf_inaddr; 4631da177e4SLinus Torvalds scount++; 4641da177e4SLinus Torvalds if ((type == IGMPV3_ALLOW_NEW_SOURCES || 4651da177e4SLinus Torvalds type == IGMPV3_BLOCK_OLD_SOURCES) && psf->sf_crcount) { 4661da177e4SLinus Torvalds psf->sf_crcount--; 4671da177e4SLinus Torvalds if ((sdeleted || gdeleted) && psf->sf_crcount == 0) { 4681da177e4SLinus Torvalds if (psf_prev) 4691da177e4SLinus Torvalds psf_prev->sf_next = psf->sf_next; 4701da177e4SLinus Torvalds else 4711da177e4SLinus Torvalds *psf_list = psf->sf_next; 4721da177e4SLinus Torvalds kfree(psf); 4731da177e4SLinus Torvalds continue; 4741da177e4SLinus Torvalds } 4751da177e4SLinus Torvalds } 4761da177e4SLinus Torvalds psf_prev = psf; 4771da177e4SLinus Torvalds } 4781da177e4SLinus Torvalds if (pgr) 4791da177e4SLinus Torvalds pgr->grec_nsrcs = htons(scount); 4801da177e4SLinus Torvalds 4811da177e4SLinus Torvalds if (isquery) 4821da177e4SLinus Torvalds pmc->gsquery = 0; /* clear query state on report */ 4831da177e4SLinus Torvalds return skb; 4841da177e4SLinus Torvalds } 4851da177e4SLinus Torvalds 4861da177e4SLinus Torvalds static int igmpv3_send_report(struct in_device *in_dev, struct ip_mc_list *pmc) 4871da177e4SLinus Torvalds { 4881da177e4SLinus Torvalds struct sk_buff *skb = NULL; 4891da177e4SLinus Torvalds int type; 4901da177e4SLinus Torvalds 4911da177e4SLinus Torvalds if (!pmc) { 4921da177e4SLinus Torvalds read_lock(&in_dev->mc_list_lock); 4931da177e4SLinus Torvalds for (pmc=in_dev->mc_list; pmc; pmc=pmc->next) { 4941da177e4SLinus Torvalds if (pmc->multiaddr == IGMP_ALL_HOSTS) 4951da177e4SLinus Torvalds continue; 4961da177e4SLinus Torvalds spin_lock_bh(&pmc->lock); 4971da177e4SLinus Torvalds if (pmc->sfcount[MCAST_EXCLUDE]) 4981da177e4SLinus Torvalds type = IGMPV3_MODE_IS_EXCLUDE; 4991da177e4SLinus Torvalds else 5001da177e4SLinus Torvalds type = IGMPV3_MODE_IS_INCLUDE; 5011da177e4SLinus Torvalds skb = add_grec(skb, pmc, type, 0, 0); 5021da177e4SLinus Torvalds spin_unlock_bh(&pmc->lock); 5031da177e4SLinus Torvalds } 5041da177e4SLinus Torvalds read_unlock(&in_dev->mc_list_lock); 5051da177e4SLinus Torvalds } else { 5061da177e4SLinus Torvalds spin_lock_bh(&pmc->lock); 5071da177e4SLinus Torvalds if (pmc->sfcount[MCAST_EXCLUDE]) 5081da177e4SLinus Torvalds type = IGMPV3_MODE_IS_EXCLUDE; 5091da177e4SLinus Torvalds else 5101da177e4SLinus Torvalds type = IGMPV3_MODE_IS_INCLUDE; 5111da177e4SLinus Torvalds skb = add_grec(skb, pmc, type, 0, 0); 5121da177e4SLinus Torvalds spin_unlock_bh(&pmc->lock); 5131da177e4SLinus Torvalds } 5141da177e4SLinus Torvalds if (!skb) 5151da177e4SLinus Torvalds return 0; 5161da177e4SLinus Torvalds return igmpv3_sendpack(skb); 5171da177e4SLinus Torvalds } 5181da177e4SLinus Torvalds 5191da177e4SLinus Torvalds /* 5201da177e4SLinus Torvalds * remove zero-count source records from a source filter list 5211da177e4SLinus Torvalds */ 5221da177e4SLinus Torvalds static void igmpv3_clear_zeros(struct ip_sf_list **ppsf) 5231da177e4SLinus Torvalds { 5241da177e4SLinus Torvalds struct ip_sf_list *psf_prev, *psf_next, *psf; 5251da177e4SLinus Torvalds 5261da177e4SLinus Torvalds psf_prev = NULL; 5271da177e4SLinus Torvalds for (psf=*ppsf; psf; psf = psf_next) { 5281da177e4SLinus Torvalds psf_next = psf->sf_next; 5291da177e4SLinus Torvalds if (psf->sf_crcount == 0) { 5301da177e4SLinus Torvalds if (psf_prev) 5311da177e4SLinus Torvalds psf_prev->sf_next = psf->sf_next; 5321da177e4SLinus Torvalds else 5331da177e4SLinus Torvalds *ppsf = psf->sf_next; 5341da177e4SLinus Torvalds kfree(psf); 5351da177e4SLinus Torvalds } else 5361da177e4SLinus Torvalds psf_prev = psf; 5371da177e4SLinus Torvalds } 5381da177e4SLinus Torvalds } 5391da177e4SLinus Torvalds 5401da177e4SLinus Torvalds static void igmpv3_send_cr(struct in_device *in_dev) 5411da177e4SLinus Torvalds { 5421da177e4SLinus Torvalds struct ip_mc_list *pmc, *pmc_prev, *pmc_next; 5431da177e4SLinus Torvalds struct sk_buff *skb = NULL; 5441da177e4SLinus Torvalds int type, dtype; 5451da177e4SLinus Torvalds 5461da177e4SLinus Torvalds read_lock(&in_dev->mc_list_lock); 5471da177e4SLinus Torvalds spin_lock_bh(&in_dev->mc_tomb_lock); 5481da177e4SLinus Torvalds 5491da177e4SLinus Torvalds /* deleted MCA's */ 5501da177e4SLinus Torvalds pmc_prev = NULL; 5511da177e4SLinus Torvalds for (pmc=in_dev->mc_tomb; pmc; pmc=pmc_next) { 5521da177e4SLinus Torvalds pmc_next = pmc->next; 5531da177e4SLinus Torvalds if (pmc->sfmode == MCAST_INCLUDE) { 5541da177e4SLinus Torvalds type = IGMPV3_BLOCK_OLD_SOURCES; 5551da177e4SLinus Torvalds dtype = IGMPV3_BLOCK_OLD_SOURCES; 5561da177e4SLinus Torvalds skb = add_grec(skb, pmc, type, 1, 0); 5571da177e4SLinus Torvalds skb = add_grec(skb, pmc, dtype, 1, 1); 5581da177e4SLinus Torvalds } 5591da177e4SLinus Torvalds if (pmc->crcount) { 5601da177e4SLinus Torvalds pmc->crcount--; 5611da177e4SLinus Torvalds if (pmc->sfmode == MCAST_EXCLUDE) { 5621da177e4SLinus Torvalds type = IGMPV3_CHANGE_TO_INCLUDE; 5631da177e4SLinus Torvalds skb = add_grec(skb, pmc, type, 1, 0); 5641da177e4SLinus Torvalds } 5651da177e4SLinus Torvalds if (pmc->crcount == 0) { 5661da177e4SLinus Torvalds igmpv3_clear_zeros(&pmc->tomb); 5671da177e4SLinus Torvalds igmpv3_clear_zeros(&pmc->sources); 5681da177e4SLinus Torvalds } 5691da177e4SLinus Torvalds } 5701da177e4SLinus Torvalds if (pmc->crcount == 0 && !pmc->tomb && !pmc->sources) { 5711da177e4SLinus Torvalds if (pmc_prev) 5721da177e4SLinus Torvalds pmc_prev->next = pmc_next; 5731da177e4SLinus Torvalds else 5741da177e4SLinus Torvalds in_dev->mc_tomb = pmc_next; 5751da177e4SLinus Torvalds in_dev_put(pmc->interface); 5761da177e4SLinus Torvalds kfree(pmc); 5771da177e4SLinus Torvalds } else 5781da177e4SLinus Torvalds pmc_prev = pmc; 5791da177e4SLinus Torvalds } 5801da177e4SLinus Torvalds spin_unlock_bh(&in_dev->mc_tomb_lock); 5811da177e4SLinus Torvalds 5821da177e4SLinus Torvalds /* change recs */ 5831da177e4SLinus Torvalds for (pmc=in_dev->mc_list; pmc; pmc=pmc->next) { 5841da177e4SLinus Torvalds spin_lock_bh(&pmc->lock); 5851da177e4SLinus Torvalds if (pmc->sfcount[MCAST_EXCLUDE]) { 5861da177e4SLinus Torvalds type = IGMPV3_BLOCK_OLD_SOURCES; 5871da177e4SLinus Torvalds dtype = IGMPV3_ALLOW_NEW_SOURCES; 5881da177e4SLinus Torvalds } else { 5891da177e4SLinus Torvalds type = IGMPV3_ALLOW_NEW_SOURCES; 5901da177e4SLinus Torvalds dtype = IGMPV3_BLOCK_OLD_SOURCES; 5911da177e4SLinus Torvalds } 5921da177e4SLinus Torvalds skb = add_grec(skb, pmc, type, 0, 0); 5931da177e4SLinus Torvalds skb = add_grec(skb, pmc, dtype, 0, 1); /* deleted sources */ 5941da177e4SLinus Torvalds 5951da177e4SLinus Torvalds /* filter mode changes */ 5961da177e4SLinus Torvalds if (pmc->crcount) { 5971da177e4SLinus Torvalds pmc->crcount--; 5981da177e4SLinus Torvalds if (pmc->sfmode == MCAST_EXCLUDE) 5991da177e4SLinus Torvalds type = IGMPV3_CHANGE_TO_EXCLUDE; 6001da177e4SLinus Torvalds else 6011da177e4SLinus Torvalds type = IGMPV3_CHANGE_TO_INCLUDE; 6021da177e4SLinus Torvalds skb = add_grec(skb, pmc, type, 0, 0); 6031da177e4SLinus Torvalds } 6041da177e4SLinus Torvalds spin_unlock_bh(&pmc->lock); 6051da177e4SLinus Torvalds } 6061da177e4SLinus Torvalds read_unlock(&in_dev->mc_list_lock); 6071da177e4SLinus Torvalds 6081da177e4SLinus Torvalds if (!skb) 6091da177e4SLinus Torvalds return; 6101da177e4SLinus Torvalds (void) igmpv3_sendpack(skb); 6111da177e4SLinus Torvalds } 6121da177e4SLinus Torvalds 6131da177e4SLinus Torvalds static int igmp_send_report(struct in_device *in_dev, struct ip_mc_list *pmc, 6141da177e4SLinus Torvalds int type) 6151da177e4SLinus Torvalds { 6161da177e4SLinus Torvalds struct sk_buff *skb; 6171da177e4SLinus Torvalds struct iphdr *iph; 6181da177e4SLinus Torvalds struct igmphdr *ih; 6191da177e4SLinus Torvalds struct rtable *rt; 6201da177e4SLinus Torvalds struct net_device *dev = in_dev->dev; 6211da177e4SLinus Torvalds u32 group = pmc ? pmc->multiaddr : 0; 6221da177e4SLinus Torvalds u32 dst; 6231da177e4SLinus Torvalds 6241da177e4SLinus Torvalds if (type == IGMPV3_HOST_MEMBERSHIP_REPORT) 6251da177e4SLinus Torvalds return igmpv3_send_report(in_dev, pmc); 6261da177e4SLinus Torvalds else if (type == IGMP_HOST_LEAVE_MESSAGE) 6271da177e4SLinus Torvalds dst = IGMP_ALL_ROUTER; 6281da177e4SLinus Torvalds else 6291da177e4SLinus Torvalds dst = group; 6301da177e4SLinus Torvalds 6311da177e4SLinus Torvalds { 6321da177e4SLinus Torvalds struct flowi fl = { .oif = dev->ifindex, 6331da177e4SLinus Torvalds .nl_u = { .ip4_u = { .daddr = dst } }, 6341da177e4SLinus Torvalds .proto = IPPROTO_IGMP }; 6351da177e4SLinus Torvalds if (ip_route_output_key(&rt, &fl)) 6361da177e4SLinus Torvalds return -1; 6371da177e4SLinus Torvalds } 6381da177e4SLinus Torvalds if (rt->rt_src == 0) { 6391da177e4SLinus Torvalds ip_rt_put(rt); 6401da177e4SLinus Torvalds return -1; 6411da177e4SLinus Torvalds } 6421da177e4SLinus Torvalds 6431da177e4SLinus Torvalds skb=alloc_skb(IGMP_SIZE+LL_RESERVED_SPACE(dev), GFP_ATOMIC); 6441da177e4SLinus Torvalds if (skb == NULL) { 6451da177e4SLinus Torvalds ip_rt_put(rt); 6461da177e4SLinus Torvalds return -1; 6471da177e4SLinus Torvalds } 6481da177e4SLinus Torvalds 6491da177e4SLinus Torvalds skb->dst = &rt->u.dst; 6501da177e4SLinus Torvalds 6511da177e4SLinus Torvalds skb_reserve(skb, LL_RESERVED_SPACE(dev)); 6521da177e4SLinus Torvalds 6531da177e4SLinus Torvalds skb->nh.iph = iph = (struct iphdr *)skb_put(skb, sizeof(struct iphdr)+4); 6541da177e4SLinus Torvalds 6551da177e4SLinus Torvalds iph->version = 4; 6561da177e4SLinus Torvalds iph->ihl = (sizeof(struct iphdr)+4)>>2; 6571da177e4SLinus Torvalds iph->tos = 0xc0; 6581da177e4SLinus Torvalds iph->frag_off = htons(IP_DF); 6591da177e4SLinus Torvalds iph->ttl = 1; 6601da177e4SLinus Torvalds iph->daddr = dst; 6611da177e4SLinus Torvalds iph->saddr = rt->rt_src; 6621da177e4SLinus Torvalds iph->protocol = IPPROTO_IGMP; 6631da177e4SLinus Torvalds iph->tot_len = htons(IGMP_SIZE); 6641da177e4SLinus Torvalds ip_select_ident(iph, &rt->u.dst, NULL); 6651da177e4SLinus Torvalds ((u8*)&iph[1])[0] = IPOPT_RA; 6661da177e4SLinus Torvalds ((u8*)&iph[1])[1] = 4; 6671da177e4SLinus Torvalds ((u8*)&iph[1])[2] = 0; 6681da177e4SLinus Torvalds ((u8*)&iph[1])[3] = 0; 6691da177e4SLinus Torvalds ip_send_check(iph); 6701da177e4SLinus Torvalds 6711da177e4SLinus Torvalds ih = (struct igmphdr *)skb_put(skb, sizeof(struct igmphdr)); 6721da177e4SLinus Torvalds ih->type=type; 6731da177e4SLinus Torvalds ih->code=0; 6741da177e4SLinus Torvalds ih->csum=0; 6751da177e4SLinus Torvalds ih->group=group; 6761da177e4SLinus Torvalds ih->csum=ip_compute_csum((void *)ih, sizeof(struct igmphdr)); 6771da177e4SLinus Torvalds 6781da177e4SLinus Torvalds return NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, rt->u.dst.dev, 6791da177e4SLinus Torvalds dst_output); 6801da177e4SLinus Torvalds } 6811da177e4SLinus Torvalds 6821da177e4SLinus Torvalds static void igmp_gq_timer_expire(unsigned long data) 6831da177e4SLinus Torvalds { 6841da177e4SLinus Torvalds struct in_device *in_dev = (struct in_device *)data; 6851da177e4SLinus Torvalds 6861da177e4SLinus Torvalds in_dev->mr_gq_running = 0; 6871da177e4SLinus Torvalds igmpv3_send_report(in_dev, NULL); 6881da177e4SLinus Torvalds __in_dev_put(in_dev); 6891da177e4SLinus Torvalds } 6901da177e4SLinus Torvalds 6911da177e4SLinus Torvalds static void igmp_ifc_timer_expire(unsigned long data) 6921da177e4SLinus Torvalds { 6931da177e4SLinus Torvalds struct in_device *in_dev = (struct in_device *)data; 6941da177e4SLinus Torvalds 6951da177e4SLinus Torvalds igmpv3_send_cr(in_dev); 6961da177e4SLinus Torvalds if (in_dev->mr_ifc_count) { 6971da177e4SLinus Torvalds in_dev->mr_ifc_count--; 6981da177e4SLinus Torvalds igmp_ifc_start_timer(in_dev, IGMP_Unsolicited_Report_Interval); 6991da177e4SLinus Torvalds } 7001da177e4SLinus Torvalds __in_dev_put(in_dev); 7011da177e4SLinus Torvalds } 7021da177e4SLinus Torvalds 7031da177e4SLinus Torvalds static void igmp_ifc_event(struct in_device *in_dev) 7041da177e4SLinus Torvalds { 7051da177e4SLinus Torvalds if (IGMP_V1_SEEN(in_dev) || IGMP_V2_SEEN(in_dev)) 7061da177e4SLinus Torvalds return; 7071da177e4SLinus Torvalds in_dev->mr_ifc_count = in_dev->mr_qrv ? in_dev->mr_qrv : 7081da177e4SLinus Torvalds IGMP_Unsolicited_Report_Count; 7091da177e4SLinus Torvalds igmp_ifc_start_timer(in_dev, 1); 7101da177e4SLinus Torvalds } 7111da177e4SLinus Torvalds 7121da177e4SLinus Torvalds 7131da177e4SLinus Torvalds static void igmp_timer_expire(unsigned long data) 7141da177e4SLinus Torvalds { 7151da177e4SLinus Torvalds struct ip_mc_list *im=(struct ip_mc_list *)data; 7161da177e4SLinus Torvalds struct in_device *in_dev = im->interface; 7171da177e4SLinus Torvalds 7181da177e4SLinus Torvalds spin_lock(&im->lock); 7191da177e4SLinus Torvalds im->tm_running=0; 7201da177e4SLinus Torvalds 7211da177e4SLinus Torvalds if (im->unsolicit_count) { 7221da177e4SLinus Torvalds im->unsolicit_count--; 7231da177e4SLinus Torvalds igmp_start_timer(im, IGMP_Unsolicited_Report_Interval); 7241da177e4SLinus Torvalds } 7251da177e4SLinus Torvalds im->reporter = 1; 7261da177e4SLinus Torvalds spin_unlock(&im->lock); 7271da177e4SLinus Torvalds 7281da177e4SLinus Torvalds if (IGMP_V1_SEEN(in_dev)) 7291da177e4SLinus Torvalds igmp_send_report(in_dev, im, IGMP_HOST_MEMBERSHIP_REPORT); 7301da177e4SLinus Torvalds else if (IGMP_V2_SEEN(in_dev)) 7311da177e4SLinus Torvalds igmp_send_report(in_dev, im, IGMPV2_HOST_MEMBERSHIP_REPORT); 7321da177e4SLinus Torvalds else 7331da177e4SLinus Torvalds igmp_send_report(in_dev, im, IGMPV3_HOST_MEMBERSHIP_REPORT); 7341da177e4SLinus Torvalds 7351da177e4SLinus Torvalds ip_ma_put(im); 7361da177e4SLinus Torvalds } 7371da177e4SLinus Torvalds 7381da177e4SLinus Torvalds static void igmp_marksources(struct ip_mc_list *pmc, int nsrcs, __u32 *srcs) 7391da177e4SLinus Torvalds { 7401da177e4SLinus Torvalds struct ip_sf_list *psf; 7411da177e4SLinus Torvalds int i, scount; 7421da177e4SLinus Torvalds 7431da177e4SLinus Torvalds scount = 0; 7441da177e4SLinus Torvalds for (psf=pmc->sources; psf; psf=psf->sf_next) { 7451da177e4SLinus Torvalds if (scount == nsrcs) 7461da177e4SLinus Torvalds break; 7471da177e4SLinus Torvalds for (i=0; i<nsrcs; i++) 7481da177e4SLinus Torvalds if (srcs[i] == psf->sf_inaddr) { 7491da177e4SLinus Torvalds psf->sf_gsresp = 1; 7501da177e4SLinus Torvalds scount++; 7511da177e4SLinus Torvalds break; 7521da177e4SLinus Torvalds } 7531da177e4SLinus Torvalds } 7541da177e4SLinus Torvalds } 7551da177e4SLinus Torvalds 7561da177e4SLinus Torvalds static void igmp_heard_report(struct in_device *in_dev, u32 group) 7571da177e4SLinus Torvalds { 7581da177e4SLinus Torvalds struct ip_mc_list *im; 7591da177e4SLinus Torvalds 7601da177e4SLinus Torvalds /* Timers are only set for non-local groups */ 7611da177e4SLinus Torvalds 7621da177e4SLinus Torvalds if (group == IGMP_ALL_HOSTS) 7631da177e4SLinus Torvalds return; 7641da177e4SLinus Torvalds 7651da177e4SLinus Torvalds read_lock(&in_dev->mc_list_lock); 7661da177e4SLinus Torvalds for (im=in_dev->mc_list; im!=NULL; im=im->next) { 7671da177e4SLinus Torvalds if (im->multiaddr == group) { 7681da177e4SLinus Torvalds igmp_stop_timer(im); 7691da177e4SLinus Torvalds break; 7701da177e4SLinus Torvalds } 7711da177e4SLinus Torvalds } 7721da177e4SLinus Torvalds read_unlock(&in_dev->mc_list_lock); 7731da177e4SLinus Torvalds } 7741da177e4SLinus Torvalds 7751da177e4SLinus Torvalds static void igmp_heard_query(struct in_device *in_dev, struct sk_buff *skb, 7761da177e4SLinus Torvalds int len) 7771da177e4SLinus Torvalds { 7781da177e4SLinus Torvalds struct igmphdr *ih = skb->h.igmph; 7791da177e4SLinus Torvalds struct igmpv3_query *ih3 = (struct igmpv3_query *)ih; 7801da177e4SLinus Torvalds struct ip_mc_list *im; 7811da177e4SLinus Torvalds u32 group = ih->group; 7821da177e4SLinus Torvalds int max_delay; 7831da177e4SLinus Torvalds int mark = 0; 7841da177e4SLinus Torvalds 7851da177e4SLinus Torvalds 7861da177e4SLinus Torvalds if (len == 8) { 7871da177e4SLinus Torvalds if (ih->code == 0) { 7881da177e4SLinus Torvalds /* Alas, old v1 router presents here. */ 7891da177e4SLinus Torvalds 7901da177e4SLinus Torvalds max_delay = IGMP_Query_Response_Interval; 7911da177e4SLinus Torvalds in_dev->mr_v1_seen = jiffies + 7921da177e4SLinus Torvalds IGMP_V1_Router_Present_Timeout; 7931da177e4SLinus Torvalds group = 0; 7941da177e4SLinus Torvalds } else { 7951da177e4SLinus Torvalds /* v2 router present */ 7961da177e4SLinus Torvalds max_delay = ih->code*(HZ/IGMP_TIMER_SCALE); 7971da177e4SLinus Torvalds in_dev->mr_v2_seen = jiffies + 7981da177e4SLinus Torvalds IGMP_V2_Router_Present_Timeout; 7991da177e4SLinus Torvalds } 8001da177e4SLinus Torvalds /* cancel the interface change timer */ 8011da177e4SLinus Torvalds in_dev->mr_ifc_count = 0; 8021da177e4SLinus Torvalds if (del_timer(&in_dev->mr_ifc_timer)) 8031da177e4SLinus Torvalds __in_dev_put(in_dev); 8041da177e4SLinus Torvalds /* clear deleted report items */ 8051da177e4SLinus Torvalds igmpv3_clear_delrec(in_dev); 8061da177e4SLinus Torvalds } else if (len < 12) { 8071da177e4SLinus Torvalds return; /* ignore bogus packet; freed by caller */ 8081da177e4SLinus Torvalds } else { /* v3 */ 8091da177e4SLinus Torvalds if (!pskb_may_pull(skb, sizeof(struct igmpv3_query))) 8101da177e4SLinus Torvalds return; 8111da177e4SLinus Torvalds 8121da177e4SLinus Torvalds ih3 = (struct igmpv3_query *) skb->h.raw; 8131da177e4SLinus Torvalds if (ih3->nsrcs) { 8141da177e4SLinus Torvalds if (!pskb_may_pull(skb, sizeof(struct igmpv3_query) 8151da177e4SLinus Torvalds + ntohs(ih3->nsrcs)*sizeof(__u32))) 8161da177e4SLinus Torvalds return; 8171da177e4SLinus Torvalds ih3 = (struct igmpv3_query *) skb->h.raw; 8181da177e4SLinus Torvalds } 8191da177e4SLinus Torvalds 8201da177e4SLinus Torvalds max_delay = IGMPV3_MRC(ih3->code)*(HZ/IGMP_TIMER_SCALE); 8211da177e4SLinus Torvalds if (!max_delay) 8221da177e4SLinus Torvalds max_delay = 1; /* can't mod w/ 0 */ 8231da177e4SLinus Torvalds in_dev->mr_maxdelay = max_delay; 8241da177e4SLinus Torvalds if (ih3->qrv) 8251da177e4SLinus Torvalds in_dev->mr_qrv = ih3->qrv; 8261da177e4SLinus Torvalds if (!group) { /* general query */ 8271da177e4SLinus Torvalds if (ih3->nsrcs) 8281da177e4SLinus Torvalds return; /* no sources allowed */ 8291da177e4SLinus Torvalds igmp_gq_start_timer(in_dev); 8301da177e4SLinus Torvalds return; 8311da177e4SLinus Torvalds } 8321da177e4SLinus Torvalds /* mark sources to include, if group & source-specific */ 8331da177e4SLinus Torvalds mark = ih3->nsrcs != 0; 8341da177e4SLinus Torvalds } 8351da177e4SLinus Torvalds 8361da177e4SLinus Torvalds /* 8371da177e4SLinus Torvalds * - Start the timers in all of our membership records 8381da177e4SLinus Torvalds * that the query applies to for the interface on 8391da177e4SLinus Torvalds * which the query arrived excl. those that belong 8401da177e4SLinus Torvalds * to a "local" group (224.0.0.X) 8411da177e4SLinus Torvalds * - For timers already running check if they need to 8421da177e4SLinus Torvalds * be reset. 8431da177e4SLinus Torvalds * - Use the igmp->igmp_code field as the maximum 8441da177e4SLinus Torvalds * delay possible 8451da177e4SLinus Torvalds */ 8461da177e4SLinus Torvalds read_lock(&in_dev->mc_list_lock); 8471da177e4SLinus Torvalds for (im=in_dev->mc_list; im!=NULL; im=im->next) { 8481da177e4SLinus Torvalds if (group && group != im->multiaddr) 8491da177e4SLinus Torvalds continue; 8501da177e4SLinus Torvalds if (im->multiaddr == IGMP_ALL_HOSTS) 8511da177e4SLinus Torvalds continue; 8521da177e4SLinus Torvalds spin_lock_bh(&im->lock); 8531da177e4SLinus Torvalds if (im->tm_running) 8541da177e4SLinus Torvalds im->gsquery = im->gsquery && mark; 8551da177e4SLinus Torvalds else 8561da177e4SLinus Torvalds im->gsquery = mark; 8571da177e4SLinus Torvalds if (im->gsquery) 8581da177e4SLinus Torvalds igmp_marksources(im, ntohs(ih3->nsrcs), ih3->srcs); 8591da177e4SLinus Torvalds spin_unlock_bh(&im->lock); 8601da177e4SLinus Torvalds igmp_mod_timer(im, max_delay); 8611da177e4SLinus Torvalds } 8621da177e4SLinus Torvalds read_unlock(&in_dev->mc_list_lock); 8631da177e4SLinus Torvalds } 8641da177e4SLinus Torvalds 8651da177e4SLinus Torvalds int igmp_rcv(struct sk_buff *skb) 8661da177e4SLinus Torvalds { 8671da177e4SLinus Torvalds /* This basically follows the spec line by line -- see RFC1112 */ 8681da177e4SLinus Torvalds struct igmphdr *ih; 8691da177e4SLinus Torvalds struct in_device *in_dev = in_dev_get(skb->dev); 8701da177e4SLinus Torvalds int len = skb->len; 8711da177e4SLinus Torvalds 8721da177e4SLinus Torvalds if (in_dev==NULL) { 8731da177e4SLinus Torvalds kfree_skb(skb); 8741da177e4SLinus Torvalds return 0; 8751da177e4SLinus Torvalds } 8761da177e4SLinus Torvalds 877fb286bb2SHerbert Xu if (!pskb_may_pull(skb, sizeof(struct igmphdr))) 878fb286bb2SHerbert Xu goto drop; 879fb286bb2SHerbert Xu 880fb286bb2SHerbert Xu switch (skb->ip_summed) { 881fb286bb2SHerbert Xu case CHECKSUM_HW: 882fb286bb2SHerbert Xu if (!(u16)csum_fold(skb->csum)) 883fb286bb2SHerbert Xu break; 884fb286bb2SHerbert Xu /* fall through */ 885fb286bb2SHerbert Xu case CHECKSUM_NONE: 886fb286bb2SHerbert Xu skb->csum = 0; 887fb286bb2SHerbert Xu if (__skb_checksum_complete(skb)) 888fb286bb2SHerbert Xu goto drop; 8891da177e4SLinus Torvalds } 8901da177e4SLinus Torvalds 8911da177e4SLinus Torvalds ih = skb->h.igmph; 8921da177e4SLinus Torvalds switch (ih->type) { 8931da177e4SLinus Torvalds case IGMP_HOST_MEMBERSHIP_QUERY: 8941da177e4SLinus Torvalds igmp_heard_query(in_dev, skb, len); 8951da177e4SLinus Torvalds break; 8961da177e4SLinus Torvalds case IGMP_HOST_MEMBERSHIP_REPORT: 8971da177e4SLinus Torvalds case IGMPV2_HOST_MEMBERSHIP_REPORT: 8981da177e4SLinus Torvalds case IGMPV3_HOST_MEMBERSHIP_REPORT: 8991da177e4SLinus Torvalds /* Is it our report looped back? */ 9001da177e4SLinus Torvalds if (((struct rtable*)skb->dst)->fl.iif == 0) 9011da177e4SLinus Torvalds break; 90224c69275SDavid Stevens /* don't rely on MC router hearing unicast reports */ 90324c69275SDavid Stevens if (skb->pkt_type == PACKET_MULTICAST || 90424c69275SDavid Stevens skb->pkt_type == PACKET_BROADCAST) 9051da177e4SLinus Torvalds igmp_heard_report(in_dev, ih->group); 9061da177e4SLinus Torvalds break; 9071da177e4SLinus Torvalds case IGMP_PIM: 9081da177e4SLinus Torvalds #ifdef CONFIG_IP_PIMSM_V1 9091da177e4SLinus Torvalds in_dev_put(in_dev); 9101da177e4SLinus Torvalds return pim_rcv_v1(skb); 9111da177e4SLinus Torvalds #endif 9121da177e4SLinus Torvalds case IGMP_DVMRP: 9131da177e4SLinus Torvalds case IGMP_TRACE: 9141da177e4SLinus Torvalds case IGMP_HOST_LEAVE_MESSAGE: 9151da177e4SLinus Torvalds case IGMP_MTRACE: 9161da177e4SLinus Torvalds case IGMP_MTRACE_RESP: 9171da177e4SLinus Torvalds break; 9181da177e4SLinus Torvalds default: 91964ce2073SPatrick McHardy NETDEBUG(KERN_DEBUG "New IGMP type=%d, why we do not know about it?\n", ih->type); 9201da177e4SLinus Torvalds } 921fb286bb2SHerbert Xu 922fb286bb2SHerbert Xu drop: 9231da177e4SLinus Torvalds in_dev_put(in_dev); 9241da177e4SLinus Torvalds kfree_skb(skb); 9251da177e4SLinus Torvalds return 0; 9261da177e4SLinus Torvalds } 9271da177e4SLinus Torvalds 9281da177e4SLinus Torvalds #endif 9291da177e4SLinus Torvalds 9301da177e4SLinus Torvalds 9311da177e4SLinus Torvalds /* 9321da177e4SLinus Torvalds * Add a filter to a device 9331da177e4SLinus Torvalds */ 9341da177e4SLinus Torvalds 9351da177e4SLinus Torvalds static void ip_mc_filter_add(struct in_device *in_dev, u32 addr) 9361da177e4SLinus Torvalds { 9371da177e4SLinus Torvalds char buf[MAX_ADDR_LEN]; 9381da177e4SLinus Torvalds struct net_device *dev = in_dev->dev; 9391da177e4SLinus Torvalds 9401da177e4SLinus Torvalds /* Checking for IFF_MULTICAST here is WRONG-WRONG-WRONG. 9411da177e4SLinus Torvalds We will get multicast token leakage, when IFF_MULTICAST 9421da177e4SLinus Torvalds is changed. This check should be done in dev->set_multicast_list 9431da177e4SLinus Torvalds routine. Something sort of: 9441da177e4SLinus Torvalds if (dev->mc_list && dev->flags&IFF_MULTICAST) { do it; } 9451da177e4SLinus Torvalds --ANK 9461da177e4SLinus Torvalds */ 9471da177e4SLinus Torvalds if (arp_mc_map(addr, buf, dev, 0) == 0) 9481da177e4SLinus Torvalds dev_mc_add(dev,buf,dev->addr_len,0); 9491da177e4SLinus Torvalds } 9501da177e4SLinus Torvalds 9511da177e4SLinus Torvalds /* 9521da177e4SLinus Torvalds * Remove a filter from a device 9531da177e4SLinus Torvalds */ 9541da177e4SLinus Torvalds 9551da177e4SLinus Torvalds static void ip_mc_filter_del(struct in_device *in_dev, u32 addr) 9561da177e4SLinus Torvalds { 9571da177e4SLinus Torvalds char buf[MAX_ADDR_LEN]; 9581da177e4SLinus Torvalds struct net_device *dev = in_dev->dev; 9591da177e4SLinus Torvalds 9601da177e4SLinus Torvalds if (arp_mc_map(addr, buf, dev, 0) == 0) 9611da177e4SLinus Torvalds dev_mc_delete(dev,buf,dev->addr_len,0); 9621da177e4SLinus Torvalds } 9631da177e4SLinus Torvalds 9641da177e4SLinus Torvalds #ifdef CONFIG_IP_MULTICAST 9651da177e4SLinus Torvalds /* 9661da177e4SLinus Torvalds * deleted ip_mc_list manipulation 9671da177e4SLinus Torvalds */ 9681da177e4SLinus Torvalds static void igmpv3_add_delrec(struct in_device *in_dev, struct ip_mc_list *im) 9691da177e4SLinus Torvalds { 9701da177e4SLinus Torvalds struct ip_mc_list *pmc; 9711da177e4SLinus Torvalds 9721da177e4SLinus Torvalds /* this is an "ip_mc_list" for convenience; only the fields below 9731da177e4SLinus Torvalds * are actually used. In particular, the refcnt and users are not 9741da177e4SLinus Torvalds * used for management of the delete list. Using the same structure 9751da177e4SLinus Torvalds * for deleted items allows change reports to use common code with 9761da177e4SLinus Torvalds * non-deleted or query-response MCA's. 9771da177e4SLinus Torvalds */ 978*8b3a7005SKris Katterjohn pmc = kmalloc(sizeof(*pmc), GFP_KERNEL); 9791da177e4SLinus Torvalds if (!pmc) 9801da177e4SLinus Torvalds return; 9811da177e4SLinus Torvalds memset(pmc, 0, sizeof(*pmc)); 9821da177e4SLinus Torvalds spin_lock_bh(&im->lock); 9831da177e4SLinus Torvalds pmc->interface = im->interface; 9841da177e4SLinus Torvalds in_dev_hold(in_dev); 9851da177e4SLinus Torvalds pmc->multiaddr = im->multiaddr; 9861da177e4SLinus Torvalds pmc->crcount = in_dev->mr_qrv ? in_dev->mr_qrv : 9871da177e4SLinus Torvalds IGMP_Unsolicited_Report_Count; 9881da177e4SLinus Torvalds pmc->sfmode = im->sfmode; 9891da177e4SLinus Torvalds if (pmc->sfmode == MCAST_INCLUDE) { 9901da177e4SLinus Torvalds struct ip_sf_list *psf; 9911da177e4SLinus Torvalds 9921da177e4SLinus Torvalds pmc->tomb = im->tomb; 9931da177e4SLinus Torvalds pmc->sources = im->sources; 9941da177e4SLinus Torvalds im->tomb = im->sources = NULL; 9951da177e4SLinus Torvalds for (psf=pmc->sources; psf; psf=psf->sf_next) 9961da177e4SLinus Torvalds psf->sf_crcount = pmc->crcount; 9971da177e4SLinus Torvalds } 9981da177e4SLinus Torvalds spin_unlock_bh(&im->lock); 9991da177e4SLinus Torvalds 10001da177e4SLinus Torvalds spin_lock_bh(&in_dev->mc_tomb_lock); 10011da177e4SLinus Torvalds pmc->next = in_dev->mc_tomb; 10021da177e4SLinus Torvalds in_dev->mc_tomb = pmc; 10031da177e4SLinus Torvalds spin_unlock_bh(&in_dev->mc_tomb_lock); 10041da177e4SLinus Torvalds } 10051da177e4SLinus Torvalds 10061da177e4SLinus Torvalds static void igmpv3_del_delrec(struct in_device *in_dev, __u32 multiaddr) 10071da177e4SLinus Torvalds { 10081da177e4SLinus Torvalds struct ip_mc_list *pmc, *pmc_prev; 10091da177e4SLinus Torvalds struct ip_sf_list *psf, *psf_next; 10101da177e4SLinus Torvalds 10111da177e4SLinus Torvalds spin_lock_bh(&in_dev->mc_tomb_lock); 10121da177e4SLinus Torvalds pmc_prev = NULL; 10131da177e4SLinus Torvalds for (pmc=in_dev->mc_tomb; pmc; pmc=pmc->next) { 10141da177e4SLinus Torvalds if (pmc->multiaddr == multiaddr) 10151da177e4SLinus Torvalds break; 10161da177e4SLinus Torvalds pmc_prev = pmc; 10171da177e4SLinus Torvalds } 10181da177e4SLinus Torvalds if (pmc) { 10191da177e4SLinus Torvalds if (pmc_prev) 10201da177e4SLinus Torvalds pmc_prev->next = pmc->next; 10211da177e4SLinus Torvalds else 10221da177e4SLinus Torvalds in_dev->mc_tomb = pmc->next; 10231da177e4SLinus Torvalds } 10241da177e4SLinus Torvalds spin_unlock_bh(&in_dev->mc_tomb_lock); 10251da177e4SLinus Torvalds if (pmc) { 10261da177e4SLinus Torvalds for (psf=pmc->tomb; psf; psf=psf_next) { 10271da177e4SLinus Torvalds psf_next = psf->sf_next; 10281da177e4SLinus Torvalds kfree(psf); 10291da177e4SLinus Torvalds } 10301da177e4SLinus Torvalds in_dev_put(pmc->interface); 10311da177e4SLinus Torvalds kfree(pmc); 10321da177e4SLinus Torvalds } 10331da177e4SLinus Torvalds } 10341da177e4SLinus Torvalds 10351da177e4SLinus Torvalds static void igmpv3_clear_delrec(struct in_device *in_dev) 10361da177e4SLinus Torvalds { 10371da177e4SLinus Torvalds struct ip_mc_list *pmc, *nextpmc; 10381da177e4SLinus Torvalds 10391da177e4SLinus Torvalds spin_lock_bh(&in_dev->mc_tomb_lock); 10401da177e4SLinus Torvalds pmc = in_dev->mc_tomb; 10411da177e4SLinus Torvalds in_dev->mc_tomb = NULL; 10421da177e4SLinus Torvalds spin_unlock_bh(&in_dev->mc_tomb_lock); 10431da177e4SLinus Torvalds 10441da177e4SLinus Torvalds for (; pmc; pmc = nextpmc) { 10451da177e4SLinus Torvalds nextpmc = pmc->next; 10461da177e4SLinus Torvalds ip_mc_clear_src(pmc); 10471da177e4SLinus Torvalds in_dev_put(pmc->interface); 10481da177e4SLinus Torvalds kfree(pmc); 10491da177e4SLinus Torvalds } 10501da177e4SLinus Torvalds /* clear dead sources, too */ 10511da177e4SLinus Torvalds read_lock(&in_dev->mc_list_lock); 10521da177e4SLinus Torvalds for (pmc=in_dev->mc_list; pmc; pmc=pmc->next) { 10531da177e4SLinus Torvalds struct ip_sf_list *psf, *psf_next; 10541da177e4SLinus Torvalds 10551da177e4SLinus Torvalds spin_lock_bh(&pmc->lock); 10561da177e4SLinus Torvalds psf = pmc->tomb; 10571da177e4SLinus Torvalds pmc->tomb = NULL; 10581da177e4SLinus Torvalds spin_unlock_bh(&pmc->lock); 10591da177e4SLinus Torvalds for (; psf; psf=psf_next) { 10601da177e4SLinus Torvalds psf_next = psf->sf_next; 10611da177e4SLinus Torvalds kfree(psf); 10621da177e4SLinus Torvalds } 10631da177e4SLinus Torvalds } 10641da177e4SLinus Torvalds read_unlock(&in_dev->mc_list_lock); 10651da177e4SLinus Torvalds } 10661da177e4SLinus Torvalds #endif 10671da177e4SLinus Torvalds 10681da177e4SLinus Torvalds static void igmp_group_dropped(struct ip_mc_list *im) 10691da177e4SLinus Torvalds { 10701da177e4SLinus Torvalds struct in_device *in_dev = im->interface; 10711da177e4SLinus Torvalds #ifdef CONFIG_IP_MULTICAST 10721da177e4SLinus Torvalds int reporter; 10731da177e4SLinus Torvalds #endif 10741da177e4SLinus Torvalds 10751da177e4SLinus Torvalds if (im->loaded) { 10761da177e4SLinus Torvalds im->loaded = 0; 10771da177e4SLinus Torvalds ip_mc_filter_del(in_dev, im->multiaddr); 10781da177e4SLinus Torvalds } 10791da177e4SLinus Torvalds 10801da177e4SLinus Torvalds #ifdef CONFIG_IP_MULTICAST 10811da177e4SLinus Torvalds if (im->multiaddr == IGMP_ALL_HOSTS) 10821da177e4SLinus Torvalds return; 10831da177e4SLinus Torvalds 10841da177e4SLinus Torvalds reporter = im->reporter; 10851da177e4SLinus Torvalds igmp_stop_timer(im); 10861da177e4SLinus Torvalds 10871da177e4SLinus Torvalds if (!in_dev->dead) { 10881da177e4SLinus Torvalds if (IGMP_V1_SEEN(in_dev)) 10891da177e4SLinus Torvalds goto done; 10901da177e4SLinus Torvalds if (IGMP_V2_SEEN(in_dev)) { 10911da177e4SLinus Torvalds if (reporter) 10921da177e4SLinus Torvalds igmp_send_report(in_dev, im, IGMP_HOST_LEAVE_MESSAGE); 10931da177e4SLinus Torvalds goto done; 10941da177e4SLinus Torvalds } 10951da177e4SLinus Torvalds /* IGMPv3 */ 10961da177e4SLinus Torvalds igmpv3_add_delrec(in_dev, im); 10971da177e4SLinus Torvalds 10981da177e4SLinus Torvalds igmp_ifc_event(in_dev); 10991da177e4SLinus Torvalds } 11001da177e4SLinus Torvalds done: 11011da177e4SLinus Torvalds #endif 11021da177e4SLinus Torvalds ip_mc_clear_src(im); 11031da177e4SLinus Torvalds } 11041da177e4SLinus Torvalds 11051da177e4SLinus Torvalds static void igmp_group_added(struct ip_mc_list *im) 11061da177e4SLinus Torvalds { 11071da177e4SLinus Torvalds struct in_device *in_dev = im->interface; 11081da177e4SLinus Torvalds 11091da177e4SLinus Torvalds if (im->loaded == 0) { 11101da177e4SLinus Torvalds im->loaded = 1; 11111da177e4SLinus Torvalds ip_mc_filter_add(in_dev, im->multiaddr); 11121da177e4SLinus Torvalds } 11131da177e4SLinus Torvalds 11141da177e4SLinus Torvalds #ifdef CONFIG_IP_MULTICAST 11151da177e4SLinus Torvalds if (im->multiaddr == IGMP_ALL_HOSTS) 11161da177e4SLinus Torvalds return; 11171da177e4SLinus Torvalds 11181da177e4SLinus Torvalds if (in_dev->dead) 11191da177e4SLinus Torvalds return; 11201da177e4SLinus Torvalds if (IGMP_V1_SEEN(in_dev) || IGMP_V2_SEEN(in_dev)) { 11211da177e4SLinus Torvalds spin_lock_bh(&im->lock); 11221da177e4SLinus Torvalds igmp_start_timer(im, IGMP_Initial_Report_Delay); 11231da177e4SLinus Torvalds spin_unlock_bh(&im->lock); 11241da177e4SLinus Torvalds return; 11251da177e4SLinus Torvalds } 11261da177e4SLinus Torvalds /* else, v3 */ 11271da177e4SLinus Torvalds 11281da177e4SLinus Torvalds im->crcount = in_dev->mr_qrv ? in_dev->mr_qrv : 11291da177e4SLinus Torvalds IGMP_Unsolicited_Report_Count; 11301da177e4SLinus Torvalds igmp_ifc_event(in_dev); 11311da177e4SLinus Torvalds #endif 11321da177e4SLinus Torvalds } 11331da177e4SLinus Torvalds 11341da177e4SLinus Torvalds 11351da177e4SLinus Torvalds /* 11361da177e4SLinus Torvalds * Multicast list managers 11371da177e4SLinus Torvalds */ 11381da177e4SLinus Torvalds 11391da177e4SLinus Torvalds 11401da177e4SLinus Torvalds /* 11411da177e4SLinus Torvalds * A socket has joined a multicast group on device dev. 11421da177e4SLinus Torvalds */ 11431da177e4SLinus Torvalds 11441da177e4SLinus Torvalds void ip_mc_inc_group(struct in_device *in_dev, u32 addr) 11451da177e4SLinus Torvalds { 11461da177e4SLinus Torvalds struct ip_mc_list *im; 11471da177e4SLinus Torvalds 11481da177e4SLinus Torvalds ASSERT_RTNL(); 11491da177e4SLinus Torvalds 11501da177e4SLinus Torvalds for (im=in_dev->mc_list; im; im=im->next) { 11511da177e4SLinus Torvalds if (im->multiaddr == addr) { 11521da177e4SLinus Torvalds im->users++; 11531da177e4SLinus Torvalds ip_mc_add_src(in_dev, &addr, MCAST_EXCLUDE, 0, NULL, 0); 11541da177e4SLinus Torvalds goto out; 11551da177e4SLinus Torvalds } 11561da177e4SLinus Torvalds } 11571da177e4SLinus Torvalds 1158*8b3a7005SKris Katterjohn im = kmalloc(sizeof(*im), GFP_KERNEL); 11591da177e4SLinus Torvalds if (!im) 11601da177e4SLinus Torvalds goto out; 11611da177e4SLinus Torvalds 11621da177e4SLinus Torvalds im->users=1; 11631da177e4SLinus Torvalds im->interface=in_dev; 11641da177e4SLinus Torvalds in_dev_hold(in_dev); 11651da177e4SLinus Torvalds im->multiaddr=addr; 11661da177e4SLinus Torvalds /* initial mode is (EX, empty) */ 11671da177e4SLinus Torvalds im->sfmode = MCAST_EXCLUDE; 11681da177e4SLinus Torvalds im->sfcount[MCAST_INCLUDE] = 0; 11691da177e4SLinus Torvalds im->sfcount[MCAST_EXCLUDE] = 1; 11701da177e4SLinus Torvalds im->sources = NULL; 11711da177e4SLinus Torvalds im->tomb = NULL; 11721da177e4SLinus Torvalds im->crcount = 0; 11731da177e4SLinus Torvalds atomic_set(&im->refcnt, 1); 11741da177e4SLinus Torvalds spin_lock_init(&im->lock); 11751da177e4SLinus Torvalds #ifdef CONFIG_IP_MULTICAST 11761da177e4SLinus Torvalds im->tm_running=0; 11771da177e4SLinus Torvalds init_timer(&im->timer); 11781da177e4SLinus Torvalds im->timer.data=(unsigned long)im; 11791da177e4SLinus Torvalds im->timer.function=&igmp_timer_expire; 11801da177e4SLinus Torvalds im->unsolicit_count = IGMP_Unsolicited_Report_Count; 11811da177e4SLinus Torvalds im->reporter = 0; 11821da177e4SLinus Torvalds im->gsquery = 0; 11831da177e4SLinus Torvalds #endif 11841da177e4SLinus Torvalds im->loaded = 0; 11851da177e4SLinus Torvalds write_lock_bh(&in_dev->mc_list_lock); 11861da177e4SLinus Torvalds im->next=in_dev->mc_list; 11871da177e4SLinus Torvalds in_dev->mc_list=im; 11881da177e4SLinus Torvalds write_unlock_bh(&in_dev->mc_list_lock); 11891da177e4SLinus Torvalds #ifdef CONFIG_IP_MULTICAST 11901da177e4SLinus Torvalds igmpv3_del_delrec(in_dev, im->multiaddr); 11911da177e4SLinus Torvalds #endif 11921da177e4SLinus Torvalds igmp_group_added(im); 11931da177e4SLinus Torvalds if (!in_dev->dead) 11941da177e4SLinus Torvalds ip_rt_multicast_event(in_dev); 11951da177e4SLinus Torvalds out: 11961da177e4SLinus Torvalds return; 11971da177e4SLinus Torvalds } 11981da177e4SLinus Torvalds 11991da177e4SLinus Torvalds /* 12001da177e4SLinus Torvalds * A socket has left a multicast group on device dev 12011da177e4SLinus Torvalds */ 12021da177e4SLinus Torvalds 12031da177e4SLinus Torvalds void ip_mc_dec_group(struct in_device *in_dev, u32 addr) 12041da177e4SLinus Torvalds { 12051da177e4SLinus Torvalds struct ip_mc_list *i, **ip; 12061da177e4SLinus Torvalds 12071da177e4SLinus Torvalds ASSERT_RTNL(); 12081da177e4SLinus Torvalds 12091da177e4SLinus Torvalds for (ip=&in_dev->mc_list; (i=*ip)!=NULL; ip=&i->next) { 12101da177e4SLinus Torvalds if (i->multiaddr==addr) { 12111da177e4SLinus Torvalds if (--i->users == 0) { 12121da177e4SLinus Torvalds write_lock_bh(&in_dev->mc_list_lock); 12131da177e4SLinus Torvalds *ip = i->next; 12141da177e4SLinus Torvalds write_unlock_bh(&in_dev->mc_list_lock); 12151da177e4SLinus Torvalds igmp_group_dropped(i); 12161da177e4SLinus Torvalds 12171da177e4SLinus Torvalds if (!in_dev->dead) 12181da177e4SLinus Torvalds ip_rt_multicast_event(in_dev); 12191da177e4SLinus Torvalds 12201da177e4SLinus Torvalds ip_ma_put(i); 12211da177e4SLinus Torvalds return; 12221da177e4SLinus Torvalds } 12231da177e4SLinus Torvalds break; 12241da177e4SLinus Torvalds } 12251da177e4SLinus Torvalds } 12261da177e4SLinus Torvalds } 12271da177e4SLinus Torvalds 12281da177e4SLinus Torvalds /* Device going down */ 12291da177e4SLinus Torvalds 12301da177e4SLinus Torvalds void ip_mc_down(struct in_device *in_dev) 12311da177e4SLinus Torvalds { 12321da177e4SLinus Torvalds struct ip_mc_list *i; 12331da177e4SLinus Torvalds 12341da177e4SLinus Torvalds ASSERT_RTNL(); 12351da177e4SLinus Torvalds 12361da177e4SLinus Torvalds for (i=in_dev->mc_list; i; i=i->next) 12371da177e4SLinus Torvalds igmp_group_dropped(i); 12381da177e4SLinus Torvalds 12391da177e4SLinus Torvalds #ifdef CONFIG_IP_MULTICAST 12401da177e4SLinus Torvalds in_dev->mr_ifc_count = 0; 12411da177e4SLinus Torvalds if (del_timer(&in_dev->mr_ifc_timer)) 12421da177e4SLinus Torvalds __in_dev_put(in_dev); 12431da177e4SLinus Torvalds in_dev->mr_gq_running = 0; 12441da177e4SLinus Torvalds if (del_timer(&in_dev->mr_gq_timer)) 12451da177e4SLinus Torvalds __in_dev_put(in_dev); 12461da177e4SLinus Torvalds igmpv3_clear_delrec(in_dev); 12471da177e4SLinus Torvalds #endif 12481da177e4SLinus Torvalds 12491da177e4SLinus Torvalds ip_mc_dec_group(in_dev, IGMP_ALL_HOSTS); 12501da177e4SLinus Torvalds } 12511da177e4SLinus Torvalds 12521da177e4SLinus Torvalds void ip_mc_init_dev(struct in_device *in_dev) 12531da177e4SLinus Torvalds { 12541da177e4SLinus Torvalds ASSERT_RTNL(); 12551da177e4SLinus Torvalds 12561da177e4SLinus Torvalds in_dev->mc_tomb = NULL; 12571da177e4SLinus Torvalds #ifdef CONFIG_IP_MULTICAST 12581da177e4SLinus Torvalds in_dev->mr_gq_running = 0; 12591da177e4SLinus Torvalds init_timer(&in_dev->mr_gq_timer); 12601da177e4SLinus Torvalds in_dev->mr_gq_timer.data=(unsigned long) in_dev; 12611da177e4SLinus Torvalds in_dev->mr_gq_timer.function=&igmp_gq_timer_expire; 12621da177e4SLinus Torvalds in_dev->mr_ifc_count = 0; 12631da177e4SLinus Torvalds init_timer(&in_dev->mr_ifc_timer); 12641da177e4SLinus Torvalds in_dev->mr_ifc_timer.data=(unsigned long) in_dev; 12651da177e4SLinus Torvalds in_dev->mr_ifc_timer.function=&igmp_ifc_timer_expire; 12661da177e4SLinus Torvalds in_dev->mr_qrv = IGMP_Unsolicited_Report_Count; 12671da177e4SLinus Torvalds #endif 12681da177e4SLinus Torvalds 12691da177e4SLinus Torvalds rwlock_init(&in_dev->mc_list_lock); 12701da177e4SLinus Torvalds spin_lock_init(&in_dev->mc_tomb_lock); 12711da177e4SLinus Torvalds } 12721da177e4SLinus Torvalds 12731da177e4SLinus Torvalds /* Device going up */ 12741da177e4SLinus Torvalds 12751da177e4SLinus Torvalds void ip_mc_up(struct in_device *in_dev) 12761da177e4SLinus Torvalds { 12771da177e4SLinus Torvalds struct ip_mc_list *i; 12781da177e4SLinus Torvalds 12791da177e4SLinus Torvalds ASSERT_RTNL(); 12801da177e4SLinus Torvalds 12811da177e4SLinus Torvalds ip_mc_inc_group(in_dev, IGMP_ALL_HOSTS); 12821da177e4SLinus Torvalds 12831da177e4SLinus Torvalds for (i=in_dev->mc_list; i; i=i->next) 12841da177e4SLinus Torvalds igmp_group_added(i); 12851da177e4SLinus Torvalds } 12861da177e4SLinus Torvalds 12871da177e4SLinus Torvalds /* 12881da177e4SLinus Torvalds * Device is about to be destroyed: clean up. 12891da177e4SLinus Torvalds */ 12901da177e4SLinus Torvalds 12911da177e4SLinus Torvalds void ip_mc_destroy_dev(struct in_device *in_dev) 12921da177e4SLinus Torvalds { 12931da177e4SLinus Torvalds struct ip_mc_list *i; 12941da177e4SLinus Torvalds 12951da177e4SLinus Torvalds ASSERT_RTNL(); 12961da177e4SLinus Torvalds 12971da177e4SLinus Torvalds /* Deactivate timers */ 12981da177e4SLinus Torvalds ip_mc_down(in_dev); 12991da177e4SLinus Torvalds 13001da177e4SLinus Torvalds write_lock_bh(&in_dev->mc_list_lock); 13011da177e4SLinus Torvalds while ((i = in_dev->mc_list) != NULL) { 13021da177e4SLinus Torvalds in_dev->mc_list = i->next; 13031da177e4SLinus Torvalds write_unlock_bh(&in_dev->mc_list_lock); 13041da177e4SLinus Torvalds 13051da177e4SLinus Torvalds igmp_group_dropped(i); 13061da177e4SLinus Torvalds ip_ma_put(i); 13071da177e4SLinus Torvalds 13081da177e4SLinus Torvalds write_lock_bh(&in_dev->mc_list_lock); 13091da177e4SLinus Torvalds } 13101da177e4SLinus Torvalds write_unlock_bh(&in_dev->mc_list_lock); 13111da177e4SLinus Torvalds } 13121da177e4SLinus Torvalds 13131da177e4SLinus Torvalds static struct in_device * ip_mc_find_dev(struct ip_mreqn *imr) 13141da177e4SLinus Torvalds { 13151da177e4SLinus Torvalds struct flowi fl = { .nl_u = { .ip4_u = 13161da177e4SLinus Torvalds { .daddr = imr->imr_multiaddr.s_addr } } }; 13171da177e4SLinus Torvalds struct rtable *rt; 13181da177e4SLinus Torvalds struct net_device *dev = NULL; 13191da177e4SLinus Torvalds struct in_device *idev = NULL; 13201da177e4SLinus Torvalds 13211da177e4SLinus Torvalds if (imr->imr_ifindex) { 13221da177e4SLinus Torvalds idev = inetdev_by_index(imr->imr_ifindex); 13231da177e4SLinus Torvalds if (idev) 13241da177e4SLinus Torvalds __in_dev_put(idev); 13251da177e4SLinus Torvalds return idev; 13261da177e4SLinus Torvalds } 13271da177e4SLinus Torvalds if (imr->imr_address.s_addr) { 13281da177e4SLinus Torvalds dev = ip_dev_find(imr->imr_address.s_addr); 13291da177e4SLinus Torvalds if (!dev) 13301da177e4SLinus Torvalds return NULL; 13311da177e4SLinus Torvalds __dev_put(dev); 13321da177e4SLinus Torvalds } 13331da177e4SLinus Torvalds 13341da177e4SLinus Torvalds if (!dev && !ip_route_output_key(&rt, &fl)) { 13351da177e4SLinus Torvalds dev = rt->u.dst.dev; 13361da177e4SLinus Torvalds ip_rt_put(rt); 13371da177e4SLinus Torvalds } 13381da177e4SLinus Torvalds if (dev) { 13391da177e4SLinus Torvalds imr->imr_ifindex = dev->ifindex; 1340e5ed6399SHerbert Xu idev = __in_dev_get_rtnl(dev); 13411da177e4SLinus Torvalds } 13421da177e4SLinus Torvalds return idev; 13431da177e4SLinus Torvalds } 13441da177e4SLinus Torvalds 13451da177e4SLinus Torvalds /* 13461da177e4SLinus Torvalds * Join a socket to a group 13471da177e4SLinus Torvalds */ 13481da177e4SLinus Torvalds int sysctl_igmp_max_memberships = IP_MAX_MEMBERSHIPS; 13491da177e4SLinus Torvalds int sysctl_igmp_max_msf = IP_MAX_MSF; 13501da177e4SLinus Torvalds 13511da177e4SLinus Torvalds 13521da177e4SLinus Torvalds static int ip_mc_del1_src(struct ip_mc_list *pmc, int sfmode, 13531da177e4SLinus Torvalds __u32 *psfsrc) 13541da177e4SLinus Torvalds { 13551da177e4SLinus Torvalds struct ip_sf_list *psf, *psf_prev; 13561da177e4SLinus Torvalds int rv = 0; 13571da177e4SLinus Torvalds 13581da177e4SLinus Torvalds psf_prev = NULL; 13591da177e4SLinus Torvalds for (psf=pmc->sources; psf; psf=psf->sf_next) { 13601da177e4SLinus Torvalds if (psf->sf_inaddr == *psfsrc) 13611da177e4SLinus Torvalds break; 13621da177e4SLinus Torvalds psf_prev = psf; 13631da177e4SLinus Torvalds } 13641da177e4SLinus Torvalds if (!psf || psf->sf_count[sfmode] == 0) { 13651da177e4SLinus Torvalds /* source filter not found, or count wrong => bug */ 13661da177e4SLinus Torvalds return -ESRCH; 13671da177e4SLinus Torvalds } 13681da177e4SLinus Torvalds psf->sf_count[sfmode]--; 13691da177e4SLinus Torvalds if (psf->sf_count[sfmode] == 0) { 13701da177e4SLinus Torvalds ip_rt_multicast_event(pmc->interface); 13711da177e4SLinus Torvalds } 13721da177e4SLinus Torvalds if (!psf->sf_count[MCAST_INCLUDE] && !psf->sf_count[MCAST_EXCLUDE]) { 13731da177e4SLinus Torvalds #ifdef CONFIG_IP_MULTICAST 13741da177e4SLinus Torvalds struct in_device *in_dev = pmc->interface; 13751da177e4SLinus Torvalds #endif 13761da177e4SLinus Torvalds 13771da177e4SLinus Torvalds /* no more filters for this source */ 13781da177e4SLinus Torvalds if (psf_prev) 13791da177e4SLinus Torvalds psf_prev->sf_next = psf->sf_next; 13801da177e4SLinus Torvalds else 13811da177e4SLinus Torvalds pmc->sources = psf->sf_next; 13821da177e4SLinus Torvalds #ifdef CONFIG_IP_MULTICAST 13831da177e4SLinus Torvalds if (psf->sf_oldin && 13841da177e4SLinus Torvalds !IGMP_V1_SEEN(in_dev) && !IGMP_V2_SEEN(in_dev)) { 13851da177e4SLinus Torvalds psf->sf_crcount = in_dev->mr_qrv ? in_dev->mr_qrv : 13861da177e4SLinus Torvalds IGMP_Unsolicited_Report_Count; 13871da177e4SLinus Torvalds psf->sf_next = pmc->tomb; 13881da177e4SLinus Torvalds pmc->tomb = psf; 13891da177e4SLinus Torvalds rv = 1; 13901da177e4SLinus Torvalds } else 13911da177e4SLinus Torvalds #endif 13921da177e4SLinus Torvalds kfree(psf); 13931da177e4SLinus Torvalds } 13941da177e4SLinus Torvalds return rv; 13951da177e4SLinus Torvalds } 13961da177e4SLinus Torvalds 13971da177e4SLinus Torvalds #ifndef CONFIG_IP_MULTICAST 13981da177e4SLinus Torvalds #define igmp_ifc_event(x) do { } while (0) 13991da177e4SLinus Torvalds #endif 14001da177e4SLinus Torvalds 14011da177e4SLinus Torvalds static int ip_mc_del_src(struct in_device *in_dev, __u32 *pmca, int sfmode, 14021da177e4SLinus Torvalds int sfcount, __u32 *psfsrc, int delta) 14031da177e4SLinus Torvalds { 14041da177e4SLinus Torvalds struct ip_mc_list *pmc; 14051da177e4SLinus Torvalds int changerec = 0; 14061da177e4SLinus Torvalds int i, err; 14071da177e4SLinus Torvalds 14081da177e4SLinus Torvalds if (!in_dev) 14091da177e4SLinus Torvalds return -ENODEV; 14101da177e4SLinus Torvalds read_lock(&in_dev->mc_list_lock); 14111da177e4SLinus Torvalds for (pmc=in_dev->mc_list; pmc; pmc=pmc->next) { 14121da177e4SLinus Torvalds if (*pmca == pmc->multiaddr) 14131da177e4SLinus Torvalds break; 14141da177e4SLinus Torvalds } 14151da177e4SLinus Torvalds if (!pmc) { 14161da177e4SLinus Torvalds /* MCA not found?? bug */ 14171da177e4SLinus Torvalds read_unlock(&in_dev->mc_list_lock); 14181da177e4SLinus Torvalds return -ESRCH; 14191da177e4SLinus Torvalds } 14201da177e4SLinus Torvalds spin_lock_bh(&pmc->lock); 14211da177e4SLinus Torvalds read_unlock(&in_dev->mc_list_lock); 14221da177e4SLinus Torvalds #ifdef CONFIG_IP_MULTICAST 14231da177e4SLinus Torvalds sf_markstate(pmc); 14241da177e4SLinus Torvalds #endif 14251da177e4SLinus Torvalds if (!delta) { 14261da177e4SLinus Torvalds err = -EINVAL; 14271da177e4SLinus Torvalds if (!pmc->sfcount[sfmode]) 14281da177e4SLinus Torvalds goto out_unlock; 14291da177e4SLinus Torvalds pmc->sfcount[sfmode]--; 14301da177e4SLinus Torvalds } 14311da177e4SLinus Torvalds err = 0; 14321da177e4SLinus Torvalds for (i=0; i<sfcount; i++) { 14331da177e4SLinus Torvalds int rv = ip_mc_del1_src(pmc, sfmode, &psfsrc[i]); 14341da177e4SLinus Torvalds 14351da177e4SLinus Torvalds changerec |= rv > 0; 14361da177e4SLinus Torvalds if (!err && rv < 0) 14371da177e4SLinus Torvalds err = rv; 14381da177e4SLinus Torvalds } 14391da177e4SLinus Torvalds if (pmc->sfmode == MCAST_EXCLUDE && 14401da177e4SLinus Torvalds pmc->sfcount[MCAST_EXCLUDE] == 0 && 14411da177e4SLinus Torvalds pmc->sfcount[MCAST_INCLUDE]) { 14421da177e4SLinus Torvalds #ifdef CONFIG_IP_MULTICAST 14431da177e4SLinus Torvalds struct ip_sf_list *psf; 14441da177e4SLinus Torvalds #endif 14451da177e4SLinus Torvalds 14461da177e4SLinus Torvalds /* filter mode change */ 14471da177e4SLinus Torvalds pmc->sfmode = MCAST_INCLUDE; 14481da177e4SLinus Torvalds #ifdef CONFIG_IP_MULTICAST 14491da177e4SLinus Torvalds pmc->crcount = in_dev->mr_qrv ? in_dev->mr_qrv : 14501da177e4SLinus Torvalds IGMP_Unsolicited_Report_Count; 14511da177e4SLinus Torvalds in_dev->mr_ifc_count = pmc->crcount; 14521da177e4SLinus Torvalds for (psf=pmc->sources; psf; psf = psf->sf_next) 14531da177e4SLinus Torvalds psf->sf_crcount = 0; 14541da177e4SLinus Torvalds igmp_ifc_event(pmc->interface); 14551da177e4SLinus Torvalds } else if (sf_setstate(pmc) || changerec) { 14561da177e4SLinus Torvalds igmp_ifc_event(pmc->interface); 14571da177e4SLinus Torvalds #endif 14581da177e4SLinus Torvalds } 14591da177e4SLinus Torvalds out_unlock: 14601da177e4SLinus Torvalds spin_unlock_bh(&pmc->lock); 14611da177e4SLinus Torvalds return err; 14621da177e4SLinus Torvalds } 14631da177e4SLinus Torvalds 14641da177e4SLinus Torvalds /* 14651da177e4SLinus Torvalds * Add multicast single-source filter to the interface list 14661da177e4SLinus Torvalds */ 14671da177e4SLinus Torvalds static int ip_mc_add1_src(struct ip_mc_list *pmc, int sfmode, 14681da177e4SLinus Torvalds __u32 *psfsrc, int delta) 14691da177e4SLinus Torvalds { 14701da177e4SLinus Torvalds struct ip_sf_list *psf, *psf_prev; 14711da177e4SLinus Torvalds 14721da177e4SLinus Torvalds psf_prev = NULL; 14731da177e4SLinus Torvalds for (psf=pmc->sources; psf; psf=psf->sf_next) { 14741da177e4SLinus Torvalds if (psf->sf_inaddr == *psfsrc) 14751da177e4SLinus Torvalds break; 14761da177e4SLinus Torvalds psf_prev = psf; 14771da177e4SLinus Torvalds } 14781da177e4SLinus Torvalds if (!psf) { 1479*8b3a7005SKris Katterjohn psf = kmalloc(sizeof(*psf), GFP_ATOMIC); 14801da177e4SLinus Torvalds if (!psf) 14811da177e4SLinus Torvalds return -ENOBUFS; 14821da177e4SLinus Torvalds memset(psf, 0, sizeof(*psf)); 14831da177e4SLinus Torvalds psf->sf_inaddr = *psfsrc; 14841da177e4SLinus Torvalds if (psf_prev) { 14851da177e4SLinus Torvalds psf_prev->sf_next = psf; 14861da177e4SLinus Torvalds } else 14871da177e4SLinus Torvalds pmc->sources = psf; 14881da177e4SLinus Torvalds } 14891da177e4SLinus Torvalds psf->sf_count[sfmode]++; 14901da177e4SLinus Torvalds if (psf->sf_count[sfmode] == 1) { 14911da177e4SLinus Torvalds ip_rt_multicast_event(pmc->interface); 14921da177e4SLinus Torvalds } 14931da177e4SLinus Torvalds return 0; 14941da177e4SLinus Torvalds } 14951da177e4SLinus Torvalds 14961da177e4SLinus Torvalds #ifdef CONFIG_IP_MULTICAST 14971da177e4SLinus Torvalds static void sf_markstate(struct ip_mc_list *pmc) 14981da177e4SLinus Torvalds { 14991da177e4SLinus Torvalds struct ip_sf_list *psf; 15001da177e4SLinus Torvalds int mca_xcount = pmc->sfcount[MCAST_EXCLUDE]; 15011da177e4SLinus Torvalds 15021da177e4SLinus Torvalds for (psf=pmc->sources; psf; psf=psf->sf_next) 15031da177e4SLinus Torvalds if (pmc->sfcount[MCAST_EXCLUDE]) { 15041da177e4SLinus Torvalds psf->sf_oldin = mca_xcount == 15051da177e4SLinus Torvalds psf->sf_count[MCAST_EXCLUDE] && 15061da177e4SLinus Torvalds !psf->sf_count[MCAST_INCLUDE]; 15071da177e4SLinus Torvalds } else 15081da177e4SLinus Torvalds psf->sf_oldin = psf->sf_count[MCAST_INCLUDE] != 0; 15091da177e4SLinus Torvalds } 15101da177e4SLinus Torvalds 15111da177e4SLinus Torvalds static int sf_setstate(struct ip_mc_list *pmc) 15121da177e4SLinus Torvalds { 15131da177e4SLinus Torvalds struct ip_sf_list *psf; 15141da177e4SLinus Torvalds int mca_xcount = pmc->sfcount[MCAST_EXCLUDE]; 15151da177e4SLinus Torvalds int qrv = pmc->interface->mr_qrv; 15161da177e4SLinus Torvalds int new_in, rv; 15171da177e4SLinus Torvalds 15181da177e4SLinus Torvalds rv = 0; 15191da177e4SLinus Torvalds for (psf=pmc->sources; psf; psf=psf->sf_next) { 15201da177e4SLinus Torvalds if (pmc->sfcount[MCAST_EXCLUDE]) { 15211da177e4SLinus Torvalds new_in = mca_xcount == psf->sf_count[MCAST_EXCLUDE] && 15221da177e4SLinus Torvalds !psf->sf_count[MCAST_INCLUDE]; 15231da177e4SLinus Torvalds } else 15241da177e4SLinus Torvalds new_in = psf->sf_count[MCAST_INCLUDE] != 0; 15251da177e4SLinus Torvalds if (new_in != psf->sf_oldin) { 15261da177e4SLinus Torvalds psf->sf_crcount = qrv; 15271da177e4SLinus Torvalds rv++; 15281da177e4SLinus Torvalds } 15291da177e4SLinus Torvalds } 15301da177e4SLinus Torvalds return rv; 15311da177e4SLinus Torvalds } 15321da177e4SLinus Torvalds #endif 15331da177e4SLinus Torvalds 15341da177e4SLinus Torvalds /* 15351da177e4SLinus Torvalds * Add multicast source filter list to the interface list 15361da177e4SLinus Torvalds */ 15371da177e4SLinus Torvalds static int ip_mc_add_src(struct in_device *in_dev, __u32 *pmca, int sfmode, 15381da177e4SLinus Torvalds int sfcount, __u32 *psfsrc, int delta) 15391da177e4SLinus Torvalds { 15401da177e4SLinus Torvalds struct ip_mc_list *pmc; 15411da177e4SLinus Torvalds int isexclude; 15421da177e4SLinus Torvalds int i, err; 15431da177e4SLinus Torvalds 15441da177e4SLinus Torvalds if (!in_dev) 15451da177e4SLinus Torvalds return -ENODEV; 15461da177e4SLinus Torvalds read_lock(&in_dev->mc_list_lock); 15471da177e4SLinus Torvalds for (pmc=in_dev->mc_list; pmc; pmc=pmc->next) { 15481da177e4SLinus Torvalds if (*pmca == pmc->multiaddr) 15491da177e4SLinus Torvalds break; 15501da177e4SLinus Torvalds } 15511da177e4SLinus Torvalds if (!pmc) { 15521da177e4SLinus Torvalds /* MCA not found?? bug */ 15531da177e4SLinus Torvalds read_unlock(&in_dev->mc_list_lock); 15541da177e4SLinus Torvalds return -ESRCH; 15551da177e4SLinus Torvalds } 15561da177e4SLinus Torvalds spin_lock_bh(&pmc->lock); 15571da177e4SLinus Torvalds read_unlock(&in_dev->mc_list_lock); 15581da177e4SLinus Torvalds 15591da177e4SLinus Torvalds #ifdef CONFIG_IP_MULTICAST 15601da177e4SLinus Torvalds sf_markstate(pmc); 15611da177e4SLinus Torvalds #endif 15621da177e4SLinus Torvalds isexclude = pmc->sfmode == MCAST_EXCLUDE; 15631da177e4SLinus Torvalds if (!delta) 15641da177e4SLinus Torvalds pmc->sfcount[sfmode]++; 15651da177e4SLinus Torvalds err = 0; 15661da177e4SLinus Torvalds for (i=0; i<sfcount; i++) { 15671da177e4SLinus Torvalds err = ip_mc_add1_src(pmc, sfmode, &psfsrc[i], delta); 15681da177e4SLinus Torvalds if (err) 15691da177e4SLinus Torvalds break; 15701da177e4SLinus Torvalds } 15711da177e4SLinus Torvalds if (err) { 15721da177e4SLinus Torvalds int j; 15731da177e4SLinus Torvalds 15741da177e4SLinus Torvalds pmc->sfcount[sfmode]--; 15751da177e4SLinus Torvalds for (j=0; j<i; j++) 15761da177e4SLinus Torvalds (void) ip_mc_del1_src(pmc, sfmode, &psfsrc[i]); 15771da177e4SLinus Torvalds } else if (isexclude != (pmc->sfcount[MCAST_EXCLUDE] != 0)) { 15781da177e4SLinus Torvalds #ifdef CONFIG_IP_MULTICAST 15791da177e4SLinus Torvalds struct in_device *in_dev = pmc->interface; 15801da177e4SLinus Torvalds struct ip_sf_list *psf; 15811da177e4SLinus Torvalds #endif 15821da177e4SLinus Torvalds 15831da177e4SLinus Torvalds /* filter mode change */ 15841da177e4SLinus Torvalds if (pmc->sfcount[MCAST_EXCLUDE]) 15851da177e4SLinus Torvalds pmc->sfmode = MCAST_EXCLUDE; 15861da177e4SLinus Torvalds else if (pmc->sfcount[MCAST_INCLUDE]) 15871da177e4SLinus Torvalds pmc->sfmode = MCAST_INCLUDE; 15881da177e4SLinus Torvalds #ifdef CONFIG_IP_MULTICAST 15891da177e4SLinus Torvalds /* else no filters; keep old mode for reports */ 15901da177e4SLinus Torvalds 15911da177e4SLinus Torvalds pmc->crcount = in_dev->mr_qrv ? in_dev->mr_qrv : 15921da177e4SLinus Torvalds IGMP_Unsolicited_Report_Count; 15931da177e4SLinus Torvalds in_dev->mr_ifc_count = pmc->crcount; 15941da177e4SLinus Torvalds for (psf=pmc->sources; psf; psf = psf->sf_next) 15951da177e4SLinus Torvalds psf->sf_crcount = 0; 15961da177e4SLinus Torvalds igmp_ifc_event(in_dev); 15971da177e4SLinus Torvalds } else if (sf_setstate(pmc)) { 15981da177e4SLinus Torvalds igmp_ifc_event(in_dev); 15991da177e4SLinus Torvalds #endif 16001da177e4SLinus Torvalds } 16011da177e4SLinus Torvalds spin_unlock_bh(&pmc->lock); 16021da177e4SLinus Torvalds return err; 16031da177e4SLinus Torvalds } 16041da177e4SLinus Torvalds 16051da177e4SLinus Torvalds static void ip_mc_clear_src(struct ip_mc_list *pmc) 16061da177e4SLinus Torvalds { 16071da177e4SLinus Torvalds struct ip_sf_list *psf, *nextpsf; 16081da177e4SLinus Torvalds 16091da177e4SLinus Torvalds for (psf=pmc->tomb; psf; psf=nextpsf) { 16101da177e4SLinus Torvalds nextpsf = psf->sf_next; 16111da177e4SLinus Torvalds kfree(psf); 16121da177e4SLinus Torvalds } 16131da177e4SLinus Torvalds pmc->tomb = NULL; 16141da177e4SLinus Torvalds for (psf=pmc->sources; psf; psf=nextpsf) { 16151da177e4SLinus Torvalds nextpsf = psf->sf_next; 16161da177e4SLinus Torvalds kfree(psf); 16171da177e4SLinus Torvalds } 16181da177e4SLinus Torvalds pmc->sources = NULL; 16191da177e4SLinus Torvalds pmc->sfmode = MCAST_EXCLUDE; 1620de9daad9SDenis Lukianov pmc->sfcount[MCAST_INCLUDE] = 0; 16211da177e4SLinus Torvalds pmc->sfcount[MCAST_EXCLUDE] = 1; 16221da177e4SLinus Torvalds } 16231da177e4SLinus Torvalds 16241da177e4SLinus Torvalds 16251da177e4SLinus Torvalds /* 16261da177e4SLinus Torvalds * Join a multicast group 16271da177e4SLinus Torvalds */ 16281da177e4SLinus Torvalds int ip_mc_join_group(struct sock *sk , struct ip_mreqn *imr) 16291da177e4SLinus Torvalds { 16301da177e4SLinus Torvalds int err; 16311da177e4SLinus Torvalds u32 addr = imr->imr_multiaddr.s_addr; 1632ca9b907dSDavid L Stevens struct ip_mc_socklist *iml=NULL, *i; 16331da177e4SLinus Torvalds struct in_device *in_dev; 16341da177e4SLinus Torvalds struct inet_sock *inet = inet_sk(sk); 1635ca9b907dSDavid L Stevens int ifindex; 16361da177e4SLinus Torvalds int count = 0; 16371da177e4SLinus Torvalds 16381da177e4SLinus Torvalds if (!MULTICAST(addr)) 16391da177e4SLinus Torvalds return -EINVAL; 16401da177e4SLinus Torvalds 16411da177e4SLinus Torvalds rtnl_shlock(); 16421da177e4SLinus Torvalds 16431da177e4SLinus Torvalds in_dev = ip_mc_find_dev(imr); 16441da177e4SLinus Torvalds 16451da177e4SLinus Torvalds if (!in_dev) { 16461da177e4SLinus Torvalds iml = NULL; 16471da177e4SLinus Torvalds err = -ENODEV; 16481da177e4SLinus Torvalds goto done; 16491da177e4SLinus Torvalds } 16501da177e4SLinus Torvalds 16511da177e4SLinus Torvalds err = -EADDRINUSE; 1652ca9b907dSDavid L Stevens ifindex = imr->imr_ifindex; 16531da177e4SLinus Torvalds for (i = inet->mc_list; i; i = i->next) { 1654ca9b907dSDavid L Stevens if (i->multi.imr_multiaddr.s_addr == addr && 1655ca9b907dSDavid L Stevens i->multi.imr_ifindex == ifindex) 16561da177e4SLinus Torvalds goto done; 16571da177e4SLinus Torvalds count++; 16581da177e4SLinus Torvalds } 16591da177e4SLinus Torvalds err = -ENOBUFS; 1660ca9b907dSDavid L Stevens if (count >= sysctl_igmp_max_memberships) 16611da177e4SLinus Torvalds goto done; 1662*8b3a7005SKris Katterjohn iml = sock_kmalloc(sk,sizeof(*iml),GFP_KERNEL); 1663ca9b907dSDavid L Stevens if (iml == NULL) 1664ca9b907dSDavid L Stevens goto done; 1665ca9b907dSDavid L Stevens 16661da177e4SLinus Torvalds memcpy(&iml->multi, imr, sizeof(*imr)); 16671da177e4SLinus Torvalds iml->next = inet->mc_list; 16681da177e4SLinus Torvalds iml->sflist = NULL; 16691da177e4SLinus Torvalds iml->sfmode = MCAST_EXCLUDE; 16701da177e4SLinus Torvalds inet->mc_list = iml; 16711da177e4SLinus Torvalds ip_mc_inc_group(in_dev, addr); 16721da177e4SLinus Torvalds err = 0; 16731da177e4SLinus Torvalds done: 16741da177e4SLinus Torvalds rtnl_shunlock(); 16751da177e4SLinus Torvalds return err; 16761da177e4SLinus Torvalds } 16771da177e4SLinus Torvalds 16781da177e4SLinus Torvalds static int ip_mc_leave_src(struct sock *sk, struct ip_mc_socklist *iml, 16791da177e4SLinus Torvalds struct in_device *in_dev) 16801da177e4SLinus Torvalds { 16811da177e4SLinus Torvalds int err; 16821da177e4SLinus Torvalds 16831da177e4SLinus Torvalds if (iml->sflist == 0) { 16841da177e4SLinus Torvalds /* any-source empty exclude case */ 16851da177e4SLinus Torvalds return ip_mc_del_src(in_dev, &iml->multi.imr_multiaddr.s_addr, 16861da177e4SLinus Torvalds iml->sfmode, 0, NULL, 0); 16871da177e4SLinus Torvalds } 16881da177e4SLinus Torvalds err = ip_mc_del_src(in_dev, &iml->multi.imr_multiaddr.s_addr, 16891da177e4SLinus Torvalds iml->sfmode, iml->sflist->sl_count, 16901da177e4SLinus Torvalds iml->sflist->sl_addr, 0); 16911da177e4SLinus Torvalds sock_kfree_s(sk, iml->sflist, IP_SFLSIZE(iml->sflist->sl_max)); 16921da177e4SLinus Torvalds iml->sflist = NULL; 16931da177e4SLinus Torvalds return err; 16941da177e4SLinus Torvalds } 16951da177e4SLinus Torvalds 16961da177e4SLinus Torvalds /* 16971da177e4SLinus Torvalds * Ask a socket to leave a group. 16981da177e4SLinus Torvalds */ 16991da177e4SLinus Torvalds 17001da177e4SLinus Torvalds int ip_mc_leave_group(struct sock *sk, struct ip_mreqn *imr) 17011da177e4SLinus Torvalds { 17021da177e4SLinus Torvalds struct inet_sock *inet = inet_sk(sk); 17031da177e4SLinus Torvalds struct ip_mc_socklist *iml, **imlp; 170484b42baeSDavid L Stevens struct in_device *in_dev; 170584b42baeSDavid L Stevens u32 group = imr->imr_multiaddr.s_addr; 170684b42baeSDavid L Stevens u32 ifindex; 17071da177e4SLinus Torvalds 17081da177e4SLinus Torvalds rtnl_lock(); 170984b42baeSDavid L Stevens in_dev = ip_mc_find_dev(imr); 171084b42baeSDavid L Stevens if (!in_dev) { 171184b42baeSDavid L Stevens rtnl_unlock(); 171284b42baeSDavid L Stevens return -ENODEV; 171384b42baeSDavid L Stevens } 171484b42baeSDavid L Stevens ifindex = imr->imr_ifindex; 17151da177e4SLinus Torvalds for (imlp = &inet->mc_list; (iml = *imlp) != NULL; imlp = &iml->next) { 171684b42baeSDavid L Stevens if (iml->multi.imr_multiaddr.s_addr == group && 171784b42baeSDavid L Stevens iml->multi.imr_ifindex == ifindex) { 17181da177e4SLinus Torvalds (void) ip_mc_leave_src(sk, iml, in_dev); 17191da177e4SLinus Torvalds 17201da177e4SLinus Torvalds *imlp = iml->next; 17211da177e4SLinus Torvalds 172284b42baeSDavid L Stevens ip_mc_dec_group(in_dev, group); 17231da177e4SLinus Torvalds rtnl_unlock(); 17241da177e4SLinus Torvalds sock_kfree_s(sk, iml, sizeof(*iml)); 17251da177e4SLinus Torvalds return 0; 17261da177e4SLinus Torvalds } 17271da177e4SLinus Torvalds } 17281da177e4SLinus Torvalds rtnl_unlock(); 17291da177e4SLinus Torvalds return -EADDRNOTAVAIL; 17301da177e4SLinus Torvalds } 17311da177e4SLinus Torvalds 17321da177e4SLinus Torvalds int ip_mc_source(int add, int omode, struct sock *sk, struct 17331da177e4SLinus Torvalds ip_mreq_source *mreqs, int ifindex) 17341da177e4SLinus Torvalds { 17351da177e4SLinus Torvalds int err; 17361da177e4SLinus Torvalds struct ip_mreqn imr; 17371da177e4SLinus Torvalds u32 addr = mreqs->imr_multiaddr; 17381da177e4SLinus Torvalds struct ip_mc_socklist *pmc; 17391da177e4SLinus Torvalds struct in_device *in_dev = NULL; 17401da177e4SLinus Torvalds struct inet_sock *inet = inet_sk(sk); 17411da177e4SLinus Torvalds struct ip_sf_socklist *psl; 17428cdaaa15SDavid L Stevens int leavegroup = 0; 17431da177e4SLinus Torvalds int i, j, rv; 17441da177e4SLinus Torvalds 17451da177e4SLinus Torvalds if (!MULTICAST(addr)) 17461da177e4SLinus Torvalds return -EINVAL; 17471da177e4SLinus Torvalds 17481da177e4SLinus Torvalds rtnl_shlock(); 17491da177e4SLinus Torvalds 17501da177e4SLinus Torvalds imr.imr_multiaddr.s_addr = mreqs->imr_multiaddr; 17511da177e4SLinus Torvalds imr.imr_address.s_addr = mreqs->imr_interface; 17521da177e4SLinus Torvalds imr.imr_ifindex = ifindex; 17531da177e4SLinus Torvalds in_dev = ip_mc_find_dev(&imr); 17541da177e4SLinus Torvalds 17551da177e4SLinus Torvalds if (!in_dev) { 17561da177e4SLinus Torvalds err = -ENODEV; 17571da177e4SLinus Torvalds goto done; 17581da177e4SLinus Torvalds } 17591da177e4SLinus Torvalds err = -EADDRNOTAVAIL; 17601da177e4SLinus Torvalds 17611da177e4SLinus Torvalds for (pmc=inet->mc_list; pmc; pmc=pmc->next) { 1762ca9b907dSDavid L Stevens if (pmc->multi.imr_multiaddr.s_addr == imr.imr_multiaddr.s_addr 1763ca9b907dSDavid L Stevens && pmc->multi.imr_ifindex == imr.imr_ifindex) 17641da177e4SLinus Torvalds break; 17651da177e4SLinus Torvalds } 1766917f2f10SDavid L Stevens if (!pmc) { /* must have a prior join */ 1767917f2f10SDavid L Stevens err = -EINVAL; 17681da177e4SLinus Torvalds goto done; 1769917f2f10SDavid L Stevens } 17701da177e4SLinus Torvalds /* if a source filter was set, must be the same mode as before */ 17711da177e4SLinus Torvalds if (pmc->sflist) { 1772917f2f10SDavid L Stevens if (pmc->sfmode != omode) { 1773917f2f10SDavid L Stevens err = -EINVAL; 17741da177e4SLinus Torvalds goto done; 1775917f2f10SDavid L Stevens } 17761da177e4SLinus Torvalds } else if (pmc->sfmode != omode) { 17771da177e4SLinus Torvalds /* allow mode switches for empty-set filters */ 17781da177e4SLinus Torvalds ip_mc_add_src(in_dev, &mreqs->imr_multiaddr, omode, 0, NULL, 0); 17791da177e4SLinus Torvalds ip_mc_del_src(in_dev, &mreqs->imr_multiaddr, pmc->sfmode, 0, 17801da177e4SLinus Torvalds NULL, 0); 17811da177e4SLinus Torvalds pmc->sfmode = omode; 17821da177e4SLinus Torvalds } 17831da177e4SLinus Torvalds 17841da177e4SLinus Torvalds psl = pmc->sflist; 17851da177e4SLinus Torvalds if (!add) { 17861da177e4SLinus Torvalds if (!psl) 1787917f2f10SDavid L Stevens goto done; /* err = -EADDRNOTAVAIL */ 17881da177e4SLinus Torvalds rv = !0; 17891da177e4SLinus Torvalds for (i=0; i<psl->sl_count; i++) { 17901da177e4SLinus Torvalds rv = memcmp(&psl->sl_addr[i], &mreqs->imr_sourceaddr, 17911da177e4SLinus Torvalds sizeof(__u32)); 17921da177e4SLinus Torvalds if (rv == 0) 17931da177e4SLinus Torvalds break; 17941da177e4SLinus Torvalds } 17951da177e4SLinus Torvalds if (rv) /* source not found */ 1796917f2f10SDavid L Stevens goto done; /* err = -EADDRNOTAVAIL */ 17971da177e4SLinus Torvalds 17988cdaaa15SDavid L Stevens /* special case - (INCLUDE, empty) == LEAVE_GROUP */ 17998cdaaa15SDavid L Stevens if (psl->sl_count == 1 && omode == MCAST_INCLUDE) { 18008cdaaa15SDavid L Stevens leavegroup = 1; 18018cdaaa15SDavid L Stevens goto done; 18028cdaaa15SDavid L Stevens } 18038cdaaa15SDavid L Stevens 18041da177e4SLinus Torvalds /* update the interface filter */ 18051da177e4SLinus Torvalds ip_mc_del_src(in_dev, &mreqs->imr_multiaddr, omode, 1, 18061da177e4SLinus Torvalds &mreqs->imr_sourceaddr, 1); 18071da177e4SLinus Torvalds 18081da177e4SLinus Torvalds for (j=i+1; j<psl->sl_count; j++) 18091da177e4SLinus Torvalds psl->sl_addr[j-1] = psl->sl_addr[j]; 18101da177e4SLinus Torvalds psl->sl_count--; 18111da177e4SLinus Torvalds err = 0; 18121da177e4SLinus Torvalds goto done; 18131da177e4SLinus Torvalds } 18141da177e4SLinus Torvalds /* else, add a new source to the filter */ 18151da177e4SLinus Torvalds 18161da177e4SLinus Torvalds if (psl && psl->sl_count >= sysctl_igmp_max_msf) { 18171da177e4SLinus Torvalds err = -ENOBUFS; 18181da177e4SLinus Torvalds goto done; 18191da177e4SLinus Torvalds } 18201da177e4SLinus Torvalds if (!psl || psl->sl_count == psl->sl_max) { 18211da177e4SLinus Torvalds struct ip_sf_socklist *newpsl; 18221da177e4SLinus Torvalds int count = IP_SFBLOCK; 18231da177e4SLinus Torvalds 18241da177e4SLinus Torvalds if (psl) 18251da177e4SLinus Torvalds count += psl->sl_max; 1826*8b3a7005SKris Katterjohn newpsl = sock_kmalloc(sk, IP_SFLSIZE(count), GFP_KERNEL); 18271da177e4SLinus Torvalds if (!newpsl) { 18281da177e4SLinus Torvalds err = -ENOBUFS; 18291da177e4SLinus Torvalds goto done; 18301da177e4SLinus Torvalds } 18311da177e4SLinus Torvalds newpsl->sl_max = count; 18321da177e4SLinus Torvalds newpsl->sl_count = count - IP_SFBLOCK; 18331da177e4SLinus Torvalds if (psl) { 18341da177e4SLinus Torvalds for (i=0; i<psl->sl_count; i++) 18351da177e4SLinus Torvalds newpsl->sl_addr[i] = psl->sl_addr[i]; 18361da177e4SLinus Torvalds sock_kfree_s(sk, psl, IP_SFLSIZE(psl->sl_max)); 18371da177e4SLinus Torvalds } 18381da177e4SLinus Torvalds pmc->sflist = psl = newpsl; 18391da177e4SLinus Torvalds } 18401da177e4SLinus Torvalds rv = 1; /* > 0 for insert logic below if sl_count is 0 */ 18411da177e4SLinus Torvalds for (i=0; i<psl->sl_count; i++) { 18421da177e4SLinus Torvalds rv = memcmp(&psl->sl_addr[i], &mreqs->imr_sourceaddr, 18431da177e4SLinus Torvalds sizeof(__u32)); 18441da177e4SLinus Torvalds if (rv == 0) 18451da177e4SLinus Torvalds break; 18461da177e4SLinus Torvalds } 18471da177e4SLinus Torvalds if (rv == 0) /* address already there is an error */ 18481da177e4SLinus Torvalds goto done; 18491da177e4SLinus Torvalds for (j=psl->sl_count-1; j>=i; j--) 18501da177e4SLinus Torvalds psl->sl_addr[j+1] = psl->sl_addr[j]; 18511da177e4SLinus Torvalds psl->sl_addr[i] = mreqs->imr_sourceaddr; 18521da177e4SLinus Torvalds psl->sl_count++; 18531da177e4SLinus Torvalds err = 0; 18541da177e4SLinus Torvalds /* update the interface list */ 18551da177e4SLinus Torvalds ip_mc_add_src(in_dev, &mreqs->imr_multiaddr, omode, 1, 18561da177e4SLinus Torvalds &mreqs->imr_sourceaddr, 1); 18571da177e4SLinus Torvalds done: 18581da177e4SLinus Torvalds rtnl_shunlock(); 18598cdaaa15SDavid L Stevens if (leavegroup) 18608cdaaa15SDavid L Stevens return ip_mc_leave_group(sk, &imr); 18611da177e4SLinus Torvalds return err; 18621da177e4SLinus Torvalds } 18631da177e4SLinus Torvalds 18641da177e4SLinus Torvalds int ip_mc_msfilter(struct sock *sk, struct ip_msfilter *msf, int ifindex) 18651da177e4SLinus Torvalds { 18669951f036SDavid L Stevens int err = 0; 18671da177e4SLinus Torvalds struct ip_mreqn imr; 18681da177e4SLinus Torvalds u32 addr = msf->imsf_multiaddr; 18691da177e4SLinus Torvalds struct ip_mc_socklist *pmc; 18701da177e4SLinus Torvalds struct in_device *in_dev; 18711da177e4SLinus Torvalds struct inet_sock *inet = inet_sk(sk); 18721da177e4SLinus Torvalds struct ip_sf_socklist *newpsl, *psl; 18739951f036SDavid L Stevens int leavegroup = 0; 18741da177e4SLinus Torvalds 18751da177e4SLinus Torvalds if (!MULTICAST(addr)) 18761da177e4SLinus Torvalds return -EINVAL; 18771da177e4SLinus Torvalds if (msf->imsf_fmode != MCAST_INCLUDE && 18781da177e4SLinus Torvalds msf->imsf_fmode != MCAST_EXCLUDE) 18791da177e4SLinus Torvalds return -EINVAL; 18801da177e4SLinus Torvalds 18811da177e4SLinus Torvalds rtnl_shlock(); 18821da177e4SLinus Torvalds 18831da177e4SLinus Torvalds imr.imr_multiaddr.s_addr = msf->imsf_multiaddr; 18841da177e4SLinus Torvalds imr.imr_address.s_addr = msf->imsf_interface; 18851da177e4SLinus Torvalds imr.imr_ifindex = ifindex; 18861da177e4SLinus Torvalds in_dev = ip_mc_find_dev(&imr); 18871da177e4SLinus Torvalds 18881da177e4SLinus Torvalds if (!in_dev) { 18891da177e4SLinus Torvalds err = -ENODEV; 18901da177e4SLinus Torvalds goto done; 18911da177e4SLinus Torvalds } 18921da177e4SLinus Torvalds 18939951f036SDavid L Stevens /* special case - (INCLUDE, empty) == LEAVE_GROUP */ 18949951f036SDavid L Stevens if (msf->imsf_fmode == MCAST_INCLUDE && msf->imsf_numsrc == 0) { 18959951f036SDavid L Stevens leavegroup = 1; 18969951f036SDavid L Stevens goto done; 18979951f036SDavid L Stevens } 18989951f036SDavid L Stevens 18991da177e4SLinus Torvalds for (pmc=inet->mc_list; pmc; pmc=pmc->next) { 19001da177e4SLinus Torvalds if (pmc->multi.imr_multiaddr.s_addr == msf->imsf_multiaddr && 19011da177e4SLinus Torvalds pmc->multi.imr_ifindex == imr.imr_ifindex) 19021da177e4SLinus Torvalds break; 19031da177e4SLinus Torvalds } 1904917f2f10SDavid L Stevens if (!pmc) { /* must have a prior join */ 1905917f2f10SDavid L Stevens err = -EINVAL; 19061da177e4SLinus Torvalds goto done; 1907917f2f10SDavid L Stevens } 19081da177e4SLinus Torvalds if (msf->imsf_numsrc) { 1909*8b3a7005SKris Katterjohn newpsl = sock_kmalloc(sk, IP_SFLSIZE(msf->imsf_numsrc), 1910*8b3a7005SKris Katterjohn GFP_KERNEL); 19111da177e4SLinus Torvalds if (!newpsl) { 19121da177e4SLinus Torvalds err = -ENOBUFS; 19131da177e4SLinus Torvalds goto done; 19141da177e4SLinus Torvalds } 19151da177e4SLinus Torvalds newpsl->sl_max = newpsl->sl_count = msf->imsf_numsrc; 19161da177e4SLinus Torvalds memcpy(newpsl->sl_addr, msf->imsf_slist, 19171da177e4SLinus Torvalds msf->imsf_numsrc * sizeof(msf->imsf_slist[0])); 19181da177e4SLinus Torvalds err = ip_mc_add_src(in_dev, &msf->imsf_multiaddr, 19191da177e4SLinus Torvalds msf->imsf_fmode, newpsl->sl_count, newpsl->sl_addr, 0); 19201da177e4SLinus Torvalds if (err) { 19211da177e4SLinus Torvalds sock_kfree_s(sk, newpsl, IP_SFLSIZE(newpsl->sl_max)); 19221da177e4SLinus Torvalds goto done; 19231da177e4SLinus Torvalds } 19248713dbf0SYan Zheng } else { 19251da177e4SLinus Torvalds newpsl = NULL; 19268713dbf0SYan Zheng (void) ip_mc_add_src(in_dev, &msf->imsf_multiaddr, 19278713dbf0SYan Zheng msf->imsf_fmode, 0, NULL, 0); 19288713dbf0SYan Zheng } 19291da177e4SLinus Torvalds psl = pmc->sflist; 19301da177e4SLinus Torvalds if (psl) { 19311da177e4SLinus Torvalds (void) ip_mc_del_src(in_dev, &msf->imsf_multiaddr, pmc->sfmode, 19321da177e4SLinus Torvalds psl->sl_count, psl->sl_addr, 0); 19331da177e4SLinus Torvalds sock_kfree_s(sk, psl, IP_SFLSIZE(psl->sl_max)); 19341da177e4SLinus Torvalds } else 19351da177e4SLinus Torvalds (void) ip_mc_del_src(in_dev, &msf->imsf_multiaddr, pmc->sfmode, 19361da177e4SLinus Torvalds 0, NULL, 0); 19371da177e4SLinus Torvalds pmc->sflist = newpsl; 19381da177e4SLinus Torvalds pmc->sfmode = msf->imsf_fmode; 1939917f2f10SDavid L Stevens err = 0; 19401da177e4SLinus Torvalds done: 19411da177e4SLinus Torvalds rtnl_shunlock(); 19429951f036SDavid L Stevens if (leavegroup) 19439951f036SDavid L Stevens err = ip_mc_leave_group(sk, &imr); 19441da177e4SLinus Torvalds return err; 19451da177e4SLinus Torvalds } 19461da177e4SLinus Torvalds 19471da177e4SLinus Torvalds int ip_mc_msfget(struct sock *sk, struct ip_msfilter *msf, 19481da177e4SLinus Torvalds struct ip_msfilter __user *optval, int __user *optlen) 19491da177e4SLinus Torvalds { 19501da177e4SLinus Torvalds int err, len, count, copycount; 19511da177e4SLinus Torvalds struct ip_mreqn imr; 19521da177e4SLinus Torvalds u32 addr = msf->imsf_multiaddr; 19531da177e4SLinus Torvalds struct ip_mc_socklist *pmc; 19541da177e4SLinus Torvalds struct in_device *in_dev; 19551da177e4SLinus Torvalds struct inet_sock *inet = inet_sk(sk); 19561da177e4SLinus Torvalds struct ip_sf_socklist *psl; 19571da177e4SLinus Torvalds 19581da177e4SLinus Torvalds if (!MULTICAST(addr)) 19591da177e4SLinus Torvalds return -EINVAL; 19601da177e4SLinus Torvalds 19611da177e4SLinus Torvalds rtnl_shlock(); 19621da177e4SLinus Torvalds 19631da177e4SLinus Torvalds imr.imr_multiaddr.s_addr = msf->imsf_multiaddr; 19641da177e4SLinus Torvalds imr.imr_address.s_addr = msf->imsf_interface; 19651da177e4SLinus Torvalds imr.imr_ifindex = 0; 19661da177e4SLinus Torvalds in_dev = ip_mc_find_dev(&imr); 19671da177e4SLinus Torvalds 19681da177e4SLinus Torvalds if (!in_dev) { 19691da177e4SLinus Torvalds err = -ENODEV; 19701da177e4SLinus Torvalds goto done; 19711da177e4SLinus Torvalds } 19721da177e4SLinus Torvalds err = -EADDRNOTAVAIL; 19731da177e4SLinus Torvalds 19741da177e4SLinus Torvalds for (pmc=inet->mc_list; pmc; pmc=pmc->next) { 19751da177e4SLinus Torvalds if (pmc->multi.imr_multiaddr.s_addr == msf->imsf_multiaddr && 19761da177e4SLinus Torvalds pmc->multi.imr_ifindex == imr.imr_ifindex) 19771da177e4SLinus Torvalds break; 19781da177e4SLinus Torvalds } 19791da177e4SLinus Torvalds if (!pmc) /* must have a prior join */ 19801da177e4SLinus Torvalds goto done; 19811da177e4SLinus Torvalds msf->imsf_fmode = pmc->sfmode; 19821da177e4SLinus Torvalds psl = pmc->sflist; 19831da177e4SLinus Torvalds rtnl_shunlock(); 19841da177e4SLinus Torvalds if (!psl) { 19851da177e4SLinus Torvalds len = 0; 19861da177e4SLinus Torvalds count = 0; 19871da177e4SLinus Torvalds } else { 19881da177e4SLinus Torvalds count = psl->sl_count; 19891da177e4SLinus Torvalds } 19901da177e4SLinus Torvalds copycount = count < msf->imsf_numsrc ? count : msf->imsf_numsrc; 19911da177e4SLinus Torvalds len = copycount * sizeof(psl->sl_addr[0]); 19921da177e4SLinus Torvalds msf->imsf_numsrc = count; 19931da177e4SLinus Torvalds if (put_user(IP_MSFILTER_SIZE(copycount), optlen) || 19941da177e4SLinus Torvalds copy_to_user(optval, msf, IP_MSFILTER_SIZE(0))) { 19951da177e4SLinus Torvalds return -EFAULT; 19961da177e4SLinus Torvalds } 19971da177e4SLinus Torvalds if (len && 19981da177e4SLinus Torvalds copy_to_user(&optval->imsf_slist[0], psl->sl_addr, len)) 19991da177e4SLinus Torvalds return -EFAULT; 20001da177e4SLinus Torvalds return 0; 20011da177e4SLinus Torvalds done: 20021da177e4SLinus Torvalds rtnl_shunlock(); 20031da177e4SLinus Torvalds return err; 20041da177e4SLinus Torvalds } 20051da177e4SLinus Torvalds 20061da177e4SLinus Torvalds int ip_mc_gsfget(struct sock *sk, struct group_filter *gsf, 20071da177e4SLinus Torvalds struct group_filter __user *optval, int __user *optlen) 20081da177e4SLinus Torvalds { 20091da177e4SLinus Torvalds int err, i, count, copycount; 20101da177e4SLinus Torvalds struct sockaddr_in *psin; 20111da177e4SLinus Torvalds u32 addr; 20121da177e4SLinus Torvalds struct ip_mc_socklist *pmc; 20131da177e4SLinus Torvalds struct inet_sock *inet = inet_sk(sk); 20141da177e4SLinus Torvalds struct ip_sf_socklist *psl; 20151da177e4SLinus Torvalds 20161da177e4SLinus Torvalds psin = (struct sockaddr_in *)&gsf->gf_group; 20171da177e4SLinus Torvalds if (psin->sin_family != AF_INET) 20181da177e4SLinus Torvalds return -EINVAL; 20191da177e4SLinus Torvalds addr = psin->sin_addr.s_addr; 20201da177e4SLinus Torvalds if (!MULTICAST(addr)) 20211da177e4SLinus Torvalds return -EINVAL; 20221da177e4SLinus Torvalds 20231da177e4SLinus Torvalds rtnl_shlock(); 20241da177e4SLinus Torvalds 20251da177e4SLinus Torvalds err = -EADDRNOTAVAIL; 20261da177e4SLinus Torvalds 20271da177e4SLinus Torvalds for (pmc=inet->mc_list; pmc; pmc=pmc->next) { 20281da177e4SLinus Torvalds if (pmc->multi.imr_multiaddr.s_addr == addr && 20291da177e4SLinus Torvalds pmc->multi.imr_ifindex == gsf->gf_interface) 20301da177e4SLinus Torvalds break; 20311da177e4SLinus Torvalds } 20321da177e4SLinus Torvalds if (!pmc) /* must have a prior join */ 20331da177e4SLinus Torvalds goto done; 20341da177e4SLinus Torvalds gsf->gf_fmode = pmc->sfmode; 20351da177e4SLinus Torvalds psl = pmc->sflist; 20361da177e4SLinus Torvalds rtnl_shunlock(); 20371da177e4SLinus Torvalds count = psl ? psl->sl_count : 0; 20381da177e4SLinus Torvalds copycount = count < gsf->gf_numsrc ? count : gsf->gf_numsrc; 20391da177e4SLinus Torvalds gsf->gf_numsrc = count; 20401da177e4SLinus Torvalds if (put_user(GROUP_FILTER_SIZE(copycount), optlen) || 20411da177e4SLinus Torvalds copy_to_user(optval, gsf, GROUP_FILTER_SIZE(0))) { 20421da177e4SLinus Torvalds return -EFAULT; 20431da177e4SLinus Torvalds } 20441da177e4SLinus Torvalds for (i=0; i<copycount; i++) { 20451da177e4SLinus Torvalds struct sockaddr_in *psin; 20461da177e4SLinus Torvalds struct sockaddr_storage ss; 20471da177e4SLinus Torvalds 20481da177e4SLinus Torvalds psin = (struct sockaddr_in *)&ss; 20491da177e4SLinus Torvalds memset(&ss, 0, sizeof(ss)); 20501da177e4SLinus Torvalds psin->sin_family = AF_INET; 20511da177e4SLinus Torvalds psin->sin_addr.s_addr = psl->sl_addr[i]; 20521da177e4SLinus Torvalds if (copy_to_user(&optval->gf_slist[i], &ss, sizeof(ss))) 20531da177e4SLinus Torvalds return -EFAULT; 20541da177e4SLinus Torvalds } 20551da177e4SLinus Torvalds return 0; 20561da177e4SLinus Torvalds done: 20571da177e4SLinus Torvalds rtnl_shunlock(); 20581da177e4SLinus Torvalds return err; 20591da177e4SLinus Torvalds } 20601da177e4SLinus Torvalds 20611da177e4SLinus Torvalds /* 20621da177e4SLinus Torvalds * check if a multicast source filter allows delivery for a given <src,dst,intf> 20631da177e4SLinus Torvalds */ 20641da177e4SLinus Torvalds int ip_mc_sf_allow(struct sock *sk, u32 loc_addr, u32 rmt_addr, int dif) 20651da177e4SLinus Torvalds { 20661da177e4SLinus Torvalds struct inet_sock *inet = inet_sk(sk); 20671da177e4SLinus Torvalds struct ip_mc_socklist *pmc; 20681da177e4SLinus Torvalds struct ip_sf_socklist *psl; 20691da177e4SLinus Torvalds int i; 20701da177e4SLinus Torvalds 20711da177e4SLinus Torvalds if (!MULTICAST(loc_addr)) 20721da177e4SLinus Torvalds return 1; 20731da177e4SLinus Torvalds 20741da177e4SLinus Torvalds for (pmc=inet->mc_list; pmc; pmc=pmc->next) { 20751da177e4SLinus Torvalds if (pmc->multi.imr_multiaddr.s_addr == loc_addr && 20761da177e4SLinus Torvalds pmc->multi.imr_ifindex == dif) 20771da177e4SLinus Torvalds break; 20781da177e4SLinus Torvalds } 20791da177e4SLinus Torvalds if (!pmc) 20801da177e4SLinus Torvalds return 1; 20811da177e4SLinus Torvalds psl = pmc->sflist; 20821da177e4SLinus Torvalds if (!psl) 20831da177e4SLinus Torvalds return pmc->sfmode == MCAST_EXCLUDE; 20841da177e4SLinus Torvalds 20851da177e4SLinus Torvalds for (i=0; i<psl->sl_count; i++) { 20861da177e4SLinus Torvalds if (psl->sl_addr[i] == rmt_addr) 20871da177e4SLinus Torvalds break; 20881da177e4SLinus Torvalds } 20891da177e4SLinus Torvalds if (pmc->sfmode == MCAST_INCLUDE && i >= psl->sl_count) 20901da177e4SLinus Torvalds return 0; 20911da177e4SLinus Torvalds if (pmc->sfmode == MCAST_EXCLUDE && i < psl->sl_count) 20921da177e4SLinus Torvalds return 0; 20931da177e4SLinus Torvalds return 1; 20941da177e4SLinus Torvalds } 20951da177e4SLinus Torvalds 20961da177e4SLinus Torvalds /* 20971da177e4SLinus Torvalds * A socket is closing. 20981da177e4SLinus Torvalds */ 20991da177e4SLinus Torvalds 21001da177e4SLinus Torvalds void ip_mc_drop_socket(struct sock *sk) 21011da177e4SLinus Torvalds { 21021da177e4SLinus Torvalds struct inet_sock *inet = inet_sk(sk); 21031da177e4SLinus Torvalds struct ip_mc_socklist *iml; 21041da177e4SLinus Torvalds 21051da177e4SLinus Torvalds if (inet->mc_list == NULL) 21061da177e4SLinus Torvalds return; 21071da177e4SLinus Torvalds 21081da177e4SLinus Torvalds rtnl_lock(); 21091da177e4SLinus Torvalds while ((iml = inet->mc_list) != NULL) { 21101da177e4SLinus Torvalds struct in_device *in_dev; 21111da177e4SLinus Torvalds inet->mc_list = iml->next; 21121da177e4SLinus Torvalds 21131da177e4SLinus Torvalds if ((in_dev = inetdev_by_index(iml->multi.imr_ifindex)) != NULL) { 21141da177e4SLinus Torvalds (void) ip_mc_leave_src(sk, iml, in_dev); 21151da177e4SLinus Torvalds ip_mc_dec_group(in_dev, iml->multi.imr_multiaddr.s_addr); 21161da177e4SLinus Torvalds in_dev_put(in_dev); 21171da177e4SLinus Torvalds } 21181da177e4SLinus Torvalds sock_kfree_s(sk, iml, sizeof(*iml)); 21191da177e4SLinus Torvalds 21201da177e4SLinus Torvalds } 21211da177e4SLinus Torvalds rtnl_unlock(); 21221da177e4SLinus Torvalds } 21231da177e4SLinus Torvalds 21241da177e4SLinus Torvalds int ip_check_mc(struct in_device *in_dev, u32 mc_addr, u32 src_addr, u16 proto) 21251da177e4SLinus Torvalds { 21261da177e4SLinus Torvalds struct ip_mc_list *im; 21271da177e4SLinus Torvalds struct ip_sf_list *psf; 21281da177e4SLinus Torvalds int rv = 0; 21291da177e4SLinus Torvalds 21301da177e4SLinus Torvalds read_lock(&in_dev->mc_list_lock); 21311da177e4SLinus Torvalds for (im=in_dev->mc_list; im; im=im->next) { 21321da177e4SLinus Torvalds if (im->multiaddr == mc_addr) 21331da177e4SLinus Torvalds break; 21341da177e4SLinus Torvalds } 21351da177e4SLinus Torvalds if (im && proto == IPPROTO_IGMP) { 21361da177e4SLinus Torvalds rv = 1; 21371da177e4SLinus Torvalds } else if (im) { 21381da177e4SLinus Torvalds if (src_addr) { 21391da177e4SLinus Torvalds for (psf=im->sources; psf; psf=psf->sf_next) { 21401da177e4SLinus Torvalds if (psf->sf_inaddr == src_addr) 21411da177e4SLinus Torvalds break; 21421da177e4SLinus Torvalds } 21431da177e4SLinus Torvalds if (psf) 21441da177e4SLinus Torvalds rv = psf->sf_count[MCAST_INCLUDE] || 21451da177e4SLinus Torvalds psf->sf_count[MCAST_EXCLUDE] != 21461da177e4SLinus Torvalds im->sfcount[MCAST_EXCLUDE]; 21471da177e4SLinus Torvalds else 21481da177e4SLinus Torvalds rv = im->sfcount[MCAST_EXCLUDE] != 0; 21491da177e4SLinus Torvalds } else 21501da177e4SLinus Torvalds rv = 1; /* unspecified source; tentatively allow */ 21511da177e4SLinus Torvalds } 21521da177e4SLinus Torvalds read_unlock(&in_dev->mc_list_lock); 21531da177e4SLinus Torvalds return rv; 21541da177e4SLinus Torvalds } 21551da177e4SLinus Torvalds 21561da177e4SLinus Torvalds #if defined(CONFIG_PROC_FS) 21571da177e4SLinus Torvalds struct igmp_mc_iter_state { 21581da177e4SLinus Torvalds struct net_device *dev; 21591da177e4SLinus Torvalds struct in_device *in_dev; 21601da177e4SLinus Torvalds }; 21611da177e4SLinus Torvalds 21621da177e4SLinus Torvalds #define igmp_mc_seq_private(seq) ((struct igmp_mc_iter_state *)(seq)->private) 21631da177e4SLinus Torvalds 21641da177e4SLinus Torvalds static inline struct ip_mc_list *igmp_mc_get_first(struct seq_file *seq) 21651da177e4SLinus Torvalds { 21661da177e4SLinus Torvalds struct ip_mc_list *im = NULL; 21671da177e4SLinus Torvalds struct igmp_mc_iter_state *state = igmp_mc_seq_private(seq); 21681da177e4SLinus Torvalds 21691da177e4SLinus Torvalds for (state->dev = dev_base, state->in_dev = NULL; 21701da177e4SLinus Torvalds state->dev; 21711da177e4SLinus Torvalds state->dev = state->dev->next) { 21721da177e4SLinus Torvalds struct in_device *in_dev; 21731da177e4SLinus Torvalds in_dev = in_dev_get(state->dev); 21741da177e4SLinus Torvalds if (!in_dev) 21751da177e4SLinus Torvalds continue; 21761da177e4SLinus Torvalds read_lock(&in_dev->mc_list_lock); 21771da177e4SLinus Torvalds im = in_dev->mc_list; 21781da177e4SLinus Torvalds if (im) { 21791da177e4SLinus Torvalds state->in_dev = in_dev; 21801da177e4SLinus Torvalds break; 21811da177e4SLinus Torvalds } 21821da177e4SLinus Torvalds read_unlock(&in_dev->mc_list_lock); 21831da177e4SLinus Torvalds in_dev_put(in_dev); 21841da177e4SLinus Torvalds } 21851da177e4SLinus Torvalds return im; 21861da177e4SLinus Torvalds } 21871da177e4SLinus Torvalds 21881da177e4SLinus Torvalds static struct ip_mc_list *igmp_mc_get_next(struct seq_file *seq, struct ip_mc_list *im) 21891da177e4SLinus Torvalds { 21901da177e4SLinus Torvalds struct igmp_mc_iter_state *state = igmp_mc_seq_private(seq); 21911da177e4SLinus Torvalds im = im->next; 21921da177e4SLinus Torvalds while (!im) { 21931da177e4SLinus Torvalds if (likely(state->in_dev != NULL)) { 21941da177e4SLinus Torvalds read_unlock(&state->in_dev->mc_list_lock); 21951da177e4SLinus Torvalds in_dev_put(state->in_dev); 21961da177e4SLinus Torvalds } 21971da177e4SLinus Torvalds state->dev = state->dev->next; 21981da177e4SLinus Torvalds if (!state->dev) { 21991da177e4SLinus Torvalds state->in_dev = NULL; 22001da177e4SLinus Torvalds break; 22011da177e4SLinus Torvalds } 22021da177e4SLinus Torvalds state->in_dev = in_dev_get(state->dev); 22031da177e4SLinus Torvalds if (!state->in_dev) 22041da177e4SLinus Torvalds continue; 22051da177e4SLinus Torvalds read_lock(&state->in_dev->mc_list_lock); 22061da177e4SLinus Torvalds im = state->in_dev->mc_list; 22071da177e4SLinus Torvalds } 22081da177e4SLinus Torvalds return im; 22091da177e4SLinus Torvalds } 22101da177e4SLinus Torvalds 22111da177e4SLinus Torvalds static struct ip_mc_list *igmp_mc_get_idx(struct seq_file *seq, loff_t pos) 22121da177e4SLinus Torvalds { 22131da177e4SLinus Torvalds struct ip_mc_list *im = igmp_mc_get_first(seq); 22141da177e4SLinus Torvalds if (im) 22151da177e4SLinus Torvalds while (pos && (im = igmp_mc_get_next(seq, im)) != NULL) 22161da177e4SLinus Torvalds --pos; 22171da177e4SLinus Torvalds return pos ? NULL : im; 22181da177e4SLinus Torvalds } 22191da177e4SLinus Torvalds 22201da177e4SLinus Torvalds static void *igmp_mc_seq_start(struct seq_file *seq, loff_t *pos) 22211da177e4SLinus Torvalds { 22221da177e4SLinus Torvalds read_lock(&dev_base_lock); 22231da177e4SLinus Torvalds return *pos ? igmp_mc_get_idx(seq, *pos - 1) : SEQ_START_TOKEN; 22241da177e4SLinus Torvalds } 22251da177e4SLinus Torvalds 22261da177e4SLinus Torvalds static void *igmp_mc_seq_next(struct seq_file *seq, void *v, loff_t *pos) 22271da177e4SLinus Torvalds { 22281da177e4SLinus Torvalds struct ip_mc_list *im; 22291da177e4SLinus Torvalds if (v == SEQ_START_TOKEN) 22301da177e4SLinus Torvalds im = igmp_mc_get_first(seq); 22311da177e4SLinus Torvalds else 22321da177e4SLinus Torvalds im = igmp_mc_get_next(seq, v); 22331da177e4SLinus Torvalds ++*pos; 22341da177e4SLinus Torvalds return im; 22351da177e4SLinus Torvalds } 22361da177e4SLinus Torvalds 22371da177e4SLinus Torvalds static void igmp_mc_seq_stop(struct seq_file *seq, void *v) 22381da177e4SLinus Torvalds { 22391da177e4SLinus Torvalds struct igmp_mc_iter_state *state = igmp_mc_seq_private(seq); 22401da177e4SLinus Torvalds if (likely(state->in_dev != NULL)) { 22411da177e4SLinus Torvalds read_unlock(&state->in_dev->mc_list_lock); 22421da177e4SLinus Torvalds in_dev_put(state->in_dev); 22431da177e4SLinus Torvalds state->in_dev = NULL; 22441da177e4SLinus Torvalds } 22451da177e4SLinus Torvalds state->dev = NULL; 22461da177e4SLinus Torvalds read_unlock(&dev_base_lock); 22471da177e4SLinus Torvalds } 22481da177e4SLinus Torvalds 22491da177e4SLinus Torvalds static int igmp_mc_seq_show(struct seq_file *seq, void *v) 22501da177e4SLinus Torvalds { 22511da177e4SLinus Torvalds if (v == SEQ_START_TOKEN) 22521da177e4SLinus Torvalds seq_puts(seq, 22531da177e4SLinus Torvalds "Idx\tDevice : Count Querier\tGroup Users Timer\tReporter\n"); 22541da177e4SLinus Torvalds else { 22551da177e4SLinus Torvalds struct ip_mc_list *im = (struct ip_mc_list *)v; 22561da177e4SLinus Torvalds struct igmp_mc_iter_state *state = igmp_mc_seq_private(seq); 22571da177e4SLinus Torvalds char *querier; 22581da177e4SLinus Torvalds #ifdef CONFIG_IP_MULTICAST 22591da177e4SLinus Torvalds querier = IGMP_V1_SEEN(state->in_dev) ? "V1" : 22601da177e4SLinus Torvalds IGMP_V2_SEEN(state->in_dev) ? "V2" : 22611da177e4SLinus Torvalds "V3"; 22621da177e4SLinus Torvalds #else 22631da177e4SLinus Torvalds querier = "NONE"; 22641da177e4SLinus Torvalds #endif 22651da177e4SLinus Torvalds 22661da177e4SLinus Torvalds if (state->in_dev->mc_list == im) { 22671da177e4SLinus Torvalds seq_printf(seq, "%d\t%-10s: %5d %7s\n", 22681da177e4SLinus Torvalds state->dev->ifindex, state->dev->name, state->dev->mc_count, querier); 22691da177e4SLinus Torvalds } 22701da177e4SLinus Torvalds 22711da177e4SLinus Torvalds seq_printf(seq, 22721da177e4SLinus Torvalds "\t\t\t\t%08lX %5d %d:%08lX\t\t%d\n", 22731da177e4SLinus Torvalds im->multiaddr, im->users, 22741da177e4SLinus Torvalds im->tm_running, im->tm_running ? 22751da177e4SLinus Torvalds jiffies_to_clock_t(im->timer.expires-jiffies) : 0, 22761da177e4SLinus Torvalds im->reporter); 22771da177e4SLinus Torvalds } 22781da177e4SLinus Torvalds return 0; 22791da177e4SLinus Torvalds } 22801da177e4SLinus Torvalds 22811da177e4SLinus Torvalds static struct seq_operations igmp_mc_seq_ops = { 22821da177e4SLinus Torvalds .start = igmp_mc_seq_start, 22831da177e4SLinus Torvalds .next = igmp_mc_seq_next, 22841da177e4SLinus Torvalds .stop = igmp_mc_seq_stop, 22851da177e4SLinus Torvalds .show = igmp_mc_seq_show, 22861da177e4SLinus Torvalds }; 22871da177e4SLinus Torvalds 22881da177e4SLinus Torvalds static int igmp_mc_seq_open(struct inode *inode, struct file *file) 22891da177e4SLinus Torvalds { 22901da177e4SLinus Torvalds struct seq_file *seq; 22911da177e4SLinus Torvalds int rc = -ENOMEM; 22921da177e4SLinus Torvalds struct igmp_mc_iter_state *s = kmalloc(sizeof(*s), GFP_KERNEL); 22931da177e4SLinus Torvalds 22941da177e4SLinus Torvalds if (!s) 22951da177e4SLinus Torvalds goto out; 22961da177e4SLinus Torvalds rc = seq_open(file, &igmp_mc_seq_ops); 22971da177e4SLinus Torvalds if (rc) 22981da177e4SLinus Torvalds goto out_kfree; 22991da177e4SLinus Torvalds 23001da177e4SLinus Torvalds seq = file->private_data; 23011da177e4SLinus Torvalds seq->private = s; 23021da177e4SLinus Torvalds memset(s, 0, sizeof(*s)); 23031da177e4SLinus Torvalds out: 23041da177e4SLinus Torvalds return rc; 23051da177e4SLinus Torvalds out_kfree: 23061da177e4SLinus Torvalds kfree(s); 23071da177e4SLinus Torvalds goto out; 23081da177e4SLinus Torvalds } 23091da177e4SLinus Torvalds 23101da177e4SLinus Torvalds static struct file_operations igmp_mc_seq_fops = { 23111da177e4SLinus Torvalds .owner = THIS_MODULE, 23121da177e4SLinus Torvalds .open = igmp_mc_seq_open, 23131da177e4SLinus Torvalds .read = seq_read, 23141da177e4SLinus Torvalds .llseek = seq_lseek, 23151da177e4SLinus Torvalds .release = seq_release_private, 23161da177e4SLinus Torvalds }; 23171da177e4SLinus Torvalds 23181da177e4SLinus Torvalds struct igmp_mcf_iter_state { 23191da177e4SLinus Torvalds struct net_device *dev; 23201da177e4SLinus Torvalds struct in_device *idev; 23211da177e4SLinus Torvalds struct ip_mc_list *im; 23221da177e4SLinus Torvalds }; 23231da177e4SLinus Torvalds 23241da177e4SLinus Torvalds #define igmp_mcf_seq_private(seq) ((struct igmp_mcf_iter_state *)(seq)->private) 23251da177e4SLinus Torvalds 23261da177e4SLinus Torvalds static inline struct ip_sf_list *igmp_mcf_get_first(struct seq_file *seq) 23271da177e4SLinus Torvalds { 23281da177e4SLinus Torvalds struct ip_sf_list *psf = NULL; 23291da177e4SLinus Torvalds struct ip_mc_list *im = NULL; 23301da177e4SLinus Torvalds struct igmp_mcf_iter_state *state = igmp_mcf_seq_private(seq); 23311da177e4SLinus Torvalds 23321da177e4SLinus Torvalds for (state->dev = dev_base, state->idev = NULL, state->im = NULL; 23331da177e4SLinus Torvalds state->dev; 23341da177e4SLinus Torvalds state->dev = state->dev->next) { 23351da177e4SLinus Torvalds struct in_device *idev; 23361da177e4SLinus Torvalds idev = in_dev_get(state->dev); 23371da177e4SLinus Torvalds if (unlikely(idev == NULL)) 23381da177e4SLinus Torvalds continue; 23391da177e4SLinus Torvalds read_lock(&idev->mc_list_lock); 23401da177e4SLinus Torvalds im = idev->mc_list; 23411da177e4SLinus Torvalds if (likely(im != NULL)) { 23421da177e4SLinus Torvalds spin_lock_bh(&im->lock); 23431da177e4SLinus Torvalds psf = im->sources; 23441da177e4SLinus Torvalds if (likely(psf != NULL)) { 23451da177e4SLinus Torvalds state->im = im; 23461da177e4SLinus Torvalds state->idev = idev; 23471da177e4SLinus Torvalds break; 23481da177e4SLinus Torvalds } 23491da177e4SLinus Torvalds spin_unlock_bh(&im->lock); 23501da177e4SLinus Torvalds } 23511da177e4SLinus Torvalds read_unlock(&idev->mc_list_lock); 23521da177e4SLinus Torvalds in_dev_put(idev); 23531da177e4SLinus Torvalds } 23541da177e4SLinus Torvalds return psf; 23551da177e4SLinus Torvalds } 23561da177e4SLinus Torvalds 23571da177e4SLinus Torvalds static struct ip_sf_list *igmp_mcf_get_next(struct seq_file *seq, struct ip_sf_list *psf) 23581da177e4SLinus Torvalds { 23591da177e4SLinus Torvalds struct igmp_mcf_iter_state *state = igmp_mcf_seq_private(seq); 23601da177e4SLinus Torvalds 23611da177e4SLinus Torvalds psf = psf->sf_next; 23621da177e4SLinus Torvalds while (!psf) { 23631da177e4SLinus Torvalds spin_unlock_bh(&state->im->lock); 23641da177e4SLinus Torvalds state->im = state->im->next; 23651da177e4SLinus Torvalds while (!state->im) { 23661da177e4SLinus Torvalds if (likely(state->idev != NULL)) { 23671da177e4SLinus Torvalds read_unlock(&state->idev->mc_list_lock); 23681da177e4SLinus Torvalds in_dev_put(state->idev); 23691da177e4SLinus Torvalds } 23701da177e4SLinus Torvalds state->dev = state->dev->next; 23711da177e4SLinus Torvalds if (!state->dev) { 23721da177e4SLinus Torvalds state->idev = NULL; 23731da177e4SLinus Torvalds goto out; 23741da177e4SLinus Torvalds } 23751da177e4SLinus Torvalds state->idev = in_dev_get(state->dev); 23761da177e4SLinus Torvalds if (!state->idev) 23771da177e4SLinus Torvalds continue; 23781da177e4SLinus Torvalds read_lock(&state->idev->mc_list_lock); 23791da177e4SLinus Torvalds state->im = state->idev->mc_list; 23801da177e4SLinus Torvalds } 23811da177e4SLinus Torvalds if (!state->im) 23821da177e4SLinus Torvalds break; 23831da177e4SLinus Torvalds spin_lock_bh(&state->im->lock); 23841da177e4SLinus Torvalds psf = state->im->sources; 23851da177e4SLinus Torvalds } 23861da177e4SLinus Torvalds out: 23871da177e4SLinus Torvalds return psf; 23881da177e4SLinus Torvalds } 23891da177e4SLinus Torvalds 23901da177e4SLinus Torvalds static struct ip_sf_list *igmp_mcf_get_idx(struct seq_file *seq, loff_t pos) 23911da177e4SLinus Torvalds { 23921da177e4SLinus Torvalds struct ip_sf_list *psf = igmp_mcf_get_first(seq); 23931da177e4SLinus Torvalds if (psf) 23941da177e4SLinus Torvalds while (pos && (psf = igmp_mcf_get_next(seq, psf)) != NULL) 23951da177e4SLinus Torvalds --pos; 23961da177e4SLinus Torvalds return pos ? NULL : psf; 23971da177e4SLinus Torvalds } 23981da177e4SLinus Torvalds 23991da177e4SLinus Torvalds static void *igmp_mcf_seq_start(struct seq_file *seq, loff_t *pos) 24001da177e4SLinus Torvalds { 24011da177e4SLinus Torvalds read_lock(&dev_base_lock); 24021da177e4SLinus Torvalds return *pos ? igmp_mcf_get_idx(seq, *pos - 1) : SEQ_START_TOKEN; 24031da177e4SLinus Torvalds } 24041da177e4SLinus Torvalds 24051da177e4SLinus Torvalds static void *igmp_mcf_seq_next(struct seq_file *seq, void *v, loff_t *pos) 24061da177e4SLinus Torvalds { 24071da177e4SLinus Torvalds struct ip_sf_list *psf; 24081da177e4SLinus Torvalds if (v == SEQ_START_TOKEN) 24091da177e4SLinus Torvalds psf = igmp_mcf_get_first(seq); 24101da177e4SLinus Torvalds else 24111da177e4SLinus Torvalds psf = igmp_mcf_get_next(seq, v); 24121da177e4SLinus Torvalds ++*pos; 24131da177e4SLinus Torvalds return psf; 24141da177e4SLinus Torvalds } 24151da177e4SLinus Torvalds 24161da177e4SLinus Torvalds static void igmp_mcf_seq_stop(struct seq_file *seq, void *v) 24171da177e4SLinus Torvalds { 24181da177e4SLinus Torvalds struct igmp_mcf_iter_state *state = igmp_mcf_seq_private(seq); 24191da177e4SLinus Torvalds if (likely(state->im != NULL)) { 24201da177e4SLinus Torvalds spin_unlock_bh(&state->im->lock); 24211da177e4SLinus Torvalds state->im = NULL; 24221da177e4SLinus Torvalds } 24231da177e4SLinus Torvalds if (likely(state->idev != NULL)) { 24241da177e4SLinus Torvalds read_unlock(&state->idev->mc_list_lock); 24251da177e4SLinus Torvalds in_dev_put(state->idev); 24261da177e4SLinus Torvalds state->idev = NULL; 24271da177e4SLinus Torvalds } 24281da177e4SLinus Torvalds state->dev = NULL; 24291da177e4SLinus Torvalds read_unlock(&dev_base_lock); 24301da177e4SLinus Torvalds } 24311da177e4SLinus Torvalds 24321da177e4SLinus Torvalds static int igmp_mcf_seq_show(struct seq_file *seq, void *v) 24331da177e4SLinus Torvalds { 24341da177e4SLinus Torvalds struct ip_sf_list *psf = (struct ip_sf_list *)v; 24351da177e4SLinus Torvalds struct igmp_mcf_iter_state *state = igmp_mcf_seq_private(seq); 24361da177e4SLinus Torvalds 24371da177e4SLinus Torvalds if (v == SEQ_START_TOKEN) { 24381da177e4SLinus Torvalds seq_printf(seq, 24391da177e4SLinus Torvalds "%3s %6s " 24401da177e4SLinus Torvalds "%10s %10s %6s %6s\n", "Idx", 24411da177e4SLinus Torvalds "Device", "MCA", 24421da177e4SLinus Torvalds "SRC", "INC", "EXC"); 24431da177e4SLinus Torvalds } else { 24441da177e4SLinus Torvalds seq_printf(seq, 24451da177e4SLinus Torvalds "%3d %6.6s 0x%08x " 24461da177e4SLinus Torvalds "0x%08x %6lu %6lu\n", 24471da177e4SLinus Torvalds state->dev->ifindex, state->dev->name, 24481da177e4SLinus Torvalds ntohl(state->im->multiaddr), 24491da177e4SLinus Torvalds ntohl(psf->sf_inaddr), 24501da177e4SLinus Torvalds psf->sf_count[MCAST_INCLUDE], 24511da177e4SLinus Torvalds psf->sf_count[MCAST_EXCLUDE]); 24521da177e4SLinus Torvalds } 24531da177e4SLinus Torvalds return 0; 24541da177e4SLinus Torvalds } 24551da177e4SLinus Torvalds 24561da177e4SLinus Torvalds static struct seq_operations igmp_mcf_seq_ops = { 24571da177e4SLinus Torvalds .start = igmp_mcf_seq_start, 24581da177e4SLinus Torvalds .next = igmp_mcf_seq_next, 24591da177e4SLinus Torvalds .stop = igmp_mcf_seq_stop, 24601da177e4SLinus Torvalds .show = igmp_mcf_seq_show, 24611da177e4SLinus Torvalds }; 24621da177e4SLinus Torvalds 24631da177e4SLinus Torvalds static int igmp_mcf_seq_open(struct inode *inode, struct file *file) 24641da177e4SLinus Torvalds { 24651da177e4SLinus Torvalds struct seq_file *seq; 24661da177e4SLinus Torvalds int rc = -ENOMEM; 24671da177e4SLinus Torvalds struct igmp_mcf_iter_state *s = kmalloc(sizeof(*s), GFP_KERNEL); 24681da177e4SLinus Torvalds 24691da177e4SLinus Torvalds if (!s) 24701da177e4SLinus Torvalds goto out; 24711da177e4SLinus Torvalds rc = seq_open(file, &igmp_mcf_seq_ops); 24721da177e4SLinus Torvalds if (rc) 24731da177e4SLinus Torvalds goto out_kfree; 24741da177e4SLinus Torvalds 24751da177e4SLinus Torvalds seq = file->private_data; 24761da177e4SLinus Torvalds seq->private = s; 24771da177e4SLinus Torvalds memset(s, 0, sizeof(*s)); 24781da177e4SLinus Torvalds out: 24791da177e4SLinus Torvalds return rc; 24801da177e4SLinus Torvalds out_kfree: 24811da177e4SLinus Torvalds kfree(s); 24821da177e4SLinus Torvalds goto out; 24831da177e4SLinus Torvalds } 24841da177e4SLinus Torvalds 24851da177e4SLinus Torvalds static struct file_operations igmp_mcf_seq_fops = { 24861da177e4SLinus Torvalds .owner = THIS_MODULE, 24871da177e4SLinus Torvalds .open = igmp_mcf_seq_open, 24881da177e4SLinus Torvalds .read = seq_read, 24891da177e4SLinus Torvalds .llseek = seq_lseek, 24901da177e4SLinus Torvalds .release = seq_release_private, 24911da177e4SLinus Torvalds }; 24921da177e4SLinus Torvalds 24931da177e4SLinus Torvalds int __init igmp_mc_proc_init(void) 24941da177e4SLinus Torvalds { 24951da177e4SLinus Torvalds proc_net_fops_create("igmp", S_IRUGO, &igmp_mc_seq_fops); 24961da177e4SLinus Torvalds proc_net_fops_create("mcfilter", S_IRUGO, &igmp_mcf_seq_fops); 24971da177e4SLinus Torvalds return 0; 24981da177e4SLinus Torvalds } 24991da177e4SLinus Torvalds #endif 25001da177e4SLinus Torvalds 25011da177e4SLinus Torvalds EXPORT_SYMBOL(ip_mc_dec_group); 25021da177e4SLinus Torvalds EXPORT_SYMBOL(ip_mc_inc_group); 25031da177e4SLinus Torvalds EXPORT_SYMBOL(ip_mc_join_group); 2504