35ecf0e06dc4c00a71af6e719ec2c560be317757
[bcm963xx.git] / kernel / linux / 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/times.h>
20 #include <linux/netdevice.h>
21 #include <linux/etherdevice.h>
22 #include <asm/atomic.h>
23 #include "br_private.h"
24
25 static kmem_cache_t *br_fdb_cache;
26 static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
27                       const unsigned char *addr, int is_local);
28
29 void __init br_fdb_init(void)
30 {
31         br_fdb_cache = kmem_cache_create("bridge_fdb_cache",
32                                          sizeof(struct net_bridge_fdb_entry),
33                                          0,
34                                          SLAB_HWCACHE_ALIGN, NULL, NULL);
35 }
36
37 void __exit br_fdb_fini(void)
38 {
39         kmem_cache_destroy(br_fdb_cache);
40 }
41
42
43 /* if topology_changing then use forward_delay (default 15 sec)
44  * otherwise keep longer (default 5 minutes)
45  */
46 static __inline__ unsigned long hold_time(const struct net_bridge *br)
47 {
48         return br->topology_change ? br->forward_delay : br->ageing_time;
49 }
50
51 static __inline__ int has_expired(const struct net_bridge *br,
52                                   const struct net_bridge_fdb_entry *fdb)
53 {
54         return !fdb->is_static 
55                 && time_before_eq(fdb->ageing_timer + hold_time(br), jiffies);
56 }
57
58 static __inline__ int br_mac_hash(const unsigned char *mac)
59 {
60         unsigned long x;
61
62         x = mac[0];
63         x = (x << 2) ^ mac[1];
64         x = (x << 2) ^ mac[2];
65         x = (x << 2) ^ mac[3];
66         x = (x << 2) ^ mac[4];
67         x = (x << 2) ^ mac[5];
68
69         x ^= x >> 8;
70
71         return x & (BR_HASH_SIZE - 1);
72 }
73
74 static __inline__ void fdb_delete(struct net_bridge_fdb_entry *f)
75 {
76         hlist_del_rcu(&f->hlist);
77         if (!f->is_static)
78                 list_del(&f->u.age_list);
79
80         br_fdb_put(f);
81 }
82
83
84 #if defined(CONFIG_MIPS_BRCM)
85 void dolist(struct net_bridge *br)
86 {
87         struct net_bridge_mc_fdb_entry *dst;
88         struct list_head *lh;
89
90         
91         if (!br)
92             return;
93
94         printk("bridge  device  group                   source                  timeout\n");
95         list_for_each_rcu(lh, &br->mc_list) {
96             dst = (struct net_bridge_mc_fdb_entry *) list_entry(lh, struct net_bridge_mc_fdb_entry, list);
97             printk("%s  %s      ", br->dev->name, dst->dst->dev->name);
98             addr_debug(&dst->addr);
99             printk("    ");
100             addr_debug(&dst->host);
101             printk("    %d\n", (dst->tstamp - jiffies)/HZ);
102         }
103 }
104
105 int br_mc_fdb_update(struct net_bridge *br, struct net_bridge_port *prt, unsigned char *dest, unsigned char *host)
106 {
107         struct net_bridge_mc_fdb_entry *dst;
108         struct list_head *lh;
109         int ret = 0;
110     
111         list_for_each_rcu(lh, &br->mc_list) {
112             dst = (struct net_bridge_mc_fdb_entry *) list_entry(lh, struct net_bridge_mc_fdb_entry, list);
113             if (!memcmp(&dst->addr, dest, ETH_ALEN)) {
114                 dst->tstamp = jiffies + QUERY_TIMEOUT*HZ;
115                 if (!memcmp(&dst->host, host, ETH_ALEN))
116                     ret = 1;
117             }
118         }
119         
120         return ret;
121 }
122
123 struct net_bridge_mc_fdb_entry *br_mc_fdb_get(struct net_bridge *br, struct net_bridge_port *prt, unsigned char *dest, unsigned char *host)
124 {
125         struct net_bridge_mc_fdb_entry *dst;
126         struct list_head *lh;
127     
128         list_for_each_rcu(lh, &br->mc_list) {
129             dst = (struct net_bridge_mc_fdb_entry *) list_entry(lh, struct net_bridge_mc_fdb_entry, list);
130             if ((!memcmp(&dst->addr, dest, ETH_ALEN)) && (!memcmp(&dst->host, host, ETH_ALEN))) {
131                 if (dst->dst == prt)
132                     return dst;
133             }
134         }
135         
136         return NULL;
137 }
138
139 mac_addr upnp_addr = {0x01, 0x00, 0x5e, 0x7f, 0xff, 0xfa};
140
141 br_mc_fdb_add(struct net_bridge *br, struct net_bridge_port *prt, unsigned char *dest, unsigned char *host)
142 {
143         struct net_bridge_mc_fdb_entry *mc_fdb;
144
145         //printk("--- add mc entry ---\n");
146
147         if (!memcmp(dest, &upnp_addr, ETH_ALEN))
148             return 0;
149             
150         if (br_mc_fdb_update(br, prt, dest, host))
151             return 0;
152             
153         mc_fdb = kmalloc(sizeof(struct net_bridge_mc_fdb_entry), GFP_KERNEL);
154         if (!mc_fdb)
155             return ENOMEM;
156         memcpy(mc_fdb->addr.addr, dest, ETH_ALEN);
157         memcpy(mc_fdb->host.addr, host, ETH_ALEN);
158         mc_fdb->dst = prt;
159         mc_fdb->tstamp = jiffies + QUERY_TIMEOUT*HZ;;
160         spin_lock_bh(&br->mcl_lock);
161         list_add_tail_rcu(&mc_fdb->list, &br->mc_list);
162         spin_unlock_bh(&br->mcl_lock);
163
164         if (!br->start_timer) {
165             init_timer(&br->igmp_timer);
166             br->igmp_timer.expires = jiffies + TIMER_CHECK_TIMEOUT*HZ;
167             br->igmp_timer.function = query_timeout;
168             br->igmp_timer.data = br;
169             add_timer(&br->igmp_timer);
170             br->start_timer = 1;
171         }
172
173         return 1;
174 }
175
176 br_mc_fdb_cleanup(struct net_bridge *br)
177 {
178         struct net_bridge_mc_fdb_entry *dst;
179         struct list_head *lh;
180         struct list_head *tmp;
181     
182         spin_lock_bh(&br->mcl_lock);
183         list_for_each_safe_rcu(lh, tmp, &br->mc_list) {
184             dst = (struct net_bridge_mc_fdb_entry *) list_entry(lh, struct net_bridge_mc_fdb_entry, list);
185             list_del_rcu(&dst->list);
186             kfree(dst);
187         }
188         spin_unlock_bh(&br->mcl_lock);
189 }
190
191 br_mc_fdb_remove_grp(struct net_bridge *br, struct net_bridge_port *prt, unsigned char *dest)
192 {
193         struct net_bridge_mc_fdb_entry *dst;
194         struct list_head *lh;
195         struct list_head *tmp;
196
197         spin_lock_bh(&br->mcl_lock);
198         list_for_each_safe_rcu(lh, tmp, &br->mc_list) {
199             dst = (struct net_bridge_mc_fdb_entry *) list_entry(lh, struct net_bridge_mc_fdb_entry, list);
200             if ((!memcmp(&dst->addr, dest, ETH_ALEN)) && (dst->dst == prt)) {
201                 list_del_rcu(&dst->list);
202                 kfree(dst);
203             }
204         }
205         spin_unlock_bh(&br->mcl_lock);
206 }
207
208 br_mc_fdb_remove(struct net_bridge *br, struct net_bridge_port *prt, unsigned char *dest, unsigned char *host)
209 {
210         struct net_bridge_mc_fdb_entry *mc_fdb;
211
212         //printk("--- remove mc entry ---\n");
213         
214         if (mc_fdb = br_mc_fdb_get(br, prt, dest, host)) {
215             spin_lock_bh(&br->mcl_lock);
216             list_del_rcu(&mc_fdb->list);
217             kfree(mc_fdb);
218             spin_unlock_bh(&br->mcl_lock);
219
220             return 1;
221         }
222         
223         return 0;
224 }
225 #endif
226
227 void br_fdb_changeaddr(struct net_bridge_port *p, const unsigned char *newaddr)
228 {
229         struct net_bridge *br = p->br;
230         int i;
231         
232         spin_lock_bh(&br->hash_lock);
233
234         /* Search all chains since old address/hash is unknown */
235         for (i = 0; i < BR_HASH_SIZE; i++) {
236                 struct hlist_node *h;
237                 hlist_for_each(h, &br->hash[i]) {
238                         struct net_bridge_fdb_entry *f;
239
240                         f = hlist_entry(h, struct net_bridge_fdb_entry, hlist);
241                         if (f->dst == p && f->is_local) {
242                                 /* maybe another port has same hw addr? */
243                                 struct net_bridge_port *op;
244                                 list_for_each_entry(op, &br->port_list, list) {
245                                         if (op != p && 
246                                             !memcmp(op->dev->dev_addr,
247                                                     f->addr.addr, ETH_ALEN)) {
248                                                 f->dst = op;
249                                                 goto insert;
250                                         }
251                                 }
252
253                                 /* delete old one */
254                                 fdb_delete(f);
255                                 goto insert;
256                         }
257                 }
258         }
259  insert:
260         /* insert new address,  may fail if invalid address or dup. */
261         fdb_insert(br, p, newaddr, 1);
262
263
264         spin_unlock_bh(&br->hash_lock);
265 }
266
267 void br_fdb_cleanup(unsigned long _data)
268 {
269         struct net_bridge *br = (struct net_bridge *)_data;
270         struct list_head *l, *n;
271         unsigned long delay;
272
273         spin_lock_bh(&br->hash_lock);
274         delay = hold_time(br);
275
276         list_for_each_safe(l, n, &br->age_list) {
277                 struct net_bridge_fdb_entry *f;
278                 unsigned long expires;
279
280                 f = list_entry(l, struct net_bridge_fdb_entry, u.age_list);
281                 expires = f->ageing_timer + delay;
282
283                 if (time_before_eq(expires, jiffies)) {
284                         WARN_ON(f->is_static);
285                         pr_debug("expire age %lu jiffies %lu\n",
286                                  f->ageing_timer, jiffies);
287                         fdb_delete(f);
288                 } else {
289                         mod_timer(&br->gc_timer, expires);
290                         break;
291                 }
292         }
293         spin_unlock_bh(&br->hash_lock);
294 }
295
296 void br_fdb_delete_by_port(struct net_bridge *br, struct net_bridge_port *p)
297 {
298         int i;
299
300         spin_lock_bh(&br->hash_lock);
301         for (i = 0; i < BR_HASH_SIZE; i++) {
302                 struct hlist_node *h, *g;
303                 
304                 hlist_for_each_safe(h, g, &br->hash[i]) {
305                         struct net_bridge_fdb_entry *f
306                                 = hlist_entry(h, struct net_bridge_fdb_entry, hlist);
307                         if (f->dst != p) 
308                                 continue;
309
310                         /*
311                          * if multiple ports all have the same device address
312                          * then when one port is deleted, assign
313                          * the local entry to other port
314                          */
315                         if (f->is_local) {
316                                 struct net_bridge_port *op;
317                                 list_for_each_entry(op, &br->port_list, list) {
318                                         if (op != p && 
319                                             !memcmp(op->dev->dev_addr,
320                                                     f->addr.addr, ETH_ALEN)) {
321                                                 f->dst = op;
322                                                 goto skip_delete;
323                                         }
324                                 }
325                         }
326
327                         fdb_delete(f);
328                 skip_delete: ;
329                 }
330         }
331         spin_unlock_bh(&br->hash_lock);
332 }
333
334 /* No locking or refcounting, assumes caller has no preempt (rcu_read_lock) */
335 struct net_bridge_fdb_entry *__br_fdb_get(struct net_bridge *br,
336                                           const unsigned char *addr)
337 {
338         struct hlist_node *h;
339         struct net_bridge_fdb_entry *fdb;
340
341         hlist_for_each_entry_rcu(fdb, h, &br->hash[br_mac_hash(addr)], hlist) {
342                 if (!memcmp(fdb->addr.addr, addr, ETH_ALEN)) {
343                         if (unlikely(has_expired(br, fdb)))
344                                 break;
345                         return fdb;
346                 }
347         }
348
349         return NULL;
350 }
351
352 /* Interface used by ATM hook that keeps a ref count */
353 struct net_bridge_fdb_entry *br_fdb_get(struct net_bridge *br, 
354                                         unsigned char *addr)
355 {
356         struct net_bridge_fdb_entry *fdb;
357
358         rcu_read_lock();
359         fdb = __br_fdb_get(br, addr);
360         if (fdb) 
361                 atomic_inc(&fdb->use_count);
362         rcu_read_unlock();
363         return fdb;
364 }
365
366 static void fdb_rcu_free(struct rcu_head *head)
367 {
368         struct net_bridge_fdb_entry *ent
369                 = container_of(head, struct net_bridge_fdb_entry, u.rcu);
370         kmem_cache_free(br_fdb_cache, ent);
371 }
372
373 /* Set entry up for deletion with RCU  */
374 void br_fdb_put(struct net_bridge_fdb_entry *ent)
375 {
376         if (atomic_dec_and_test(&ent->use_count))
377                 call_rcu(&ent->u.rcu, fdb_rcu_free);
378 }
379
380 /*
381  * Fill buffer with forwarding table records in 
382  * the API format.
383  */
384 int br_fdb_fillbuf(struct net_bridge *br, void *buf,
385                    unsigned long maxnum, unsigned long skip)
386 {
387         struct __fdb_entry *fe = buf;
388         int i, num = 0;
389         struct hlist_node *h;
390         struct net_bridge_fdb_entry *f;
391
392         memset(buf, 0, maxnum*sizeof(struct __fdb_entry));
393
394         rcu_read_lock();
395         for (i = 0; i < BR_HASH_SIZE; i++) {
396                 hlist_for_each_entry_rcu(f, h, &br->hash[i], hlist) {
397                         if (num >= maxnum)
398                                 goto out;
399
400                         if (has_expired(br, f)) 
401                                 continue;
402
403                         if (skip) {
404                                 --skip;
405                                 continue;
406                         }
407
408                         /* convert from internal format to API */
409                         memcpy(fe->mac_addr, f->addr.addr, ETH_ALEN);
410                         fe->port_no = f->dst->port_no;
411                         fe->is_local = f->is_local;
412                         if (!f->is_static)
413                                 fe->ageing_timer_value = jiffies_to_clock_t(jiffies - f->ageing_timer);
414                         ++fe;
415                         ++num;
416                 }
417         }
418
419  out:
420         rcu_read_unlock();
421
422         return num;
423 }
424
425 static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
426                   const unsigned char *addr, int is_local)
427 {
428         struct hlist_node *h;
429         struct net_bridge_fdb_entry *fdb;
430         int hash = br_mac_hash(addr);
431
432         if (!is_valid_ether_addr(addr))
433                 return -EADDRNOTAVAIL;
434
435         hlist_for_each_entry(fdb, h, &br->hash[hash], hlist) {
436                 if (!memcmp(fdb->addr.addr, addr, ETH_ALEN)) {
437                         /* attempt to update an entry for a local interface */
438                         if (fdb->is_local) {
439                                 /* it is okay to have multiple ports with same 
440                                  * address, just don't allow to be spoofed.
441                                  */
442                                 if (is_local) 
443                                         return 0;
444                                         
445 #if 0                   /* Begin Broadcom commented out code */
446                                  
447                                 if (net_ratelimit()) 
448                                         printk(KERN_WARNING "%s: received packet with "
449                                                " own address as source address\n",
450                                                source->dev->name);
451                                                
452 #endif                  /* End Broadcom commented out code */
453                                 return -EEXIST;
454                         }
455
456                         if (is_local) {
457                                 printk(KERN_WARNING "%s adding interface with same address "
458                                        "as a received packet\n",
459                                        source->dev->name);
460                                 goto update;
461                         }
462
463                         if (fdb->is_static)
464                                 return 0;
465
466                         /* move to end of age list */
467                         list_del(&fdb->u.age_list);
468                         goto update;
469                 }
470         }
471
472         fdb = kmem_cache_alloc(br_fdb_cache, GFP_ATOMIC);
473         if (!fdb)
474                 return ENOMEM;
475
476         memcpy(fdb->addr.addr, addr, ETH_ALEN);
477         atomic_set(&fdb->use_count, 1);
478         hlist_add_head_rcu(&fdb->hlist, &br->hash[hash]);
479
480         if (!timer_pending(&br->gc_timer)) {
481                 br->gc_timer.expires = jiffies + hold_time(br);
482                 add_timer(&br->gc_timer);
483         }
484
485  update:
486         fdb->dst = source;
487         fdb->is_local = is_local;
488         fdb->is_static = is_local;
489         fdb->ageing_timer = jiffies;
490         if (!is_local) 
491                 list_add_tail(&fdb->u.age_list, &br->age_list);
492
493         return 0;
494 }
495
496 int br_fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
497                   const unsigned char *addr, int is_local)
498 {
499         int ret;
500
501         spin_lock_bh(&br->hash_lock);
502         ret = fdb_insert(br, source, addr, is_local);
503         spin_unlock_bh(&br->hash_lock);
504         return ret;
505 }