[PATCH] jiffies.h
[opensuse:kernel.git] / net / ipv4 / igmp.c
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.47 2002/02/01 22:01:03 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/jiffies.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)
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, NULL);
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)
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         int len = skb->len;
350
351         if (in_dev==NULL) {
352                 kfree_skb(skb);
353                 return 0;
354         }
355
356         if (skb_is_nonlinear(skb)) {
357                 if (skb_linearize(skb, GFP_ATOMIC) != 0) {
358                         kfree_skb(skb);
359                         return -ENOMEM;
360                 }
361                 ih = skb->h.igmph;
362         }
363
364         if (len < sizeof(struct igmphdr) || ip_compute_csum((void *)ih, len)) {
365                 in_dev_put(in_dev);
366                 kfree_skb(skb);
367                 return 0;
368         }
369
370         switch (ih->type) {
371         case IGMP_HOST_MEMBERSHIP_QUERY:
372                 igmp_heard_query(in_dev, ih->code, ih->group);
373                 break;
374         case IGMP_HOST_MEMBERSHIP_REPORT:
375         case IGMP_HOST_NEW_MEMBERSHIP_REPORT:
376                 /* Is it our report looped back? */
377                 if (((struct rtable*)skb->dst)->key.iif == 0)
378                         break;
379                 igmp_heard_report(in_dev, ih->group);
380                 break;
381         case IGMP_PIM:
382 #ifdef CONFIG_IP_PIMSM_V1
383                 in_dev_put(in_dev);
384                 return pim_rcv_v1(skb);
385 #endif
386         case IGMP_DVMRP:
387         case IGMP_TRACE:
388         case IGMP_HOST_LEAVE_MESSAGE:
389         case IGMP_MTRACE:
390         case IGMP_MTRACE_RESP:
391                 break;
392         default:
393                 NETDEBUG(printk(KERN_DEBUG "New IGMP type=%d, why we do not know about it?\n", ih->type));
394         }
395         in_dev_put(in_dev);
396         kfree_skb(skb);
397         return 0;
398 }
399
400 #endif
401
402
403 /*
404  *      Add a filter to a device
405  */
406
407 static void ip_mc_filter_add(struct in_device *in_dev, u32 addr)
408 {
409         char buf[MAX_ADDR_LEN];
410         struct net_device *dev = in_dev->dev;
411
412         /* Checking for IFF_MULTICAST here is WRONG-WRONG-WRONG.
413            We will get multicast token leakage, when IFF_MULTICAST
414            is changed. This check should be done in dev->set_multicast_list
415            routine. Something sort of:
416            if (dev->mc_list && dev->flags&IFF_MULTICAST) { do it; }
417            --ANK
418            */
419         if (arp_mc_map(addr, buf, dev, 0) == 0)
420                 dev_mc_add(dev,buf,dev->addr_len,0);
421 }
422
423 /*
424  *      Remove a filter from a device
425  */
426
427 static void ip_mc_filter_del(struct in_device *in_dev, u32 addr)
428 {
429         char buf[MAX_ADDR_LEN];
430         struct net_device *dev = in_dev->dev;
431
432         if (arp_mc_map(addr, buf, dev, 0) == 0)
433                 dev_mc_delete(dev,buf,dev->addr_len,0);
434 }
435
436 static void igmp_group_dropped(struct ip_mc_list *im)
437 {
438 #ifdef CONFIG_IP_MULTICAST
439         int reporter;
440 #endif
441
442         if (im->loaded) {
443                 im->loaded = 0;
444                 ip_mc_filter_del(im->interface, im->multiaddr);
445         }
446
447 #ifdef CONFIG_IP_MULTICAST
448         if (im->multiaddr == IGMP_ALL_HOSTS)
449                 return;
450
451         reporter = im->reporter;
452         igmp_stop_timer(im);
453
454         if (reporter && !IGMP_V1_SEEN(im->interface))
455                 igmp_send_report(im->interface->dev, im->multiaddr, IGMP_HOST_LEAVE_MESSAGE);
456 #endif
457 }
458
459 static void igmp_group_added(struct ip_mc_list *im)
460 {
461         if (im->loaded == 0) {
462                 im->loaded = 1;
463                 ip_mc_filter_add(im->interface, im->multiaddr);
464         }
465
466 #ifdef CONFIG_IP_MULTICAST
467         if (im->multiaddr == IGMP_ALL_HOSTS)
468                 return;
469
470         spin_lock_bh(&im->lock);
471         igmp_start_timer(im, IGMP_Initial_Report_Delay);
472         spin_unlock_bh(&im->lock);
473 #endif
474 }
475
476
477 /*
478  *      Multicast list managers
479  */
480
481
482 /*
483  *      A socket has joined a multicast group on device dev.
484  */
485
486 void ip_mc_inc_group(struct in_device *in_dev, u32 addr)
487 {
488         struct ip_mc_list *im;
489
490         ASSERT_RTNL();
491
492         for (im=in_dev->mc_list; im; im=im->next) {
493                 if (im->multiaddr == addr) {
494                         im->users++;
495                         goto out;
496                 }
497         }
498
499         im = (struct ip_mc_list *)kmalloc(sizeof(*im), GFP_KERNEL);
500         if (!im)
501                 goto out;
502
503         im->users=1;
504         im->interface=in_dev;
505         in_dev_hold(in_dev);
506         im->multiaddr=addr;
507         atomic_set(&im->refcnt, 1);
508         spin_lock_init(&im->lock);
509 #ifdef  CONFIG_IP_MULTICAST
510         im->tm_running=0;
511         init_timer(&im->timer);
512         im->timer.data=(unsigned long)im;
513         im->timer.function=&igmp_timer_expire;
514         im->unsolicit_count = IGMP_Unsolicited_Report_Count;
515         im->reporter = 0;
516 #endif
517         im->loaded = 0;
518         write_lock_bh(&in_dev->lock);
519         im->next=in_dev->mc_list;
520         in_dev->mc_list=im;
521         write_unlock_bh(&in_dev->lock);
522         igmp_group_added(im);
523         if (in_dev->dev->flags & IFF_UP)
524                 ip_rt_multicast_event(in_dev);
525 out:
526         return;
527 }
528
529 /*
530  *      A socket has left a multicast group on device dev
531  */
532
533 int ip_mc_dec_group(struct in_device *in_dev, u32 addr)
534 {
535         int err = -ESRCH;
536         struct ip_mc_list *i, **ip;
537         
538         ASSERT_RTNL();
539         
540         for (ip=&in_dev->mc_list; (i=*ip)!=NULL; ip=&i->next) {
541                 if (i->multiaddr==addr) {
542                         if (--i->users == 0) {
543                                 write_lock_bh(&in_dev->lock);
544                                 *ip = i->next;
545                                 write_unlock_bh(&in_dev->lock);
546                                 igmp_group_dropped(i);
547
548                                 if (in_dev->dev->flags & IFF_UP)
549                                         ip_rt_multicast_event(in_dev);
550
551                                 ip_ma_put(i);
552                                 return 0;
553                         }
554                         err = 0;
555                         break;
556                 }
557         }
558         return -ESRCH;
559 }
560
561 /* Device going down */
562
563 void ip_mc_down(struct in_device *in_dev)
564 {
565         struct ip_mc_list *i;
566
567         ASSERT_RTNL();
568
569         for (i=in_dev->mc_list; i; i=i->next)
570                 igmp_group_dropped(i);
571
572         ip_mc_dec_group(in_dev, IGMP_ALL_HOSTS);
573 }
574
575 /* Device going up */
576
577 void ip_mc_up(struct in_device *in_dev)
578 {
579         struct ip_mc_list *i;
580
581         ASSERT_RTNL();
582
583         ip_mc_inc_group(in_dev, IGMP_ALL_HOSTS);
584
585         for (i=in_dev->mc_list; i; i=i->next)
586                 igmp_group_added(i);
587 }
588
589 /*
590  *      Device is about to be destroyed: clean up.
591  */
592
593 void ip_mc_destroy_dev(struct in_device *in_dev)
594 {
595         struct ip_mc_list *i;
596
597         ASSERT_RTNL();
598
599         write_lock_bh(&in_dev->lock);
600         while ((i = in_dev->mc_list) != NULL) {
601                 in_dev->mc_list = i->next;
602                 write_unlock_bh(&in_dev->lock);
603
604                 igmp_group_dropped(i);
605                 ip_ma_put(i);
606
607                 write_lock_bh(&in_dev->lock);
608         }
609         write_unlock_bh(&in_dev->lock);
610 }
611
612 static struct in_device * ip_mc_find_dev(struct ip_mreqn *imr)
613 {
614         struct rtable *rt;
615         struct net_device *dev = NULL;
616         struct in_device *idev = NULL;
617
618         if (imr->imr_address.s_addr) {
619                 dev = ip_dev_find(imr->imr_address.s_addr);
620                 if (!dev)
621                         return NULL;
622                 __dev_put(dev);
623         }
624
625         if (!dev && !ip_route_output(&rt, imr->imr_multiaddr.s_addr, 0, 0, 0)) {
626                 dev = rt->u.dst.dev;
627                 ip_rt_put(rt);
628         }
629         if (dev) {
630                 imr->imr_ifindex = dev->ifindex;
631                 idev = __in_dev_get(dev);
632         }
633         return idev;
634 }
635
636 /*
637  *      Join a socket to a group
638  */
639 int sysctl_igmp_max_memberships = IP_MAX_MEMBERSHIPS;
640
641 int ip_mc_join_group(struct sock *sk , struct ip_mreqn *imr)
642 {
643         int err;
644         u32 addr = imr->imr_multiaddr.s_addr;
645         struct ip_mc_socklist *iml, *i;
646         struct in_device *in_dev;
647         struct inet_opt *inet = inet_sk(sk);
648         int count = 0;
649
650         if (!MULTICAST(addr))
651                 return -EINVAL;
652
653         rtnl_shlock();
654
655         if (!imr->imr_ifindex)
656                 in_dev = ip_mc_find_dev(imr);
657         else {
658                 in_dev = inetdev_by_index(imr->imr_ifindex);
659                 if (in_dev)
660                         __in_dev_put(in_dev);
661         }
662
663         if (!in_dev) {
664                 iml = NULL;
665                 err = -ENODEV;
666                 goto done;
667         }
668
669         iml = (struct ip_mc_socklist *)sock_kmalloc(sk, sizeof(*iml), GFP_KERNEL);
670
671         err = -EADDRINUSE;
672         for (i = inet->mc_list; i; i = i->next) {
673                 if (memcmp(&i->multi, imr, sizeof(*imr)) == 0) {
674                         /* New style additions are reference counted */
675                         if (imr->imr_address.s_addr == 0) {
676                                 i->count++;
677                                 err = 0;
678                         }
679                         goto done;
680                 }
681                 count++;
682         }
683         err = -ENOBUFS;
684         if (iml == NULL || count >= sysctl_igmp_max_memberships)
685                 goto done;
686         memcpy(&iml->multi, imr, sizeof(*imr));
687         iml->next = inet->mc_list;
688         iml->count = 1;
689         inet->mc_list = iml;
690         ip_mc_inc_group(in_dev, addr);
691         iml = NULL;
692         err = 0;
693
694 done:
695         rtnl_shunlock();
696         if (iml)
697                 sock_kfree_s(sk, iml, sizeof(*iml));
698         return err;
699 }
700
701 /*
702  *      Ask a socket to leave a group.
703  */
704
705 int ip_mc_leave_group(struct sock *sk, struct ip_mreqn *imr)
706 {
707         struct inet_opt *inet = inet_sk(sk);
708         struct ip_mc_socklist *iml, **imlp;
709
710         rtnl_lock();
711         for (imlp = &inet->mc_list; (iml = *imlp) != NULL; imlp = &iml->next) {
712                 if (iml->multi.imr_multiaddr.s_addr==imr->imr_multiaddr.s_addr &&
713                     iml->multi.imr_address.s_addr==imr->imr_address.s_addr &&
714                     (!imr->imr_ifindex || iml->multi.imr_ifindex==imr->imr_ifindex)) {
715                         struct in_device *in_dev;
716                         if (--iml->count) {
717                                 rtnl_unlock();
718                                 return 0;
719                         }
720
721                         *imlp = iml->next;
722
723                         in_dev = inetdev_by_index(iml->multi.imr_ifindex);
724                         if (in_dev) {
725                                 ip_mc_dec_group(in_dev, imr->imr_multiaddr.s_addr);
726                                 in_dev_put(in_dev);
727                         }
728                         rtnl_unlock();
729                         sock_kfree_s(sk, iml, sizeof(*iml));
730                         return 0;
731                 }
732         }
733         rtnl_unlock();
734         return -EADDRNOTAVAIL;
735 }
736
737 /*
738  *      A socket is closing.
739  */
740
741 void ip_mc_drop_socket(struct sock *sk)
742 {
743         struct inet_opt *inet = inet_sk(sk);
744         struct ip_mc_socklist *iml;
745
746         if (inet->mc_list == NULL)
747                 return;
748
749         rtnl_lock();
750         while ((iml = inet->mc_list) != NULL) {
751                 struct in_device *in_dev;
752                 inet->mc_list = iml->next;
753
754                 if ((in_dev = inetdev_by_index(iml->multi.imr_ifindex)) != NULL) {
755                         ip_mc_dec_group(in_dev, iml->multi.imr_multiaddr.s_addr);
756                         in_dev_put(in_dev);
757                 }
758                 sock_kfree_s(sk, iml, sizeof(*iml));
759
760         }
761         rtnl_unlock();
762 }
763
764 int ip_check_mc(struct in_device *in_dev, u32 mc_addr)
765 {
766         struct ip_mc_list *im;
767
768         read_lock(&in_dev->lock);
769         for (im=in_dev->mc_list; im; im=im->next) {
770                 if (im->multiaddr == mc_addr) {
771                         read_unlock(&in_dev->lock);
772                         return 1;
773                 }
774         }
775         read_unlock(&in_dev->lock);
776         return 0;
777 }
778
779
780 #ifdef CONFIG_IP_MULTICAST
781  
782 int ip_mc_procinfo(char *buffer, char **start, off_t offset, int length)
783 {
784         off_t pos=0, begin=0;
785         struct ip_mc_list *im;
786         int len=0;
787         struct net_device *dev;
788
789         len=sprintf(buffer,"Idx\tDevice    : Count Querier\tGroup    Users Timer\tReporter\n");  
790
791         read_lock(&dev_base_lock);
792         for(dev = dev_base; dev; dev = dev->next) {
793                 struct in_device *in_dev = in_dev_get(dev);
794                 char   *querier = "NONE";
795
796                 if (in_dev == NULL)
797                         continue;
798
799                 querier = IGMP_V1_SEEN(in_dev) ? "V1" : "V2";
800
801                 len+=sprintf(buffer+len,"%d\t%-10s: %5d %7s\n",
802                              dev->ifindex, dev->name, dev->mc_count, querier);
803
804                 read_lock(&in_dev->lock);
805                 for (im = in_dev->mc_list; im; im = im->next) {
806                         len+=sprintf(buffer+len,
807                                      "\t\t\t\t%08lX %5d %d:%08lX\t\t%d\n",
808                                      im->multiaddr, im->users,
809                                      im->tm_running, im->timer.expires-jiffies, im->reporter);
810
811                         pos=begin+len;
812                         if(pos<offset)
813                         {
814                                 len=0;
815                                 begin=pos;
816                         }
817                         if(pos>offset+length) {
818                                 read_unlock(&in_dev->lock);
819                                 in_dev_put(in_dev);
820                                 goto done;
821                         }
822                 }
823                 read_unlock(&in_dev->lock);
824                 in_dev_put(in_dev);
825         }
826 done:
827         read_unlock(&dev_base_lock);
828
829         *start=buffer+(offset-begin);
830         len-=(offset-begin);
831         if(len>length)
832                 len=length;
833         if(len<0)
834                 len=0;
835         return len;
836 }
837 #endif
838