at v2.6.19-rc5 297 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/sched.h> 31#include <linux/string.h> 32#include <linux/mm.h> 33#include <linux/socket.h> 34#include <linux/sockios.h> 35#include <linux/in.h> 36#include <linux/errno.h> 37#include <linux/interrupt.h> 38#include <linux/if_ether.h> 39#include <linux/inet.h> 40#include <linux/netdevice.h> 41#include <linux/etherdevice.h> 42#include <linux/proc_fs.h> 43#include <linux/seq_file.h> 44#include <linux/init.h> 45#include <net/ip.h> 46#include <net/route.h> 47#include <linux/skbuff.h> 48#include <net/sock.h> 49#include <net/arp.h> 50 51 52/* 53 * Device multicast list maintenance. 54 * 55 * This is used both by IP and by the user level maintenance functions. 56 * Unlike BSD we maintain a usage count on a given multicast address so 57 * that a casual user application can add/delete multicasts used by 58 * protocols without doing damage to the protocols when it deletes the 59 * entries. It also helps IP as it tracks overlapping maps. 60 * 61 * Device mc lists are changed by bh at least if IPv6 is enabled, 62 * so that it must be bh protected. 63 * 64 * We block accesses to device mc filters with netif_tx_lock. 65 */ 66 67/* 68 * Update the multicast list into the physical NIC controller. 69 */ 70 71static void __dev_mc_upload(struct net_device *dev) 72{ 73 /* Don't do anything till we up the interface 74 * [dev_open will call this function so the list will 75 * stay sane] 76 */ 77 78 if (!(dev->flags&IFF_UP)) 79 return; 80 81 /* 82 * Devices with no set multicast or which have been 83 * detached don't get set. 84 */ 85 86 if (dev->set_multicast_list == NULL || 87 !netif_device_present(dev)) 88 return; 89 90 dev->set_multicast_list(dev); 91} 92 93void dev_mc_upload(struct net_device *dev) 94{ 95 netif_tx_lock_bh(dev); 96 __dev_mc_upload(dev); 97 netif_tx_unlock_bh(dev); 98} 99 100/* 101 * Delete a device level multicast 102 */ 103 104int dev_mc_delete(struct net_device *dev, void *addr, int alen, int glbl) 105{ 106 int err = 0; 107 struct dev_mc_list *dmi, **dmip; 108 109 netif_tx_lock_bh(dev); 110 111 for (dmip = &dev->mc_list; (dmi = *dmip) != NULL; dmip = &dmi->next) { 112 /* 113 * Find the entry we want to delete. The device could 114 * have variable length entries so check these too. 115 */ 116 if (memcmp(dmi->dmi_addr, addr, dmi->dmi_addrlen) == 0 && 117 alen == dmi->dmi_addrlen) { 118 if (glbl) { 119 int old_glbl = dmi->dmi_gusers; 120 dmi->dmi_gusers = 0; 121 if (old_glbl == 0) 122 break; 123 } 124 if (--dmi->dmi_users) 125 goto done; 126 127 /* 128 * Last user. So delete the entry. 129 */ 130 *dmip = dmi->next; 131 dev->mc_count--; 132 133 kfree(dmi); 134 135 /* 136 * We have altered the list, so the card 137 * loaded filter is now wrong. Fix it 138 */ 139 __dev_mc_upload(dev); 140 141 netif_tx_unlock_bh(dev); 142 return 0; 143 } 144 } 145 err = -ENOENT; 146done: 147 netif_tx_unlock_bh(dev); 148 return err; 149} 150 151/* 152 * Add a device level multicast 153 */ 154 155int dev_mc_add(struct net_device *dev, void *addr, int alen, int glbl) 156{ 157 int err = 0; 158 struct dev_mc_list *dmi, *dmi1; 159 160 dmi1 = kmalloc(sizeof(*dmi), GFP_ATOMIC); 161 162 netif_tx_lock_bh(dev); 163 for (dmi = dev->mc_list; dmi != NULL; dmi = dmi->next) { 164 if (memcmp(dmi->dmi_addr, addr, dmi->dmi_addrlen) == 0 && 165 dmi->dmi_addrlen == alen) { 166 if (glbl) { 167 int old_glbl = dmi->dmi_gusers; 168 dmi->dmi_gusers = 1; 169 if (old_glbl) 170 goto done; 171 } 172 dmi->dmi_users++; 173 goto done; 174 } 175 } 176 177 if ((dmi = dmi1) == NULL) { 178 netif_tx_unlock_bh(dev); 179 return -ENOMEM; 180 } 181 memcpy(dmi->dmi_addr, addr, alen); 182 dmi->dmi_addrlen = alen; 183 dmi->next = dev->mc_list; 184 dmi->dmi_users = 1; 185 dmi->dmi_gusers = glbl ? 1 : 0; 186 dev->mc_list = dmi; 187 dev->mc_count++; 188 189 __dev_mc_upload(dev); 190 191 netif_tx_unlock_bh(dev); 192 return 0; 193 194done: 195 netif_tx_unlock_bh(dev); 196 kfree(dmi1); 197 return err; 198} 199 200/* 201 * Discard multicast list when a device is downed 202 */ 203 204void dev_mc_discard(struct net_device *dev) 205{ 206 netif_tx_lock_bh(dev); 207 208 while (dev->mc_list != NULL) { 209 struct dev_mc_list *tmp = dev->mc_list; 210 dev->mc_list = tmp->next; 211 if (tmp->dmi_users > tmp->dmi_gusers) 212 printk("dev_mc_discard: multicast leakage! dmi_users=%d\n", tmp->dmi_users); 213 kfree(tmp); 214 } 215 dev->mc_count = 0; 216 217 netif_tx_unlock_bh(dev); 218} 219 220#ifdef CONFIG_PROC_FS 221static void *dev_mc_seq_start(struct seq_file *seq, loff_t *pos) 222{ 223 struct net_device *dev; 224 loff_t off = 0; 225 226 read_lock(&dev_base_lock); 227 for (dev = dev_base; dev; dev = dev->next) { 228 if (off++ == *pos) 229 return dev; 230 } 231 return NULL; 232} 233 234static void *dev_mc_seq_next(struct seq_file *seq, void *v, loff_t *pos) 235{ 236 struct net_device *dev = v; 237 ++*pos; 238 return dev->next; 239} 240 241static void dev_mc_seq_stop(struct seq_file *seq, void *v) 242{ 243 read_unlock(&dev_base_lock); 244} 245 246 247static int dev_mc_seq_show(struct seq_file *seq, void *v) 248{ 249 struct dev_mc_list *m; 250 struct net_device *dev = v; 251 252 netif_tx_lock_bh(dev); 253 for (m = dev->mc_list; m; m = m->next) { 254 int i; 255 256 seq_printf(seq, "%-4d %-15s %-5d %-5d ", dev->ifindex, 257 dev->name, m->dmi_users, m->dmi_gusers); 258 259 for (i = 0; i < m->dmi_addrlen; i++) 260 seq_printf(seq, "%02x", m->dmi_addr[i]); 261 262 seq_putc(seq, '\n'); 263 } 264 netif_tx_unlock_bh(dev); 265 return 0; 266} 267 268static struct seq_operations dev_mc_seq_ops = { 269 .start = dev_mc_seq_start, 270 .next = dev_mc_seq_next, 271 .stop = dev_mc_seq_stop, 272 .show = dev_mc_seq_show, 273}; 274 275static int dev_mc_seq_open(struct inode *inode, struct file *file) 276{ 277 return seq_open(file, &dev_mc_seq_ops); 278} 279 280static struct file_operations dev_mc_seq_fops = { 281 .owner = THIS_MODULE, 282 .open = dev_mc_seq_open, 283 .read = seq_read, 284 .llseek = seq_lseek, 285 .release = seq_release, 286}; 287 288#endif 289 290void __init dev_mcast_init(void) 291{ 292 proc_net_fops_create("dev_mcast", 0, &dev_mc_seq_fops); 293} 294 295EXPORT_SYMBOL(dev_mc_add); 296EXPORT_SYMBOL(dev_mc_delete); 297EXPORT_SYMBOL(dev_mc_upload);