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

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