ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / net / bridge / br_fdb.c
1 /*
2  *      Forwarding database
3  *      Linux ethernet bridge
4  *
5  *      Authors:
6  *      Lennert Buytenhek               <buytenh@gnu.org>
7  *
8  *      $Id: br_fdb.c,v 1.6 2002/01/17 00:57:07 davem Exp $
9  *
10  *      This program is free software; you can redistribute it and/or
11  *      modify it under the terms of the GNU General Public License
12  *      as published by the Free Software Foundation; either version
13  *      2 of the License, or (at your option) any later version.
14  */
15
16 #include <linux/kernel.h>
17 #include <linux/init.h>
18 #include <linux/spinlock.h>
19 #include <linux/if_bridge.h>
20 #include <linux/times.h>
21 #include <linux/etherdevice.h>
22 #include <asm/atomic.h>
23 #include <asm/uaccess.h>
24 #include "br_private.h"
25
26 static kmem_cache_t *br_fdb_cache;
27
28 void __init br_fdb_init(void)
29 {
30         br_fdb_cache = kmem_cache_create("bridge_fdb_cache",
31                                          sizeof(struct net_bridge_fdb_entry),
32                                          0,
33                                          SLAB_HWCACHE_ALIGN, NULL, NULL);
34 }
35
36 void __exit br_fdb_fini(void)
37 {
38         kmem_cache_destroy(br_fdb_cache);
39 }
40
41
42 /* if topology_changing then use forward_delay (default 15 sec)
43  * otherwise keep longer (default 5 minutes)
44  */
45 static __inline__ unsigned long hold_time(const struct net_bridge *br)
46 {
47         return br->topology_change ? br->forward_delay : br->ageing_time;
48 }
49
50 static __inline__ int has_expired(const struct net_bridge *br,
51                                   const struct net_bridge_fdb_entry *fdb)
52 {
53         return !fdb->is_static 
54                 && time_before_eq(fdb->ageing_timer + hold_time(br), jiffies);
55 }
56
57 static inline void copy_fdb(struct __fdb_entry *ent, 
58                                 const struct net_bridge_fdb_entry *f)
59 {
60         memset(ent, 0, sizeof(struct __fdb_entry));
61         memcpy(ent->mac_addr, f->addr.addr, ETH_ALEN);
62         ent->port_no = f->dst?f->dst->port_no:0;
63         ent->is_local = f->is_local;
64         ent->ageing_timer_value = f->is_static ? 0 
65                 : jiffies_to_clock_t(jiffies - f->ageing_timer);
66 }
67
68 static __inline__ int br_mac_hash(const unsigned char *mac)
69 {
70         unsigned long x;
71
72         x = mac[0];
73         x = (x << 2) ^ mac[1];
74         x = (x << 2) ^ mac[2];
75         x = (x << 2) ^ mac[3];
76         x = (x << 2) ^ mac[4];
77         x = (x << 2) ^ mac[5];
78
79         x ^= x >> 8;
80
81         return x & (BR_HASH_SIZE - 1);
82 }
83
84 static __inline__ void fdb_delete(struct net_bridge_fdb_entry *f)
85 {
86         hlist_del(&f->hlist);
87         list_del(&f->age_list);
88         br_fdb_put(f);
89 }
90
91 void br_fdb_changeaddr(struct net_bridge_port *p, const unsigned char *newaddr)
92 {
93         struct net_bridge *br;
94         int i;
95         int newhash = br_mac_hash(newaddr);
96
97         br = p->br;
98         write_lock_bh(&br->hash_lock);
99         for (i=0;i<BR_HASH_SIZE;i++) {
100                 struct hlist_node *h;
101                 
102                 hlist_for_each(h, &br->hash[i]) {
103                         struct net_bridge_fdb_entry *f
104                                 = hlist_entry(h, struct net_bridge_fdb_entry, hlist);
105
106                         if (f->dst == p && f->is_local) {
107                                 memcpy(f->addr.addr, newaddr, ETH_ALEN);
108                                 if (newhash != i) {
109                                         hlist_del(&f->hlist);
110                                         hlist_add_head(&f->hlist,
111                                                        &br->hash[newhash]);
112                                 }
113                                 goto out;
114                         }
115                 }
116         }
117  out:
118         write_unlock_bh(&br->hash_lock);
119 }
120
121 void br_fdb_cleanup(unsigned long _data)
122 {
123         struct net_bridge *br = (struct net_bridge *)_data;
124         struct list_head *l, *n;
125         unsigned long delay;
126
127         write_lock_bh(&br->hash_lock);
128         delay = hold_time(br);
129
130         list_for_each_safe(l, n, &br->age_list) {
131                 struct net_bridge_fdb_entry *f
132                         = list_entry(l, struct net_bridge_fdb_entry, age_list);
133                 unsigned long expires = f->ageing_timer + delay;
134
135                 if (time_before_eq(expires, jiffies)) {
136                         if (!f->is_static) {
137                                 pr_debug("expire age %lu jiffies %lu\n",
138                                          f->ageing_timer, jiffies);
139                                 fdb_delete(f);
140                         }
141                 } else {
142                         mod_timer(&br->gc_timer, expires);
143                         break;
144                 }
145         }
146         write_unlock_bh(&br->hash_lock);
147 }
148
149 void br_fdb_delete_by_port(struct net_bridge *br, struct net_bridge_port *p)
150 {
151         int i;
152
153         write_lock_bh(&br->hash_lock);
154         for (i=0;i<BR_HASH_SIZE;i++) {
155                 struct hlist_node *h, *g;
156                 
157                 hlist_for_each_safe(h, g, &br->hash[i]) {
158                         struct net_bridge_fdb_entry *f
159                                 = hlist_entry(h, struct net_bridge_fdb_entry, hlist);
160                         if (f->dst == p) {
161                                 fdb_delete(f);
162                         }
163                 }
164         }
165         write_unlock_bh(&br->hash_lock);
166 }
167
168 struct net_bridge_fdb_entry *br_fdb_get(struct net_bridge *br, unsigned char *addr)
169 {
170         struct hlist_node *h;
171
172         read_lock_bh(&br->hash_lock);
173                 
174         hlist_for_each(h, &br->hash[br_mac_hash(addr)]) {
175                 struct net_bridge_fdb_entry *fdb
176                         = hlist_entry(h, struct net_bridge_fdb_entry, hlist);
177
178                 if (!memcmp(fdb->addr.addr, addr, ETH_ALEN)) {
179                         if (has_expired(br, fdb))
180                                 goto ret_null;
181
182                         atomic_inc(&fdb->use_count);
183                         read_unlock_bh(&br->hash_lock);
184                         return fdb;
185                 }
186         }
187  ret_null:
188         read_unlock_bh(&br->hash_lock);
189         return NULL;
190 }
191
192 void br_fdb_put(struct net_bridge_fdb_entry *ent)
193 {
194         if (atomic_dec_and_test(&ent->use_count))
195                 kmem_cache_free(br_fdb_cache, ent);
196 }
197
198 int br_fdb_get_entries(struct net_bridge *br,
199                        unsigned char *_buf,
200                        int maxnum,
201                        int offset)
202 {
203         int i;
204         int num;
205         struct __fdb_entry *walk;
206
207         num = 0;
208         walk = (struct __fdb_entry *)_buf;
209
210         read_lock_bh(&br->hash_lock);
211         for (i=0;i<BR_HASH_SIZE;i++) {
212                 struct hlist_node *h;
213                 
214                 hlist_for_each(h, &br->hash[i]) {
215                         struct net_bridge_fdb_entry *f
216                                 = hlist_entry(h, struct net_bridge_fdb_entry, hlist);
217                         struct __fdb_entry ent;
218
219                         if (num >= maxnum)
220                                 goto out;
221
222                         if (has_expired(br, f)) 
223                                 continue;
224
225                         if (offset) {
226                                 offset--;
227                                 continue;
228                         }
229
230                         copy_fdb(&ent, f);
231
232                         atomic_inc(&f->use_count);
233                         read_unlock_bh(&br->hash_lock);
234                         
235                         if (copy_to_user(walk, &ent, sizeof(struct __fdb_entry)))
236                                 return -EFAULT;
237
238                         read_lock_bh(&br->hash_lock);
239                         
240                         /* entry was deleted during copy_to_user */
241                         if (atomic_dec_and_test(&f->use_count)) {
242                                 kmem_cache_free(br_fdb_cache, f);
243                                 num = -EAGAIN;
244                                 goto out;
245                         }
246
247                         /* entry changed address hash while copying */
248                         if (br_mac_hash(f->addr.addr) != i) {
249                                 num = -EAGAIN;
250                                 goto out;
251                         }
252
253                         num++;
254                         walk++;
255                 }
256         }
257
258  out:
259         read_unlock_bh(&br->hash_lock);
260         return num;
261 }
262
263 int br_fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
264                   const unsigned char *addr, int is_local)
265 {
266         struct hlist_node *h;
267         struct net_bridge_fdb_entry *fdb;
268         int hash = br_mac_hash(addr);
269         int ret = 0;
270
271         if (!is_valid_ether_addr(addr))
272                 return -EADDRNOTAVAIL;
273
274         write_lock_bh(&br->hash_lock);
275         hlist_for_each(h, &br->hash[hash]) {
276                 fdb = hlist_entry(h, struct net_bridge_fdb_entry, hlist);
277                 if (!memcmp(fdb->addr.addr, addr, ETH_ALEN)) {
278                         /* attempt to update an entry for a local interface */
279                         if (unlikely(fdb->is_local)) {
280                                 if (is_local) 
281                                         printk(KERN_INFO "%s: attempt to add"
282                                                " interface with same source address.\n",
283                                                source->dev->name);
284                                 else if (net_ratelimit()) 
285                                         printk(KERN_WARNING "%s: received packet with "
286                                                " own address as source address\n",
287                                                source->dev->name);
288                                 ret = -EEXIST;
289                                 goto out;
290                         }
291
292
293                         if (likely(!fdb->is_static || is_local)) {
294                                 /* move to end of age list */
295                                 list_del(&fdb->age_list);
296                                 goto update;
297                         }
298                         goto out;
299                 }
300         }
301
302         fdb = kmem_cache_alloc(br_fdb_cache, GFP_ATOMIC);
303         if (unlikely(fdb == NULL)) {
304                 ret = -ENOMEM;
305                 goto out;
306         }
307
308         memcpy(fdb->addr.addr, addr, ETH_ALEN);
309         atomic_set(&fdb->use_count, 1);
310         hlist_add_head(&fdb->hlist, &br->hash[hash]);
311
312         if (!timer_pending(&br->gc_timer)) {
313                 br->gc_timer.expires = jiffies + hold_time(br);
314                 add_timer(&br->gc_timer);
315         }
316
317  update:
318         fdb->dst = source;
319         fdb->is_local = is_local;
320         fdb->is_static = is_local;
321         fdb->ageing_timer = jiffies;
322         list_add_tail(&fdb->age_list, &br->age_list);
323  out:
324         write_unlock_bh(&br->hash_lock);
325
326         return ret;
327 }