~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Linux Cross Reference
Linux/net/ipv4/igmp.c

Version: ~ [ 2.4.0 ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 /*
  2  *      Linux NET3:     Internet Group Management Protocol  [IGMP]
  3  *
  4  *      This code implements the IGMP protocol as defined in RFC1112. 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 didn't come out right using gcc 2.5.8, the newer one
  9  *      seems to fall out with gcc 2.6.2.
 10  *
 11  *      Version: $Id: igmp.c,v 1.41 2000/08/31 23:39:12 davem Exp $
 12  *
 13  *      Authors:
 14  *              Alan Cox <Alan.Cox@linux.org>
 15  *
 16  *      This program is free software; you can redistribute it and/or
 17  *      modify it under the terms of the GNU General Public License
 18  *      as published by the Free Software Foundation; either version
 19  *      2 of the License, or (at your option) any later version.
 20  *
 21  *      Fixes:
 22  *
 23  *              Alan Cox        :       Added lots of __inline__ to optimise
 24  *                                      the memory usage of all the tiny little
 25  *                                      functions.
 26  *              Alan Cox        :       Dumped the header building experiment.
 27  *              Alan Cox        :       Minor tweaks ready for multicast routing
 28  *                                      and extended IGMP protocol.
 29  *              Alan Cox        :       Removed a load of inline directives. Gcc 2.5.8
 30  *                                      writes utterly bogus code otherwise (sigh)
 31  *                                      fixed IGMP loopback to behave in the manner
 32  *                                      desired by mrouted, fixed the fact it has been
 33  *                                      broken since 1.3.6 and cleaned up a few minor
 34  *                                      points.
 35  *
 36  *              Chih-Jen Chang  :       Tried to revise IGMP to Version 2
 37  *              Tsu-Sheng Tsao          E-mail: chihjenc@scf.usc.edu and tsusheng@scf.usc.edu
 38  *                                      The enhancements are mainly based on Steve Deering's 
 39  *                                      ipmulti-3.5 source code.
 40  *              Chih-Jen Chang  :       Added the igmp_get_mrouter_info and
 41  *              Tsu-Sheng Tsao          igmp_set_mrouter_info to keep track of
 42  *                                      the mrouted version on that device.
 43  *              Chih-Jen Chang  :       Added the max_resp_time parameter to
 44  *              Tsu-Sheng Tsao          igmp_heard_query(). Using this parameter
 45  *                                      to identify the multicast router version
 46  *                                      and do what the IGMP version 2 specified.
 47  *              Chih-Jen Chang  :       Added a timer to revert to IGMP V2 router
 48  *              Tsu-Sheng Tsao          if the specified time expired.
 49  *              Alan Cox        :       Stop IGMP from 0.0.0.0 being accepted.
 50  *              Alan Cox        :       Use GFP_ATOMIC in the right places.
 51  *              Christian Daudt :       igmp timer wasn't set for local group
 52  *                                      memberships but was being deleted, 
 53  *                                      which caused a "del_timer() called 
 54  *                                      from %p with timer not initialized\n"
 55  *                                      message (960131).
 56  *              Christian Daudt :       removed del_timer from 
 57  *                                      igmp_timer_expire function (960205).
 58  *             Christian Daudt :       igmp_heard_report now only calls
 59  *                                     igmp_timer_expire if tm->running is
 60  *                                     true (960216).
 61  *              Malcolm Beattie :       ttl comparison wrong in igmp_rcv made
 62  *                                      igmp_heard_query never trigger. Expiry
 63  *                                      miscalculation fixed in igmp_heard_query
 64  *                                      and random() made to return unsigned to
 65  *                                      prevent negative expiry times.
 66  *              Alexey Kuznetsov:       Wrong group leaving behaviour, backport
 67  *                                      fix from pending 2.1.x patches.
 68  *              Alan Cox:               Forget to enable FDDI support earlier.
 69  *              Alexey Kuznetsov:       Fixed leaving groups on device down.
 70  *              Alexey Kuznetsov:       Accordance to igmp-v2-06 draft.
 71  */
 72 
 73 
 74 #include <linux/config.h>
 75 #include <asm/uaccess.h>
 76 #include <asm/system.h>
 77 #include <linux/types.h>
 78 #include <linux/kernel.h>
 79 #include <linux/sched.h>
 80 #include <linux/string.h>
 81 #include <linux/socket.h>
 82 #include <linux/sockios.h>
 83 #include <linux/in.h>
 84 #include <linux/inet.h>
 85 #include <linux/netdevice.h>
 86 #include <linux/skbuff.h>
 87 #include <linux/inetdevice.h>
 88 #include <linux/igmp.h>
 89 #include <linux/if_arp.h>
 90 #include <linux/rtnetlink.h>
 91 #include <net/ip.h>
 92 #include <net/protocol.h>
 93 #include <net/route.h>
 94 #include <net/sock.h>
 95 #include <net/checksum.h>
 96 #include <linux/netfilter_ipv4.h>
 97 #ifdef CONFIG_IP_MROUTE
 98 #include <linux/mroute.h>
 99 #endif
100 
101 
102 #define IP_MAX_MEMBERSHIPS 20
103 
104 #ifdef CONFIG_IP_MULTICAST
105 
106 
107 /* Parameter names and values are taken from igmp-v2-06 draft */
108 
109 #define IGMP_V1_Router_Present_Timeout          (400*HZ)
110 #define IGMP_Unsolicited_Report_Interval        (10*HZ)
111 #define IGMP_Query_Response_Interval            (10*HZ)
112 #define IGMP_Unsolicited_Report_Count           2
113 
114 
115 #define IGMP_Initial_Report_Delay               (1*HZ)
116 
117 /* IGMP_Initial_Report_Delay is not from IGMP specs!
118  * IGMP specs require to report membership immediately after
119  * joining a group, but we delay the first report by a
120  * small interval. It seems more natural and still does not
121  * contradict to specs provided this delay is small enough.
122  */
123 
124 #define IGMP_V1_SEEN(in_dev) ((in_dev)->mr_v1_seen && (long)(jiffies - (in_dev)->mr_v1_seen) < 0)
125 
126 #endif
127 
128 static void ip_ma_put(struct ip_mc_list *im)
129 {
130         if (atomic_dec_and_test(&im->refcnt)) {
131                 in_dev_put(im->interface);
132                 kfree(im);
133         }
134 }
135 
136 #ifdef CONFIG_IP_MULTICAST
137 
138 /*
139  *      Timer management
140  */
141 
142 static __inline__ void igmp_stop_timer(struct ip_mc_list *im)
143 {
144         spin_lock_bh(&im->lock);
145         if (del_timer(&im->timer))
146                 atomic_dec(&im->refcnt);
147         im->tm_running=0;
148         im->reporter = 0;
149         im->unsolicit_count = 0;
150         spin_unlock_bh(&im->lock);
151 }
152 
153 /* It must be called with locked im->lock */
154 static void igmp_start_timer(struct ip_mc_list *im, int max_delay)
155 {
156         int tv=net_random() % max_delay;
157 
158         im->tm_running=1;
159         if (!mod_timer(&im->timer, jiffies+tv+2))
160                 atomic_inc(&im->refcnt);
161 }
162 
163 static void igmp_mod_timer(struct ip_mc_list *im, int max_delay)
164 {
165         spin_lock_bh(&im->lock);
166         im->unsolicit_count = 0;
167         if (del_timer(&im->timer)) {
168                 if ((long)(im->timer.expires-jiffies) < max_delay) {
169                         add_timer(&im->timer);
170                         im->tm_running=1;
171                         spin_unlock_bh(&im->lock);
172                         return;
173                 }
174                 atomic_dec(&im->refcnt);
175         }
176         igmp_start_timer(im, max_delay);
177         spin_unlock_bh(&im->lock);
178 }
179 
180 
181 /*
182  *      Send an IGMP report.
183  */
184 
185 #define IGMP_SIZE (sizeof(struct igmphdr)+sizeof(struct iphdr)+4)
186 
187 /* Don't just hand NF_HOOK skb->dst->output, in case netfilter hook
188    changes route */
189 static inline int
190 output_maybe_reroute(struct sk_buff *skb)
191 {
192         return skb->dst->output(skb);
193 }
194 
195 static int igmp_send_report(struct net_device *dev, u32 group, int type)
196 {
197         struct sk_buff *skb;
198         struct iphdr *iph;
199         struct igmphdr *ih;
200         struct rtable *rt;
201         u32     dst;
202 
203         /* According to IGMPv2 specs, LEAVE messages are
204          * sent to all-routers group.
205          */
206         dst = group;
207         if (type == IGMP_HOST_LEAVE_MESSAGE)
208                 dst = IGMP_ALL_ROUTER;
209 
210         if (ip_route_output(&rt, dst, 0, 0, dev->ifindex))
211                 return -1;
212         if (rt->rt_src == 0) {
213                 ip_rt_put(rt);
214                 return -1;
215         }
216 
217         skb=alloc_skb(IGMP_SIZE+dev->hard_header_len+15, GFP_ATOMIC);
218         if (skb == NULL) {
219                 ip_rt_put(rt);
220                 return -1;
221         }
222 
223         skb->dst = &rt->u.dst;
224 
225         skb_reserve(skb, (dev->hard_header_len+15)&~15);
226 
227         skb->nh.iph = iph = (struct iphdr *)skb_put(skb, sizeof(struct iphdr)+4);
228 
229         iph->version  = 4;
230         iph->ihl      = (sizeof(struct iphdr)+4)>>2;
231         iph->tos      = 0;
232         iph->frag_off = __constant_htons(IP_DF);
233         iph->ttl      = 1;
234         iph->daddr    = dst;
235         iph->saddr    = rt->rt_src;
236         iph->protocol = IPPROTO_IGMP;
237         iph->tot_len  = htons(IGMP_SIZE);
238         ip_select_ident(iph, &rt->u.dst);
239         ((u8*)&iph[1])[0] = IPOPT_RA;
240         ((u8*)&iph[1])[1] = 4;
241         ((u8*)&iph[1])[2] = 0;
242         ((u8*)&iph[1])[3] = 0;
243         ip_send_check(iph);
244 
245         ih = (struct igmphdr *)skb_put(skb, sizeof(struct igmphdr));
246         ih->type=type;
247         ih->code=0;
248         ih->csum=0;
249         ih->group=group;
250         ih->csum=ip_compute_csum((void *)ih, sizeof(struct igmphdr));
251 
252         return NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, rt->u.dst.dev,
253                        output_maybe_reroute);
254 }
255 
256 
257 static void igmp_timer_expire(unsigned long data)
258 {
259         struct ip_mc_list *im=(struct ip_mc_list *)data;
260         struct in_device *in_dev = im->interface;
261         int err;
262 
263         spin_lock(&im->lock);
264         im->tm_running=0;
265 
266         if (IGMP_V1_SEEN(in_dev))
267                 err = igmp_send_report(in_dev->dev, im->multiaddr, IGMP_HOST_MEMBERSHIP_REPORT);
268         else
269                 err = igmp_send_report(in_dev->dev, im->multiaddr, IGMP_HOST_NEW_MEMBERSHIP_REPORT);
270 
271         /* Failed. Retry later. */
272         if (err) {
273                 if (!in_dev->dead)
274                         igmp_start_timer(im, IGMP_Unsolicited_Report_Interval);
275                 goto out;
276         }
277 
278         if (im->unsolicit_count) {
279                 im->unsolicit_count--;
280                 igmp_start_timer(im, IGMP_Unsolicited_Report_Interval);
281         }
282         im->reporter = 1;
283 out:
284         spin_unlock(&im->lock);
285         ip_ma_put(im);
286 }
287 
288 static void igmp_heard_report(struct in_device *in_dev, u32 group)
289 {
290         struct ip_mc_list *im;
291 
292         /* Timers are only set for non-local groups */
293 
294         if (group == IGMP_ALL_HOSTS)
295                 return;
296 
297         read_lock(&in_dev->lock);
298         for (im=in_dev->mc_list; im!=NULL; im=im->next) {
299                 if (im->multiaddr == group) {
300                         igmp_stop_timer(im);
301                         break;
302                 }
303         }
304         read_unlock(&in_dev->lock);
305 }
306 
307 static void igmp_heard_query(struct in_device *in_dev, unsigned char max_resp_time,
308                              u32 group)
309 {
310         struct ip_mc_list       *im;
311         int                     max_delay;
312 
313         max_delay = max_resp_time*(HZ/IGMP_TIMER_SCALE);
314 
315         if (max_resp_time == 0) {
316                 /* Alas, old v1 router presents here. */
317 
318                 max_delay = IGMP_Query_Response_Interval;
319                 in_dev->mr_v1_seen = jiffies + IGMP_V1_Router_Present_Timeout;
320                 group = 0;
321         }
322 
323         /*
324          * - Start the timers in all of our membership records
325          *   that the query applies to for the interface on
326          *   which the query arrived excl. those that belong
327          *   to a "local" group (224.0.0.X)
328          * - For timers already running check if they need to
329          *   be reset.
330          * - Use the igmp->igmp_code field as the maximum
331          *   delay possible
332          */
333         read_lock(&in_dev->lock);
334         for (im=in_dev->mc_list; im!=NULL; im=im->next) {
335                 if (group && group != im->multiaddr)
336                         continue;
337                 if (im->multiaddr == IGMP_ALL_HOSTS)
338                         continue;
339                 igmp_mod_timer(im, max_delay);
340         }
341         read_unlock(&in_dev->lock);
342 }
343 
344 int igmp_rcv(struct sk_buff *skb, unsigned short len)
345 {
346         /* This basically follows the spec line by line -- see RFC1112 */
347         struct igmphdr *ih = skb->h.igmph;
348         struct in_device *in_dev = in_dev_get(skb->dev);
349 
350         if (in_dev==NULL) {
351                 kfree_skb(skb);
352                 return 0;
353         }
354 
355         if (len < sizeof(struct igmphdr) || ip_compute_csum((void *)ih, len)) {
356                 in_dev_put(in_dev);
357                 kfree_skb(skb);
358                 return 0;
359         }
360         
361         switch (ih->type) {
362         case IGMP_HOST_MEMBERSHIP_QUERY:
363                 igmp_heard_query(in_dev, ih->code, ih->group);
364                 break;
365         case IGMP_HOST_MEMBERSHIP_REPORT:
366         case IGMP_HOST_NEW_MEMBERSHIP_REPORT:
367                 /* Is it our report looped back? */
368                 if (((struct rtable*)skb->dst)->key.iif == 0)
369                         break;
370                 igmp_heard_report(in_dev, ih->group);
371                 break;
372         case IGMP_PIM:
373 #ifdef CONFIG_IP_PIMSM_V1
374                 in_dev_put(in_dev);
375                 return pim_rcv_v1(skb, len);
376 #endif
377         case IGMP_DVMRP:
378         case IGMP_TRACE:
379         case IGMP_HOST_LEAVE_MESSAGE:
380         case IGMP_MTRACE:
381         case IGMP_MTRACE_RESP:
382                 break;
383         default:
384                 NETDEBUG(printk(KERN_DEBUG "New IGMP type=%d, why we do not know about it?\n", ih->type));
385         }
386         in_dev_put(in_dev);
387         kfree_skb(skb);
388         return 0;
389 }
390 
391 #endif
392 
393 
394 /*
395  *      Add a filter to a device
396  */
397 
398 static void ip_mc_filter_add(struct in_device *in_dev, u32 addr)
399 {
400         char buf[MAX_ADDR_LEN];
401         struct net_device *dev = in_dev->dev;
402 
403         /* Checking for IFF_MULTICAST here is WRONG-WRONG-WRONG.
404            We will get multicast token leakage, when IFF_MULTICAST
405            is changed. This check should be done in dev->set_multicast_list
406            routine. Something sort of:
407            if (dev->mc_list && dev->flags&IFF_MULTICAST) { do it; }
408            --ANK
409            */
410         if (arp_mc_map(addr, buf, dev, 0) == 0)
411                 dev_mc_add(dev,buf,dev->addr_len,0);
412 }
413 
414 /*
415  *      Remove a filter from a device
416  */
417 
418 static void ip_mc_filter_del(struct in_device *in_dev, u32 addr)
419 {
420         char buf[MAX_ADDR_LEN];
421         struct net_device *dev = in_dev->dev;
422 
423         if (arp_mc_map(addr, buf, dev, 0) == 0)
424                 dev_mc_delete(dev,buf,dev->addr_len,0);
425 }
426 
427 static void igmp_group_dropped(struct ip_mc_list *im)
428 {
429 #ifdef CONFIG_IP_MULTICAST
430         int reporter;
431 #endif
432 
433         if (im->loaded) {
434                 im->loaded = 0;
435                 ip_mc_filter_del(im->interface, im->multiaddr);
436         }
437 
438 #ifdef CONFIG_IP_MULTICAST
439         if (im->multiaddr == IGMP_ALL_HOSTS)
440                 return;
441 
442         reporter = im->reporter;
443         igmp_stop_timer(im);
444 
445         if (reporter && !IGMP_V1_SEEN(im->interface))
446                 igmp_send_report(im->interface->dev, im->multiaddr, IGMP_HOST_LEAVE_MESSAGE);
447 #endif
448 }
449 
450 static void igmp_group_added(struct ip_mc_list *im)
451 {
452         if (im->loaded == 0) {
453                 im->loaded = 1;
454                 ip_mc_filter_add(im->interface, im->multiaddr);
455         }
456 
457 #ifdef CONFIG_IP_MULTICAST
458         if (im->multiaddr == IGMP_ALL_HOSTS)
459                 return;
460 
461         spin_lock_bh(&im->lock);
462         igmp_start_timer(im, IGMP_Initial_Report_Delay);
463         spin_unlock_bh(&im->lock);
464 #endif
465 }
466 
467 
468 /*
469  *      Multicast list managers
470  */
471 
472 
473 /*
474  *      A socket has joined a multicast group on device dev.
475  */
476 
477 void ip_mc_inc_group(struct in_device *in_dev, u32 addr)
478 {
479         struct ip_mc_list *im;
480 
481         ASSERT_RTNL();
482 
483         for (im=in_dev->mc_list; im; im=im->next) {
484                 if (im->multiaddr == addr) {
485                         im->users++;
486                         goto out;
487                 }
488         }
489 
490         im = (struct ip_mc_list *)kmalloc(sizeof(*im), GFP_KERNEL);
491         if (!im)
492                 goto out;
493 
494         im->users=1;
495         im->interface=in_dev;
496         in_dev_hold(in_dev);
497         im->multiaddr=addr;
498         atomic_set(&im->refcnt, 1);
499         spin_lock_init(&im->lock);
500 #ifdef  CONFIG_IP_MULTICAST
501         im->tm_running=0;
502         init_timer(&im->timer);
503         im->timer.data=(unsigned long)im;
504         im->timer.function=&igmp_timer_expire;
505         im->unsolicit_count = IGMP_Unsolicited_Report_Count;
506         im->reporter = 0;
507         im->loaded = 0;
508 #endif
509         write_lock_bh(&in_dev->lock);
510         im->next=in_dev->mc_list;
511         in_dev->mc_list=im;
512         write_unlock_bh(&in_dev->lock);
513         igmp_group_added(im);
514         if (in_dev->dev->flags & IFF_UP)
515                 ip_rt_multicast_event(in_dev);
516 out:
517         return;
518 }
519 
520 /*
521  *      A socket has left a multicast group on device dev
522  */
523 
524 int ip_mc_dec_group(struct in_device *in_dev, u32 addr)
525 {
526         int err = -ESRCH;
527         struct ip_mc_list *i, **ip;
528         
529         ASSERT_RTNL();
530         
531         for (ip=&in_dev->mc_list; (i=*ip)!=NULL; ip=&i->next) {
532                 if (i->multiaddr==addr) {
533                         if (--i->users == 0) {
534                                 write_lock_bh(&in_dev->lock);
535                                 *ip = i->next;
536                                 write_unlock_bh(&in_dev->lock);
537                                 igmp_group_dropped(i);
538 
539                                 if (in_dev->dev->flags & IFF_UP)
540                                         ip_rt_multicast_event(in_dev);
541 
542                                 ip_ma_put(i);
543                                 return 0;
544                         }
545                         err = 0;
546                         break;
547                 }
548         }
549         return -ESRCH;
550 }
551 
552 /* Device going down */
553 
554 void ip_mc_down(struct in_device *in_dev)
555 {
556         struct ip_mc_list *i;
557 
558         ASSERT_RTNL();
559 
560         for (i=in_dev->mc_list; i; i=i->next)
561                 igmp_group_dropped(i);
562 
563         ip_mc_dec_group(in_dev, IGMP_ALL_HOSTS);
564 }
565 
566 /* Device going up */
567 
568 void ip_mc_up(struct in_device *in_dev)
569 {
570         struct ip_mc_list *i;
571 
572         ASSERT_RTNL();
573 
574         ip_mc_inc_group(in_dev, IGMP_ALL_HOSTS);
575 
576         for (i=in_dev->mc_list; i; i=i->next)
577                 igmp_group_added(i);
578 }
579 
580 /*
581  *      Device is about to be destroyed: clean up.
582  */
583 
584 void ip_mc_destroy_dev(struct in_device *in_dev)
585 {
586         struct ip_mc_list *i;
587 
588         ASSERT_RTNL();
589 
590         write_lock_bh(&in_dev->lock);
591         while ((i = in_dev->mc_list) != NULL) {
592                 in_dev->mc_list = i->next;
593                 write_unlock_bh(&in_dev->lock);
594 
595                 igmp_group_dropped(i);
596                 ip_ma_put(i);
597 
598                 write_lock_bh(&in_dev->lock);
599         }
600         write_unlock_bh(&in_dev->lock);
601 }
602 
603 static struct in_device * ip_mc_find_dev(struct ip_mreqn *imr)
604 {
605         struct rtable *rt;
606         struct net_device *dev = NULL;
607         struct in_device *idev = NULL;
608 
609         if (imr->imr_address.s_addr) {
610                 dev = ip_dev_find(imr->imr_address.s_addr);
611                 if (!dev)
612                         return NULL;
613                 __dev_put(dev);
614         }
615 
616         if (!dev && !ip_route_output(&rt, imr->imr_multiaddr.s_addr, 0, 0, 0)) {
617                 dev = rt->u.dst.dev;
618                 ip_rt_put(rt);
619         }
620         if (dev) {
621                 imr->imr_ifindex = dev->ifindex;
622                 idev = __in_dev_get(dev);
623         }
624         return idev;
625 }
626 
627 /*
628  *      Join a socket to a group
629  */
630 int sysctl_igmp_max_memberships = IP_MAX_MEMBERSHIPS;
631 
632 int ip_mc_join_group(struct sock *sk , struct ip_mreqn *imr)
633 {
634         int err;
635         u32 addr = imr->imr_multiaddr.s_addr;
636         struct ip_mc_socklist *iml, *i;
637         struct in_device *in_dev;
638         int count = 0;
639 
640         if (!MULTICAST(addr))
641                 return -EINVAL;
642 
643         rtnl_shlock();
644 
645         if (!imr->imr_ifindex)
646                 in_dev = ip_mc_find_dev(imr);
647         else {
648                 in_dev = inetdev_by_index(imr->imr_ifindex);
649                 if (in_dev)
650                         __in_dev_put(in_dev);
651         }
652 
653         if (!in_dev) {
654                 iml = NULL;
655                 err = -ENODEV;
656                 goto done;
657         }
658 
659         iml = (struct ip_mc_socklist *)sock_kmalloc(sk, sizeof(*iml), GFP_KERNEL);
660 
661         err = -EADDRINUSE;
662         for (i=sk->protinfo.af_inet.mc_list; i; i=i->next) {
663                 if (memcmp(&i->multi, imr, sizeof(*imr)) == 0) {
664                         /* New style additions are reference counted */
665                         if (imr->imr_address.s_addr == 0) {
666                                 i->count++;
667                                 err = 0;
668                         }
669                         goto done;
670                 }
671                 count++;
672         }
673         err = -ENOBUFS;
674         if (iml == NULL || count >= sysctl_igmp_max_memberships)
675                 goto done;
676         memcpy(&iml->multi, imr, sizeof(*imr));
677         iml->next = sk->protinfo.af_inet.mc_list;
678         iml->count = 1;
679         sk->protinfo.af_inet.mc_list = iml;
680         ip_mc_inc_group(in_dev, addr);
681         iml = NULL;
682         err = 0;
683 
684 done:
685         rtnl_shunlock();
686         if (iml)
687                 sock_kfree_s(sk, iml, sizeof(*iml));
688         return err;
689 }
690 
691 /*
692  *      Ask a socket to leave a group.
693  */
694 
695 int ip_mc_leave_group(struct sock *sk, struct ip_mreqn *imr)
696 {
697         struct ip_mc_socklist *iml, **imlp;
698 
699         rtnl_lock();
700         for (imlp=&sk->protinfo.af_inet.mc_list; (iml=*imlp)!=NULL; imlp=&iml->next) {
701                 if (iml->multi.imr_multiaddr.s_addr==imr->imr_multiaddr.s_addr &&
702                     iml->multi.imr_address.s_addr==imr->imr_address.s_addr &&
703                     (!imr->imr_ifindex || iml->multi.imr_ifindex==imr->imr_ifindex)) {
704                         struct in_device *in_dev;
705                         if (--iml->count) {
706                                 rtnl_unlock();
707                                 return 0;
708                         }
709 
710                         *imlp = iml->next;
711 
712                         in_dev = inetdev_by_index(iml->multi.imr_ifindex);
713                         if (in_dev) {
714                                 ip_mc_dec_group(in_dev, imr->imr_multiaddr.s_addr);
715                                 in_dev_put(in_dev);
716                         }
717                         rtnl_unlock();
718                         sock_kfree_s(sk, iml, sizeof(*iml));
719                         return 0;
720                 }
721         }
722         rtnl_unlock();
723         return -EADDRNOTAVAIL;
724 }
725 
726 /*
727  *      A socket is closing.
728  */
729 
730 void ip_mc_drop_socket(struct sock *sk)
731 {
732         struct ip_mc_socklist *iml;
733 
734         if (sk->protinfo.af_inet.mc_list == NULL)
735                 return;
736 
737         rtnl_lock();
738         while ((iml=sk->protinfo.af_inet.mc_list) != NULL) {
739                 struct in_device *in_dev;
740                 sk->protinfo.af_inet.mc_list = iml->next;
741 
742                 if ((in_dev = inetdev_by_index(iml->multi.imr_ifindex)) != NULL) {
743                         ip_mc_dec_group(in_dev, iml->multi.imr_multiaddr.s_addr);
744                         in_dev_put(in_dev);
745                 }
746                 sock_kfree_s(sk, iml, sizeof(*iml));
747 
748         }
749         rtnl_unlock();
750 }
751 
752 int ip_check_mc(struct in_device *in_dev, u32 mc_addr)
753 {
754         struct ip_mc_list *im;
755 
756         read_lock(&in_dev->lock);
757         for (im=in_dev->mc_list; im; im=im->next) {
758                 if (im->multiaddr == mc_addr) {
759                         read_unlock(&in_dev->lock);
760                         return 1;
761                 }
762         }
763         read_unlock(&in_dev->lock);
764         return 0;
765 }
766 
767 
768 #ifdef CONFIG_IP_MULTICAST
769  
770 int ip_mc_procinfo(char *buffer, char **start, off_t offset, int length)
771 {
772         off_t pos=0, begin=0;
773         struct ip_mc_list *im;
774         int len=0;
775         struct net_device *dev;
776 
777         len=sprintf(buffer,"Idx\tDevice    : Count Querier\tGroup    Users Timer\tReporter\n");  
778 
779         read_lock(&dev_base_lock);
780         for(dev = dev_base; dev; dev = dev->next) {
781                 struct in_device *in_dev = in_dev_get(dev);
782                 char   *querier = "NONE";
783 
784                 if (in_dev == NULL)
785                         continue;
786 
787                 querier = IGMP_V1_SEEN(in_dev) ? "V1" : "V2";
788 
789                 len+=sprintf(buffer+len,"%d\t%-10s: %5d %7s\n",
790                              dev->ifindex, dev->name, dev->mc_count, querier);
791 
792                 read_lock(&in_dev->lock);
793                 for (im = in_dev->mc_list; im; im = im->next) {
794                         len+=sprintf(buffer+len,
795                                      "\t\t\t\t%08lX %5d %d:%08lX\t\t%d\n",
796                                      im->multiaddr, im->users,
797                                      im->tm_running, im->timer.expires-jiffies, im->reporter);
798 
799                         pos=begin+len;
800                         if(pos<offset)
801                         {
802                                 len=0;
803                                 begin=pos;
804                         }
805                         if(pos>offset+length) {
806                                 read_unlock(&in_dev->lock);
807                                 in_dev_put(in_dev);
808                                 goto done;
809                         }
810                 }
811                 read_unlock(&in_dev->lock);
812                 in_dev_put(in_dev);
813         }
814 done:
815         read_unlock(&dev_base_lock);
816 
817         *start=buffer+(offset-begin);
818         len-=(offset-begin);
819         if(len>length)
820                 len=length;
821         if(len<0)
822                 len=0;
823         return len;
824 }
825 #endif
826 
827 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

This page was automatically generated by the LXR engine.
Visit the LXR main site for more information.