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

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