📄 igmp.c
字号:
{ 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 in_dev->mc_list_lock = RW_LOCK_UNLOCKED; in_dev->mc_tomb_lock = SPIN_LOCK_UNLOCKED;}/* 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(dev); } return idev;}/* * Join a socket to a group */int sysctl_igmp_max_memberships = IP_MAX_MEMBERSHIPS;int sysctl_igmp_max_msf = IP_MAX_MSF;static int ip_mc_del1_src(struct ip_mc_list *pmc, int sfmode, __u32 *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)#endifint ip_mc_del_src(struct in_device *in_dev, __u32 *pmca, int sfmode, int sfcount, __u32 *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, __u32 *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) { psf = (struct ip_sf_list *)kmalloc(sizeof(*psf), GFP_ATOMIC); if (!psf) return -ENOBUFS; memset(psf, 0, sizeof(*psf)); psf->sf_inaddr = *psfsrc; if (psf_prev) { psf_prev->sf_next = psf; } else pmc->sources = psf; } psf->sf_count[sfmode]++; if (psf->sf_count[sfmode] == 1) { ip_rt_multicast_event(pmc->interface); } return 0;}#ifdef CONFIG_IP_MULTICASTstatic void sf_markstate(struct ip_mc_list *pmc){ struct ip_sf_list *psf; int mca_xcount = pmc->sfcount[MCAST_EXCLUDE]; for (psf=pmc->sources; psf; psf=psf->sf_next) if (pmc->sfcount[MCAST_EXCLUDE]) { psf->sf_oldin = mca_xcount == psf->sf_count[MCAST_EXCLUDE] && !psf->sf_count[MCAST_INCLUDE]; } else psf->sf_oldin = psf->sf_count[MCAST_INCLUDE] != 0;}static int sf_setstate(struct ip_mc_list *pmc){ struct ip_sf_list *psf; int mca_xcount = pmc->sfcount[MCAST_EXCLUDE]; int qrv = pmc->interface->mr_qrv; int new_in, rv; rv = 0; for (psf=pmc->sources; psf; psf=psf->sf_next) { if (pmc->sfcount[MCAST_EXCLUDE]) { new_in = mca_xcount == psf->sf_count[MCAST_EXCLUDE] && !psf->sf_count[MCAST_INCLUDE]; } else new_in = psf->sf_count[MCAST_INCLUDE] != 0; if (new_in != psf->sf_oldin) { psf->sf_crcount = qrv; rv++; } } return rv;}#endif/* * Add multicast source filter list to the interface list */int ip_mc_add_src(struct in_device *in_dev, __u32 *pmca, int sfmode, int sfcount, __u32 *psfsrc, int delta){ struct ip_mc_list *pmc; int isexclude; 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 isexclude = pmc->sfmode == MCAST_EXCLUDE; if (!delta) pmc->sfcount[sfmode]++; err = 0; for (i=0; i<sfcount; i++) { err = ip_mc_add1_src(pmc, sfmode, &psfsrc[i], delta); if (err) break; } if (err) { int j; pmc->sfcount[sfmode]--; for (j=0; j<i; j++) (void) ip_mc_del1_src(pmc, sfmode, &psfsrc[i]); } else if (isexclude != (pmc->sfcount[MCAST_EXCLUDE] != 0)) {#ifdef CONFIG_IP_MULTICAST struct in_device *in_dev = pmc->interface; struct ip_sf_list *psf;#endif /* filter mode change */ if (pmc->sfcount[MCAST_EXCLUDE]) pmc->sfmode = MCAST_EXCLUDE; else if (pmc->sfcount[MCAST_INCLUDE]) pmc->sfmode = MCAST_INCLUDE;#ifdef CONFIG_IP_MULTICAST /* else no filters; keep old mode for reports */ 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(in_dev); } else if (sf_setstate(pmc)) { igmp_ifc_event(in_dev);#endif } spin_unlock_bh(&pmc->lock); return err;}static void ip_mc_clear_src(struct ip_mc_list *pmc){ struct ip_sf_list *psf, *nextpsf; for (psf=pmc->tomb; psf; psf=nextpsf) { nextpsf = psf->sf_next; kfree(psf); } pmc->tomb = NULL; for (psf=pmc->sources; psf; psf=nextpsf) { nextpsf = psf->sf_next; kfree(psf); } pmc->sources = NULL; pmc->sfmode = MCAST_EXCLUDE; pmc->sfcount[MCAST_EXCLUDE] = 0; pmc->sfcount[MCAST_EXCLUDE] = 1;}/* * Join a multicast group */int ip_mc_join_group(struct sock *sk , struct ip_mreqn *imr){ int err; u32 addr = imr->imr_multiaddr.s_addr; struct ip_mc_socklist *iml, *i; struct in_device *in_dev; struct inet_opt *inet = inet_sk(sk); int count = 0; if (!MULTICAST(addr)) return -EINVAL; rtnl_shlock(); in_dev = ip_mc_find_dev(imr); if (!in_dev) { iml = NULL; err = -ENODEV; goto done; } iml = (struct ip_mc_socklist *)sock_kmalloc(sk, sizeof(*iml), GFP_KERNEL); err = -EADDRINUSE; for (i = inet->mc_list; i; i = i->next) { if (memcmp(&i->multi, imr, sizeof(*imr)) == 0) { /* New style additions are reference counted */ if (imr->imr_address.s_addr == 0) { i->count++; err = 0; } goto done; } count++; } err = -ENOBUFS; if (iml == NULL || count >= sysctl_igmp_max_memberships) goto done; memcpy(&iml->multi, imr, sizeof(*imr)); iml->next = inet->mc_list; iml->count = 1; iml->sflist = NULL; iml->sfmode = MCAST_EXCLUDE; inet->mc_list = iml; ip_mc_inc_group(in_dev, addr); iml = NULL; err = 0;done: rtnl_shunlock(); if (iml) sock_kfree_s(sk, iml, sizeof(*iml)); return err;}int ip_mc_leave_src(struct sock *sk, struct ip_mc_socklist *iml, struct in_device *in_dev){ int err; if (iml->sflist == 0) { /* any-source empty exclude case */ return ip_mc_del_src(in_dev, &iml->multi.imr_multiaddr.s_addr, iml->sfmode, 0, NULL, 0); } err = ip_mc_del_src(in_dev, &iml->multi.imr_multiaddr.s_addr, iml->sfmode, iml->sflist->sl_count, iml->sflist->sl_addr, 0); sock_kfree_s(sk, iml->sflist, IP_SFLSIZE(iml->sflist->sl_max)); iml->sflist = NULL; return err;}/* * Ask a socket to leave a group. */int ip_mc_leave_group(struct sock *sk, struct ip_mreqn *imr){ struct inet_opt *inet = inet_sk(sk); struct ip_mc_socklist *iml, **imlp; rtnl_lock(); for (imlp = &inet->mc_list; (iml = *imlp) != NULL; imlp = &iml->next) { if (iml->multi.imr_multiaddr.s_addr==imr->imr_multiaddr.s_addr && iml->multi.imr_address.s_addr==imr->imr_address.s_addr && (!imr->imr_ifindex || iml->multi.imr_ifindex==imr->imr_ifindex)) { struct in_device *in_dev; in_dev = inetdev_by_index(iml->multi.imr_ifindex); if (in_dev) (void) ip_mc_leave_src(sk, iml, in_dev); if (--iml->count) { rtnl_unlock(); if (in_dev) in_dev_put(in_dev); return 0; } *imlp = iml->next; if (in_dev) { ip_mc_dec_group(in_dev, imr->imr_multiaddr.s_addr); in_dev_put(in_dev); } rtnl_unlock(); sock_kfree_s(sk, iml, sizeof(*iml)); return 0; } } rtnl_unlock(); return -EADDRNOTAVAIL;}int ip_mc_source(int add, int omode, struct sock *sk, struct ip_mreq_source *mreqs, int ifindex){ int err; struct ip_mreqn imr; u32 addr = mreqs->imr_multiaddr; struct ip_mc_socklist *pmc; struct in_device *in_dev = NULL; struct inet_opt *inet = inet_sk(sk); struct ip_sf_socklist *psl; int i, j, rv; if (!MULTICAST(addr)) return -EINVAL; rtnl_shlock(); imr.imr_multiaddr.s_addr = mreqs->imr_multiaddr; imr.imr_address.s_addr = mreqs->imr_interface; imr.imr_ifindex = ifindex; in_dev = ip_mc_find_dev(&imr); if (!in_dev) { err = -ENODEV; goto done; } err = -EADDRNOTAVAIL; for (pmc=inet->mc_list; pmc; pmc=pmc->next) { if (memcmp(&pmc->multi, mreqs, 2*sizeof(__u32)) == 0) break; } if (!pmc) /* must have a prior join */ goto done; /* if a source filter was set, must be the same mode as before */ if (pmc->sflist) { if (pmc->sfmode != omode) goto done; } else if (pmc->sfmode != omode) { /* allow mode switches for empty-set filters */ ip_mc_add_src(in_dev, &mreqs->imr_multiaddr, omode, 0, NULL, 0); ip_mc_del_src(in_dev, &mreqs->imr_multiaddr, pmc->sfmode, 0, NULL, 0); pmc->sfmode = omode; } psl = pmc->sflist; if (!add) { if (!psl) goto done; rv = !0; for (i=0; i<psl->sl_count; i++) { rv = memcmp(&psl->sl_addr, &mreqs->imr_multiaddr, sizeof(__u32)); if (rv >= 0) break; } if (!rv) /* source not found */ goto done; /* update the interface filter */ ip_mc_del_src(in_dev, &mreqs->imr_multiaddr, omode, 1, &mreqs->imr_sourceaddr, 1); for (j=i+1; j<psl->sl_count; j++) psl->sl_addr[j-1] = psl->sl_addr[j]; psl->sl_count--; err = 0; goto done; } /* else, add a new source to the filter */ if (psl && psl->sl_count >= sysctl_igmp_max_msf) { err = -ENOBUFS; goto done; } if (!psl || psl->sl_count == psl->sl_max) { struct ip_sf_socklist *newpsl; int count = IP_SFBLOCK; if (psl) count += psl->sl_max; newpsl = (struct ip_sf_socklist *)sock_kmalloc(sk, IP_SFLSIZE(count), GFP_KERNEL); if (!newpsl) { err = -ENOBUFS; goto done; } newpsl->sl_max = count; newpsl->sl_count = count - IP_SFBLOCK; if (psl) { for (i=0; i<psl->sl_count; i++) newpsl->sl_addr[i] = psl->sl_addr[i]; sock_kfree_s(sk, psl, IP_SFLSIZE(psl->sl_max)); } pmc->sflist = psl = newpsl; } rv = 1; /* > 0 for insert logic below if sl_count is 0 */ for (i=0; i<psl->sl_count; i++) { rv = memcmp(&psl->sl_addr, &mreqs->imr_multiaddr, sizeof(__u32)); if (rv >= 0) break; } if (rv == 0) /* address already there is an error */ goto done; for (j=psl->sl_count-1; j>=i; j--) psl->sl_addr[j+1] = psl->sl_addr[j]; psl->sl_addr[i] = mreqs->imr_sourceaddr; psl->sl_count++; err = 0; /* update the interface list */ ip_mc_add_src(in_dev, &mreqs->imr_multiaddr, omode, 1, &mreqs->imr_sourceaddr, 1);done: rtnl_shunlock(); return err;}int ip_mc_msfilter(struct sock *sk, struct ip_msfilter *msf, int ifindex){ int err; struct ip_mreqn imr; u32 addr = msf->imsf_multiaddr; struct ip_mc_socklist *pmc; struct in_device *in_dev; struct inet_opt *inet = inet_sk(sk); struct ip_sf_socklist *newpsl, *psl;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -