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

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