2005-04-16 15:20:36 -07:00
/*
2007-02-09 23:24:36 +09:00
* Linux NET3 : Multicast List maintenance .
2005-04-16 15:20:36 -07:00
*
* Authors :
2007-02-09 23:24:36 +09:00
* Tim Kordas < tjk @ nostromo . eeap . cwru . edu >
2005-04-16 15:20:36 -07:00
* Richard Underwood < richard @ wuzz . demon . co . uk >
*
* Stir fried together from the IP multicast and CAP patches above
2007-02-09 23:24:36 +09:00
* Alan Cox < Alan . Cox @ linux . org >
2005-04-16 15:20:36 -07:00
*
* Fixes :
* Alan Cox : Update the device on a real delete
* rather than any time but . . .
* Alan Cox : IFF_ALLMULTI support .
* Alan Cox : New format set_multicast_list ( ) calls .
* Gleb Natapov : Remove dev_mc_lock .
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; either version
* 2 of the License , or ( at your option ) any later version .
*/
2006-09-22 14:00:29 -07:00
# include <linux/module.h>
2005-04-16 15:20:36 -07:00
# include <asm/uaccess.h>
# include <asm/system.h>
# include <linux/bitops.h>
# include <linux/types.h>
# include <linux/kernel.h>
# include <linux/string.h>
# include <linux/mm.h>
# include <linux/socket.h>
# include <linux/sockios.h>
# include <linux/in.h>
# include <linux/errno.h>
# include <linux/interrupt.h>
# include <linux/if_ether.h>
# include <linux/inet.h>
# include <linux/netdevice.h>
# include <linux/etherdevice.h>
# include <linux/proc_fs.h>
# include <linux/seq_file.h>
# include <linux/init.h>
# include <net/ip.h>
# include <net/route.h>
# include <linux/skbuff.h>
# include <net/sock.h>
# include <net/arp.h>
/*
2007-02-09 23:24:36 +09:00
* Device multicast list maintenance .
2005-04-16 15:20:36 -07:00
*
2007-02-09 23:24:36 +09:00
* This is used both by IP and by the user level maintenance functions .
* Unlike BSD we maintain a usage count on a given multicast address so
* that a casual user application can add / delete multicasts used by
2005-04-16 15:20:36 -07:00
* protocols without doing damage to the protocols when it deletes the
* entries . It also helps IP as it tracks overlapping maps .
*
* Device mc lists are changed by bh at least if IPv6 is enabled ,
* so that it must be bh protected .
*
2006-06-09 12:20:56 -07:00
* We block accesses to device mc filters with netif_tx_lock .
2005-04-16 15:20:36 -07:00
*/
/*
* Update the multicast list into the physical NIC controller .
*/
2007-02-09 23:24:36 +09:00
2005-04-16 15:20:36 -07:00
static void __dev_mc_upload ( struct net_device * dev )
{
/* Don't do anything till we up the interface
* [ dev_open will call this function so the list will
* stay sane ]
*/
if ( ! ( dev - > flags & IFF_UP ) )
return ;
/*
* Devices with no set multicast or which have been
* detached don ' t get set .
*/
if ( dev - > set_multicast_list = = NULL | |
! netif_device_present ( dev ) )
return ;
dev - > set_multicast_list ( dev ) ;
}
void dev_mc_upload ( struct net_device * dev )
{
2006-06-09 12:20:56 -07:00
netif_tx_lock_bh ( dev ) ;
2005-04-16 15:20:36 -07:00
__dev_mc_upload ( dev ) ;
2006-06-09 12:20:56 -07:00
netif_tx_unlock_bh ( dev ) ;
2005-04-16 15:20:36 -07:00
}
/*
* Delete a device level multicast
*/
2007-02-09 23:24:36 +09:00
2005-04-16 15:20:36 -07:00
int dev_mc_delete ( struct net_device * dev , void * addr , int alen , int glbl )
{
2007-06-27 01:26:58 -07:00
int err ;
2005-04-16 15:20:36 -07:00
2006-06-09 12:20:56 -07:00
netif_tx_lock_bh ( dev ) ;
2007-06-27 01:26:58 -07:00
err = __dev_addr_delete ( & dev - > mc_list , addr , alen , glbl ) ;
if ( ! err ) {
dev - > mc_count - - ;
2005-04-16 15:20:36 -07:00
/*
2007-06-27 01:26:58 -07:00
* We have altered the list , so the card
* loaded filter is now wrong . Fix it
2005-04-16 15:20:36 -07:00
*/
2007-06-27 01:26:58 -07:00
__dev_mc_upload ( dev ) ;
2005-04-16 15:20:36 -07:00
}
2006-06-09 12:20:56 -07:00
netif_tx_unlock_bh ( dev ) ;
2005-04-16 15:20:36 -07:00
return err ;
}
/*
* Add a device level multicast
*/
2007-02-09 23:24:36 +09:00
2005-04-16 15:20:36 -07:00
int dev_mc_add ( struct net_device * dev , void * addr , int alen , int glbl )
{
2007-06-27 01:26:58 -07:00
int err ;
2005-04-16 15:20:36 -07:00
2006-06-09 12:20:56 -07:00
netif_tx_lock_bh ( dev ) ;
2007-06-27 01:26:58 -07:00
err = __dev_addr_add ( & dev - > mc_list , addr , alen , glbl ) ;
if ( ! err ) {
dev - > mc_count + + ;
__dev_mc_upload ( dev ) ;
2005-04-16 15:20:36 -07:00
}
2006-06-09 12:20:56 -07:00
netif_tx_unlock_bh ( dev ) ;
2005-04-16 15:20:36 -07:00
return err ;
}
/*
* Discard multicast list when a device is downed
*/
void dev_mc_discard ( struct net_device * dev )
{
2006-06-09 12:20:56 -07:00
netif_tx_lock_bh ( dev ) ;
2007-06-27 01:26:58 -07:00
__dev_addr_discard ( & dev - > mc_list ) ;
2005-04-16 15:20:36 -07:00
dev - > mc_count = 0 ;
2006-06-09 12:20:56 -07:00
netif_tx_unlock_bh ( dev ) ;
2005-04-16 15:20:36 -07:00
}
# ifdef CONFIG_PROC_FS
static void * dev_mc_seq_start ( struct seq_file * seq , loff_t * pos )
{
struct net_device * dev ;
loff_t off = 0 ;
read_lock ( & dev_base_lock ) ;
2007-05-03 15:13:45 -07:00
for_each_netdev ( dev ) {
2007-02-09 23:24:36 +09:00
if ( off + + = = * pos )
2005-04-16 15:20:36 -07:00
return dev ;
}
return NULL ;
}
static void * dev_mc_seq_next ( struct seq_file * seq , void * v , loff_t * pos )
{
+ + * pos ;
2007-05-03 15:13:45 -07:00
return next_net_device ( ( struct net_device * ) v ) ;
2005-04-16 15:20:36 -07:00
}
static void dev_mc_seq_stop ( struct seq_file * seq , void * v )
{
read_unlock ( & dev_base_lock ) ;
}
static int dev_mc_seq_show ( struct seq_file * seq , void * v )
{
2007-06-27 01:26:58 -07:00
struct dev_addr_list * m ;
2005-04-16 15:20:36 -07:00
struct net_device * dev = v ;
2006-06-09 12:20:56 -07:00
netif_tx_lock_bh ( dev ) ;
2005-04-16 15:20:36 -07:00
for ( m = dev - > mc_list ; m ; m = m - > next ) {
int i ;
seq_printf ( seq , " %-4d %-15s %-5d %-5d " , dev - > ifindex ,
dev - > name , m - > dmi_users , m - > dmi_gusers ) ;
for ( i = 0 ; i < m - > dmi_addrlen ; i + + )
seq_printf ( seq , " %02x " , m - > dmi_addr [ i ] ) ;
seq_putc ( seq , ' \n ' ) ;
}
2006-06-09 12:20:56 -07:00
netif_tx_unlock_bh ( dev ) ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
2007-03-12 14:34:29 -07:00
static const struct seq_operations dev_mc_seq_ops = {
2005-04-16 15:20:36 -07:00
. start = dev_mc_seq_start ,
. next = dev_mc_seq_next ,
. stop = dev_mc_seq_stop ,
. show = dev_mc_seq_show ,
} ;
static int dev_mc_seq_open ( struct inode * inode , struct file * file )
{
return seq_open ( file , & dev_mc_seq_ops ) ;
}
2007-02-12 00:55:35 -08:00
static const struct file_operations dev_mc_seq_fops = {
2005-04-16 15:20:36 -07:00
. owner = THIS_MODULE ,
. open = dev_mc_seq_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = seq_release ,
} ;
# endif
void __init dev_mcast_init ( void )
{
proc_net_fops_create ( " dev_mcast " , 0 , & dev_mc_seq_fops ) ;
}
EXPORT_SYMBOL ( dev_mc_add ) ;
EXPORT_SYMBOL ( dev_mc_delete ) ;