mcast.c
来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 2,496 行 · 第 1/4 页
C
2,496 行
/* * Multicast support for IPv6 * Linux INET6 implementation * * Authors: * Pedro Roque <roque@di.fc.ul.pt> * * $Id: mcast.c,v 1.40 2002/02/08 03:57:19 davem Exp $ * * Based on linux/ipv4/igmp.c and linux/ipv4/ip_sockglue.c * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. *//* Changes: * * yoshfuji : fix format of router-alert option * YOSHIFUJI Hideaki @USAGI: * Fixed source address for MLD message based on * <draft-ietf-magma-mld-source-05.txt>. * YOSHIFUJI Hideaki @USAGI: * - Ignore Queries for invalid addresses. * - MLD for link-local addresses. * David L Stevens <dlstevens@us.ibm.com>: * - MLDv2 support */#include <linux/config.h>#include <linux/module.h>#include <linux/errno.h>#include <linux/types.h>#include <linux/string.h>#include <linux/socket.h>#include <linux/sockios.h>#include <linux/jiffies.h>#include <linux/times.h>#include <linux/net.h>#include <linux/in.h>#include <linux/in6.h>#include <linux/netdevice.h>#include <linux/if_arp.h>#include <linux/route.h>#include <linux/init.h>#include <linux/proc_fs.h>#include <linux/seq_file.h>#include <linux/netfilter.h>#include <linux/netfilter_ipv6.h>#include <net/sock.h>#include <net/snmp.h>#include <net/ipv6.h>#include <net/protocol.h>#include <net/if_inet6.h>#include <net/ndisc.h>#include <net/addrconf.h>#include <net/ip6_route.h>#include <net/ip6_checksum.h>/* Set to 3 to get tracing... */#define MCAST_DEBUG 2#if MCAST_DEBUG >= 3#define MDBG(x) printk x#else#define MDBG(x)#endif/* * These header formats should be in a separate include file, but icmpv6.h * doesn't have in6_addr defined in all cases, there is no __u128, and no * other files reference these. * * +-DLS 4/14/03 *//* Multicast Listener Discovery version 2 headers */struct mld2_grec { __u8 grec_type; __u8 grec_auxwords; __u16 grec_nsrcs; struct in6_addr grec_mca; struct in6_addr grec_src[0];};struct mld2_report { __u8 type; __u8 resv1; __u16 csum; __u16 resv2; __u16 ngrec; struct mld2_grec grec[0];};struct mld2_query { __u8 type; __u8 code; __u16 csum; __u16 mrc; __u16 resv1; struct in6_addr mca;#if defined(__LITTLE_ENDIAN_BITFIELD) __u8 qrv:3, suppress:1, resv2:4;#elif defined(__BIG_ENDIAN_BITFIELD) __u8 resv2:4, suppress:1, qrv:3;#else#error "Please fix <asm/byteorder.h>"#endif __u8 qqic; __u16 nsrcs; struct in6_addr srcs[0];};struct in6_addr mld2_all_mcr = MLD2_ALL_MCR_INIT;/* Big mc list lock for all the sockets */static rwlock_t ipv6_sk_mc_lock = RW_LOCK_UNLOCKED;static struct socket *igmp6_socket;int __ipv6_dev_mc_dec(struct inet6_dev *idev, struct in6_addr *addr);static void igmp6_join_group(struct ifmcaddr6 *ma);static void igmp6_leave_group(struct ifmcaddr6 *ma);static void igmp6_timer_handler(unsigned long data);static void mld_gq_timer_expire(unsigned long data);static void mld_ifc_timer_expire(unsigned long data);static void mld_ifc_event(struct inet6_dev *idev);static void mld_add_delrec(struct inet6_dev *idev, struct ifmcaddr6 *pmc);static void mld_del_delrec(struct inet6_dev *idev, struct in6_addr *addr);static void mld_clear_delrec(struct inet6_dev *idev);static int sf_setstate(struct ifmcaddr6 *pmc);static void sf_markstate(struct ifmcaddr6 *pmc);static void ip6_mc_clear_src(struct ifmcaddr6 *pmc);int ip6_mc_del_src(struct inet6_dev *idev, struct in6_addr *pmca, int sfmode, int sfcount, struct in6_addr *psfsrc, int delta);int ip6_mc_add_src(struct inet6_dev *idev, struct in6_addr *pmca, int sfmode, int sfcount, struct in6_addr *psfsrc, int delta);int ip6_mc_leave_src(struct sock *sk, struct ipv6_mc_socklist *iml, struct inet6_dev *idev);#define IGMP6_UNSOLICITED_IVAL (10*HZ)#define MLD_QRV_DEFAULT 2#define MLD_V1_SEEN(idev) (ipv6_devconf.force_mld_version == 1 || \ (idev)->cnf.force_mld_version == 1 || \ ((idev)->mc_v1_seen && \ time_before(jiffies, (idev)->mc_v1_seen)))#define MLDV2_MASK(value, nb) ((nb)>=32 ? (value) : ((1<<(nb))-1) & (value))#define MLDV2_EXP(thresh, nbmant, nbexp, value) \ ((value) < (thresh) ? (value) : \ ((MLDV2_MASK(value, nbmant) | (1<<(nbmant+nbexp))) << \ (MLDV2_MASK((value) >> (nbmant), nbexp) + (nbexp))))#define MLDV2_QQIC(value) MLDV2_EXP(0x80, 4, 3, value)#define MLDV2_MRC(value) MLDV2_EXP(0x8000, 12, 3, value)#define IPV6_MLD_MAX_MSF 10int sysctl_mld_max_msf = IPV6_MLD_MAX_MSF;/* * socket join on multicast group */int ipv6_sock_mc_join(struct sock *sk, int ifindex, struct in6_addr *addr){ struct net_device *dev = NULL; struct ipv6_mc_socklist *mc_lst; struct ipv6_pinfo *np = inet6_sk(sk); int err; if (!ipv6_addr_is_multicast(addr)) return -EINVAL; mc_lst = sock_kmalloc(sk, sizeof(struct ipv6_mc_socklist), GFP_KERNEL); if (mc_lst == NULL) return -ENOMEM; mc_lst->next = NULL; ipv6_addr_copy(&mc_lst->addr, addr); if (ifindex == 0) { struct rt6_info *rt; rt = rt6_lookup(addr, NULL, 0, 0); if (rt) { dev = rt->rt6i_dev; dev_hold(dev); dst_release(&rt->u.dst); } } else dev = dev_get_by_index(ifindex); if (dev == NULL) { sock_kfree_s(sk, mc_lst, sizeof(*mc_lst)); return -ENODEV; } mc_lst->ifindex = dev->ifindex; mc_lst->sfmode = MCAST_EXCLUDE; mc_lst->sflist = NULL; /* * now add/increase the group membership on the device */ err = ipv6_dev_mc_inc(dev, addr); if (err) { sock_kfree_s(sk, mc_lst, sizeof(*mc_lst)); dev_put(dev); return err; } write_lock_bh(&ipv6_sk_mc_lock); mc_lst->next = np->ipv6_mc_list; np->ipv6_mc_list = mc_lst; write_unlock_bh(&ipv6_sk_mc_lock); dev_put(dev); return 0;}/* * socket leave on multicast group */int ipv6_sock_mc_drop(struct sock *sk, int ifindex, struct in6_addr *addr){ struct ipv6_pinfo *np = inet6_sk(sk); struct ipv6_mc_socklist *mc_lst, **lnk; write_lock_bh(&ipv6_sk_mc_lock); for (lnk = &np->ipv6_mc_list; (mc_lst = *lnk) !=NULL ; lnk = &mc_lst->next) { if ((ifindex == 0 || mc_lst->ifindex == ifindex) && ipv6_addr_cmp(&mc_lst->addr, addr) == 0) { struct net_device *dev; *lnk = mc_lst->next; write_unlock_bh(&ipv6_sk_mc_lock); if ((dev = dev_get_by_index(mc_lst->ifindex)) != NULL) { struct inet6_dev *idev = in6_dev_get(dev); if (idev) { (void) ip6_mc_leave_src(sk,mc_lst,idev); __ipv6_dev_mc_dec(idev, &mc_lst->addr); in6_dev_put(idev); } dev_put(dev); } sock_kfree_s(sk, mc_lst, sizeof(*mc_lst)); return 0; } } write_unlock_bh(&ipv6_sk_mc_lock); return -ENOENT;}struct inet6_dev *ip6_mc_find_dev(struct in6_addr *group, int ifindex){ struct net_device *dev = NULL; struct inet6_dev *idev = NULL; if (ifindex == 0) { struct rt6_info *rt; rt = rt6_lookup(group, NULL, 0, 0); if (rt) { dev = rt->rt6i_dev; dev_hold(dev); dst_release(&rt->u.dst); } } else dev = dev_get_by_index(ifindex); if (!dev) return NULL; idev = in6_dev_get(dev); if (!idev) { dev_put(dev); return NULL; } read_lock_bh(&idev->lock); if (idev->dead) { read_unlock_bh(&idev->lock); in6_dev_put(idev); dev_put(dev); return NULL; } return idev;}void ipv6_sock_mc_close(struct sock *sk){ struct ipv6_pinfo *np = inet6_sk(sk); struct ipv6_mc_socklist *mc_lst; write_lock_bh(&ipv6_sk_mc_lock); while ((mc_lst = np->ipv6_mc_list) != NULL) { struct net_device *dev; np->ipv6_mc_list = mc_lst->next; write_unlock_bh(&ipv6_sk_mc_lock); dev = dev_get_by_index(mc_lst->ifindex); if (dev) { struct inet6_dev *idev = in6_dev_get(dev); if (idev) { (void) ip6_mc_leave_src(sk, mc_lst, idev); __ipv6_dev_mc_dec(idev, &mc_lst->addr); in6_dev_put(idev); } dev_put(dev); } sock_kfree_s(sk, mc_lst, sizeof(*mc_lst)); write_lock_bh(&ipv6_sk_mc_lock); } write_unlock_bh(&ipv6_sk_mc_lock);}int ip6_mc_source(int add, int omode, struct sock *sk, struct group_source_req *pgsr){ struct in6_addr *source, *group; struct ipv6_mc_socklist *pmc; struct net_device *dev; struct inet6_dev *idev; struct ipv6_pinfo *inet6 = inet6_sk(sk); struct ip6_sf_socklist *psl; int i, j, rv; int err; if (pgsr->gsr_group.ss_family != AF_INET6 || pgsr->gsr_source.ss_family != AF_INET6) return -EINVAL; source = &((struct sockaddr_in6 *)&pgsr->gsr_source)->sin6_addr; group = &((struct sockaddr_in6 *)&pgsr->gsr_group)->sin6_addr; if (!ipv6_addr_is_multicast(group)) return -EINVAL; idev = ip6_mc_find_dev(group, pgsr->gsr_interface); if (!idev) return -ENODEV; dev = idev->dev; err = -EADDRNOTAVAIL; for (pmc=inet6->ipv6_mc_list; pmc; pmc=pmc->next) { if (pgsr->gsr_interface && pmc->ifindex != pgsr->gsr_interface) continue; if (ipv6_addr_cmp(&pmc->addr, group) == 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 */ ip6_mc_add_src(idev, group, omode, 0, NULL, 0); ip6_mc_del_src(idev, group, 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, group, sizeof(struct in6_addr)); if (rv >= 0) break; } if (!rv) /* source not found */ goto done; /* update the interface filter */ ip6_mc_del_src(idev, group, omode, 1, source, 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_mld_max_msf) { err = -ENOBUFS; goto done; } if (!psl || psl->sl_count == psl->sl_max) { struct ip6_sf_socklist *newpsl; int count = IP6_SFBLOCK; if (psl) count += psl->sl_max; newpsl = (struct ip6_sf_socklist *)sock_kmalloc(sk, IP6_SFLSIZE(count), GFP_ATOMIC); if (!newpsl) { err = -ENOBUFS; goto done; } newpsl->sl_max = count; newpsl->sl_count = count - IP6_SFBLOCK; if (psl) { for (i=0; i<psl->sl_count; i++) newpsl->sl_addr[i] = psl->sl_addr[i]; sock_kfree_s(sk, psl, IP6_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, group, sizeof(struct in6_addr)); 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] = *source; psl->sl_count++; err = 0; /* update the interface list */ ip6_mc_add_src(idev, group, omode, 1, source, 1);done: read_unlock_bh(&idev->lock); in6_dev_put(idev); dev_put(dev); return err;}int ip6_mc_msfilter(struct sock *sk, struct group_filter *gsf){ struct in6_addr *group; struct ipv6_mc_socklist *pmc; struct net_device *dev; struct inet6_dev *idev; struct ipv6_pinfo *inet6 = inet6_sk(sk); struct ip6_sf_socklist *newpsl, *psl; int i, err; group = &((struct sockaddr_in6 *)&gsf->gf_group)->sin6_addr; if (!ipv6_addr_is_multicast(group)) return -EINVAL; if (gsf->gf_fmode != MCAST_INCLUDE && gsf->gf_fmode != MCAST_EXCLUDE) return -EINVAL; idev = ip6_mc_find_dev(group, gsf->gf_interface); if (!idev) return -ENODEV; dev = idev->dev; err = -EADDRNOTAVAIL; for (pmc=inet6->ipv6_mc_list; pmc; pmc=pmc->next) { if (pmc->ifindex != gsf->gf_interface) continue; if (ipv6_addr_cmp(&pmc->addr, group) == 0) break; } if (!pmc) /* must have a prior join */ goto done; if (gsf->gf_numsrc) { newpsl = (struct ip6_sf_socklist *)sock_kmalloc(sk, IP6_SFLSIZE(gsf->gf_numsrc), GFP_ATOMIC); if (!newpsl) { err = -ENOBUFS; goto done; } newpsl->sl_max = newpsl->sl_count = gsf->gf_numsrc; for (i=0; i<newpsl->sl_count; ++i) { struct sockaddr_in6 *psin6; psin6 = (struct sockaddr_in6 *)&gsf->gf_slist[i]; newpsl->sl_addr[i] = psin6->sin6_addr; } err = ip6_mc_add_src(idev, group, gsf->gf_fmode, newpsl->sl_count, newpsl->sl_addr, 0); if (err) { sock_kfree_s(sk, newpsl, IP6_SFLSIZE(newpsl->sl_max)); goto done; } } else newpsl = NULL; psl = pmc->sflist; if (psl) { (void) ip6_mc_del_src(idev, group, pmc->sfmode, psl->sl_count, psl->sl_addr, 0); sock_kfree_s(sk, psl, IP6_SFLSIZE(psl->sl_max)); } else (void) ip6_mc_del_src(idev, group, pmc->sfmode, 0, NULL, 0); pmc->sflist = newpsl; pmc->sfmode = gsf->gf_fmode;done: read_unlock_bh(&idev->lock); in6_dev_put(idev); dev_put(dev); return err;}int ip6_mc_msfget(struct sock *sk, struct group_filter *gsf, struct group_filter __user *optval, int __user *optlen){ int err, i, count, copycount; struct in6_addr *group; struct ipv6_mc_socklist *pmc; struct inet6_dev *idev; struct net_device *dev; struct ipv6_pinfo *inet6 = inet6_sk(sk); struct ip6_sf_socklist *psl; group = &((struct sockaddr_in6 *)&gsf->gf_group)->sin6_addr; if (!ipv6_addr_is_multicast(group)) return -EINVAL; idev = ip6_mc_find_dev(group, gsf->gf_interface); if (!idev) return -ENODEV; dev = idev->dev; err = -EADDRNOTAVAIL; for (pmc=inet6->ipv6_mc_list; pmc; pmc=pmc->next) { if (pmc->ifindex != gsf->gf_interface) continue; if (ipv6_addr_cmp(group, &pmc->addr) == 0) break; } if (!pmc) /* must have a prior join */ goto done; gsf->gf_fmode = pmc->sfmode; psl = pmc->sflist; count = psl ? psl->sl_count : 0; read_unlock_bh(&idev->lock); in6_dev_put(idev); dev_put(dev); copycount = count < gsf->gf_numsrc ? count : gsf->gf_numsrc; gsf->gf_numsrc = count; if (put_user(GROUP_FILTER_SIZE(copycount), optlen) || copy_to_user(optval, gsf, GROUP_FILTER_SIZE(0))) { return -EFAULT; } for (i=0; i<copycount; i++) { struct sockaddr_in6 *psin6; struct sockaddr_storage ss; psin6 = (struct sockaddr_in6 *)&ss; memset(&ss, 0, sizeof(ss)); psin6->sin6_family = AF_INET6; psin6->sin6_addr = psl->sl_addr[i]; if (copy_to_user(&optval->gf_slist[i], &ss, sizeof(ss))) return -EFAULT; } return 0;done: read_unlock_bh(&idev->lock); in6_dev_put(idev); dev_put(dev); return err;}int inet6_mc_check(struct sock *sk, struct in6_addr *mc_addr, struct in6_addr *src_addr){ struct ipv6_pinfo *np = inet6_sk(sk); struct ipv6_mc_socklist *mc; struct ip6_sf_socklist *psl; int rv = 1; read_lock(&ipv6_sk_mc_lock); for (mc = np->ipv6_mc_list; mc; mc = mc->next) { if (ipv6_addr_cmp(&mc->addr, mc_addr) == 0) break; } if (!mc) { read_unlock(&ipv6_sk_mc_lock); return 1; } psl = mc->sflist; if (!psl) { rv = mc->sfmode == MCAST_EXCLUDE; } else { int i; for (i=0; i<psl->sl_count; i++) { if (ipv6_addr_cmp(&psl->sl_addr[i], src_addr) == 0) break; } if (mc->sfmode == MCAST_INCLUDE && i >= psl->sl_count) rv = 0;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?