Commit | Line | Data |
---|---|---|
1da177e4 | 1 | /* |
4ec93edb | 2 | * Linux NET3: Multicast List maintenance. |
1da177e4 LT |
3 | * |
4 | * Authors: | |
4ec93edb | 5 | * Tim Kordas <tjk@nostromo.eeap.cwru.edu> |
1da177e4 LT |
6 | * Richard Underwood <richard@wuzz.demon.co.uk> |
7 | * | |
8 | * Stir fried together from the IP multicast and CAP patches above | |
4ec93edb | 9 | * Alan Cox <Alan.Cox@linux.org> |
1da177e4 LT |
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 | ||
bf0d5249 | 24 | #include <linux/module.h> |
1da177e4 LT |
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> | |
1da177e4 LT |
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 | /* | |
4ec93edb | 52 | * Device multicast list maintenance. |
1da177e4 | 53 | * |
4ec93edb YH |
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 | |
1da177e4 LT |
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 | * | |
932ff279 | 63 | * We block accesses to device mc filters with netif_tx_lock. |
1da177e4 LT |
64 | */ |
65 | ||
66 | /* | |
67 | * Update the multicast list into the physical NIC controller. | |
68 | */ | |
4ec93edb | 69 | |
1da177e4 LT |
70 | static 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 | ||
92 | void dev_mc_upload(struct net_device *dev) | |
93 | { | |
932ff279 | 94 | netif_tx_lock_bh(dev); |
1da177e4 | 95 | __dev_mc_upload(dev); |
932ff279 | 96 | netif_tx_unlock_bh(dev); |
1da177e4 LT |
97 | } |
98 | ||
99 | /* | |
100 | * Delete a device level multicast | |
101 | */ | |
4ec93edb | 102 | |
1da177e4 LT |
103 | int 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 | ||
932ff279 | 108 | netif_tx_lock_bh(dev); |
1da177e4 LT |
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); | |
4ec93edb | 139 | |
932ff279 | 140 | netif_tx_unlock_bh(dev); |
1da177e4 LT |
141 | return 0; |
142 | } | |
143 | } | |
144 | err = -ENOENT; | |
145 | done: | |
932ff279 | 146 | netif_tx_unlock_bh(dev); |
1da177e4 LT |
147 | return err; |
148 | } | |
149 | ||
150 | /* | |
151 | * Add a device level multicast | |
152 | */ | |
4ec93edb | 153 | |
1da177e4 LT |
154 | int 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 | ||
8b3a7005 | 159 | dmi1 = kmalloc(sizeof(*dmi), GFP_ATOMIC); |
1da177e4 | 160 | |
932ff279 | 161 | netif_tx_lock_bh(dev); |
1da177e4 LT |
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) { | |
932ff279 | 177 | netif_tx_unlock_bh(dev); |
1da177e4 LT |
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); | |
4ec93edb | 189 | |
932ff279 | 190 | netif_tx_unlock_bh(dev); |
1da177e4 LT |
191 | return 0; |
192 | ||
193 | done: | |
932ff279 | 194 | netif_tx_unlock_bh(dev); |
a51482bd | 195 | kfree(dmi1); |
1da177e4 LT |
196 | return err; |
197 | } | |
198 | ||
199 | /* | |
200 | * Discard multicast list when a device is downed | |
201 | */ | |
202 | ||
203 | void dev_mc_discard(struct net_device *dev) | |
204 | { | |
932ff279 | 205 | netif_tx_lock_bh(dev); |
4ec93edb | 206 | |
1da177e4 LT |
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 | ||
932ff279 | 216 | netif_tx_unlock_bh(dev); |
1da177e4 LT |
217 | } |
218 | ||
219 | #ifdef CONFIG_PROC_FS | |
220 | static 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) { | |
4ec93edb | 227 | if (off++ == *pos) |
1da177e4 LT |
228 | return dev; |
229 | } | |
230 | return NULL; | |
231 | } | |
232 | ||
233 | static 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 | ||
240 | static void dev_mc_seq_stop(struct seq_file *seq, void *v) | |
241 | { | |
242 | read_unlock(&dev_base_lock); | |
243 | } | |
244 | ||
245 | ||
246 | static 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 | ||
932ff279 | 251 | netif_tx_lock_bh(dev); |
1da177e4 LT |
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 | } | |
932ff279 | 263 | netif_tx_unlock_bh(dev); |
1da177e4 LT |
264 | return 0; |
265 | } | |
266 | ||
267 | static 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 | ||
274 | static int dev_mc_seq_open(struct inode *inode, struct file *file) | |
275 | { | |
276 | return seq_open(file, &dev_mc_seq_ops); | |
277 | } | |
278 | ||
9a32144e | 279 | static const struct file_operations dev_mc_seq_fops = { |
1da177e4 LT |
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 | ||
289 | void __init dev_mcast_init(void) | |
290 | { | |
291 | proc_net_fops_create("dev_mcast", 0, &dev_mc_seq_fops); | |
292 | } | |
293 | ||
294 | EXPORT_SYMBOL(dev_mc_add); | |
295 | EXPORT_SYMBOL(dev_mc_delete); | |
296 | EXPORT_SYMBOL(dev_mc_upload); |