root/net/ipv4/igmp.c

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

DEFINITIONS

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

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