at v2.6.23-rc1 255 lines 6.2 kB view raw
1/* 2 * Linux NET3: Multicast List maintenance. 3 * 4 * Authors: 5 * Tim Kordas <tjk@nostromo.eeap.cwru.edu> 6 * Richard Underwood <richard@wuzz.demon.co.uk> 7 * 8 * Stir fried together from the IP multicast and CAP patches above 9 * Alan Cox <Alan.Cox@linux.org> 10 * 11 * Fixes: 12 * Alan Cox : Update the device on a real delete 13 * rather than any time but... 14 * Alan Cox : IFF_ALLMULTI support. 15 * Alan Cox : New format set_multicast_list() calls. 16 * Gleb Natapov : Remove dev_mc_lock. 17 * 18 * This program is free software; you can redistribute it and/or 19 * modify it under the terms of the GNU General Public License 20 * as published by the Free Software Foundation; either version 21 * 2 of the License, or (at your option) any later version. 22 */ 23 24#include <linux/module.h> 25#include <asm/uaccess.h> 26#include <asm/system.h> 27#include <linux/bitops.h> 28#include <linux/types.h> 29#include <linux/kernel.h> 30#include <linux/string.h> 31#include <linux/mm.h> 32#include <linux/socket.h> 33#include <linux/sockios.h> 34#include <linux/in.h> 35#include <linux/errno.h> 36#include <linux/interrupt.h> 37#include <linux/if_ether.h> 38#include <linux/inet.h> 39#include <linux/netdevice.h> 40#include <linux/etherdevice.h> 41#include <linux/proc_fs.h> 42#include <linux/seq_file.h> 43#include <linux/init.h> 44#include <net/ip.h> 45#include <net/route.h> 46#include <linux/skbuff.h> 47#include <net/sock.h> 48#include <net/arp.h> 49 50 51/* 52 * Device multicast list maintenance. 53 * 54 * This is used both by IP and by the user level maintenance functions. 55 * Unlike BSD we maintain a usage count on a given multicast address so 56 * that a casual user application can add/delete multicasts used by 57 * protocols without doing damage to the protocols when it deletes the 58 * entries. It also helps IP as it tracks overlapping maps. 59 * 60 * Device mc lists are changed by bh at least if IPv6 is enabled, 61 * so that it must be bh protected. 62 * 63 * We block accesses to device mc filters with netif_tx_lock. 64 */ 65 66/* 67 * Delete a device level multicast 68 */ 69 70int dev_mc_delete(struct net_device *dev, void *addr, int alen, int glbl) 71{ 72 int err; 73 74 netif_tx_lock_bh(dev); 75 err = __dev_addr_delete(&dev->mc_list, &dev->mc_count, 76 addr, alen, glbl); 77 if (!err) { 78 /* 79 * We have altered the list, so the card 80 * loaded filter is now wrong. Fix it 81 */ 82 83 __dev_set_rx_mode(dev); 84 } 85 netif_tx_unlock_bh(dev); 86 return err; 87} 88 89/* 90 * Add a device level multicast 91 */ 92 93int dev_mc_add(struct net_device *dev, void *addr, int alen, int glbl) 94{ 95 int err; 96 97 netif_tx_lock_bh(dev); 98 err = __dev_addr_add(&dev->mc_list, &dev->mc_count, addr, alen, glbl); 99 if (!err) 100 __dev_set_rx_mode(dev); 101 netif_tx_unlock_bh(dev); 102 return err; 103} 104 105/** 106 * dev_mc_sync - Synchronize device's multicast list to another device 107 * @to: destination device 108 * @from: source device 109 * 110 * Add newly added addresses to the destination device and release 111 * addresses that have no users left. The source device must be 112 * locked by netif_tx_lock_bh. 113 * 114 * This function is intended to be called from the dev->set_multicast_list 115 * function of layered software devices. 116 */ 117int dev_mc_sync(struct net_device *to, struct net_device *from) 118{ 119 struct dev_addr_list *da; 120 int err = 0; 121 122 netif_tx_lock_bh(to); 123 for (da = from->mc_list; da != NULL; da = da->next) { 124 if (!da->da_synced) { 125 err = __dev_addr_add(&to->mc_list, &to->mc_count, 126 da->da_addr, da->da_addrlen, 0); 127 if (err < 0) 128 break; 129 da->da_synced = 1; 130 da->da_users++; 131 } else if (da->da_users == 1) { 132 __dev_addr_delete(&to->mc_list, &to->mc_count, 133 da->da_addr, da->da_addrlen, 0); 134 __dev_addr_delete(&from->mc_list, &from->mc_count, 135 da->da_addr, da->da_addrlen, 0); 136 } 137 } 138 if (!err) 139 __dev_set_rx_mode(to); 140 netif_tx_unlock_bh(to); 141 142 return err; 143} 144EXPORT_SYMBOL(dev_mc_sync); 145 146 147/** 148 * dev_mc_unsync - Remove synchronized addresses from the destination 149 * device 150 * @to: destination device 151 * @from: source device 152 * 153 * Remove all addresses that were added to the destination device by 154 * dev_mc_sync(). This function is intended to be called from the 155 * dev->stop function of layered software devices. 156 */ 157void dev_mc_unsync(struct net_device *to, struct net_device *from) 158{ 159 struct dev_addr_list *da; 160 161 netif_tx_lock_bh(from); 162 netif_tx_lock_bh(to); 163 164 for (da = from->mc_list; da != NULL; da = da->next) { 165 if (!da->da_synced) 166 continue; 167 __dev_addr_delete(&to->mc_list, &to->mc_count, 168 da->da_addr, da->da_addrlen, 0); 169 da->da_synced = 0; 170 __dev_addr_delete(&from->mc_list, &from->mc_count, 171 da->da_addr, da->da_addrlen, 0); 172 } 173 __dev_set_rx_mode(to); 174 175 netif_tx_unlock_bh(to); 176 netif_tx_unlock_bh(from); 177} 178EXPORT_SYMBOL(dev_mc_unsync); 179 180#ifdef CONFIG_PROC_FS 181static void *dev_mc_seq_start(struct seq_file *seq, loff_t *pos) 182{ 183 struct net_device *dev; 184 loff_t off = 0; 185 186 read_lock(&dev_base_lock); 187 for_each_netdev(dev) { 188 if (off++ == *pos) 189 return dev; 190 } 191 return NULL; 192} 193 194static void *dev_mc_seq_next(struct seq_file *seq, void *v, loff_t *pos) 195{ 196 ++*pos; 197 return next_net_device((struct net_device *)v); 198} 199 200static void dev_mc_seq_stop(struct seq_file *seq, void *v) 201{ 202 read_unlock(&dev_base_lock); 203} 204 205 206static int dev_mc_seq_show(struct seq_file *seq, void *v) 207{ 208 struct dev_addr_list *m; 209 struct net_device *dev = v; 210 211 netif_tx_lock_bh(dev); 212 for (m = dev->mc_list; m; m = m->next) { 213 int i; 214 215 seq_printf(seq, "%-4d %-15s %-5d %-5d ", dev->ifindex, 216 dev->name, m->dmi_users, m->dmi_gusers); 217 218 for (i = 0; i < m->dmi_addrlen; i++) 219 seq_printf(seq, "%02x", m->dmi_addr[i]); 220 221 seq_putc(seq, '\n'); 222 } 223 netif_tx_unlock_bh(dev); 224 return 0; 225} 226 227static const struct seq_operations dev_mc_seq_ops = { 228 .start = dev_mc_seq_start, 229 .next = dev_mc_seq_next, 230 .stop = dev_mc_seq_stop, 231 .show = dev_mc_seq_show, 232}; 233 234static int dev_mc_seq_open(struct inode *inode, struct file *file) 235{ 236 return seq_open(file, &dev_mc_seq_ops); 237} 238 239static const struct file_operations dev_mc_seq_fops = { 240 .owner = THIS_MODULE, 241 .open = dev_mc_seq_open, 242 .read = seq_read, 243 .llseek = seq_lseek, 244 .release = seq_release, 245}; 246 247#endif 248 249void __init dev_mcast_init(void) 250{ 251 proc_net_fops_create("dev_mcast", 0, &dev_mc_seq_fops); 252} 253 254EXPORT_SYMBOL(dev_mc_add); 255EXPORT_SYMBOL(dev_mc_delete);