linux-old/net/ipv4/igmp.c
<<
>>
Prefs
   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.30 1999/03/25 10:04:10 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#ifdef CONFIG_IP_MROUTE
  97#include <linux/mroute.h>
  98#endif
  99
 100#define IP_MAX_MEMBERSHIPS 20
 101
 102#ifdef CONFIG_IP_MULTICAST
 103
 104/* Parameter names and values are taken from igmp-v2-06 draft */
 105
 106#define IGMP_V1_Router_Present_Timeout          (400*HZ)
 107#define IGMP_Unsolicited_Report_Interval        (10*HZ)
 108#define IGMP_Query_Response_Interval            (10*HZ)
 109#define IGMP_Unsolicited_Report_Count           2
 110
 111
 112#define IGMP_Initial_Report_Delay               (1*HZ)
 113
 114/* IGMP_Initial_Report_Delay is not from IGMP specs!
 115 * IGMP specs require to report membership immediately after
 116 * joining a group, but we delay the first report by a
 117 * small interval. It seems more natural and still does not
 118 * contradict to specs provided this delay is small enough.
 119 */
 120
 121#define IGMP_V1_SEEN(in_dev) ((in_dev)->mr_v1_seen && (long)(jiffies - (in_dev)->mr_v1_seen) < 0)
 122
 123/*
 124 *      Timer management
 125 */
 126
 127static __inline__ void igmp_stop_timer(struct ip_mc_list *im)
 128{
 129        if (im->tm_running) {
 130                del_timer(&im->timer);
 131                im->tm_running=0;
 132        }
 133}
 134
 135static __inline__ void igmp_start_timer(struct ip_mc_list *im, int max_delay)
 136{
 137        int tv;
 138        if (im->tm_running)
 139                return;
 140        tv=net_random() % max_delay;
 141        im->timer.expires=jiffies+tv+2;
 142        im->tm_running=1;
 143        add_timer(&im->timer);
 144}
 145
 146/*
 147 *      Send an IGMP report.
 148 */
 149
 150#define IGMP_SIZE (sizeof(struct igmphdr)+sizeof(struct iphdr)+4)
 151
 152static int igmp_send_report(struct device *dev, u32 group, int type)
 153{
 154        struct sk_buff *skb;
 155        struct iphdr *iph;
 156        struct igmphdr *ih;
 157        struct rtable *rt;
 158        u32     dst;
 159
 160        /* According to IGMPv2 specs, LEAVE messages are
 161         * sent to all-routers group.
 162         */
 163        dst = group;
 164        if (type == IGMP_HOST_LEAVE_MESSAGE)
 165                dst = IGMP_ALL_ROUTER;
 166
 167        if (ip_route_output(&rt, dst, 0, 0, dev->ifindex))
 168                return -1;
 169        if (rt->rt_src == 0) {
 170                ip_rt_put(rt);
 171                return -1;
 172        }
 173
 174        skb=alloc_skb(IGMP_SIZE+dev->hard_header_len+15, GFP_ATOMIC);
 175        if (skb == NULL) {
 176                ip_rt_put(rt);
 177                return -1;
 178        }
 179
 180        skb->dst = &rt->u.dst;
 181
 182        skb_reserve(skb, (dev->hard_header_len+15)&~15);
 183
 184        skb->nh.iph = iph = (struct iphdr *)skb_put(skb, sizeof(struct iphdr)+4);
 185
 186        iph->version  = 4;
 187        iph->ihl      = (sizeof(struct iphdr)+4)>>2;
 188        iph->tos      = 0;
 189        iph->frag_off = 0;
 190        iph->ttl      = 1;
 191        iph->daddr    = dst;
 192        iph->saddr    = rt->rt_src;
 193        iph->protocol = IPPROTO_IGMP;
 194        iph->tot_len  = htons(IGMP_SIZE);
 195        iph->id       = htons(ip_id_count++);
 196        ((u8*)&iph[1])[0] = IPOPT_RA;
 197        ((u8*)&iph[1])[1] = 4;
 198        ((u8*)&iph[1])[2] = 0;
 199        ((u8*)&iph[1])[3] = 0;
 200        ip_send_check(iph);
 201
 202        ih = (struct igmphdr *)skb_put(skb, sizeof(struct igmphdr));
 203        ih->type=type;
 204        ih->code=0;
 205        ih->csum=0;
 206        ih->group=group;
 207        ih->csum=ip_compute_csum((void *)ih, sizeof(struct igmphdr));
 208
 209        return skb->dst->output(skb);
 210}
 211
 212
 213static void igmp_timer_expire(unsigned long data)
 214{
 215        struct ip_mc_list *im=(struct ip_mc_list *)data;
 216        struct in_device *in_dev = im->interface;
 217        int err;
 218
 219        im->tm_running=0;
 220
 221        if (IGMP_V1_SEEN(in_dev))
 222                err = igmp_send_report(in_dev->dev, im->multiaddr, IGMP_HOST_MEMBERSHIP_REPORT);
 223        else
 224                err = igmp_send_report(in_dev->dev, im->multiaddr, IGMP_HOST_NEW_MEMBERSHIP_REPORT);
 225
 226        /* Failed. Retry later. */
 227        if (err) {
 228                igmp_start_timer(im, IGMP_Unsolicited_Report_Interval);
 229                return;
 230        }
 231
 232        if (im->unsolicit_count) {
 233                im->unsolicit_count--;
 234                igmp_start_timer(im, IGMP_Unsolicited_Report_Interval);
 235        }
 236        im->reporter = 1;
 237}
 238
 239static void igmp_heard_report(struct in_device *in_dev, u32 group)
 240{
 241        struct ip_mc_list *im;
 242
 243        /* Timers are only set for non-local groups */
 244
 245        if (LOCAL_MCAST(group))
 246                return;
 247
 248        for (im=in_dev->mc_list; im!=NULL; im=im->next) {
 249                if (im->multiaddr == group) {
 250                        igmp_stop_timer(im);
 251                        im->reporter = 0;
 252                        im->unsolicit_count = 0;
 253                        return;
 254                }
 255        }
 256}
 257
 258static void igmp_heard_query(struct in_device *in_dev, unsigned char max_resp_time,
 259                             u32 group)
 260{
 261        struct ip_mc_list       *im;
 262        int                     max_delay;
 263
 264        max_delay = max_resp_time*(HZ/IGMP_TIMER_SCALE);
 265
 266        if (max_resp_time == 0) {
 267                /* Alas, old v1 router presents here. */
 268
 269                max_delay = IGMP_Query_Response_Interval;
 270                in_dev->mr_v1_seen = jiffies + IGMP_V1_Router_Present_Timeout;
 271                group = 0;
 272        }
 273                
 274        /*
 275         * - Start the timers in all of our membership records
 276         *   that the query applies to for the interface on
 277         *   which the query arrived excl. those that belong
 278         *   to a "local" group (224.0.0.X)
 279         * - For timers already running check if they need to
 280         *   be reset.
 281         * - Use the igmp->igmp_code field as the maximum
 282         *   delay possible
 283         */
 284        for (im=in_dev->mc_list; im!=NULL; im=im->next) {
 285                if (group && group != im->multiaddr)
 286                        continue;
 287                if (LOCAL_MCAST(im->multiaddr))
 288                        continue;
 289                im->unsolicit_count = 0;
 290                if (im->tm_running && (long)(im->timer.expires-jiffies) > max_delay)
 291                        igmp_stop_timer(im);
 292                igmp_start_timer(im, max_delay);
 293        }
 294}
 295
 296int igmp_rcv(struct sk_buff *skb, unsigned short len)
 297{
 298        /* This basically follows the spec line by line -- see RFC1112 */
 299        struct igmphdr *ih = skb->h.igmph;
 300        struct in_device *in_dev = skb->dev->ip_ptr;
 301
 302        if (len < sizeof(struct igmphdr) || ip_compute_csum((void *)ih, len)
 303            || in_dev==NULL) {
 304                kfree_skb(skb);
 305                return 0;
 306        }
 307        
 308        switch (ih->type) {
 309        case IGMP_HOST_MEMBERSHIP_QUERY:
 310                igmp_heard_query(in_dev, ih->code, ih->group);
 311                break;
 312        case IGMP_HOST_MEMBERSHIP_REPORT:
 313        case IGMP_HOST_NEW_MEMBERSHIP_REPORT:
 314                /* Is it our report looped back? */
 315                if (((struct rtable*)skb->dst)->key.iif == 0)
 316                        break;
 317                igmp_heard_report(in_dev, ih->group);
 318                break;
 319        case IGMP_PIM:
 320#ifdef CONFIG_IP_PIMSM_V1
 321                return pim_rcv_v1(skb, len);
 322#endif
 323        case IGMP_DVMRP:
 324        case IGMP_TRACE:
 325        case IGMP_HOST_LEAVE_MESSAGE:
 326        case IGMP_MTRACE:
 327        case IGMP_MTRACE_RESP:
 328                break;
 329        default:
 330                NETDEBUG(printk(KERN_DEBUG "New IGMP type=%d, why we do not know about it?\n", ih->type));
 331        }
 332        kfree_skb(skb);
 333        return 0;
 334}
 335
 336#endif
 337
 338
 339/*
 340 *      Add a filter to a device
 341 */
 342
 343static void ip_mc_filter_add(struct in_device *in_dev, u32 addr)
 344{
 345        char buf[MAX_ADDR_LEN];
 346        struct device *dev = in_dev->dev;
 347
 348        /* Checking for IFF_MULTICAST here is WRONG-WRONG-WRONG.
 349           We will get multicast token leakage, when IFF_MULTICAST
 350           is changed. This check should be done in dev->set_multicast_list
 351           routine. Something sort of:
 352           if (dev->mc_list && dev->flags&IFF_MULTICAST) { do it; }
 353           --ANK
 354         */
 355        if (arp_mc_map(addr, buf, dev, 0) == 0)
 356                dev_mc_add(dev,buf,dev->addr_len,0);
 357}
 358
 359/*
 360 *      Remove a filter from a device
 361 */
 362
 363static void ip_mc_filter_del(struct in_device *in_dev, u32 addr)
 364{
 365        char buf[MAX_ADDR_LEN];
 366        struct device *dev = in_dev->dev;
 367
 368        if (arp_mc_map(addr, buf, dev, 0) == 0)
 369                dev_mc_delete(dev,buf,dev->addr_len,0);
 370}
 371
 372static void igmp_group_dropped(struct ip_mc_list *im)
 373{
 374        if (im->loaded) {
 375                im->loaded = 0;
 376                ip_mc_filter_del(im->interface, im->multiaddr);
 377        }
 378
 379#ifdef CONFIG_IP_MULTICAST
 380        if (LOCAL_MCAST(im->multiaddr))
 381                return;
 382
 383        start_bh_atomic();
 384        igmp_stop_timer(im);
 385        end_bh_atomic();
 386
 387        if (im->reporter && !IGMP_V1_SEEN(im->interface))
 388                igmp_send_report(im->interface->dev, im->multiaddr, IGMP_HOST_LEAVE_MESSAGE);
 389#endif
 390}
 391
 392static void igmp_group_added(struct ip_mc_list *im)
 393{
 394        if (im->loaded == 0) {
 395                im->loaded = 1;
 396                ip_mc_filter_add(im->interface, im->multiaddr);
 397        }
 398
 399#ifdef CONFIG_IP_MULTICAST
 400        if (LOCAL_MCAST(im->multiaddr))
 401                return;
 402
 403        start_bh_atomic();
 404        igmp_start_timer(im, IGMP_Initial_Report_Delay);
 405        end_bh_atomic();
 406#endif
 407}
 408
 409
 410/*
 411 *      Multicast list managers
 412 */
 413
 414
 415/*
 416 *      A socket has joined a multicast group on device dev.
 417 */
 418
 419void ip_mc_inc_group(struct in_device *in_dev, u32 addr)
 420{
 421        struct ip_mc_list *i, *im;
 422
 423        im = (struct ip_mc_list *)kmalloc(sizeof(*im), GFP_KERNEL);
 424
 425        for (i=in_dev->mc_list; i; i=i->next) {
 426                if (i->multiaddr == addr) {
 427                        i->users++;
 428                        if (im)
 429                                kfree(im);
 430                        return;
 431                }
 432        }
 433        if (!im)
 434                return;
 435        im->users=1;
 436        im->interface=in_dev;
 437        im->multiaddr=addr;
 438#ifdef  CONFIG_IP_MULTICAST
 439        im->tm_running=0;
 440        init_timer(&im->timer);
 441        im->timer.data=(unsigned long)im;
 442        im->timer.function=&igmp_timer_expire;
 443        im->unsolicit_count = IGMP_Unsolicited_Report_Count;
 444        im->reporter = 0;
 445        im->loaded = 0;
 446#endif
 447        im->next=in_dev->mc_list;
 448        in_dev->mc_list=im;
 449        igmp_group_added(im);
 450        if (in_dev->dev->flags & IFF_UP)
 451                ip_rt_multicast_event(in_dev);
 452        return;
 453}
 454
 455/*
 456 *      A socket has left a multicast group on device dev
 457 */
 458
 459int ip_mc_dec_group(struct in_device *in_dev, u32 addr)
 460{
 461        struct ip_mc_list *i, **ip;
 462
 463        for (ip=&in_dev->mc_list; (i=*ip)!=NULL; ip=&i->next) {
 464                if (i->multiaddr==addr) {
 465                        if (--i->users == 0) {
 466                                *ip = i->next;
 467                                synchronize_bh();
 468
 469                                igmp_group_dropped(i);
 470                                if (in_dev->dev->flags & IFF_UP)
 471                                        ip_rt_multicast_event(in_dev);
 472                                kfree_s(i, sizeof(*i));
 473                        }
 474                        return 0;
 475                }
 476        }
 477        return -ESRCH;
 478}
 479
 480/* Device going down */
 481
 482void ip_mc_down(struct in_device *in_dev)
 483{
 484        struct ip_mc_list *i;
 485
 486        for (i=in_dev->mc_list; i; i=i->next)
 487                igmp_group_dropped(i);
 488
 489        ip_mc_dec_group(in_dev, IGMP_ALL_HOSTS);
 490}
 491
 492/* Device going up */
 493
 494void ip_mc_up(struct in_device *in_dev)
 495{
 496        struct ip_mc_list *i;
 497
 498        ip_mc_inc_group(in_dev, IGMP_ALL_HOSTS);
 499
 500        for (i=in_dev->mc_list; i; i=i->next)
 501                igmp_group_added(i);
 502}
 503
 504/*
 505 *      Device is about to be destroyed: clean up.
 506 */
 507
 508void ip_mc_destroy_dev(struct in_device *in_dev)
 509{
 510        struct ip_mc_list *i;
 511
 512        while ((i = in_dev->mc_list) != NULL) {
 513                in_dev->mc_list = i->next;
 514                igmp_group_dropped(i);
 515                kfree_s(i, sizeof(*i));
 516        }
 517}
 518
 519static struct in_device * ip_mc_find_dev(struct ip_mreqn *imr)
 520{
 521        struct rtable *rt;
 522        struct device *dev = NULL;
 523
 524        if (imr->imr_address.s_addr) {
 525                dev = ip_dev_find(imr->imr_address.s_addr);
 526                if (!dev)
 527                        return NULL;
 528        }
 529
 530        if (!dev && !ip_route_output(&rt, imr->imr_multiaddr.s_addr, 0, 0, 0)) {
 531                dev = rt->u.dst.dev;
 532                ip_rt_put(rt);
 533        }
 534        if (dev) {
 535                imr->imr_ifindex = dev->ifindex;
 536                return dev->ip_ptr;
 537        }
 538        return NULL;
 539}
 540
 541/*
 542 *      Join a socket to a group
 543 */
 544int sysctl_igmp_max_memberships = IP_MAX_MEMBERSHIPS;
 545
 546int ip_mc_join_group(struct sock *sk , struct ip_mreqn *imr)
 547{
 548        int err;
 549        u32 addr = imr->imr_multiaddr.s_addr;
 550        struct ip_mc_socklist *iml, *i;
 551        struct in_device *in_dev;
 552        int count = 0;
 553
 554        if (!MULTICAST(addr))
 555                return -EINVAL;
 556
 557        rtnl_shlock();
 558
 559        if (!imr->imr_ifindex)
 560                in_dev = ip_mc_find_dev(imr);
 561        else
 562                in_dev = inetdev_by_index(imr->imr_ifindex);
 563
 564        if (!in_dev) {
 565                iml = NULL;
 566                err = -ENODEV;
 567                goto done;
 568        }
 569
 570        iml = (struct ip_mc_socklist *)sock_kmalloc(sk, sizeof(*iml), GFP_KERNEL);
 571
 572        err = -EADDRINUSE;
 573        for (i=sk->ip_mc_list; i; i=i->next) {
 574                if (memcmp(&i->multi, imr, sizeof(*imr)) == 0) {
 575                        /* New style additions are reference counted */
 576                        if (imr->imr_address.s_addr == 0) {
 577                                i->count++;
 578                                err = 0;
 579                        }
 580                        goto done;
 581                }
 582                count++;
 583        }
 584        err = -ENOBUFS;
 585        if (iml == NULL || count >= sysctl_igmp_max_memberships)
 586                goto done;
 587        memcpy(&iml->multi, imr, sizeof(*imr));
 588        iml->next = sk->ip_mc_list;
 589        iml->count = 1;
 590        sk->ip_mc_list = iml;
 591        ip_mc_inc_group(in_dev, addr);
 592        iml = NULL;
 593        err = 0;
 594done:
 595        rtnl_shunlock();
 596        if (iml)
 597                sock_kfree_s(sk, iml, sizeof(*iml));
 598        return err;
 599}
 600
 601/*
 602 *      Ask a socket to leave a group.
 603 */
 604
 605int ip_mc_leave_group(struct sock *sk, struct ip_mreqn *imr)
 606{
 607        struct ip_mc_socklist *iml, **imlp;
 608
 609        for (imlp=&sk->ip_mc_list; (iml=*imlp)!=NULL; imlp=&iml->next) {
 610                if (iml->multi.imr_multiaddr.s_addr==imr->imr_multiaddr.s_addr &&
 611                    iml->multi.imr_address.s_addr==imr->imr_address.s_addr &&
 612                    (!imr->imr_ifindex || iml->multi.imr_ifindex==imr->imr_ifindex)) {
 613                        struct in_device *in_dev;
 614                        if (--iml->count)
 615                                return 0;
 616
 617                        *imlp = iml->next;
 618                        synchronize_bh();
 619
 620                        in_dev = inetdev_by_index(iml->multi.imr_ifindex);
 621                        if (in_dev)
 622                                ip_mc_dec_group(in_dev, imr->imr_multiaddr.s_addr);
 623                        sock_kfree_s(sk, iml, sizeof(*iml));
 624                        return 0;
 625                }
 626        }
 627        return -EADDRNOTAVAIL;
 628}
 629
 630/*
 631 *      A socket is closing.
 632 */
 633
 634void ip_mc_drop_socket(struct sock *sk)
 635{
 636        struct ip_mc_socklist *iml;
 637
 638        while ((iml=sk->ip_mc_list) != NULL) {
 639                struct in_device *in_dev;
 640                sk->ip_mc_list = iml->next;
 641                if ((in_dev = inetdev_by_index(iml->multi.imr_ifindex)) != NULL)
 642                        ip_mc_dec_group(in_dev, iml->multi.imr_multiaddr.s_addr);
 643                sock_kfree_s(sk, iml, sizeof(*iml));
 644        }
 645}
 646
 647
 648#ifdef CONFIG_IP_MULTICAST
 649 
 650int ip_mc_procinfo(char *buffer, char **start, off_t offset, int length, int dummy)
 651{
 652        off_t pos=0, begin=0;
 653        struct ip_mc_list *im;
 654        int len=0;
 655        struct device *dev;
 656        
 657        len=sprintf(buffer,"Idx\tDevice    : Count Querier\tGroup    Users Timer\tReporter\n");  
 658        
 659        for(dev = dev_base; dev; dev = dev->next)
 660        {
 661                struct in_device *in_dev = dev->ip_ptr;
 662                char   *querier = "NONE";
 663                
 664                if (in_dev == NULL)
 665                        continue;
 666
 667                querier = IGMP_V1_SEEN(in_dev) ? "V1" : "V2";
 668
 669                len+=sprintf(buffer+len,"%d\t%-10s: %5d %7s\n",
 670                             dev->ifindex, dev->name, dev->mc_count, querier);
 671
 672                for (im = in_dev->mc_list; im; im = im->next) {
 673                        len+=sprintf(buffer+len,
 674                                     "\t\t\t\t%08lX %5d %d:%08lX\t\t%d\n",
 675                                     im->multiaddr, im->users,
 676                                     im->tm_running, im->timer.expires-jiffies, im->reporter);
 677
 678                        pos=begin+len;
 679                        if(pos<offset)
 680                        {
 681                                len=0;
 682                                begin=pos;
 683                        }
 684                        if(pos>offset+length)
 685                                goto done;
 686                }
 687        }
 688done:
 689        *start=buffer+(offset-begin);
 690        len-=(offset-begin);
 691        if(len>length)
 692                len=length;
 693        if(len<0)
 694                len=0;
 695        return len;
 696}
 697#endif
 698
 699
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.