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

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