]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * Linux NET3: Multicast List maintenance. | |
3 | * | |
4 | * Authors: | |
5 | * Tim Kordas <[email protected]> | |
6 | * Richard Underwood <[email protected]> | |
7 | * | |
8 | * Stir fried together from the IP multicast and CAP patches above | |
9 | * Alan Cox <[email protected]> | |
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 | * | |
932ff279 | 65 | * We block accesses to device mc filters with netif_tx_lock. |
1da177e4 LT |
66 | */ |
67 | ||
68 | /* | |
69 | * Update the multicast list into the physical NIC controller. | |
70 | */ | |
71 | ||
72 | static 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 | ||
94 | void dev_mc_upload(struct net_device *dev) | |
95 | { | |
932ff279 | 96 | netif_tx_lock_bh(dev); |
1da177e4 | 97 | __dev_mc_upload(dev); |
932ff279 | 98 | netif_tx_unlock_bh(dev); |
1da177e4 LT |
99 | } |
100 | ||
101 | /* | |
102 | * Delete a device level multicast | |
103 | */ | |
104 | ||
105 | int 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 | ||
932ff279 | 110 | netif_tx_lock_bh(dev); |
1da177e4 LT |
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 | ||
932ff279 | 142 | netif_tx_unlock_bh(dev); |
1da177e4 LT |
143 | return 0; |
144 | } | |
145 | } | |
146 | err = -ENOENT; | |
147 | done: | |
932ff279 | 148 | netif_tx_unlock_bh(dev); |
1da177e4 LT |
149 | return err; |
150 | } | |
151 | ||
152 | /* | |
153 | * Add a device level multicast | |
154 | */ | |
155 | ||
156 | int 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 | ||
8b3a7005 | 161 | dmi1 = kmalloc(sizeof(*dmi), GFP_ATOMIC); |
1da177e4 | 162 | |
932ff279 | 163 | netif_tx_lock_bh(dev); |
1da177e4 LT |
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) { | |
932ff279 | 179 | netif_tx_unlock_bh(dev); |
1da177e4 LT |
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 | ||
932ff279 | 192 | netif_tx_unlock_bh(dev); |
1da177e4 LT |
193 | return 0; |
194 | ||
195 | done: | |
932ff279 | 196 | netif_tx_unlock_bh(dev); |
a51482bd | 197 | kfree(dmi1); |
1da177e4 LT |
198 | return err; |
199 | } | |
200 | ||
201 | /* | |
202 | * Discard multicast list when a device is downed | |
203 | */ | |
204 | ||
205 | void dev_mc_discard(struct net_device *dev) | |
206 | { | |
932ff279 | 207 | netif_tx_lock_bh(dev); |
1da177e4 LT |
208 | |
209 | while (dev->mc_list != NULL) { | |
210 | struct dev_mc_list *tmp = dev->mc_list; | |
211 | dev->mc_list = tmp->next; | |
212 | if (tmp->dmi_users > tmp->dmi_gusers) | |
213 | printk("dev_mc_discard: multicast leakage! dmi_users=%d\n", tmp->dmi_users); | |
214 | kfree(tmp); | |
215 | } | |
216 | dev->mc_count = 0; | |
217 | ||
932ff279 | 218 | netif_tx_unlock_bh(dev); |
1da177e4 LT |
219 | } |
220 | ||
221 | #ifdef CONFIG_PROC_FS | |
222 | static void *dev_mc_seq_start(struct seq_file *seq, loff_t *pos) | |
223 | { | |
224 | struct net_device *dev; | |
225 | loff_t off = 0; | |
226 | ||
227 | read_lock(&dev_base_lock); | |
228 | for (dev = dev_base; dev; dev = dev->next) { | |
229 | if (off++ == *pos) | |
230 | return dev; | |
231 | } | |
232 | return NULL; | |
233 | } | |
234 | ||
235 | static void *dev_mc_seq_next(struct seq_file *seq, void *v, loff_t *pos) | |
236 | { | |
237 | struct net_device *dev = v; | |
238 | ++*pos; | |
239 | return dev->next; | |
240 | } | |
241 | ||
242 | static void dev_mc_seq_stop(struct seq_file *seq, void *v) | |
243 | { | |
244 | read_unlock(&dev_base_lock); | |
245 | } | |
246 | ||
247 | ||
248 | static int dev_mc_seq_show(struct seq_file *seq, void *v) | |
249 | { | |
250 | struct dev_mc_list *m; | |
251 | struct net_device *dev = v; | |
252 | ||
932ff279 | 253 | netif_tx_lock_bh(dev); |
1da177e4 LT |
254 | for (m = dev->mc_list; m; m = m->next) { |
255 | int i; | |
256 | ||
257 | seq_printf(seq, "%-4d %-15s %-5d %-5d ", dev->ifindex, | |
258 | dev->name, m->dmi_users, m->dmi_gusers); | |
259 | ||
260 | for (i = 0; i < m->dmi_addrlen; i++) | |
261 | seq_printf(seq, "%02x", m->dmi_addr[i]); | |
262 | ||
263 | seq_putc(seq, '\n'); | |
264 | } | |
932ff279 | 265 | netif_tx_unlock_bh(dev); |
1da177e4 LT |
266 | return 0; |
267 | } | |
268 | ||
269 | static struct seq_operations dev_mc_seq_ops = { | |
270 | .start = dev_mc_seq_start, | |
271 | .next = dev_mc_seq_next, | |
272 | .stop = dev_mc_seq_stop, | |
273 | .show = dev_mc_seq_show, | |
274 | }; | |
275 | ||
276 | static int dev_mc_seq_open(struct inode *inode, struct file *file) | |
277 | { | |
278 | return seq_open(file, &dev_mc_seq_ops); | |
279 | } | |
280 | ||
281 | static struct file_operations dev_mc_seq_fops = { | |
282 | .owner = THIS_MODULE, | |
283 | .open = dev_mc_seq_open, | |
284 | .read = seq_read, | |
285 | .llseek = seq_lseek, | |
286 | .release = seq_release, | |
287 | }; | |
288 | ||
289 | #endif | |
290 | ||
291 | void __init dev_mcast_init(void) | |
292 | { | |
293 | proc_net_fops_create("dev_mcast", 0, &dev_mc_seq_fops); | |
294 | } | |
295 | ||
296 | EXPORT_SYMBOL(dev_mc_add); | |
297 | EXPORT_SYMBOL(dev_mc_delete); | |
298 | EXPORT_SYMBOL(dev_mc_upload); |