📄 igmp.c
字号:
return; spin_lock_bh(&im->lock); pmc->interface = im->interface; in_dev_hold(in_dev); pmc->multiaddr = im->multiaddr; pmc->crcount = in_dev->mr_qrv ? in_dev->mr_qrv : IGMP_Unsolicited_Report_Count; pmc->sfmode = im->sfmode; if (pmc->sfmode == MCAST_INCLUDE) { struct ip_sf_list *psf; pmc->tomb = im->tomb; pmc->sources = im->sources; im->tomb = im->sources = NULL; for (psf=pmc->sources; psf; psf=psf->sf_next) psf->sf_crcount = pmc->crcount; } spin_unlock_bh(&im->lock); spin_lock_bh(&in_dev->mc_tomb_lock); pmc->next = in_dev->mc_tomb; in_dev->mc_tomb = pmc; spin_unlock_bh(&in_dev->mc_tomb_lock);}static void igmpv3_del_delrec(struct in_device *in_dev, __be32 multiaddr){ struct ip_mc_list *pmc, *pmc_prev; struct ip_sf_list *psf, *psf_next; spin_lock_bh(&in_dev->mc_tomb_lock); pmc_prev = NULL; for (pmc=in_dev->mc_tomb; pmc; pmc=pmc->next) { if (pmc->multiaddr == multiaddr) break; pmc_prev = pmc; } if (pmc) { if (pmc_prev) pmc_prev->next = pmc->next; else in_dev->mc_tomb = pmc->next; } spin_unlock_bh(&in_dev->mc_tomb_lock); if (pmc) { for (psf=pmc->tomb; psf; psf=psf_next) { psf_next = psf->sf_next; kfree(psf); } in_dev_put(pmc->interface); kfree(pmc); }}static void igmpv3_clear_delrec(struct in_device *in_dev){ struct ip_mc_list *pmc, *nextpmc; spin_lock_bh(&in_dev->mc_tomb_lock); pmc = in_dev->mc_tomb; in_dev->mc_tomb = NULL; spin_unlock_bh(&in_dev->mc_tomb_lock); for (; pmc; pmc = nextpmc) { nextpmc = pmc->next; ip_mc_clear_src(pmc); in_dev_put(pmc->interface); kfree(pmc); } /* clear dead sources, too */ read_lock(&in_dev->mc_list_lock); for (pmc=in_dev->mc_list; pmc; pmc=pmc->next) { struct ip_sf_list *psf, *psf_next; spin_lock_bh(&pmc->lock); psf = pmc->tomb; pmc->tomb = NULL; spin_unlock_bh(&pmc->lock); for (; psf; psf=psf_next) { psf_next = psf->sf_next; kfree(psf); } } read_unlock(&in_dev->mc_list_lock);}#endifstatic void igmp_group_dropped(struct ip_mc_list *im){ struct in_device *in_dev = im->interface;#ifdef CONFIG_IP_MULTICAST int reporter;#endif if (im->loaded) { im->loaded = 0; ip_mc_filter_del(in_dev, im->multiaddr); }#ifdef CONFIG_IP_MULTICAST if (im->multiaddr == IGMP_ALL_HOSTS) return; reporter = im->reporter; igmp_stop_timer(im); if (!in_dev->dead) { if (IGMP_V1_SEEN(in_dev)) goto done; if (IGMP_V2_SEEN(in_dev)) { if (reporter) igmp_send_report(in_dev, im, IGMP_HOST_LEAVE_MESSAGE); goto done; } /* IGMPv3 */ igmpv3_add_delrec(in_dev, im); igmp_ifc_event(in_dev); }done:#endif ip_mc_clear_src(im);}static void igmp_group_added(struct ip_mc_list *im){ struct in_device *in_dev = im->interface; if (im->loaded == 0) { im->loaded = 1; ip_mc_filter_add(in_dev, im->multiaddr); }#ifdef CONFIG_IP_MULTICAST if (im->multiaddr == IGMP_ALL_HOSTS) return; if (in_dev->dead) return; if (IGMP_V1_SEEN(in_dev) || IGMP_V2_SEEN(in_dev)) { spin_lock_bh(&im->lock); igmp_start_timer(im, IGMP_Initial_Report_Delay); spin_unlock_bh(&im->lock); return; } /* else, v3 */ im->crcount = in_dev->mr_qrv ? in_dev->mr_qrv : IGMP_Unsolicited_Report_Count; igmp_ifc_event(in_dev);#endif}/* * Multicast list managers *//* * A socket has joined a multicast group on device dev. */void ip_mc_inc_group(struct in_device *in_dev, __be32 addr){ struct ip_mc_list *im; ASSERT_RTNL(); for (im=in_dev->mc_list; im; im=im->next) { if (im->multiaddr == addr) { im->users++; ip_mc_add_src(in_dev, &addr, MCAST_EXCLUDE, 0, NULL, 0); goto out; } } im = kmalloc(sizeof(*im), GFP_KERNEL); if (!im) goto out; im->users=1; im->interface=in_dev; in_dev_hold(in_dev); im->multiaddr=addr; /* initial mode is (EX, empty) */ im->sfmode = MCAST_EXCLUDE; im->sfcount[MCAST_INCLUDE] = 0; im->sfcount[MCAST_EXCLUDE] = 1; im->sources = NULL; im->tomb = NULL; im->crcount = 0; atomic_set(&im->refcnt, 1); spin_lock_init(&im->lock);#ifdef CONFIG_IP_MULTICAST im->tm_running=0; init_timer(&im->timer); im->timer.data=(unsigned long)im; im->timer.function=&igmp_timer_expire; im->unsolicit_count = IGMP_Unsolicited_Report_Count; im->reporter = 0; im->gsquery = 0;#endif im->loaded = 0; write_lock_bh(&in_dev->mc_list_lock); im->next=in_dev->mc_list; in_dev->mc_list=im; write_unlock_bh(&in_dev->mc_list_lock);#ifdef CONFIG_IP_MULTICAST igmpv3_del_delrec(in_dev, im->multiaddr);#endif igmp_group_added(im); if (!in_dev->dead) ip_rt_multicast_event(in_dev);out: return;}/* * Resend IGMP JOIN report; used for bonding. */void ip_mc_rejoin_group(struct ip_mc_list *im){#ifdef CONFIG_IP_MULTICAST struct in_device *in_dev = im->interface; if (im->multiaddr == IGMP_ALL_HOSTS) return; if (IGMP_V1_SEEN(in_dev) || IGMP_V2_SEEN(in_dev)) { igmp_mod_timer(im, IGMP_Initial_Report_Delay); return; } /* else, v3 */ im->crcount = in_dev->mr_qrv ? in_dev->mr_qrv : IGMP_Unsolicited_Report_Count; igmp_ifc_event(in_dev);#endif}/* * A socket has left a multicast group on device dev */void ip_mc_dec_group(struct in_device *in_dev, __be32 addr){ struct ip_mc_list *i, **ip; ASSERT_RTNL(); for (ip=&in_dev->mc_list; (i=*ip)!=NULL; ip=&i->next) { if (i->multiaddr==addr) { if (--i->users == 0) { write_lock_bh(&in_dev->mc_list_lock); *ip = i->next; write_unlock_bh(&in_dev->mc_list_lock); igmp_group_dropped(i); if (!in_dev->dead) ip_rt_multicast_event(in_dev); ip_ma_put(i); return; } break; } }}/* Device going down */void ip_mc_down(struct in_device *in_dev){ struct ip_mc_list *i; ASSERT_RTNL(); for (i=in_dev->mc_list; i; i=i->next) igmp_group_dropped(i);#ifdef CONFIG_IP_MULTICAST in_dev->mr_ifc_count = 0; if (del_timer(&in_dev->mr_ifc_timer)) __in_dev_put(in_dev); in_dev->mr_gq_running = 0; if (del_timer(&in_dev->mr_gq_timer)) __in_dev_put(in_dev); igmpv3_clear_delrec(in_dev);#endif ip_mc_dec_group(in_dev, IGMP_ALL_HOSTS);}void ip_mc_init_dev(struct in_device *in_dev){ ASSERT_RTNL(); in_dev->mc_tomb = NULL;#ifdef CONFIG_IP_MULTICAST in_dev->mr_gq_running = 0; init_timer(&in_dev->mr_gq_timer); in_dev->mr_gq_timer.data=(unsigned long) in_dev; in_dev->mr_gq_timer.function=&igmp_gq_timer_expire; in_dev->mr_ifc_count = 0; init_timer(&in_dev->mr_ifc_timer); in_dev->mr_ifc_timer.data=(unsigned long) in_dev; in_dev->mr_ifc_timer.function=&igmp_ifc_timer_expire; in_dev->mr_qrv = IGMP_Unsolicited_Report_Count;#endif rwlock_init(&in_dev->mc_list_lock); spin_lock_init(&in_dev->mc_tomb_lock);}/* Device going up */void ip_mc_up(struct in_device *in_dev){ struct ip_mc_list *i; ASSERT_RTNL(); ip_mc_inc_group(in_dev, IGMP_ALL_HOSTS); for (i=in_dev->mc_list; i; i=i->next) igmp_group_added(i);}/* * Device is about to be destroyed: clean up. */void ip_mc_destroy_dev(struct in_device *in_dev){ struct ip_mc_list *i; ASSERT_RTNL(); /* Deactivate timers */ ip_mc_down(in_dev); write_lock_bh(&in_dev->mc_list_lock); while ((i = in_dev->mc_list) != NULL) { in_dev->mc_list = i->next; write_unlock_bh(&in_dev->mc_list_lock); igmp_group_dropped(i); ip_ma_put(i); write_lock_bh(&in_dev->mc_list_lock); } write_unlock_bh(&in_dev->mc_list_lock);}static struct in_device * ip_mc_find_dev(struct ip_mreqn *imr){ struct flowi fl = { .nl_u = { .ip4_u = { .daddr = imr->imr_multiaddr.s_addr } } }; struct rtable *rt; struct net_device *dev = NULL; struct in_device *idev = NULL; if (imr->imr_ifindex) { idev = inetdev_by_index(imr->imr_ifindex); if (idev) __in_dev_put(idev); return idev; } if (imr->imr_address.s_addr) { dev = ip_dev_find(imr->imr_address.s_addr); if (!dev) return NULL; dev_put(dev); } if (!dev && !ip_route_output_key(&rt, &fl)) { dev = rt->u.dst.dev; ip_rt_put(rt); } if (dev) { imr->imr_ifindex = dev->ifindex; idev = __in_dev_get_rtnl(dev); } return idev;}/* * Join a socket to a group */int sysctl_igmp_max_memberships __read_mostly = IP_MAX_MEMBERSHIPS;int sysctl_igmp_max_msf __read_mostly = IP_MAX_MSF;static int ip_mc_del1_src(struct ip_mc_list *pmc, int sfmode, __be32 *psfsrc){ struct ip_sf_list *psf, *psf_prev; int rv = 0; psf_prev = NULL; for (psf=pmc->sources; psf; psf=psf->sf_next) { if (psf->sf_inaddr == *psfsrc) break; psf_prev = psf; } if (!psf || psf->sf_count[sfmode] == 0) { /* source filter not found, or count wrong => bug */ return -ESRCH; } psf->sf_count[sfmode]--; if (psf->sf_count[sfmode] == 0) { ip_rt_multicast_event(pmc->interface); } if (!psf->sf_count[MCAST_INCLUDE] && !psf->sf_count[MCAST_EXCLUDE]) {#ifdef CONFIG_IP_MULTICAST struct in_device *in_dev = pmc->interface;#endif /* no more filters for this source */ if (psf_prev) psf_prev->sf_next = psf->sf_next; else pmc->sources = psf->sf_next;#ifdef CONFIG_IP_MULTICAST if (psf->sf_oldin && !IGMP_V1_SEEN(in_dev) && !IGMP_V2_SEEN(in_dev)) { psf->sf_crcount = in_dev->mr_qrv ? in_dev->mr_qrv : IGMP_Unsolicited_Report_Count; psf->sf_next = pmc->tomb; pmc->tomb = psf; rv = 1; } else#endif kfree(psf); } return rv;}#ifndef CONFIG_IP_MULTICAST#define igmp_ifc_event(x) do { } while (0)#endifstatic int ip_mc_del_src(struct in_device *in_dev, __be32 *pmca, int sfmode, int sfcount, __be32 *psfsrc, int delta){ struct ip_mc_list *pmc; int changerec = 0; int i, err; if (!in_dev) return -ENODEV; read_lock(&in_dev->mc_list_lock); for (pmc=in_dev->mc_list; pmc; pmc=pmc->next) { if (*pmca == pmc->multiaddr) break; } if (!pmc) { /* MCA not found?? bug */ read_unlock(&in_dev->mc_list_lock); return -ESRCH; } spin_lock_bh(&pmc->lock); read_unlock(&in_dev->mc_list_lock);#ifdef CONFIG_IP_MULTICAST sf_markstate(pmc);#endif if (!delta) { err = -EINVAL; if (!pmc->sfcount[sfmode]) goto out_unlock; pmc->sfcount[sfmode]--; } err = 0; for (i=0; i<sfcount; i++) { int rv = ip_mc_del1_src(pmc, sfmode, &psfsrc[i]); changerec |= rv > 0; if (!err && rv < 0) err = rv; } if (pmc->sfmode == MCAST_EXCLUDE && pmc->sfcount[MCAST_EXCLUDE] == 0 && pmc->sfcount[MCAST_INCLUDE]) {#ifdef CONFIG_IP_MULTICAST struct ip_sf_list *psf;#endif /* filter mode change */ pmc->sfmode = MCAST_INCLUDE;#ifdef CONFIG_IP_MULTICAST pmc->crcount = in_dev->mr_qrv ? in_dev->mr_qrv : IGMP_Unsolicited_Report_Count; in_dev->mr_ifc_count = pmc->crcount; for (psf=pmc->sources; psf; psf = psf->sf_next) psf->sf_crcount = 0; igmp_ifc_event(pmc->interface); } else if (sf_setstate(pmc) || changerec) { igmp_ifc_event(pmc->interface);#endif }out_unlock: spin_unlock_bh(&pmc->lock); return err;}/* * Add multicast single-source filter to the interface list */static int ip_mc_add1_src(struct ip_mc_list *pmc, int sfmode, __be32 *psfsrc, int delta){ struct ip_sf_list *psf, *psf_prev; psf_prev = NULL; for (psf=pmc->sources; psf; psf=psf->sf_next) { if (psf->sf_inaddr == *psfsrc) break; psf_prev = psf; } if (!psf) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -