patch-1.3.44 linux/net/ipv4/igmp.c

Next file: linux/net/ipv4/ip_forward.c
Previous file: linux/net/ipv4/arp.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v1.3.43/linux/net/ipv4/igmp.c linux/net/ipv4/igmp.c
@@ -2,12 +2,12 @@
  *	Linux NET3:	Internet Gateway Management Protocol  [IGMP]
  *
  *	This code implements the IGMP protocol as defined in RFC1122. There has
- *	been a further revision of this protocol since, but since it is not 
+ *	been a further revision of this protocol since, but since it is not
  *	cleanly specified in any IETF standards we implement the old one properly
  *	rather than play a game of guess the BSD unofficial extensions.
  *
  *	Authors:
- *		Alan Cox <Alan.Cox@linux.org>	
+ *		Alan Cox <Alan.Cox@linux.org>
  *
  *	This program is free software; you can redistribute it and/or
  *	modify it under the terms of the GNU General Public License
@@ -15,7 +15,7 @@
  *	2 of the License, or (at your option) any later version.
  *
  *	Fixes:
- *	
+ *
  *		Alan Cox	:	Added lots of __inline__ to optimise
  *					the memory usage of all the tiny little
  *					functions.
@@ -28,9 +28,19 @@
  *					desired by mrouted, fixed the fact it has been
  *					broken since 1.3.6 and cleaned up a few minor
  *					points.
+ *		Chih-Jen Chang	:	Tried to revise IGMP to Version 2
+ *		Tsu-Sheng Tsao		chihjenc@scf.usc.edu and tsusheng@scf.usc.edu
+ *					The enhancements are based on Steve Deering's ipmulti-3.5 code
+ *		Chih-Jen Chang	:	Added the igmp_get_mrouter_info and
+ *		Tsu-Sheng Tsao		igmp_set_mrouter_info to keep track of
+ *					the mrouted version on that device.
+ *		Chih-Jen Chang	:	Added the max_resp_time parameter to
+ *		Tsu-Sheng Tsao		igmp_heard_query(). Using this parameter
+ *					to identify the multicast router verion
+ *					and do what the IGMP version 2 specified.
  */
- 
- 
+
+
 #include <asm/segment.h>
 #include <asm/system.h>
 #include <linux/types.h>
@@ -56,10 +66,75 @@
 
 
 /*
+ *	Multicast router info manager
+ */
+
+struct	router_info	*router_info_head=(struct router_info *)0;
+
+/*
+ *	Get the multicast router info on that device
+ */
+
+static	struct	router_info	*igmp_get_mrouter_info(struct device *dev)
+{
+	register struct router_info *i;
+
+	for(i=router_info_head;i!=NULL;i=i->next)
+	{
+		if (i->dev == dev)
+		{
+			return i;
+		}
+	}
+
+	/*
+	 *  Not found. Create a new entry. The default is IGMP V2 router
+	 */
+	i=(struct router_info *)kmalloc(sizeof(*i), GFP_KERNEL);
+	i->dev = dev;
+	i->type = IGMP_NEW_ROUTER;
+	i->time = IGMP_AGE_THRESHOLD;
+	i->next = router_info_head;
+	router_info_head = i;
+	return i;
+}
+
+/*
+ *	Set the multicast router info on that device
+ */
+
+static	struct	router_info	*igmp_set_mrouter_info(struct device *dev,int type,int time)
+{
+	register struct router_info *i;
+
+	for(i=router_info_head;i!=NULL;i=i->next)
+	{
+		if (i->dev == dev)
+		{
+			i->type = type;
+			i->time = time;
+			return i;
+		}
+	}
+
+	/*
+	 *  Not found. Create a new entry.
+	 */
+	i=(struct router_info *)kmalloc(sizeof(*i), GFP_KERNEL);
+	i->dev = dev;
+	i->type = type;
+	i->time = time;
+	i->next = router_info_head;
+	router_info_head = i;
+	return i;
+}
+
+
+/*
  *	Timer management
  */
- 
- 
+
+
 static void igmp_stop_timer(struct ip_mc_list *im)
 {
 	del_timer(&im->timer);
@@ -77,17 +152,17 @@
  *	Inlined as its only called once.
  */
 
-static void igmp_start_timer(struct ip_mc_list *im)
+static void igmp_start_timer(struct ip_mc_list *im,unsigned char max_resp_time)
 {
 	int tv;
 	if(im->tm_running)
 		return;
-	tv=random()%(10*HZ);		/* Pick a number any number 8) */
+	tv=random()%(max_resp_time*HZ/IGMP_TIMER_SCALE); /* Pick a number any number 8) */
 	im->timer.expires=jiffies+tv;
 	im->tm_running=1;
 	add_timer(&im->timer);
 }
- 
+
 /*
  *	Send an IGMP report.
  */
@@ -99,7 +174,7 @@
 	struct sk_buff *skb=alloc_skb(MAX_IGMP_SIZE, GFP_ATOMIC);
 	int tmp;
 	struct igmphdr *ih;
-	
+
 	if(skb==NULL)
 		return;
 	tmp=ip_build_header(skb, INADDR_ANY, address, &dev, IPPROTO_IGMP, NULL,
@@ -110,7 +185,7 @@
 		return;
 	}
 	ih=(struct igmphdr *)skb_put(skb,sizeof(struct igmphdr));
-	ih->type=IGMP_HOST_MEMBERSHIP_REPORT;
+	ih->type=type;
 	ih->code=0;
 	ih->csum=0;
 	ih->group=address;
@@ -122,8 +197,13 @@
 static void igmp_timer_expire(unsigned long data)
 {
 	struct ip_mc_list *im=(struct ip_mc_list *)data;
+	struct router_info *r;
 	igmp_stop_timer(im);
-	igmp_send_report(im->interface, im->multiaddr, IGMP_HOST_MEMBERSHIP_REPORT);
+	r=igmp_get_mrouter_info(im->interface);
+	if(r->type==IGMP_NEW_ROUTER)
+		igmp_send_report(im->interface, im->multiaddr, IGMP_HOST_NEW_MEMBERSHIP_REPORT);
+	else
+		igmp_send_report(im->interface, im->multiaddr, IGMP_HOST_MEMBERSHIP_REPORT);
 }
 
 static void igmp_init_timer(struct ip_mc_list *im)
@@ -133,7 +213,7 @@
 	im->timer.data=(unsigned long)im;
 	im->timer.function=&igmp_timer_expire;
 }
-	
+
 
 static void igmp_heard_report(struct device *dev, unsigned long address)
 {
@@ -143,20 +223,72 @@
 			igmp_stop_timer(im);
 }
 
-static void igmp_heard_query(struct device *dev)
+static void igmp_heard_query(struct device *dev,unsigned char max_resp_time)
 {
 	struct ip_mc_list *im;
-	for(im=dev->ip_mc_list;im!=NULL;im=im->next)
+	int	mrouter_type;
+
+	/*
+	 *	The max_resp_time is in units of 1/10 second.
+	 */
+	if(max_resp_time>0)
 	{
-		if(!im->tm_running && im->multiaddr!=IGMP_ALL_HOSTS)
-			igmp_start_timer(im);
+		mrouter_type=IGMP_NEW_ROUTER;
+
+		igmp_set_mrouter_info(dev,mrouter_type,0);
+		/*
+		 * - Start the timers in all of our membership records
+		 *   that the query applies to for the interface on
+		 *   which the query arrived excl. those that belong
+		 *   to a "local" group (224.0.0.X)
+		 * - For timers already running check if they need to
+		 *   be reset.
+		 * - Use the igmp->igmp_code field as the maximum
+		 *   delay possible
+		 */
+		for(im=dev->ip_mc_list;im!=NULL;im=im->next)
+		{
+			if(im->tm_running)
+			{
+				if(im->timer.expires>max_resp_time*HZ/IGMP_TIMER_SCALE)
+				{
+					igmp_stop_timer(im);
+					igmp_start_timer(im,max_resp_time);
+				}
+			}
+			else
+			{
+				if((im->multiaddr & IGMP_LOCAL_GROUP_MASK)!=IGMP_LOCAL_GROUP)
+					igmp_start_timer(im,max_resp_time);
+			}
+		}
+	}
+	else
+	{
+		mrouter_type=IGMP_OLD_ROUTER;
+		max_resp_time=IGMP_MAX_HOST_REPORT_DELAY*IGMP_TIMER_SCALE;
+
+		igmp_set_mrouter_info(dev,mrouter_type,IGMP_AGE_THRESHOLD);
+
+		/*
+		 * Start the timers in all of our membership records for
+		 * the interface on which the query arrived, except those
+		 * that are already running and those that belong to a
+		 * "local" group (224.0.0.X).
+		 */
+
+		for(im=dev->ip_mc_list;im!=NULL;im=im->next)
+		{
+			if(!im->tm_running && (im->multiaddr & IGMP_LOCAL_GROUP_MASK)!=IGMP_LOCAL_GROUP)
+				igmp_start_timer(im,max_resp_time);
+		}
 	}
 }
 
 /*
  *	Map a multicast IP onto multicast MAC for type ethernet.
  */
- 
+
 extern __inline__ void ip_mc_map(unsigned long addr, char *buf)
 {
 	addr=ntohl(addr);
@@ -173,26 +305,26 @@
 /*
  *	Add a filter to a device
  */
- 
+
 void ip_mc_filter_add(struct device *dev, unsigned long addr)
 {
 	char buf[6];
 	if(dev->type!=ARPHRD_ETHER)
-		return;	/* Only do ethernet now */
-	ip_mc_map(addr,buf);	
+		return; /* Only do ethernet now */
+	ip_mc_map(addr,buf);
 	dev_mc_add(dev,buf,ETH_ALEN,0);
 }
 
 /*
  *	Remove a filter from a device
  */
- 
+
 void ip_mc_filter_del(struct device *dev, unsigned long addr)
 {
 	char buf[6];
 	if(dev->type!=ARPHRD_ETHER)
-		return;	/* Only do ethernet now */
-	ip_mc_map(addr,buf);	
+		return; /* Only do ethernet now */
+	ip_mc_map(addr,buf);
 	dev_mc_delete(dev,buf,ETH_ALEN,0);
 }
 
@@ -205,9 +337,14 @@
 
 extern __inline__ void igmp_group_added(struct ip_mc_list *im)
 {
+	struct router_info *r;
 	igmp_init_timer(im);
 	ip_mc_filter_add(im->interface, im->multiaddr);
-	igmp_send_report(im->interface, im->multiaddr, IGMP_HOST_MEMBERSHIP_REPORT);
+	r=igmp_get_mrouter_info(im->interface);
+	if(r->type==IGMP_NEW_ROUTER)
+		igmp_send_report(im->interface, im->multiaddr, IGMP_HOST_NEW_MEMBERSHIP_REPORT);
+	else
+		igmp_send_report(im->interface, im->multiaddr, IGMP_HOST_MEMBERSHIP_REPORT);
 }
 
 int igmp_rcv(struct sk_buff *skb, struct device *dev, struct options *opt,
@@ -216,13 +353,13 @@
 {
 	/* This basically follows the spec line by line -- see RFC1112 */
 	struct igmphdr *ih;
-	
+
 	/*
 	 *	Mrouted needs to able to query local interfaces. So
 	 *	report for the device this was sent at. (Which can
 	 *	be the loopback this time)
 	 */
-	 
+
 	if(dev->flags&IFF_LOOPBACK)
 	{
 		dev=ip_dev_find(saddr);
@@ -230,17 +367,19 @@
 			dev=&loopback_dev;
 	}
 	ih=(struct igmphdr *)skb->h.raw;
-		
+
 	if(skb->len <sizeof(struct igmphdr) || skb->ip_hdr->ttl>1 || ip_compute_csum((void *)skb->h.raw,sizeof(struct igmphdr)))
 	{
 		kfree_skb(skb, FREE_READ);
 		return 0;
 	}
-	
+
 	if(ih->type==IGMP_HOST_MEMBERSHIP_QUERY && daddr==IGMP_ALL_HOSTS)
-		igmp_heard_query(dev);
+		igmp_heard_query(dev,ih->code);
 	if(ih->type==IGMP_HOST_MEMBERSHIP_REPORT && daddr==ih->group)
 		igmp_heard_report(dev,ih->group);
+	if(ih->type==IGMP_HOST_NEW_MEMBERSHIP_REPORT && daddr==ih->group)
+		igmp_heard_report(dev,ih->group);
 	kfree_skb(skb, FREE_READ);
 	return 0;
 }
@@ -248,12 +387,12 @@
 /*
  *	Multicast list managers
  */
- 
- 
+
+
 /*
  *	A socket has joined a multicast group on device dev.
  */
-  
+
 static void ip_mc_inc_group(struct device *dev, unsigned long addr)
 {
 	struct ip_mc_list *i;
@@ -279,7 +418,7 @@
 /*
  *	A socket has left a multicast group on device dev
  */
-	
+
 static void ip_mc_dec_group(struct device *dev, unsigned long addr)
 {
 	struct ip_mc_list **i;
@@ -303,7 +442,7 @@
 /*
  *	Device going down: Clean up.
  */
- 
+
 void ip_mc_drop_device(struct device *dev)
 {
 	struct ip_mc_list *i;
@@ -319,13 +458,24 @@
 /*
  *	Device going up. Make sure it is in all hosts
  */
- 
+
 void ip_mc_allhost(struct device *dev)
 {
 	struct ip_mc_list *i;
+	/*
+	 *	Don't add to non multicast units
+	 */
+	if(!(dev->flags&IFF_MULTICAST))
+		return;
+	/*
+	 *	No duplicates
+	 */
 	for(i=dev->ip_mc_list;i!=NULL;i=i->next)
 		if(i->multiaddr==IGMP_ALL_HOSTS)
 			return;
+	/*
+	 *	Add it
+	 */
 	i=(struct ip_mc_list *)kmalloc(sizeof(*i), GFP_KERNEL);
 	if(!i)
 		return;
@@ -337,12 +487,12 @@
 	dev->ip_mc_list=i;
 	ip_mc_filter_add(i->interface, i->multiaddr);
 
-}	
- 
+}
+
 /*
  *	Join a socket to a group
  */
- 
+
 int ip_mc_join_group(struct sock *sk , struct device *dev, unsigned long addr)
 {
 	int unused= -1;
@@ -364,7 +514,7 @@
 		if(sk->ip_mc_list->multidev[i]==NULL)
 			unused=i;
 	}
-	
+
 	if(unused==-1)
 		return -ENOBUFS;
 	sk->ip_mc_list->multiaddr[unused]=addr;
@@ -376,7 +526,7 @@
 /*
  *	Ask a socket to leave a group.
  */
- 
+
 int ip_mc_leave_group(struct sock *sk, struct device *dev, unsigned long addr)
 {
 	int i;
@@ -386,7 +536,7 @@
 		return -EADDRNOTAVAIL;
 	if(sk->ip_mc_list==NULL)
 		return -EADDRNOTAVAIL;
-		
+
 	for(i=0;i<IP_MAX_MEMBERSHIPS;i++)
 	{
 		if(sk->ip_mc_list->multiaddr[i]==addr && sk->ip_mc_list->multidev[i]==dev)
@@ -402,14 +552,14 @@
 /*
  *	A socket is closing.
  */
- 
+
 void ip_mc_drop_socket(struct sock *sk)
 {
 	int i;
-	
+
 	if(sk->ip_mc_list==NULL)
 		return;
-		
+
 	for(i=0;i<IP_MAX_MEMBERSHIPS;i++)
 	{
 		if(sk->ip_mc_list->multidev[i])

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov with Sam's (original) version
of this