linux/net/core/dev_addr_lists.c
<<
>>
Prefs
   1/*
   2 * net/core/dev_addr_lists.c - Functions for handling net device lists
   3 * Copyright (c) 2010 Jiri Pirko <jpirko@redhat.com>
   4 *
   5 * This file contains functions for working with unicast, multicast and device
   6 * addresses lists.
   7 *
   8 * This program is free software; you can redistribute it and/or modify
   9 * it under the terms of the GNU General Public License as published by
  10 * the Free Software Foundation; either version 2 of the License, or
  11 * (at your option) any later version.
  12 */
  13
  14#include <linux/netdevice.h>
  15#include <linux/rtnetlink.h>
  16#include <linux/export.h>
  17#include <linux/list.h>
  18#include <linux/proc_fs.h>
  19
  20/*
  21 * General list handling functions
  22 */
  23
  24static int __hw_addr_add_ex(struct netdev_hw_addr_list *list,
  25                            unsigned char *addr, int addr_len,
  26                            unsigned char addr_type, bool global)
  27{
  28        struct netdev_hw_addr *ha;
  29        int alloc_size;
  30
  31        if (addr_len > MAX_ADDR_LEN)
  32                return -EINVAL;
  33
  34        list_for_each_entry(ha, &list->list, list) {
  35                if (!memcmp(ha->addr, addr, addr_len) &&
  36                    ha->type == addr_type) {
  37                        if (global) {
  38                                /* check if addr is already used as global */
  39                                if (ha->global_use)
  40                                        return 0;
  41                                else
  42                                        ha->global_use = true;
  43                        }
  44                        ha->refcount++;
  45                        return 0;
  46                }
  47        }
  48
  49
  50        alloc_size = sizeof(*ha);
  51        if (alloc_size < L1_CACHE_BYTES)
  52                alloc_size = L1_CACHE_BYTES;
  53        ha = kmalloc(alloc_size, GFP_ATOMIC);
  54        if (!ha)
  55                return -ENOMEM;
  56        memcpy(ha->addr, addr, addr_len);
  57        ha->type = addr_type;
  58        ha->refcount = 1;
  59        ha->global_use = global;
  60        ha->synced = false;
  61        list_add_tail_rcu(&ha->list, &list->list);
  62        list->count++;
  63        return 0;
  64}
  65
  66static int __hw_addr_add(struct netdev_hw_addr_list *list, unsigned char *addr,
  67                         int addr_len, unsigned char addr_type)
  68{
  69        return __hw_addr_add_ex(list, addr, addr_len, addr_type, false);
  70}
  71
  72static int __hw_addr_del_ex(struct netdev_hw_addr_list *list,
  73                            unsigned char *addr, int addr_len,
  74                            unsigned char addr_type, bool global)
  75{
  76        struct netdev_hw_addr *ha;
  77
  78        list_for_each_entry(ha, &list->list, list) {
  79                if (!memcmp(ha->addr, addr, addr_len) &&
  80                    (ha->type == addr_type || !addr_type)) {
  81                        if (global) {
  82                                if (!ha->global_use)
  83                                        break;
  84                                else
  85                                        ha->global_use = false;
  86                        }
  87                        if (--ha->refcount)
  88                                return 0;
  89                        list_del_rcu(&ha->list);
  90                        kfree_rcu(ha, rcu_head);
  91                        list->count--;
  92                        return 0;
  93                }
  94        }
  95        return -ENOENT;
  96}
  97
  98static int __hw_addr_del(struct netdev_hw_addr_list *list, unsigned char *addr,
  99                         int addr_len, unsigned char addr_type)
 100{
 101        return __hw_addr_del_ex(list, addr, addr_len, addr_type, false);
 102}
 103
 104int __hw_addr_add_multiple(struct netdev_hw_addr_list *to_list,
 105                           struct netdev_hw_addr_list *from_list,
 106                           int addr_len, unsigned char addr_type)
 107{
 108        int err;
 109        struct netdev_hw_addr *ha, *ha2;
 110        unsigned char type;
 111
 112        list_for_each_entry(ha, &from_list->list, list) {
 113                type = addr_type ? addr_type : ha->type;
 114                err = __hw_addr_add(to_list, ha->addr, addr_len, type);
 115                if (err)
 116                        goto unroll;
 117        }
 118        return 0;
 119
 120unroll:
 121        list_for_each_entry(ha2, &from_list->list, list) {
 122                if (ha2 == ha)
 123                        break;
 124                type = addr_type ? addr_type : ha2->type;
 125                __hw_addr_del(to_list, ha2->addr, addr_len, type);
 126        }
 127        return err;
 128}
 129EXPORT_SYMBOL(__hw_addr_add_multiple);
 130
 131void __hw_addr_del_multiple(struct netdev_hw_addr_list *to_list,
 132                            struct netdev_hw_addr_list *from_list,
 133                            int addr_len, unsigned char addr_type)
 134{
 135        struct netdev_hw_addr *ha;
 136        unsigned char type;
 137
 138        list_for_each_entry(ha, &from_list->list, list) {
 139                type = addr_type ? addr_type : ha->type;
 140                __hw_addr_del(to_list, ha->addr, addr_len, type);
 141        }
 142}
 143EXPORT_SYMBOL(__hw_addr_del_multiple);
 144
 145int __hw_addr_sync(struct netdev_hw_addr_list *to_list,
 146                   struct netdev_hw_addr_list *from_list,
 147                   int addr_len)
 148{
 149        int err = 0;
 150        struct netdev_hw_addr *ha, *tmp;
 151
 152        list_for_each_entry_safe(ha, tmp, &from_list->list, list) {
 153                if (!ha->synced) {
 154                        err = __hw_addr_add(to_list, ha->addr,
 155                                            addr_len, ha->type);
 156                        if (err)
 157                                break;
 158                        ha->synced = true;
 159                        ha->refcount++;
 160                } else if (ha->refcount == 1) {
 161                        __hw_addr_del(to_list, ha->addr, addr_len, ha->type);
 162                        __hw_addr_del(from_list, ha->addr, addr_len, ha->type);
 163                }
 164        }
 165        return err;
 166}
 167EXPORT_SYMBOL(__hw_addr_sync);
 168
 169void __hw_addr_unsync(struct netdev_hw_addr_list *to_list,
 170                      struct netdev_hw_addr_list *from_list,
 171                      int addr_len)
 172{
 173        struct netdev_hw_addr *ha, *tmp;
 174
 175        list_for_each_entry_safe(ha, tmp, &from_list->list, list) {
 176                if (ha->synced) {
 177                        __hw_addr_del(to_list, ha->addr,
 178                                      addr_len, ha->type);
 179                        ha->synced = false;
 180                        __hw_addr_del(from_list, ha->addr,
 181                                      addr_len, ha->type);
 182                }
 183        }
 184}
 185EXPORT_SYMBOL(__hw_addr_unsync);
 186
 187void __hw_addr_flush(struct netdev_hw_addr_list *list)
 188{
 189        struct netdev_hw_addr *ha, *tmp;
 190
 191        list_for_each_entry_safe(ha, tmp, &list->list, list) {
 192                list_del_rcu(&ha->list);
 193                kfree_rcu(ha, rcu_head);
 194        }
 195        list->count = 0;
 196}
 197EXPORT_SYMBOL(__hw_addr_flush);
 198
 199void __hw_addr_init(struct netdev_hw_addr_list *list)
 200{
 201        INIT_LIST_HEAD(&list->list);
 202        list->count = 0;
 203}
 204EXPORT_SYMBOL(__hw_addr_init);
 205
 206/*
 207 * Device addresses handling functions
 208 */
 209
 210/**
 211 *      dev_addr_flush - Flush device address list
 212 *      @dev: device
 213 *
 214 *      Flush device address list and reset ->dev_addr.
 215 *
 216 *      The caller must hold the rtnl_mutex.
 217 */
 218void dev_addr_flush(struct net_device *dev)
 219{
 220        /* rtnl_mutex must be held here */
 221
 222        __hw_addr_flush(&dev->dev_addrs);
 223        dev->dev_addr = NULL;
 224}
 225EXPORT_SYMBOL(dev_addr_flush);
 226
 227/**
 228 *      dev_addr_init - Init device address list
 229 *      @dev: device
 230 *
 231 *      Init device address list and create the first element,
 232 *      used by ->dev_addr.
 233 *
 234 *      The caller must hold the rtnl_mutex.
 235 */
 236int dev_addr_init(struct net_device *dev)
 237{
 238        unsigned char addr[MAX_ADDR_LEN];
 239        struct netdev_hw_addr *ha;
 240        int err;
 241
 242        /* rtnl_mutex must be held here */
 243
 244        __hw_addr_init(&dev->dev_addrs);
 245        memset(addr, 0, sizeof(addr));
 246        err = __hw_addr_add(&dev->dev_addrs, addr, sizeof(addr),
 247                            NETDEV_HW_ADDR_T_LAN);
 248        if (!err) {
 249                /*
 250                 * Get the first (previously created) address from the list
 251                 * and set dev_addr pointer to this location.
 252                 */
 253                ha = list_first_entry(&dev->dev_addrs.list,
 254                                      struct netdev_hw_addr, list);
 255                dev->dev_addr = ha->addr;
 256        }
 257        return err;
 258}
 259EXPORT_SYMBOL(dev_addr_init);
 260
 261/**
 262 *      dev_addr_add - Add a device address
 263 *      @dev: device
 264 *      @addr: address to add
 265 *      @addr_type: address type
 266 *
 267 *      Add a device address to the device or increase the reference count if
 268 *      it already exists.
 269 *
 270 *      The caller must hold the rtnl_mutex.
 271 */
 272int dev_addr_add(struct net_device *dev, unsigned char *addr,
 273                 unsigned char addr_type)
 274{
 275        int err;
 276
 277        ASSERT_RTNL();
 278
 279        err = __hw_addr_add(&dev->dev_addrs, addr, dev->addr_len, addr_type);
 280        if (!err)
 281                call_netdevice_notifiers(NETDEV_CHANGEADDR, dev);
 282        return err;
 283}
 284EXPORT_SYMBOL(dev_addr_add);
 285
 286/**
 287 *      dev_addr_del - Release a device address.
 288 *      @dev: device
 289 *      @addr: address to delete
 290 *      @addr_type: address type
 291 *
 292 *      Release reference to a device address and remove it from the device
 293 *      if the reference count drops to zero.
 294 *
 295 *      The caller must hold the rtnl_mutex.
 296 */
 297int dev_addr_del(struct net_device *dev, unsigned char *addr,
 298                 unsigned char addr_type)
 299{
 300        int err;
 301        struct netdev_hw_addr *ha;
 302
 303        ASSERT_RTNL();
 304
 305        /*
 306         * We can not remove the first address from the list because
 307         * dev->dev_addr points to that.
 308         */
 309        ha = list_first_entry(&dev->dev_addrs.list,
 310                              struct netdev_hw_addr, list);
 311        if (ha->addr == dev->dev_addr && ha->refcount == 1)
 312                return -ENOENT;
 313
 314        err = __hw_addr_del(&dev->dev_addrs, addr, dev->addr_len,
 315                            addr_type);
 316        if (!err)
 317                call_netdevice_notifiers(NETDEV_CHANGEADDR, dev);
 318        return err;
 319}
 320EXPORT_SYMBOL(dev_addr_del);
 321
 322/**
 323 *      dev_addr_add_multiple - Add device addresses from another device
 324 *      @to_dev: device to which addresses will be added
 325 *      @from_dev: device from which addresses will be added
 326 *      @addr_type: address type - 0 means type will be used from from_dev
 327 *
 328 *      Add device addresses of the one device to another.
 329 **
 330 *      The caller must hold the rtnl_mutex.
 331 */
 332int dev_addr_add_multiple(struct net_device *to_dev,
 333                          struct net_device *from_dev,
 334                          unsigned char addr_type)
 335{
 336        int err;
 337
 338        ASSERT_RTNL();
 339
 340        if (from_dev->addr_len != to_dev->addr_len)
 341                return -EINVAL;
 342        err = __hw_addr_add_multiple(&to_dev->dev_addrs, &from_dev->dev_addrs,
 343                                     to_dev->addr_len, addr_type);
 344        if (!err)
 345                call_netdevice_notifiers(NETDEV_CHANGEADDR, to_dev);
 346        return err;
 347}
 348EXPORT_SYMBOL(dev_addr_add_multiple);
 349
 350/**
 351 *      dev_addr_del_multiple - Delete device addresses by another device
 352 *      @to_dev: device where the addresses will be deleted
 353 *      @from_dev: device supplying the addresses to be deleted
 354 *      @addr_type: address type - 0 means type will be used from from_dev
 355 *
 356 *      Deletes addresses in to device by the list of addresses in from device.
 357 *
 358 *      The caller must hold the rtnl_mutex.
 359 */
 360int dev_addr_del_multiple(struct net_device *to_dev,
 361                          struct net_device *from_dev,
 362                          unsigned char addr_type)
 363{
 364        ASSERT_RTNL();
 365
 366        if (from_dev->addr_len != to_dev->addr_len)
 367                return -EINVAL;
 368        __hw_addr_del_multiple(&to_dev->dev_addrs, &from_dev->dev_addrs,
 369                               to_dev->addr_len, addr_type);
 370        call_netdevice_notifiers(NETDEV_CHANGEADDR, to_dev);
 371        return 0;
 372}
 373EXPORT_SYMBOL(dev_addr_del_multiple);
 374
 375/*
 376 * Unicast list handling functions
 377 */
 378
 379/**
 380 *      dev_uc_add - Add a secondary unicast address
 381 *      @dev: device
 382 *      @addr: address to add
 383 *
 384 *      Add a secondary unicast address to the device or increase
 385 *      the reference count if it already exists.
 386 */
 387int dev_uc_add(struct net_device *dev, unsigned char *addr)
 388{
 389        int err;
 390
 391        netif_addr_lock_bh(dev);
 392        err = __hw_addr_add(&dev->uc, addr, dev->addr_len,
 393                            NETDEV_HW_ADDR_T_UNICAST);
 394        if (!err)
 395                __dev_set_rx_mode(dev);
 396        netif_addr_unlock_bh(dev);
 397        return err;
 398}
 399EXPORT_SYMBOL(dev_uc_add);
 400
 401/**
 402 *      dev_uc_del - Release secondary unicast address.
 403 *      @dev: device
 404 *      @addr: address to delete
 405 *
 406 *      Release reference to a secondary unicast address and remove it
 407 *      from the device if the reference count drops to zero.
 408 */
 409int dev_uc_del(struct net_device *dev, unsigned char *addr)
 410{
 411        int err;
 412
 413        netif_addr_lock_bh(dev);
 414        err = __hw_addr_del(&dev->uc, addr, dev->addr_len,
 415                            NETDEV_HW_ADDR_T_UNICAST);
 416        if (!err)
 417                __dev_set_rx_mode(dev);
 418        netif_addr_unlock_bh(dev);
 419        return err;
 420}
 421EXPORT_SYMBOL(dev_uc_del);
 422
 423/**
 424 *      dev_uc_sync - Synchronize device's unicast list to another device
 425 *      @to: destination device
 426 *      @from: source device
 427 *
 428 *      Add newly added addresses to the destination device and release
 429 *      addresses that have no users left. The source device must be
 430 *      locked by netif_tx_lock_bh.
 431 *
 432 *      This function is intended to be called from the dev->set_rx_mode
 433 *      function of layered software devices.
 434 */
 435int dev_uc_sync(struct net_device *to, struct net_device *from)
 436{
 437        int err = 0;
 438
 439        if (to->addr_len != from->addr_len)
 440                return -EINVAL;
 441
 442        netif_addr_lock_bh(to);
 443        err = __hw_addr_sync(&to->uc, &from->uc, to->addr_len);
 444        if (!err)
 445                __dev_set_rx_mode(to);
 446        netif_addr_unlock_bh(to);
 447        return err;
 448}
 449EXPORT_SYMBOL(dev_uc_sync);
 450
 451/**
 452 *      dev_uc_unsync - Remove synchronized addresses from the destination device
 453 *      @to: destination device
 454 *      @from: source device
 455 *
 456 *      Remove all addresses that were added to the destination device by
 457 *      dev_uc_sync(). This function is intended to be called from the
 458 *      dev->stop function of layered software devices.
 459 */
 460void dev_uc_unsync(struct net_device *to, struct net_device *from)
 461{
 462        if (to->addr_len != from->addr_len)
 463                return;
 464
 465        netif_addr_lock_bh(from);
 466        netif_addr_lock(to);
 467        __hw_addr_unsync(&to->uc, &from->uc, to->addr_len);
 468        __dev_set_rx_mode(to);
 469        netif_addr_unlock(to);
 470        netif_addr_unlock_bh(from);
 471}
 472EXPORT_SYMBOL(dev_uc_unsync);
 473
 474/**
 475 *      dev_uc_flush - Flush unicast addresses
 476 *      @dev: device
 477 *
 478 *      Flush unicast addresses.
 479 */
 480void dev_uc_flush(struct net_device *dev)
 481{
 482        netif_addr_lock_bh(dev);
 483        __hw_addr_flush(&dev->uc);
 484        netif_addr_unlock_bh(dev);
 485}
 486EXPORT_SYMBOL(dev_uc_flush);
 487
 488/**
 489 *      dev_uc_flush - Init unicast address list
 490 *      @dev: device
 491 *
 492 *      Init unicast address list.
 493 */
 494void dev_uc_init(struct net_device *dev)
 495{
 496        __hw_addr_init(&dev->uc);
 497}
 498EXPORT_SYMBOL(dev_uc_init);
 499
 500/*
 501 * Multicast list handling functions
 502 */
 503
 504static int __dev_mc_add(struct net_device *dev, unsigned char *addr,
 505                        bool global)
 506{
 507        int err;
 508
 509        netif_addr_lock_bh(dev);
 510        err = __hw_addr_add_ex(&dev->mc, addr, dev->addr_len,
 511                               NETDEV_HW_ADDR_T_MULTICAST, global);
 512        if (!err)
 513                __dev_set_rx_mode(dev);
 514        netif_addr_unlock_bh(dev);
 515        return err;
 516}
 517/**
 518 *      dev_mc_add - Add a multicast address
 519 *      @dev: device
 520 *      @addr: address to add
 521 *
 522 *      Add a multicast address to the device or increase
 523 *      the reference count if it already exists.
 524 */
 525int dev_mc_add(struct net_device *dev, unsigned char *addr)
 526{
 527        return __dev_mc_add(dev, addr, false);
 528}
 529EXPORT_SYMBOL(dev_mc_add);
 530
 531/**
 532 *      dev_mc_add_global - Add a global multicast address
 533 *      @dev: device
 534 *      @addr: address to add
 535 *
 536 *      Add a global multicast address to the device.
 537 */
 538int dev_mc_add_global(struct net_device *dev, unsigned char *addr)
 539{
 540        return __dev_mc_add(dev, addr, true);
 541}
 542EXPORT_SYMBOL(dev_mc_add_global);
 543
 544static int __dev_mc_del(struct net_device *dev, unsigned char *addr,
 545                        bool global)
 546{
 547        int err;
 548
 549        netif_addr_lock_bh(dev);
 550        err = __hw_addr_del_ex(&dev->mc, addr, dev->addr_len,
 551                               NETDEV_HW_ADDR_T_MULTICAST, global);
 552        if (!err)
 553                __dev_set_rx_mode(dev);
 554        netif_addr_unlock_bh(dev);
 555        return err;
 556}
 557
 558/**
 559 *      dev_mc_del - Delete a multicast address.
 560 *      @dev: device
 561 *      @addr: address to delete
 562 *
 563 *      Release reference to a multicast address and remove it
 564 *      from the device if the reference count drops to zero.
 565 */
 566int dev_mc_del(struct net_device *dev, unsigned char *addr)
 567{
 568        return __dev_mc_del(dev, addr, false);
 569}
 570EXPORT_SYMBOL(dev_mc_del);
 571
 572/**
 573 *      dev_mc_del_global - Delete a global multicast address.
 574 *      @dev: device
 575 *      @addr: address to delete
 576 *
 577 *      Release reference to a multicast address and remove it
 578 *      from the device if the reference count drops to zero.
 579 */
 580int dev_mc_del_global(struct net_device *dev, unsigned char *addr)
 581{
 582        return __dev_mc_del(dev, addr, true);
 583}
 584EXPORT_SYMBOL(dev_mc_del_global);
 585
 586/**
 587 *      dev_mc_sync - Synchronize device's unicast list to another device
 588 *      @to: destination device
 589 *      @from: source device
 590 *
 591 *      Add newly added addresses to the destination device and release
 592 *      addresses that have no users left. The source device must be
 593 *      locked by netif_tx_lock_bh.
 594 *
 595 *      This function is intended to be called from the ndo_set_rx_mode
 596 *      function of layered software devices.
 597 */
 598int dev_mc_sync(struct net_device *to, struct net_device *from)
 599{
 600        int err = 0;
 601
 602        if (to->addr_len != from->addr_len)
 603                return -EINVAL;
 604
 605        netif_addr_lock_bh(to);
 606        err = __hw_addr_sync(&to->mc, &from->mc, to->addr_len);
 607        if (!err)
 608                __dev_set_rx_mode(to);
 609        netif_addr_unlock_bh(to);
 610        return err;
 611}
 612EXPORT_SYMBOL(dev_mc_sync);
 613
 614/**
 615 *      dev_mc_unsync - Remove synchronized addresses from the destination device
 616 *      @to: destination device
 617 *      @from: source device
 618 *
 619 *      Remove all addresses that were added to the destination device by
 620 *      dev_mc_sync(). This function is intended to be called from the
 621 *      dev->stop function of layered software devices.
 622 */
 623void dev_mc_unsync(struct net_device *to, struct net_device *from)
 624{
 625        if (to->addr_len != from->addr_len)
 626                return;
 627
 628        netif_addr_lock_bh(from);
 629        netif_addr_lock(to);
 630        __hw_addr_unsync(&to->mc, &from->mc, to->addr_len);
 631        __dev_set_rx_mode(to);
 632        netif_addr_unlock(to);
 633        netif_addr_unlock_bh(from);
 634}
 635EXPORT_SYMBOL(dev_mc_unsync);
 636
 637/**
 638 *      dev_mc_flush - Flush multicast addresses
 639 *      @dev: device
 640 *
 641 *      Flush multicast addresses.
 642 */
 643void dev_mc_flush(struct net_device *dev)
 644{
 645        netif_addr_lock_bh(dev);
 646        __hw_addr_flush(&dev->mc);
 647        netif_addr_unlock_bh(dev);
 648}
 649EXPORT_SYMBOL(dev_mc_flush);
 650
 651/**
 652 *      dev_mc_flush - Init multicast address list
 653 *      @dev: device
 654 *
 655 *      Init multicast address list.
 656 */
 657void dev_mc_init(struct net_device *dev)
 658{
 659        __hw_addr_init(&dev->mc);
 660}
 661EXPORT_SYMBOL(dev_mc_init);
 662
 663#ifdef CONFIG_PROC_FS
 664#include <linux/seq_file.h>
 665
 666static int dev_mc_seq_show(struct seq_file *seq, void *v)
 667{
 668        struct netdev_hw_addr *ha;
 669        struct net_device *dev = v;
 670
 671        if (v == SEQ_START_TOKEN)
 672                return 0;
 673
 674        netif_addr_lock_bh(dev);
 675        netdev_for_each_mc_addr(ha, dev) {
 676                int i;
 677
 678                seq_printf(seq, "%-4d %-15s %-5d %-5d ", dev->ifindex,
 679                           dev->name, ha->refcount, ha->global_use);
 680
 681                for (i = 0; i < dev->addr_len; i++)
 682                        seq_printf(seq, "%02x", ha->addr[i]);
 683
 684                seq_putc(seq, '\n');
 685        }
 686        netif_addr_unlock_bh(dev);
 687        return 0;
 688}
 689
 690static const struct seq_operations dev_mc_seq_ops = {
 691        .start = dev_seq_start,
 692        .next  = dev_seq_next,
 693        .stop  = dev_seq_stop,
 694        .show  = dev_mc_seq_show,
 695};
 696
 697static int dev_mc_seq_open(struct inode *inode, struct file *file)
 698{
 699        return dev_seq_open_ops(inode, file, &dev_mc_seq_ops);
 700}
 701
 702static const struct file_operations dev_mc_seq_fops = {
 703        .owner   = THIS_MODULE,
 704        .open    = dev_mc_seq_open,
 705        .read    = seq_read,
 706        .llseek  = seq_lseek,
 707        .release = seq_release_net,
 708};
 709
 710#endif
 711
 712static int __net_init dev_mc_net_init(struct net *net)
 713{
 714        if (!proc_net_fops_create(net, "dev_mcast", 0, &dev_mc_seq_fops))
 715                return -ENOMEM;
 716        return 0;
 717}
 718
 719static void __net_exit dev_mc_net_exit(struct net *net)
 720{
 721        proc_net_remove(net, "dev_mcast");
 722}
 723
 724static struct pernet_operations __net_initdata dev_mc_net_ops = {
 725        .init = dev_mc_net_init,
 726        .exit = dev_mc_net_exit,
 727};
 728
 729void __init dev_mcast_init(void)
 730{
 731        register_pernet_subsys(&dev_mc_net_ops);
 732}
 733
 734
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.