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