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

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