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

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