root/net/inet/igmp.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. igmp_stop_timer
  2. random
  3. igmp_start_timer
  4. igmp_timer_expire
  5. igmp_init_timer
  6. igmp_heard_report
  7. igmp_heard_query
  8. ip_mc_map
  9. ip_mc_filter_add
  10. ip_mc_filter_del
  11. igmp_group_dropped
  12. igmp_group_added
  13. igmp_rcv
  14. ip_mc_inc_group
  15. ip_mc_dec_group
  16. ip_mc_drop_device
  17. ip_mc_allhost
  18. ip_mc_join_group
  19. ip_mc_leave_group
  20. ip_mc_drop_socket

   1 /*
   2  *      Linux NET3:     Internet Gateway Management Protocol  [IGMP]
   3  *
   4  *      Authors:
   5  *              Alan Cox <Alan.Cox@linux.org>   
   6  *
   7  *      WARNING:
   8  *              This is a 'preliminary' implementation... on your own head
   9  *      be it.
  10  *
  11  *      This program is free software; you can redistribute it and/or
  12  *      modify it under the terms of the GNU General Public License
  13  *      as published by the Free Software Foundation; either version
  14  *      2 of the License, or (at your option) any later version.
  15  */
  16  
  17  
  18 #include <asm/segment.h>
  19 #include <asm/system.h>
  20 #include <linux/types.h>
  21 #include <linux/kernel.h>
  22 #include <linux/sched.h>
  23 #include <linux/string.h>
  24 #include <linux/config.h>
  25 #include <linux/socket.h>
  26 #include <linux/sockios.h>
  27 #include <linux/in.h>
  28 #include <linux/inet.h>
  29 #include <linux/netdevice.h>
  30 #include "ip.h"
  31 #include "protocol.h"
  32 #include "route.h"
  33 #include <linux/skbuff.h>
  34 #include "sock.h"
  35 #include <linux/igmp.h>
  36 
  37 #ifdef CONFIG_IP_MULTICAST
  38 
  39 
  40 /*
  41  *      Timer management
  42  */
  43  
  44  
  45 static void igmp_stop_timer(struct ip_mc_list *im)
     /* [previous][next][first][last][top][bottom][index][help] */
  46 {
  47         del_timer(&im->timer);
  48         im->tm_running=0;
  49 }
  50 
  51 static int random(void)
     /* [previous][next][first][last][top][bottom][index][help] */
  52 {
  53         static unsigned long seed=152L;
  54         seed=seed*69069L+1;
  55         return seed^jiffies;
  56 }
  57 
  58 
  59 static void igmp_start_timer(struct ip_mc_list *im)
     /* [previous][next][first][last][top][bottom][index][help] */
  60 {
  61         int tv;
  62         if(im->tm_running)
  63                 return;
  64         tv=random()%(10*HZ);            /* Pick a number any number 8) */
  65         im->timer.expires=tv;
  66         im->tm_running=1;
  67         add_timer(&im->timer);
  68 }
  69  
  70 /*
  71  *      Send an IGMP report.
  72  */
  73 
  74 #define MAX_IGMP_SIZE (sizeof(struct igmphdr)+sizeof(struct iphdr)+64)
  75 
  76 static void igmp_send_report(struct device *dev, unsigned long address, int type)
  77 {
  78         struct sk_buff *skb=alloc_skb(MAX_IGMP_SIZE, GFP_ATOMIC);
  79         int tmp;
  80         struct igmphdr *igh;
  81         
  82         if(skb==NULL)
  83                 return;
  84         tmp=ip_build_header(skb, INADDR_ANY, address, &dev, IPPROTO_IGMP, NULL,
  85                                 skb->mem_len, 0, 1);
  86         if(tmp<0)
  87         {
  88                 kfree_skb(skb, FREE_WRITE);
  89                 return;
  90         }
  91         igh=(struct igmphdr *)(skb->data+tmp);
  92         skb->len=tmp+sizeof(*igh);
  93         igh->csum=0;
  94         igh->unused=0;
  95         igh->type=type;
  96         igh->group=address;
  97         igh->csum=ip_compute_csum((void *)igh,sizeof(*igh));
  98         ip_queue_xmit(NULL,dev,skb,1);
  99 }
 100 
 101 
 102 static void igmp_timer_expire(unsigned long data)
     /* [previous][next][first][last][top][bottom][index][help] */
 103 {
 104         struct ip_mc_list *im=(struct ip_mc_list *)data;
 105         igmp_stop_timer(im);
 106         igmp_send_report(im->interface, im->multiaddr, IGMP_HOST_MEMBERSHIP_REPORT);
 107 }
 108 
 109 static void igmp_init_timer(struct ip_mc_list *im)
     /* [previous][next][first][last][top][bottom][index][help] */
 110 {
 111         im->tm_running=0;
 112         init_timer(&im->timer);
 113         im->timer.data=(unsigned long)im;
 114         im->timer.function=&igmp_timer_expire;
 115 }
 116         
 117 
 118 static void igmp_heard_report(struct device *dev, unsigned long address)
     /* [previous][next][first][last][top][bottom][index][help] */
 119 {
 120         struct ip_mc_list *im;
 121         for(im=dev->ip_mc_list;im!=NULL;im=im->next)
 122                 if(im->multiaddr==address)
 123                         igmp_stop_timer(im);
 124 }
 125 
 126 static void igmp_heard_query(struct device *dev)
     /* [previous][next][first][last][top][bottom][index][help] */
 127 {
 128         struct ip_mc_list *im;
 129         for(im=dev->ip_mc_list;im!=NULL;im=im->next)
 130                 if(!im->tm_running && im->multiaddr!=IGMP_ALL_HOSTS)
 131                         igmp_start_timer(im);
 132 }
 133 
 134 /*
 135  *      Map a multicast IP onto multicast MAC for type ethernet.
 136  */
 137  
 138 static void ip_mc_map(unsigned long addr, char *buf)
     /* [previous][next][first][last][top][bottom][index][help] */
 139 {
 140         addr=ntohl(addr);
 141         buf[0]=0x01;
 142         buf[1]=0x00;
 143         buf[2]=0x5e;
 144         buf[5]=addr&0xFF;
 145         addr>>=8;
 146         buf[4]=addr&0xFF;
 147         addr>>=8;
 148         buf[3]=addr&0x7F;
 149 }
 150 
 151 /*
 152  *      Add a filter to a device
 153  */
 154  
 155 void ip_mc_filter_add(struct device *dev, unsigned long addr)
     /* [previous][next][first][last][top][bottom][index][help] */
 156 {
 157         char buf[6];
 158         if(dev->type!=ARPHRD_ETHER)
 159                 return; /* Only do ethernet now */
 160         ip_mc_map(addr,buf);    
 161         dev_mc_add(dev,buf,ETH_ALEN,0);
 162 }
 163 
 164 /*
 165  *      Remove a filter from a device
 166  */
 167  
 168 void ip_mc_filter_del(struct device *dev, unsigned long addr)
     /* [previous][next][first][last][top][bottom][index][help] */
 169 {
 170         char buf[6];
 171         if(dev->type!=ARPHRD_ETHER)
 172                 return; /* Only do ethernet now */
 173         ip_mc_map(addr,buf);    
 174         dev_mc_delete(dev,buf,ETH_ALEN,0);
 175 }
 176 
 177 static void igmp_group_dropped(struct ip_mc_list *im)
     /* [previous][next][first][last][top][bottom][index][help] */
 178 {
 179         del_timer(&im->timer);
 180         igmp_send_report(im->interface, im->multiaddr, IGMP_HOST_LEAVE_MESSAGE);
 181         ip_mc_filter_del(im->interface, im->multiaddr);
 182 /*      printk("Left group %lX\n",im->multiaddr);*/
 183 }
 184 
 185 static void igmp_group_added(struct ip_mc_list *im)
     /* [previous][next][first][last][top][bottom][index][help] */
 186 {
 187         igmp_init_timer(im);
 188         igmp_send_report(im->interface, im->multiaddr, IGMP_HOST_MEMBERSHIP_REPORT);
 189         ip_mc_filter_add(im->interface, im->multiaddr);
 190 /*      printk("Joined group %lX\n",im->multiaddr);*/
 191 }
 192 
 193 int igmp_rcv(struct sk_buff *skb, struct device *dev, struct options *opt,
     /* [previous][next][first][last][top][bottom][index][help] */
 194         unsigned long daddr, unsigned short len, unsigned long saddr, int redo,
 195         struct inet_protocol *protocol)
 196 {
 197         /* This basically follows the spec line by line -- see RFC1112 */
 198         struct igmphdr *igh=(struct igmphdr *)skb->h.raw;
 199         
 200         if(skb->ip_hdr->ttl!=1 || ip_compute_csum((void *)igh,sizeof(*igh)))
 201         {
 202                 kfree_skb(skb, FREE_READ);
 203                 return 0;
 204         }
 205         
 206         if(igh->type==IGMP_HOST_MEMBERSHIP_QUERY && daddr==IGMP_ALL_HOSTS)
 207                 igmp_heard_query(dev);
 208         if(igh->type==IGMP_HOST_MEMBERSHIP_REPORT && daddr==igh->group)
 209                 igmp_heard_report(dev,igh->group);
 210         kfree_skb(skb, FREE_READ);
 211         return 0;
 212 }
 213 
 214 /*
 215  *      Multicast list managers
 216  */
 217  
 218  
 219 /*
 220  *      A socket has joined a multicast group on device dev.
 221  */
 222   
 223 static void ip_mc_inc_group(struct device *dev, unsigned long addr)
     /* [previous][next][first][last][top][bottom][index][help] */
 224 {
 225         struct ip_mc_list *i;
 226         for(i=dev->ip_mc_list;i!=NULL;i=i->next)
 227         {
 228                 if(i->multiaddr==addr)
 229                 {
 230                         i->users++;
 231                         return;
 232                 }
 233         }
 234         i=(struct ip_mc_list *)kmalloc(sizeof(*i), GFP_KERNEL);
 235         if(!i)
 236                 return;
 237         i->users=1;
 238         i->interface=dev;
 239         i->multiaddr=addr;
 240         i->next=dev->ip_mc_list;
 241         igmp_group_added(i);
 242         dev->ip_mc_list=i;
 243 }
 244 
 245 /*
 246  *      A socket has left a multicast group on device dev
 247  */
 248         
 249 static void ip_mc_dec_group(struct device *dev, unsigned long addr)
     /* [previous][next][first][last][top][bottom][index][help] */
 250 {
 251         struct ip_mc_list **i;
 252         for(i=&(dev->ip_mc_list);(*i)!=NULL;i=&(*i)->next)
 253         {
 254                 if((*i)->multiaddr==addr)
 255                 {
 256                         if(--((*i)->users))
 257                                 return;
 258                         else
 259                         {
 260                                 struct ip_mc_list *tmp= *i;
 261                                 igmp_group_dropped(tmp);
 262                                 *i=(*i)->next;
 263                                 kfree_s(tmp,sizeof(*tmp));
 264                         }
 265                 }
 266         }
 267 }
 268 
 269 /*
 270  *      Device going down: Clean up.
 271  */
 272  
 273 void ip_mc_drop_device(struct device *dev)
     /* [previous][next][first][last][top][bottom][index][help] */
 274 {
 275         struct ip_mc_list *i;
 276         struct ip_mc_list *j;
 277         for(i=dev->ip_mc_list;i!=NULL;i=j)
 278         {
 279                 j=i->next;
 280                 kfree_s(i,sizeof(*i));
 281         }
 282         dev->ip_mc_list=NULL;
 283 }
 284 
 285 /*
 286  *      Device going up. Make sure it is in all hosts
 287  */
 288  
 289 void ip_mc_allhost(struct device *dev)
     /* [previous][next][first][last][top][bottom][index][help] */
 290 {
 291         struct ip_mc_list *i;
 292         for(i=dev->ip_mc_list;i!=NULL;i=i->next)
 293                 if(i->multiaddr==IGMP_ALL_HOSTS)
 294                         return;
 295         i=(struct ip_mc_list *)kmalloc(sizeof(*i), GFP_KERNEL);
 296         if(!i)
 297                 return;
 298         i->users=1;
 299         i->interface=dev;
 300         i->multiaddr=IGMP_ALL_HOSTS;
 301         i->next=dev->ip_mc_list;
 302         dev->ip_mc_list=i;
 303         ip_mc_filter_add(i->interface, i->multiaddr);
 304 
 305 }       
 306  
 307 /*
 308  *      Join a socket to a group
 309  */
 310  
 311 int ip_mc_join_group(struct sock *sk , struct device *dev, unsigned long addr)
     /* [previous][next][first][last][top][bottom][index][help] */
 312 {
 313         int unused= -1;
 314         int i;
 315         if(!MULTICAST(addr))
 316                 return -EINVAL;
 317         if(!(dev->flags&IFF_MULTICAST))
 318                 return -EADDRNOTAVAIL;
 319         if(sk->ip_mc_list==NULL)
 320         {
 321                 if((sk->ip_mc_list=(struct ip_mc_socklist *)kmalloc(sizeof(*sk->ip_mc_list), GFP_KERNEL))==NULL)
 322                         return -ENOMEM;
 323                 memset(sk->ip_mc_list,'\0',sizeof(*sk->ip_mc_list));
 324         }
 325         for(i=0;i<IP_MAX_MEMBERSHIPS;i++)
 326         {
 327                 if(sk->ip_mc_list->multiaddr[i]==addr && sk->ip_mc_list->multidev[i]==dev)
 328                         return -EADDRINUSE;
 329                 if(sk->ip_mc_list->multidev[i]==NULL)
 330                         unused=i;
 331         }
 332         
 333         if(unused==-1)
 334                 return -ENOBUFS;
 335         sk->ip_mc_list->multiaddr[unused]=addr;
 336         sk->ip_mc_list->multidev[unused]=dev;
 337         ip_mc_inc_group(dev,addr);
 338         return 0;
 339 }
 340 
 341 /*
 342  *      Ask a socket to leave a group.
 343  */
 344  
 345 int ip_mc_leave_group(struct sock *sk, struct device *dev, unsigned long addr)
     /* [previous][next][first][last][top][bottom][index][help] */
 346 {
 347         int i;
 348         if(!MULTICAST(addr))
 349                 return -EINVAL;
 350         if(!(dev->flags&IFF_MULTICAST))
 351                 return -EADDRNOTAVAIL;
 352         if(sk->ip_mc_list==NULL)
 353                 return -EADDRNOTAVAIL;
 354                 
 355         for(i=0;i<IP_MAX_MEMBERSHIPS;i++)
 356         {
 357                 if(sk->ip_mc_list->multiaddr[i]==addr && sk->ip_mc_list->multidev[i]==dev)
 358                 {
 359                         sk->ip_mc_list->multidev[i]=NULL;
 360                         ip_mc_dec_group(dev,addr);
 361                         return 0;
 362                 }
 363         }
 364         return -EADDRNOTAVAIL;
 365 }
 366 
 367 /*
 368  *      A socket is closing.
 369  */
 370  
 371 void ip_mc_drop_socket(struct sock *sk)
     /* [previous][next][first][last][top][bottom][index][help] */
 372 {
 373         int i;
 374         
 375         if(sk->ip_mc_list==NULL)
 376                 return;
 377                 
 378         for(i=0;i<IP_MAX_MEMBERSHIPS;i++)
 379         {
 380                 if(sk->ip_mc_list->multidev[i])
 381                 {
 382                         ip_mc_dec_group(sk->ip_mc_list->multidev[i], sk->ip_mc_list->multiaddr[i]);
 383                         sk->ip_mc_list->multidev[i]=NULL;
 384                 }
 385         }
 386         kfree_s(sk->ip_mc_list,sizeof(*sk->ip_mc_list));
 387         sk->ip_mc_list=NULL;
 388 }
 389 
 390 #endif

/* [previous][next][first][last][top][bottom][index][help] */