root/net/ipv4/igmp.c

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

DEFINITIONS

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

   1 /*
   2  *      Linux NET3:     Internet Gateway Management Protocol  [IGMP]
   3  *
   4  *      This code implements the IGMP protocol as defined in RFC1122. There has
   5  *      been a further revision of this protocol since, but since it is not
   6  *      cleanly specified in any IETF standards we implement the old one properly
   7  *      rather than play a game of guess the BSD unofficial extensions.
   8  *
   9  *      Authors:
  10  *              Alan Cox <Alan.Cox@linux.org>
  11  *
  12  *      This program is free software; you can redistribute it and/or
  13  *      modify it under the terms of the GNU General Public License
  14  *      as published by the Free Software Foundation; either version
  15  *      2 of the License, or (at your option) any later version.
  16  *
  17  *      Fixes:
  18  *
  19  *              Alan Cox        :       Added lots of __inline__ to optimise
  20  *                                      the memory usage of all the tiny little
  21  *                                      functions.
  22  *              Alan Cox        :       Dumped the header building experiment.
  23  *              Alan Cox        :       Minor tweaks ready for multicast routing
  24  *                                      and extended IGMP protocol.
  25  *              Alan Cox        :       Removed a load of inline directives. Gcc 2.5.8
  26  *                                      writes utterly bogus code otherwise (sigh)
  27  *                                      fixed IGMP loopback to behave in the manner
  28  *                                      desired by mrouted, fixed the fact it has been
  29  *                                      broken since 1.3.6 and cleaned up a few minor
  30  *                                      points.
  31  *              Chih-Jen Chang  :       Tried to revise IGMP to Version 2
  32  *              Tsu-Sheng Tsao          chihjenc@scf.usc.edu and tsusheng@scf.usc.edu
  33  *                                      The enhancements are based on Steve Deering's ipmulti-3.5 code
  34  *              Chih-Jen Chang  :       Added the igmp_get_mrouter_info and
  35  *              Tsu-Sheng Tsao          igmp_set_mrouter_info to keep track of
  36  *                                      the mrouted version on that device.
  37  *              Chih-Jen Chang  :       Added the max_resp_time parameter to
  38  *              Tsu-Sheng Tsao          igmp_heard_query(). Using this parameter
  39  *                                      to identify the multicast router verion
  40  *                                      and do what the IGMP version 2 specified.
  41  */
  42 
  43 
  44 #include <asm/segment.h>
  45 #include <asm/system.h>
  46 #include <linux/types.h>
  47 #include <linux/kernel.h>
  48 #include <linux/sched.h>
  49 #include <linux/string.h>
  50 #include <linux/config.h>
  51 #include <linux/socket.h>
  52 #include <linux/sockios.h>
  53 #include <linux/in.h>
  54 #include <linux/inet.h>
  55 #include <linux/netdevice.h>
  56 #include <linux/if_arp.h>
  57 #include <net/ip.h>
  58 #include <net/protocol.h>
  59 #include <net/route.h>
  60 #include <linux/skbuff.h>
  61 #include <net/sock.h>
  62 #include <linux/igmp.h>
  63 #include <net/checksum.h>
  64 
  65 #ifdef CONFIG_IP_MULTICAST
  66 
  67 
  68 /*
  69  *      Multicast router info manager
  70  */
  71 
  72 struct  router_info     *router_info_head=(struct router_info *)0;
  73 
  74 /*
  75  *      Get the multicast router info on that device
  76  */
  77 
  78 static  struct  router_info     *igmp_get_mrouter_info(struct device *dev)
     /* [previous][next][first][last][top][bottom][index][help] */
  79 {
  80         register struct router_info *i;
  81 
  82         for(i=router_info_head;i!=NULL;i=i->next)
  83         {
  84                 if (i->dev == dev)
  85                 {
  86                         return i;
  87                 }
  88         }
  89 
  90         /*
  91          *  Not found. Create a new entry. The default is IGMP V2 router
  92          */
  93         i=(struct router_info *)kmalloc(sizeof(*i), GFP_KERNEL);
  94         i->dev = dev;
  95         i->type = IGMP_NEW_ROUTER;
  96         i->time = IGMP_AGE_THRESHOLD;
  97         i->next = router_info_head;
  98         router_info_head = i;
  99         return i;
 100 }
 101 
 102 /*
 103  *      Set the multicast router info on that device
 104  */
 105 
 106 static  struct  router_info     *igmp_set_mrouter_info(struct device *dev,int type,int time)
     /* [previous][next][first][last][top][bottom][index][help] */
 107 {
 108         register struct router_info *i;
 109 
 110         for(i=router_info_head;i!=NULL;i=i->next)
 111         {
 112                 if (i->dev == dev)
 113                 {
 114                         i->type = type;
 115                         i->time = time;
 116                         return i;
 117                 }
 118         }
 119 
 120         /*
 121          *  Not found. Create a new entry.
 122          */
 123         i=(struct router_info *)kmalloc(sizeof(*i), GFP_KERNEL);
 124         i->dev = dev;
 125         i->type = type;
 126         i->time = time;
 127         i->next = router_info_head;
 128         router_info_head = i;
 129         return i;
 130 }
 131 
 132 
 133 /*
 134  *      Timer management
 135  */
 136 
 137 
 138 static void igmp_stop_timer(struct ip_mc_list *im)
     /* [previous][next][first][last][top][bottom][index][help] */
 139 {
 140         del_timer(&im->timer);
 141         im->tm_running=0;
 142 }
 143 
 144 extern __inline__ int random(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 145 {
 146         static unsigned long seed=152L;
 147         seed=seed*69069L+1;
 148         return seed^jiffies;
 149 }
 150 
 151 /*
 152  *      Inlined as its only called once.
 153  */
 154 
 155 static void igmp_start_timer(struct ip_mc_list *im,unsigned char max_resp_time)
     /* [previous][next][first][last][top][bottom][index][help] */
 156 {
 157         int tv;
 158         if(im->tm_running)
 159                 return;
 160         tv=random()%(max_resp_time*HZ/IGMP_TIMER_SCALE); /* Pick a number any number 8) */
 161         im->timer.expires=jiffies+tv;
 162         im->tm_running=1;
 163         add_timer(&im->timer);
 164 }
 165 
 166 /*
 167  *      Send an IGMP report.
 168  */
 169 
 170 #define MAX_IGMP_SIZE (sizeof(struct igmphdr)+sizeof(struct iphdr)+64)
 171 
 172 static void igmp_send_report(struct device *dev, unsigned long address, int type)
 173 {
 174         struct sk_buff *skb=alloc_skb(MAX_IGMP_SIZE, GFP_ATOMIC);
 175         int tmp;
 176         struct igmphdr *ih;
 177 
 178         if(skb==NULL)
 179                 return;
 180         tmp=ip_build_header(skb, INADDR_ANY, address, &dev, IPPROTO_IGMP, NULL,
 181                                 28 , 0, 1, NULL);
 182         if(tmp<0)
 183         {
 184                 kfree_skb(skb, FREE_WRITE);
 185                 return;
 186         }
 187         ih=(struct igmphdr *)skb_put(skb,sizeof(struct igmphdr));
 188         ih->type=type;
 189         ih->code=0;
 190         ih->csum=0;
 191         ih->group=address;
 192         ih->csum=ip_compute_csum((void *)ih,sizeof(struct igmphdr));    /* Checksum fill */
 193         ip_queue_xmit(NULL,dev,skb,1);
 194 }
 195 
 196 
 197 static void igmp_timer_expire(unsigned long data)
     /* [previous][next][first][last][top][bottom][index][help] */
 198 {
 199         struct ip_mc_list *im=(struct ip_mc_list *)data;
 200         struct router_info *r;
 201         igmp_stop_timer(im);
 202         r=igmp_get_mrouter_info(im->interface);
 203         if(r->type==IGMP_NEW_ROUTER)
 204                 igmp_send_report(im->interface, im->multiaddr, IGMP_HOST_NEW_MEMBERSHIP_REPORT);
 205         else
 206                 igmp_send_report(im->interface, im->multiaddr, IGMP_HOST_MEMBERSHIP_REPORT);
 207 }
 208 
 209 static void igmp_init_timer(struct ip_mc_list *im)
     /* [previous][next][first][last][top][bottom][index][help] */
 210 {
 211         im->tm_running=0;
 212         init_timer(&im->timer);
 213         im->timer.data=(unsigned long)im;
 214         im->timer.function=&igmp_timer_expire;
 215 }
 216 
 217 
 218 static void igmp_heard_report(struct device *dev, unsigned long address)
     /* [previous][next][first][last][top][bottom][index][help] */
 219 {
 220         struct ip_mc_list *im;
 221         for(im=dev->ip_mc_list;im!=NULL;im=im->next)
 222                 if(im->multiaddr==address)
 223                         igmp_stop_timer(im);
 224 }
 225 
 226 static void igmp_heard_query(struct device *dev,unsigned char max_resp_time)
     /* [previous][next][first][last][top][bottom][index][help] */
 227 {
 228         struct ip_mc_list *im;
 229         int     mrouter_type;
 230 
 231         /*
 232          *      The max_resp_time is in units of 1/10 second.
 233          */
 234         if(max_resp_time>0)
 235         {
 236                 mrouter_type=IGMP_NEW_ROUTER;
 237 
 238                 igmp_set_mrouter_info(dev,mrouter_type,0);
 239                 /*
 240                  * - Start the timers in all of our membership records
 241                  *   that the query applies to for the interface on
 242                  *   which the query arrived excl. those that belong
 243                  *   to a "local" group (224.0.0.X)
 244                  * - For timers already running check if they need to
 245                  *   be reset.
 246                  * - Use the igmp->igmp_code field as the maximum
 247                  *   delay possible
 248                  */
 249                 for(im=dev->ip_mc_list;im!=NULL;im=im->next)
 250                 {
 251                         if(im->tm_running)
 252                         {
 253                                 if(im->timer.expires>max_resp_time*HZ/IGMP_TIMER_SCALE)
 254                                 {
 255                                         igmp_stop_timer(im);
 256                                         igmp_start_timer(im,max_resp_time);
 257                                 }
 258                         }
 259                         else
 260                         {
 261                                 if((im->multiaddr & IGMP_LOCAL_GROUP_MASK)!=IGMP_LOCAL_GROUP)
 262                                         igmp_start_timer(im,max_resp_time);
 263                         }
 264                 }
 265         }
 266         else
 267         {
 268                 mrouter_type=IGMP_OLD_ROUTER;
 269                 max_resp_time=IGMP_MAX_HOST_REPORT_DELAY*IGMP_TIMER_SCALE;
 270 
 271                 igmp_set_mrouter_info(dev,mrouter_type,IGMP_AGE_THRESHOLD);
 272 
 273                 /*
 274                  * Start the timers in all of our membership records for
 275                  * the interface on which the query arrived, except those
 276                  * that are already running and those that belong to a
 277                  * "local" group (224.0.0.X).
 278                  */
 279 
 280                 for(im=dev->ip_mc_list;im!=NULL;im=im->next)
 281                 {
 282                         if(!im->tm_running && (im->multiaddr & IGMP_LOCAL_GROUP_MASK)!=IGMP_LOCAL_GROUP)
 283                                 igmp_start_timer(im,max_resp_time);
 284                 }
 285         }
 286 }
 287 
 288 /*
 289  *      Map a multicast IP onto multicast MAC for type ethernet.
 290  */
 291 
 292 extern __inline__ void ip_mc_map(unsigned long addr, char *buf)
     /* [previous][next][first][last][top][bottom][index][help] */
 293 {
 294         addr=ntohl(addr);
 295         buf[0]=0x01;
 296         buf[1]=0x00;
 297         buf[2]=0x5e;
 298         buf[5]=addr&0xFF;
 299         addr>>=8;
 300         buf[4]=addr&0xFF;
 301         addr>>=8;
 302         buf[3]=addr&0x7F;
 303 }
 304 
 305 /*
 306  *      Add a filter to a device
 307  */
 308 
 309 void ip_mc_filter_add(struct device *dev, unsigned long addr)
     /* [previous][next][first][last][top][bottom][index][help] */
 310 {
 311         char buf[6];
 312         if(dev->type!=ARPHRD_ETHER)
 313                 return; /* Only do ethernet now */
 314         ip_mc_map(addr,buf);
 315         dev_mc_add(dev,buf,ETH_ALEN,0);
 316 }
 317 
 318 /*
 319  *      Remove a filter from a device
 320  */
 321 
 322 void ip_mc_filter_del(struct device *dev, unsigned long addr)
     /* [previous][next][first][last][top][bottom][index][help] */
 323 {
 324         char buf[6];
 325         if(dev->type!=ARPHRD_ETHER)
 326                 return; /* Only do ethernet now */
 327         ip_mc_map(addr,buf);
 328         dev_mc_delete(dev,buf,ETH_ALEN,0);
 329 }
 330 
 331 extern __inline__ void igmp_group_dropped(struct ip_mc_list *im)
     /* [previous][next][first][last][top][bottom][index][help] */
 332 {
 333         del_timer(&im->timer);
 334         igmp_send_report(im->interface, im->multiaddr, IGMP_HOST_LEAVE_MESSAGE);
 335         ip_mc_filter_del(im->interface, im->multiaddr);
 336 }
 337 
 338 extern __inline__ void igmp_group_added(struct ip_mc_list *im)
     /* [previous][next][first][last][top][bottom][index][help] */
 339 {
 340         struct router_info *r;
 341         igmp_init_timer(im);
 342         ip_mc_filter_add(im->interface, im->multiaddr);
 343         r=igmp_get_mrouter_info(im->interface);
 344         if(r->type==IGMP_NEW_ROUTER)
 345                 igmp_send_report(im->interface, im->multiaddr, IGMP_HOST_NEW_MEMBERSHIP_REPORT);
 346         else
 347                 igmp_send_report(im->interface, im->multiaddr, IGMP_HOST_MEMBERSHIP_REPORT);
 348 }
 349 
 350 int igmp_rcv(struct sk_buff *skb, struct device *dev, struct options *opt,
     /* [previous][next][first][last][top][bottom][index][help] */
 351         __u32 daddr, unsigned short len, __u32 saddr, int redo,
 352         struct inet_protocol *protocol)
 353 {
 354         /* This basically follows the spec line by line -- see RFC1112 */
 355         struct igmphdr *ih;
 356 
 357         /*
 358          *      Mrouted needs to able to query local interfaces. So
 359          *      report for the device this was sent at. (Which can
 360          *      be the loopback this time)
 361          */
 362 
 363         if(dev->flags&IFF_LOOPBACK)
 364         {
 365                 dev=ip_dev_find(saddr);
 366                 if(dev==NULL)
 367                         dev=&loopback_dev;
 368         }
 369         ih=(struct igmphdr *)skb->h.raw;
 370 
 371         if(skb->len <sizeof(struct igmphdr) || skb->ip_hdr->ttl>1 || ip_compute_csum((void *)skb->h.raw,sizeof(struct igmphdr)))
 372         {
 373                 kfree_skb(skb, FREE_READ);
 374                 return 0;
 375         }
 376 
 377         if(ih->type==IGMP_HOST_MEMBERSHIP_QUERY && daddr==IGMP_ALL_HOSTS)
 378                 igmp_heard_query(dev,ih->code);
 379         if(ih->type==IGMP_HOST_MEMBERSHIP_REPORT && daddr==ih->group)
 380                 igmp_heard_report(dev,ih->group);
 381         if(ih->type==IGMP_HOST_NEW_MEMBERSHIP_REPORT && daddr==ih->group)
 382                 igmp_heard_report(dev,ih->group);
 383         kfree_skb(skb, FREE_READ);
 384         return 0;
 385 }
 386 
 387 /*
 388  *      Multicast list managers
 389  */
 390 
 391 
 392 /*
 393  *      A socket has joined a multicast group on device dev.
 394  */
 395 
 396 static void ip_mc_inc_group(struct device *dev, unsigned long addr)
     /* [previous][next][first][last][top][bottom][index][help] */
 397 {
 398         struct ip_mc_list *i;
 399         for(i=dev->ip_mc_list;i!=NULL;i=i->next)
 400         {
 401                 if(i->multiaddr==addr)
 402                 {
 403                         i->users++;
 404                         return;
 405                 }
 406         }
 407         i=(struct ip_mc_list *)kmalloc(sizeof(*i), GFP_KERNEL);
 408         if(!i)
 409                 return;
 410         i->users=1;
 411         i->interface=dev;
 412         i->multiaddr=addr;
 413         i->next=dev->ip_mc_list;
 414         igmp_group_added(i);
 415         dev->ip_mc_list=i;
 416 }
 417 
 418 /*
 419  *      A socket has left a multicast group on device dev
 420  */
 421 
 422 static void ip_mc_dec_group(struct device *dev, unsigned long addr)
     /* [previous][next][first][last][top][bottom][index][help] */
 423 {
 424         struct ip_mc_list **i;
 425         for(i=&(dev->ip_mc_list);(*i)!=NULL;i=&(*i)->next)
 426         {
 427                 if((*i)->multiaddr==addr)
 428                 {
 429                         if(--((*i)->users))
 430                                 return;
 431                         else
 432                         {
 433                                 struct ip_mc_list *tmp= *i;
 434                                 igmp_group_dropped(tmp);
 435                                 *i=(*i)->next;
 436                                 kfree_s(tmp,sizeof(*tmp));
 437                         }
 438                 }
 439         }
 440 }
 441 
 442 /*
 443  *      Device going down: Clean up.
 444  */
 445 
 446 void ip_mc_drop_device(struct device *dev)
     /* [previous][next][first][last][top][bottom][index][help] */
 447 {
 448         struct ip_mc_list *i;
 449         struct ip_mc_list *j;
 450         for(i=dev->ip_mc_list;i!=NULL;i=j)
 451         {
 452                 j=i->next;
 453                 kfree_s(i,sizeof(*i));
 454         }
 455         dev->ip_mc_list=NULL;
 456 }
 457 
 458 /*
 459  *      Device going up. Make sure it is in all hosts
 460  */
 461 
 462 void ip_mc_allhost(struct device *dev)
     /* [previous][next][first][last][top][bottom][index][help] */
 463 {
 464         struct ip_mc_list *i;
 465         /*
 466          *      Don't add to non multicast units
 467          */
 468         if(!(dev->flags&IFF_MULTICAST))
 469                 return;
 470         /*
 471          *      No duplicates
 472          */
 473         for(i=dev->ip_mc_list;i!=NULL;i=i->next)
 474                 if(i->multiaddr==IGMP_ALL_HOSTS)
 475                         return;
 476         /*
 477          *      Add it
 478          */
 479         i=(struct ip_mc_list *)kmalloc(sizeof(*i), GFP_KERNEL);
 480         if(!i)
 481                 return;
 482         i->users=1;
 483         i->interface=dev;
 484         i->multiaddr=IGMP_ALL_HOSTS;
 485         i->tm_running=0;
 486         i->next=dev->ip_mc_list;
 487         dev->ip_mc_list=i;
 488         ip_mc_filter_add(i->interface, i->multiaddr);
 489 
 490 }
 491 
 492 /*
 493  *      Join a socket to a group
 494  */
 495 
 496 int ip_mc_join_group(struct sock *sk , struct device *dev, unsigned long addr)
     /* [previous][next][first][last][top][bottom][index][help] */
 497 {
 498         int unused= -1;
 499         int i;
 500         if(!MULTICAST(addr))
 501                 return -EINVAL;
 502         if(!(dev->flags&IFF_MULTICAST))
 503                 return -EADDRNOTAVAIL;
 504         if(sk->ip_mc_list==NULL)
 505         {
 506                 if((sk->ip_mc_list=(struct ip_mc_socklist *)kmalloc(sizeof(*sk->ip_mc_list), GFP_KERNEL))==NULL)
 507                         return -ENOMEM;
 508                 memset(sk->ip_mc_list,'\0',sizeof(*sk->ip_mc_list));
 509         }
 510         for(i=0;i<IP_MAX_MEMBERSHIPS;i++)
 511         {
 512                 if(sk->ip_mc_list->multiaddr[i]==addr && sk->ip_mc_list->multidev[i]==dev)
 513                         return -EADDRINUSE;
 514                 if(sk->ip_mc_list->multidev[i]==NULL)
 515                         unused=i;
 516         }
 517 
 518         if(unused==-1)
 519                 return -ENOBUFS;
 520         sk->ip_mc_list->multiaddr[unused]=addr;
 521         sk->ip_mc_list->multidev[unused]=dev;
 522         ip_mc_inc_group(dev,addr);
 523         return 0;
 524 }
 525 
 526 /*
 527  *      Ask a socket to leave a group.
 528  */
 529 
 530 int ip_mc_leave_group(struct sock *sk, struct device *dev, unsigned long addr)
     /* [previous][next][first][last][top][bottom][index][help] */
 531 {
 532         int i;
 533         if(!MULTICAST(addr))
 534                 return -EINVAL;
 535         if(!(dev->flags&IFF_MULTICAST))
 536                 return -EADDRNOTAVAIL;
 537         if(sk->ip_mc_list==NULL)
 538                 return -EADDRNOTAVAIL;
 539 
 540         for(i=0;i<IP_MAX_MEMBERSHIPS;i++)
 541         {
 542                 if(sk->ip_mc_list->multiaddr[i]==addr && sk->ip_mc_list->multidev[i]==dev)
 543                 {
 544                         sk->ip_mc_list->multidev[i]=NULL;
 545                         ip_mc_dec_group(dev,addr);
 546                         return 0;
 547                 }
 548         }
 549         return -EADDRNOTAVAIL;
 550 }
 551 
 552 /*
 553  *      A socket is closing.
 554  */
 555 
 556 void ip_mc_drop_socket(struct sock *sk)
     /* [previous][next][first][last][top][bottom][index][help] */
 557 {
 558         int i;
 559 
 560         if(sk->ip_mc_list==NULL)
 561                 return;
 562 
 563         for(i=0;i<IP_MAX_MEMBERSHIPS;i++)
 564         {
 565                 if(sk->ip_mc_list->multidev[i])
 566                 {
 567                         ip_mc_dec_group(sk->ip_mc_list->multidev[i], sk->ip_mc_list->multiaddr[i]);
 568                         sk->ip_mc_list->multidev[i]=NULL;
 569                 }
 570         }
 571         kfree_s(sk->ip_mc_list,sizeof(*sk->ip_mc_list));
 572         sk->ip_mc_list=NULL;
 573 }
 574 
 575 #endif

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