mcast.c
来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 2,496 行 · 第 1/4 页
C
2,496 行
static intmld_scount(struct ifmcaddr6 *pmc, int type, int gdeleted, int sdeleted){ struct ip6_sf_list *psf; int scount = 0; for (psf=pmc->mca_sources; psf; psf=psf->sf_next) { if (!is_in(pmc, psf, type, gdeleted, sdeleted)) continue; scount++; } return scount;}static struct sk_buff *mld_newpack(struct net_device *dev, int size){ struct sock *sk = igmp6_socket->sk; struct sk_buff *skb; struct mld2_report *pmr; struct in6_addr addr_buf; int err; u8 ra[8] = { IPPROTO_ICMPV6, 0, IPV6_TLV_ROUTERALERT, 2, 0, 0, IPV6_TLV_PADN, 0 }; /* we assume size > sizeof(ra) here */ skb = sock_alloc_send_skb(sk, size + LL_RESERVED_SPACE(dev), 1, &err); if (skb == 0) return NULL; skb_reserve(skb, LL_RESERVED_SPACE(dev)); if (dev->hard_header) { unsigned char ha[MAX_ADDR_LEN]; ndisc_mc_map(&mld2_all_mcr, ha, dev, 1); if (dev->hard_header(skb, dev, ETH_P_IPV6,ha,NULL,size) < 0) { kfree_skb(skb); return NULL; } } if (ipv6_get_lladdr(dev, &addr_buf)) { /* <draft-ietf-magma-mld-source-05.txt>: * use unspecified address as the source address * when a valid link-local address is not available. */ memset(&addr_buf, 0, sizeof(addr_buf)); } ip6_nd_hdr(sk, skb, dev, &addr_buf, &mld2_all_mcr, NEXTHDR_HOP, 0); memcpy(skb_put(skb, sizeof(ra)), ra, sizeof(ra)); pmr =(struct mld2_report *)skb_put(skb, sizeof(*pmr)); skb->h.raw = (unsigned char *)pmr; pmr->type = ICMPV6_MLD2_REPORT; pmr->resv1 = 0; pmr->csum = 0; pmr->resv2 = 0; pmr->ngrec = 0; return skb;}static void mld_sendpack(struct sk_buff *skb){ struct ipv6hdr *pip6 = skb->nh.ipv6h; struct mld2_report *pmr = (struct mld2_report *)skb->h.raw; int payload_len, mldlen; struct inet6_dev *idev = in6_dev_get(skb->dev); int err; IP6_INC_STATS(IPSTATS_MIB_OUTREQUESTS); payload_len = skb->tail - (unsigned char *)skb->nh.ipv6h - sizeof(struct ipv6hdr); mldlen = skb->tail - skb->h.raw; pip6->payload_len = htons(payload_len); pmr->csum = csum_ipv6_magic(&pip6->saddr, &pip6->daddr, mldlen, IPPROTO_ICMPV6, csum_partial(skb->h.raw, mldlen, 0)); err = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, skb->dev, dev_queue_xmit); if (!err) { ICMP6_INC_STATS(idev,ICMP6_MIB_OUTMSGS); IP6_INC_STATS(IPSTATS_MIB_OUTMCASTPKTS); } else IP6_INC_STATS(IPSTATS_MIB_OUTDISCARDS); if (likely(idev != NULL)) in6_dev_put(idev);}static int grec_size(struct ifmcaddr6 *pmc, int type, int gdel, int sdel){ return sizeof(struct mld2_grec) + 4*mld_scount(pmc,type,gdel,sdel);}static struct sk_buff *add_grhead(struct sk_buff *skb, struct ifmcaddr6 *pmc, int type, struct mld2_grec **ppgr){ struct net_device *dev = pmc->idev->dev; struct mld2_report *pmr; struct mld2_grec *pgr; if (!skb) skb = mld_newpack(dev, dev->mtu); if (!skb) return NULL; pgr = (struct mld2_grec *)skb_put(skb, sizeof(struct mld2_grec)); pgr->grec_type = type; pgr->grec_auxwords = 0; pgr->grec_nsrcs = 0; pgr->grec_mca = pmc->mca_addr; /* structure copy */ pmr = (struct mld2_report *)skb->h.raw; pmr->ngrec = htons(ntohs(pmr->ngrec)+1); *ppgr = pgr; return skb;}#define AVAILABLE(skb) ((skb) ? ((skb)->dev ? (skb)->dev->mtu - (skb)->len : \ skb_tailroom(skb)) : 0)static struct sk_buff *add_grec(struct sk_buff *skb, struct ifmcaddr6 *pmc, int type, int gdeleted, int sdeleted){ struct net_device *dev = pmc->idev->dev; struct mld2_report *pmr; struct mld2_grec *pgr = NULL; struct ip6_sf_list *psf, *psf_next, *psf_prev, **psf_list; int scount, first, isquery, truncate; if (pmc->mca_flags & MAF_NOREPORT) return skb; isquery = type == MLD2_MODE_IS_INCLUDE || type == MLD2_MODE_IS_EXCLUDE; truncate = type == MLD2_MODE_IS_EXCLUDE || type == MLD2_CHANGE_TO_EXCLUDE; psf_list = sdeleted ? &pmc->mca_tomb : &pmc->mca_sources; if (!*psf_list) { if (type == MLD2_ALLOW_NEW_SOURCES || type == MLD2_BLOCK_OLD_SOURCES) return skb; if (pmc->mca_crcount || isquery) { /* make sure we have room for group header and at * least one source. */ if (skb && AVAILABLE(skb) < sizeof(struct mld2_grec)+ sizeof(struct in6_addr)) { mld_sendpack(skb); skb = NULL; /* add_grhead will get a new one */ } skb = add_grhead(skb, pmc, type, &pgr); } return skb; } pmr = skb ? (struct mld2_report *)skb->h.raw : NULL; /* EX and TO_EX get a fresh packet, if needed */ if (truncate) { if (pmr && pmr->ngrec && AVAILABLE(skb) < grec_size(pmc, type, gdeleted, sdeleted)) { if (skb) mld_sendpack(skb); skb = mld_newpack(dev, dev->mtu); } } first = 1; scount = 0; psf_prev = NULL; for (psf=*psf_list; psf; psf=psf_next) { struct in6_addr *psrc; psf_next = psf->sf_next; if (!is_in(pmc, psf, type, gdeleted, sdeleted)) { psf_prev = psf; continue; } /* clear marks on query responses */ if (isquery) psf->sf_gsresp = 0; if (AVAILABLE(skb) < sizeof(*psrc) + first*sizeof(struct mld2_grec)) { if (truncate && !first) break; /* truncate these */ if (pgr) pgr->grec_nsrcs = htons(scount); if (skb) mld_sendpack(skb); skb = mld_newpack(dev, dev->mtu); first = 1; scount = 0; } if (first) { skb = add_grhead(skb, pmc, type, &pgr); first = 0; } psrc = (struct in6_addr *)skb_put(skb, sizeof(*psrc)); *psrc = psf->sf_addr; scount++; if ((type == MLD2_ALLOW_NEW_SOURCES || type == MLD2_BLOCK_OLD_SOURCES) && psf->sf_crcount) { psf->sf_crcount--; if ((sdeleted || gdeleted) && psf->sf_crcount == 0) { if (psf_prev) psf_prev->sf_next = psf->sf_next; else *psf_list = psf->sf_next; kfree(psf); continue; } } psf_prev = psf; } if (pgr) pgr->grec_nsrcs = htons(scount); if (isquery) pmc->mca_flags &= ~MAF_GSQUERY; /* clear query state */ return skb;}static void mld_send_report(struct inet6_dev *idev, struct ifmcaddr6 *pmc){ struct sk_buff *skb = NULL; int type; if (!pmc) { read_lock_bh(&idev->lock); for (pmc=idev->mc_list; pmc; pmc=pmc->next) { if (pmc->mca_flags & MAF_NOREPORT) continue; spin_lock_bh(&pmc->mca_lock); if (pmc->mca_sfcount[MCAST_EXCLUDE]) type = MLD2_MODE_IS_EXCLUDE; else type = MLD2_MODE_IS_INCLUDE; skb = add_grec(skb, pmc, type, 0, 0); spin_unlock_bh(&pmc->mca_lock); } read_unlock_bh(&idev->lock); } else { spin_lock_bh(&pmc->mca_lock); if (pmc->mca_sfcount[MCAST_EXCLUDE]) type = MLD2_MODE_IS_EXCLUDE; else type = MLD2_MODE_IS_INCLUDE; skb = add_grec(skb, pmc, type, 0, 0); spin_unlock_bh(&pmc->mca_lock); } if (skb) mld_sendpack(skb);}/* * remove zero-count source records from a source filter list */static void mld_clear_zeros(struct ip6_sf_list **ppsf){ struct ip6_sf_list *psf_prev, *psf_next, *psf; psf_prev = NULL; for (psf=*ppsf; psf; psf = psf_next) { psf_next = psf->sf_next; if (psf->sf_crcount == 0) { if (psf_prev) psf_prev->sf_next = psf->sf_next; else *ppsf = psf->sf_next; kfree(psf); } else psf_prev = psf; }}static void mld_send_cr(struct inet6_dev *idev){ struct ifmcaddr6 *pmc, *pmc_prev, *pmc_next; struct sk_buff *skb = NULL; int type, dtype; read_lock_bh(&idev->lock); write_lock_bh(&idev->mc_lock); /* deleted MCA's */ pmc_prev = NULL; for (pmc=idev->mc_tomb; pmc; pmc=pmc_next) { pmc_next = pmc->next; if (pmc->mca_sfmode == MCAST_INCLUDE) { type = MLD2_BLOCK_OLD_SOURCES; dtype = MLD2_BLOCK_OLD_SOURCES; skb = add_grec(skb, pmc, type, 1, 0); skb = add_grec(skb, pmc, dtype, 1, 1); } if (pmc->mca_crcount) { pmc->mca_crcount--; if (pmc->mca_sfmode == MCAST_EXCLUDE) { type = MLD2_CHANGE_TO_INCLUDE; skb = add_grec(skb, pmc, type, 1, 0); } if (pmc->mca_crcount == 0) { mld_clear_zeros(&pmc->mca_tomb); mld_clear_zeros(&pmc->mca_sources); } } if (pmc->mca_crcount == 0 && !pmc->mca_tomb && !pmc->mca_sources) { if (pmc_prev) pmc_prev->next = pmc_next; else idev->mc_tomb = pmc_next; in6_dev_put(pmc->idev); kfree(pmc); } else pmc_prev = pmc; } write_unlock_bh(&idev->mc_lock); /* change recs */ for (pmc=idev->mc_list; pmc; pmc=pmc->next) { spin_lock_bh(&pmc->mca_lock); if (pmc->mca_sfcount[MCAST_EXCLUDE]) { type = MLD2_BLOCK_OLD_SOURCES; dtype = MLD2_ALLOW_NEW_SOURCES; } else { type = MLD2_ALLOW_NEW_SOURCES; dtype = MLD2_BLOCK_OLD_SOURCES; } skb = add_grec(skb, pmc, type, 0, 0); skb = add_grec(skb, pmc, dtype, 0, 1); /* deleted sources */ /* filter mode changes */ if (pmc->mca_crcount) { pmc->mca_crcount--; if (pmc->mca_sfmode == MCAST_EXCLUDE) type = MLD2_CHANGE_TO_EXCLUDE; else type = MLD2_CHANGE_TO_INCLUDE; skb = add_grec(skb, pmc, type, 0, 0); } spin_unlock_bh(&pmc->mca_lock); } read_unlock_bh(&idev->lock); if (!skb) return; (void) mld_sendpack(skb);}static void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type){ struct sock *sk = igmp6_socket->sk; struct inet6_dev *idev; struct sk_buff *skb; struct icmp6hdr *hdr; struct in6_addr *snd_addr; struct in6_addr *addrp; struct in6_addr addr_buf; struct in6_addr all_routers; int err, len, payload_len, full_len; u8 ra[8] = { IPPROTO_ICMPV6, 0, IPV6_TLV_ROUTERALERT, 2, 0, 0, IPV6_TLV_PADN, 0 }; IP6_INC_STATS(IPSTATS_MIB_OUTREQUESTS); snd_addr = addr; if (type == ICMPV6_MGM_REDUCTION) { snd_addr = &all_routers; ipv6_addr_all_routers(&all_routers); } len = sizeof(struct icmp6hdr) + sizeof(struct in6_addr); payload_len = len + sizeof(ra); full_len = sizeof(struct ipv6hdr) + payload_len; skb = sock_alloc_send_skb(sk, LL_RESERVED_SPACE(dev) + full_len, 1, &err); if (skb == NULL) { IP6_INC_STATS(IPSTATS_MIB_OUTDISCARDS); return; } skb_reserve(skb, LL_RESERVED_SPACE(dev)); if (dev->hard_header) { unsigned char ha[MAX_ADDR_LEN]; ndisc_mc_map(snd_addr, ha, dev, 1); if (dev->hard_header(skb, dev, ETH_P_IPV6, ha, NULL, full_len) < 0) goto out; } if (ipv6_get_lladdr(dev, &addr_buf)) { /* <draft-ietf-magma-mld-source-05.txt>: * use unspecified address as the source address * when a valid link-local address is not available. */ memset(&addr_buf, 0, sizeof(addr_buf)); } ip6_nd_hdr(sk, skb, dev, &addr_buf, snd_addr, NEXTHDR_HOP, payload_len); memcpy(skb_put(skb, sizeof(ra)), ra, sizeof(ra)); hdr = (struct icmp6hdr *) skb_put(skb, sizeof(struct icmp6hdr)); memset(hdr, 0, sizeof(struct icmp6hdr)); hdr->icmp6_type = type; addrp = (struct in6_addr *) skb_put(skb, sizeof(struct in6_addr)); ipv6_addr_copy(addrp, addr); hdr->icmp6_cksum = csum_ipv6_magic(&addr_buf, snd_addr, len, IPPROTO_ICMPV6, csum_partial((__u8 *) hdr, len, 0)); idev = in6_dev_get(skb->dev); err = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, skb->dev, dev_queue_xmit); if (!err) { if (type == ICMPV6_MGM_REDUCTION) ICMP6_INC_STATS(idev, ICMP6_MIB_OUTGROUPMEMBREDUCTIONS); else ICMP6_INC_STATS(idev, ICMP6_MIB_OUTGROUPMEMBRESPONSES); ICMP6_INC_STATS(idev, ICMP6_MIB_OUTMSGS); IP6_INC_STATS(IPSTATS_MIB_OUTMCASTPKTS); } else IP6_INC_STATS(IPSTATS_MIB_OUTDISCARDS); if (likely(idev != NULL)) in6_dev_put(idev); return;out: IP6_INC_STATS(IPSTATS_MIB_OUTDISCARDS); kfree_skb(skb);}static int ip6_mc_del1_src(struct ifmcaddr6 *pmc, int sfmode, struct in6_addr *psfsrc){ struct ip6_sf_list *psf, *psf_prev; int rv = 0; psf_prev = NULL; for (psf=pmc->mca_sources; psf; psf=psf->sf_next) { if (ipv6_addr_cmp(&psf->sf_addr, psfsrc) == 0) 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[MCAST_INCLUDE] && !psf->sf_count[MCAST_EXCLUDE]) { struct inet6_dev *idev = pmc->idev; /* no more filters for this source */ if (psf_prev) psf_prev->sf_next = psf->sf_next; else pmc->mca_sources = psf->sf_next; if (psf->sf_oldin && !(pmc->mca_flags & MAF_NOREPORT) && !MLD_V1_SEEN(idev)) { psf->sf_crcount = idev->mc_qrv; psf->sf_next = pmc->mca_tomb; pmc->mca_tomb = psf; rv = 1; } else kfree(psf); } return rv;}int ip6_mc_del_src(struct inet6_dev *idev, struct in6_addr *pmca, int sfmode, int sfcount, struct in6_addr *psfsrc, int delta){ struct ifmcaddr6 *pmc; int changerec = 0; int i, err; if (!idev) return -ENODEV; read_lock_bh(&idev->lock); for (pmc=idev->mc_list; pmc; pmc=pmc->next) { if (ipv6_addr_cmp(pmca, &pmc->mca_addr) == 0) break; } if (!pmc) { /* MCA not found?? bug */ read_unlock_bh(&idev->lock); return -ESRCH; } spin_lock_bh(&pmc->mca_lock); sf_markstate(pmc); if (!delta) { if (!pmc->mca_sfcount[sfmode]) { spin_unlock_bh(&pmc->mca_lock); read_unlock_bh(&idev->lock); return -EINVAL; } pmc->mca_sfcount[sfmode]--; } err = 0; for (i=0; i<sfcount; i++) { int rv = ip6_mc_del1_src(pmc, sfmode, &psfsrc[i]); changerec |= rv > 0; if (!err && rv < 0) err = rv; } if (pmc->mca_sfmode == MCAST_EXCLUDE && pmc->mca_sfcount[MCAST_EXCLUDE] == 0 && pmc->mca_sfcount[MCAST_INCLUDE]) { struct ip6_sf_list *psf; /* filter mode change */ pmc->mca_sfmode = MCAST_INCLUDE; pmc->mca_crcount = idev->mc_qrv; idev->mc_ifc_count = pmc->mca_crcount; for (psf=pmc->mca_sources; psf; psf = psf->sf_next) psf->sf_crcount = 0; mld_ifc_event(pmc->idev); } else if (sf_setstate(pmc) || changerec) mld_ifc_event(pmc->idev); spin_unlock_bh(&pmc->mca_lock); read_unlock_bh(&idev->lock); return err;}/* * Add multicast single-source filter to the interface list */static int ip6_mc_add1_src(struct ifmcaddr6 *pmc, int sfmode, struct in6_addr *psfsrc, int delta){ struct ip6_sf_list *psf, *psf_prev; psf_prev = NULL; for (psf=pmc->mca_sources; psf; psf=psf->sf_next) { if (ipv6_addr_cmp(&psf->sf_addr, psfsrc) == 0) break; psf_prev = psf; } if (!psf) { psf = (struct ip6_sf_list *)kmalloc(sizeof(*psf), GFP_ATOMIC); if (!psf) return -ENOBUFS; memset(psf, 0, sizeof(*psf)); psf->sf_addr = *psfsrc; if (psf_prev) { psf_prev->sf_next = psf; } else pmc->mca_sources = psf; } psf->sf_count[sfmode]++; return 0;}static void sf_markstate(struct ifmcaddr6 *pmc){ struct ip6_sf_list *psf; int mca_xcount = pmc->mca_sfcount[MCAST_EXCLUDE]; for (psf=pmc->mca_sources; psf; psf=psf->sf_next) if (pmc->mca_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 ifmcaddr6 *pmc){ struct ip6_sf_list *psf; int mca_xcount = pmc->mca_sfcount[MCAST_EXCLUDE]; int qrv = pmc->idev->mc_qrv; int new_in, rv; rv = 0; for (psf=pmc->mca_sources; psf; psf=psf->sf_next) { if (pmc->mca_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;}/* * Add multicast source filter list to the interface list */int ip6_mc_add_src(struct inet6_dev *idev, struct in6_addr *pmca, int sfmode, int sfcount, struct in6_addr *psfsrc, int delta){ struct ifmcaddr6 *pmc; int isexclude; int i, err; if (!idev) return -ENODEV; read_lock_bh(&idev->lock); for (pmc=idev->mc_list; pmc; pmc=pmc->next) { if (ipv6_addr_cmp(pmca, &pmc->mca_addr) == 0) break; } if (!pmc) { /* MCA not found?? bug */ read_unlock_bh(&idev->lock); return -ESRCH; } spin_lock_bh(&pmc->mca_lock); sf_markstate(pmc);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?