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

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