⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 mcast.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
/* *	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/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/net_namespace.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;	__be16		grec_nsrcs;	struct in6_addr	grec_mca;	struct in6_addr	grec_src[0];};struct mld2_report {	__u8	type;	__u8	resv1;	__sum16	csum;	__be16	resv2;	__be16	ngrec;	struct mld2_grec grec[0];};struct mld2_query {	__u8 type;	__u8 code;	__sum16 csum;	__be16 mrc;	__be16 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;	__be16 nsrcs;	struct in6_addr srcs[0];};static struct in6_addr mld2_all_mcr = MLD2_ALL_MCR_INIT;/* Big mc list lock for all the sockets */static DEFINE_RWLOCK(ipv6_sk_mc_lock);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);static int ip6_mc_del_src(struct inet6_dev *idev, struct in6_addr *pmca,			  int sfmode, int sfcount, struct in6_addr *psfsrc,			  int delta);static int ip6_mc_add_src(struct inet6_dev *idev, struct in6_addr *pmca,			  int sfmode, int sfcount, struct in6_addr *psfsrc,			  int delta);static 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))) << \	(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	64int sysctl_mld_max_msf __read_mostly = 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;	read_lock_bh(&ipv6_sk_mc_lock);	for (mc_lst=np->ipv6_mc_list; mc_lst; mc_lst=mc_lst->next) {		if ((ifindex == 0 || mc_lst->ifindex == ifindex) &&		    ipv6_addr_equal(&mc_lst->addr, addr)) {			read_unlock_bh(&ipv6_sk_mc_lock);			return -EADDRINUSE;		}	}	read_unlock_bh(&ipv6_sk_mc_lock);	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(&init_net, 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;	rwlock_init(&mc_lst->sflock);	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_equal(&mc_lst->addr, addr)) {			struct net_device *dev;			*lnk = mc_lst->next;			write_unlock_bh(&ipv6_sk_mc_lock);			if ((dev = dev_get_by_index(&init_net, mc_lst->ifindex)) != NULL) {				struct inet6_dev *idev = in6_dev_get(dev);				(void) ip6_mc_leave_src(sk, mc_lst, idev);				if (idev) {					__ipv6_dev_mc_dec(idev, &mc_lst->addr);					in6_dev_put(idev);				}				dev_put(dev);			} else				(void) ip6_mc_leave_src(sk, mc_lst, NULL);			sock_kfree_s(sk, mc_lst, sizeof(*mc_lst));			return 0;		}	}	write_unlock_bh(&ipv6_sk_mc_lock);	return -EADDRNOTAVAIL;}static 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(&init_net, 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(&init_net, mc_lst->ifindex);		if (dev) {			struct inet6_dev *idev = in6_dev_get(dev);			(void) ip6_mc_leave_src(sk, mc_lst, idev);			if (idev) {				__ipv6_dev_mc_dec(idev, &mc_lst->addr);				in6_dev_put(idev);			}			dev_put(dev);		} else			(void) ip6_mc_leave_src(sk, mc_lst, NULL);		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 leavegroup = 0;	int pmclocked = 0;	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;	read_lock_bh(&ipv6_sk_mc_lock);	for (pmc=inet6->ipv6_mc_list; pmc; pmc=pmc->next) {		if (pgsr->gsr_interface && pmc->ifindex != pgsr->gsr_interface)			continue;		if (ipv6_addr_equal(&pmc->addr, group))			break;	}	if (!pmc) {		/* must have a prior join */		err = -EINVAL;		goto done;	}	/* if a source filter was set, must be the same mode as before */	if (pmc->sflist) {		if (pmc->sfmode != omode) {			err = -EINVAL;			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;	}	write_lock_bh(&pmc->sflock);	pmclocked = 1;	psl = pmc->sflist;	if (!add) {		if (!psl)			goto done;	/* err = -EADDRNOTAVAIL */		rv = !0;		for (i=0; i<psl->sl_count; i++) {			rv = memcmp(&psl->sl_addr[i], source,				sizeof(struct in6_addr));			if (rv == 0)				break;		}		if (rv)		/* source not found */			goto done;	/* err = -EADDRNOTAVAIL */		/* special case - (INCLUDE, empty) == LEAVE_GROUP */		if (psl->sl_count == 1 && omode == MCAST_INCLUDE) {			leavegroup = 1;			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 = 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[i], source, 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:	if (pmclocked)		write_unlock_bh(&pmc->sflock);	read_unlock_bh(&ipv6_sk_mc_lock);	read_unlock_bh(&idev->lock);	in6_dev_put(idev);	dev_put(dev);	if (leavegroup)		return ipv6_sock_mc_drop(sk, pgsr->gsr_interface, group);	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 leavegroup = 0;	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 = 0;	read_lock_bh(&ipv6_sk_mc_lock);	if (gsf->gf_fmode == MCAST_INCLUDE && gsf->gf_numsrc == 0) {		leavegroup = 1;		goto done;	}	for (pmc=inet6->ipv6_mc_list; pmc; pmc=pmc->next) {

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -