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
- Lines: 474
- Date:
Fri Nov 24 16:39:54 1995
- Orig file:
v1.3.43/linux/net/ipv4/igmp.c
- Orig date:
Fri Nov 17 08:42:33 1995
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