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

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