at v2.6.23-rc5 261 lines 6.3 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, *next; 120 int err = 0; 121 122 netif_tx_lock_bh(to); 123 da = from->mc_list; 124 while (da != NULL) { 125 next = da->next; 126 if (!da->da_synced) { 127 err = __dev_addr_add(&to->mc_list, &to->mc_count, 128 da->da_addr, da->da_addrlen, 0); 129 if (err < 0) 130 break; 131 da->da_synced = 1; 132 da->da_users++; 133 } else if (da->da_users == 1) { 134 __dev_addr_delete(&to->mc_list, &to->mc_count, 135 da->da_addr, da->da_addrlen, 0); 136 __dev_addr_delete(&from->mc_list, &from->mc_count, 137 da->da_addr, da->da_addrlen, 0); 138 } 139 da = next; 140 } 141 if (!err) 142 __dev_set_rx_mode(to); 143 netif_tx_unlock_bh(to); 144 145 return err; 146} 147EXPORT_SYMBOL(dev_mc_sync); 148 149 150/** 151 * dev_mc_unsync - Remove synchronized addresses from the destination 152 * device 153 * @to: destination device 154 * @from: source device 155 * 156 * Remove all addresses that were added to the destination device by 157 * dev_mc_sync(). This function is intended to be called from the 158 * dev->stop function of layered software devices. 159 */ 160void dev_mc_unsync(struct net_device *to, struct net_device *from) 161{ 162 struct dev_addr_list *da, *next; 163 164 netif_tx_lock_bh(from); 165 netif_tx_lock_bh(to); 166 167 da = from->mc_list; 168 while (da != NULL) { 169 next = da->next; 170 if (!da->da_synced) 171 continue; 172 __dev_addr_delete(&to->mc_list, &to->mc_count, 173 da->da_addr, da->da_addrlen, 0); 174 da->da_synced = 0; 175 __dev_addr_delete(&from->mc_list, &from->mc_count, 176 da->da_addr, da->da_addrlen, 0); 177 da = next; 178 } 179 __dev_set_rx_mode(to); 180 181 netif_tx_unlock_bh(to); 182 netif_tx_unlock_bh(from); 183} 184EXPORT_SYMBOL(dev_mc_unsync); 185 186#ifdef CONFIG_PROC_FS 187static void *dev_mc_seq_start(struct seq_file *seq, loff_t *pos) 188{ 189 struct net_device *dev; 190 loff_t off = 0; 191 192 read_lock(&dev_base_lock); 193 for_each_netdev(dev) { 194 if (off++ == *pos) 195 return dev; 196 } 197 return NULL; 198} 199 200static void *dev_mc_seq_next(struct seq_file *seq, void *v, loff_t *pos) 201{ 202 ++*pos; 203 return next_net_device((struct net_device *)v); 204} 205 206static void dev_mc_seq_stop(struct seq_file *seq, void *v) 207{ 208 read_unlock(&dev_base_lock); 209} 210 211 212static int dev_mc_seq_show(struct seq_file *seq, void *v) 213{ 214 struct dev_addr_list *m; 215 struct net_device *dev = v; 216 217 netif_tx_lock_bh(dev); 218 for (m = dev->mc_list; m; m = m->next) { 219 int i; 220 221 seq_printf(seq, "%-4d %-15s %-5d %-5d ", dev->ifindex, 222 dev->name, m->dmi_users, m->dmi_gusers); 223 224 for (i = 0; i < m->dmi_addrlen; i++) 225 seq_printf(seq, "%02x", m->dmi_addr[i]); 226 227 seq_putc(seq, '\n'); 228 } 229 netif_tx_unlock_bh(dev); 230 return 0; 231} 232 233static const struct seq_operations dev_mc_seq_ops = { 234 .start = dev_mc_seq_start, 235 .next = dev_mc_seq_next, 236 .stop = dev_mc_seq_stop, 237 .show = dev_mc_seq_show, 238}; 239 240static int dev_mc_seq_open(struct inode *inode, struct file *file) 241{ 242 return seq_open(file, &dev_mc_seq_ops); 243} 244 245static const struct file_operations dev_mc_seq_fops = { 246 .owner = THIS_MODULE, 247 .open = dev_mc_seq_open, 248 .read = seq_read, 249 .llseek = seq_lseek, 250 .release = seq_release, 251}; 252 253#endif 254 255void __init dev_mcast_init(void) 256{ 257 proc_net_fops_create("dev_mcast", 0, &dev_mc_seq_fops); 258} 259 260EXPORT_SYMBOL(dev_mc_add); 261EXPORT_SYMBOL(dev_mc_delete);