at v2.6.21 296 lines 6.5 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 * Update the multicast list into the physical NIC controller. 68 */ 69 70static void __dev_mc_upload(struct net_device *dev) 71{ 72 /* Don't do anything till we up the interface 73 * [dev_open will call this function so the list will 74 * stay sane] 75 */ 76 77 if (!(dev->flags&IFF_UP)) 78 return; 79 80 /* 81 * Devices with no set multicast or which have been 82 * detached don't get set. 83 */ 84 85 if (dev->set_multicast_list == NULL || 86 !netif_device_present(dev)) 87 return; 88 89 dev->set_multicast_list(dev); 90} 91 92void dev_mc_upload(struct net_device *dev) 93{ 94 netif_tx_lock_bh(dev); 95 __dev_mc_upload(dev); 96 netif_tx_unlock_bh(dev); 97} 98 99/* 100 * Delete a device level multicast 101 */ 102 103int dev_mc_delete(struct net_device *dev, void *addr, int alen, int glbl) 104{ 105 int err = 0; 106 struct dev_mc_list *dmi, **dmip; 107 108 netif_tx_lock_bh(dev); 109 110 for (dmip = &dev->mc_list; (dmi = *dmip) != NULL; dmip = &dmi->next) { 111 /* 112 * Find the entry we want to delete. The device could 113 * have variable length entries so check these too. 114 */ 115 if (memcmp(dmi->dmi_addr, addr, dmi->dmi_addrlen) == 0 && 116 alen == dmi->dmi_addrlen) { 117 if (glbl) { 118 int old_glbl = dmi->dmi_gusers; 119 dmi->dmi_gusers = 0; 120 if (old_glbl == 0) 121 break; 122 } 123 if (--dmi->dmi_users) 124 goto done; 125 126 /* 127 * Last user. So delete the entry. 128 */ 129 *dmip = dmi->next; 130 dev->mc_count--; 131 132 kfree(dmi); 133 134 /* 135 * We have altered the list, so the card 136 * loaded filter is now wrong. Fix it 137 */ 138 __dev_mc_upload(dev); 139 140 netif_tx_unlock_bh(dev); 141 return 0; 142 } 143 } 144 err = -ENOENT; 145done: 146 netif_tx_unlock_bh(dev); 147 return err; 148} 149 150/* 151 * Add a device level multicast 152 */ 153 154int dev_mc_add(struct net_device *dev, void *addr, int alen, int glbl) 155{ 156 int err = 0; 157 struct dev_mc_list *dmi, *dmi1; 158 159 dmi1 = kmalloc(sizeof(*dmi), GFP_ATOMIC); 160 161 netif_tx_lock_bh(dev); 162 for (dmi = dev->mc_list; dmi != NULL; dmi = dmi->next) { 163 if (memcmp(dmi->dmi_addr, addr, dmi->dmi_addrlen) == 0 && 164 dmi->dmi_addrlen == alen) { 165 if (glbl) { 166 int old_glbl = dmi->dmi_gusers; 167 dmi->dmi_gusers = 1; 168 if (old_glbl) 169 goto done; 170 } 171 dmi->dmi_users++; 172 goto done; 173 } 174 } 175 176 if ((dmi = dmi1) == NULL) { 177 netif_tx_unlock_bh(dev); 178 return -ENOMEM; 179 } 180 memcpy(dmi->dmi_addr, addr, alen); 181 dmi->dmi_addrlen = alen; 182 dmi->next = dev->mc_list; 183 dmi->dmi_users = 1; 184 dmi->dmi_gusers = glbl ? 1 : 0; 185 dev->mc_list = dmi; 186 dev->mc_count++; 187 188 __dev_mc_upload(dev); 189 190 netif_tx_unlock_bh(dev); 191 return 0; 192 193done: 194 netif_tx_unlock_bh(dev); 195 kfree(dmi1); 196 return err; 197} 198 199/* 200 * Discard multicast list when a device is downed 201 */ 202 203void dev_mc_discard(struct net_device *dev) 204{ 205 netif_tx_lock_bh(dev); 206 207 while (dev->mc_list != NULL) { 208 struct dev_mc_list *tmp = dev->mc_list; 209 dev->mc_list = tmp->next; 210 if (tmp->dmi_users > tmp->dmi_gusers) 211 printk("dev_mc_discard: multicast leakage! dmi_users=%d\n", tmp->dmi_users); 212 kfree(tmp); 213 } 214 dev->mc_count = 0; 215 216 netif_tx_unlock_bh(dev); 217} 218 219#ifdef CONFIG_PROC_FS 220static void *dev_mc_seq_start(struct seq_file *seq, loff_t *pos) 221{ 222 struct net_device *dev; 223 loff_t off = 0; 224 225 read_lock(&dev_base_lock); 226 for (dev = dev_base; dev; dev = dev->next) { 227 if (off++ == *pos) 228 return dev; 229 } 230 return NULL; 231} 232 233static void *dev_mc_seq_next(struct seq_file *seq, void *v, loff_t *pos) 234{ 235 struct net_device *dev = v; 236 ++*pos; 237 return dev->next; 238} 239 240static void dev_mc_seq_stop(struct seq_file *seq, void *v) 241{ 242 read_unlock(&dev_base_lock); 243} 244 245 246static int dev_mc_seq_show(struct seq_file *seq, void *v) 247{ 248 struct dev_mc_list *m; 249 struct net_device *dev = v; 250 251 netif_tx_lock_bh(dev); 252 for (m = dev->mc_list; m; m = m->next) { 253 int i; 254 255 seq_printf(seq, "%-4d %-15s %-5d %-5d ", dev->ifindex, 256 dev->name, m->dmi_users, m->dmi_gusers); 257 258 for (i = 0; i < m->dmi_addrlen; i++) 259 seq_printf(seq, "%02x", m->dmi_addr[i]); 260 261 seq_putc(seq, '\n'); 262 } 263 netif_tx_unlock_bh(dev); 264 return 0; 265} 266 267static struct seq_operations dev_mc_seq_ops = { 268 .start = dev_mc_seq_start, 269 .next = dev_mc_seq_next, 270 .stop = dev_mc_seq_stop, 271 .show = dev_mc_seq_show, 272}; 273 274static int dev_mc_seq_open(struct inode *inode, struct file *file) 275{ 276 return seq_open(file, &dev_mc_seq_ops); 277} 278 279static const struct file_operations dev_mc_seq_fops = { 280 .owner = THIS_MODULE, 281 .open = dev_mc_seq_open, 282 .read = seq_read, 283 .llseek = seq_lseek, 284 .release = seq_release, 285}; 286 287#endif 288 289void __init dev_mcast_init(void) 290{ 291 proc_net_fops_create("dev_mcast", 0, &dev_mc_seq_fops); 292} 293 294EXPORT_SYMBOL(dev_mc_add); 295EXPORT_SYMBOL(dev_mc_delete); 296EXPORT_SYMBOL(dev_mc_upload);