3 * Linux ethernet bridge
6 * Lennert Buytenhek <buytenh@gnu.org>
8 * $Id: br_fdb.c,v 1.6 2002/01/17 00:57:07 davem Exp $
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.
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"
26 static kmem_cache_t *br_fdb_cache;
28 void __init br_fdb_init(void)
30 br_fdb_cache = kmem_cache_create("bridge_fdb_cache",
31 sizeof(struct net_bridge_fdb_entry),
33 SLAB_HWCACHE_ALIGN, NULL, NULL);
36 void __exit br_fdb_fini(void)
38 kmem_cache_destroy(br_fdb_cache);
42 /* if topology_changing then use forward_delay (default 15 sec)
43 * otherwise keep longer (default 5 minutes)
45 static __inline__ unsigned long hold_time(const struct net_bridge *br)
47 return br->topology_change ? br->forward_delay : br->ageing_time;
50 static __inline__ int has_expired(const struct net_bridge *br,
51 const struct net_bridge_fdb_entry *fdb)
53 return !fdb->is_static
54 && time_before_eq(fdb->ageing_timer + hold_time(br), jiffies);
57 static inline void copy_fdb(struct __fdb_entry *ent,
58 const struct net_bridge_fdb_entry *f)
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);
68 static __inline__ int br_mac_hash(const unsigned char *mac)
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];
81 return x & (BR_HASH_SIZE - 1);
84 static __inline__ void fdb_delete(struct net_bridge_fdb_entry *f)
87 list_del(&f->age_list);
91 void br_fdb_changeaddr(struct net_bridge_port *p, const unsigned char *newaddr)
93 struct net_bridge *br;
95 int newhash = br_mac_hash(newaddr);
98 write_lock_bh(&br->hash_lock);
99 for (i=0;i<BR_HASH_SIZE;i++) {
100 struct hlist_node *h;
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);
106 if (f->dst == p && f->is_local) {
107 memcpy(f->addr.addr, newaddr, ETH_ALEN);
109 hlist_del(&f->hlist);
110 hlist_add_head(&f->hlist,
118 write_unlock_bh(&br->hash_lock);
121 void br_fdb_cleanup(unsigned long _data)
123 struct net_bridge *br = (struct net_bridge *)_data;
124 struct list_head *l, *n;
127 write_lock_bh(&br->hash_lock);
128 delay = hold_time(br);
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;
135 if (time_before_eq(expires, jiffies)) {
137 pr_debug("expire age %lu jiffies %lu\n",
138 f->ageing_timer, jiffies);
142 mod_timer(&br->gc_timer, expires);
146 write_unlock_bh(&br->hash_lock);
149 void br_fdb_delete_by_port(struct net_bridge *br, struct net_bridge_port *p)
153 write_lock_bh(&br->hash_lock);
154 for (i=0;i<BR_HASH_SIZE;i++) {
155 struct hlist_node *h, *g;
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);
165 write_unlock_bh(&br->hash_lock);
168 struct net_bridge_fdb_entry *br_fdb_get(struct net_bridge *br, unsigned char *addr)
170 struct hlist_node *h;
172 read_lock_bh(&br->hash_lock);
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);
178 if (!memcmp(fdb->addr.addr, addr, ETH_ALEN)) {
179 if (has_expired(br, fdb))
182 atomic_inc(&fdb->use_count);
183 read_unlock_bh(&br->hash_lock);
188 read_unlock_bh(&br->hash_lock);
192 void br_fdb_put(struct net_bridge_fdb_entry *ent)
194 if (atomic_dec_and_test(&ent->use_count))
195 kmem_cache_free(br_fdb_cache, ent);
198 int br_fdb_get_entries(struct net_bridge *br,
205 struct __fdb_entry *walk;
208 walk = (struct __fdb_entry *)_buf;
210 read_lock_bh(&br->hash_lock);
211 for (i=0;i<BR_HASH_SIZE;i++) {
212 struct hlist_node *h;
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;
222 if (has_expired(br, f))
232 atomic_inc(&f->use_count);
233 read_unlock_bh(&br->hash_lock);
235 if (copy_to_user(walk, &ent, sizeof(struct __fdb_entry)))
238 read_lock_bh(&br->hash_lock);
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);
247 /* entry changed address hash while copying */
248 if (br_mac_hash(f->addr.addr) != i) {
259 read_unlock_bh(&br->hash_lock);
263 int br_fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
264 const unsigned char *addr, int is_local)
266 struct hlist_node *h;
267 struct net_bridge_fdb_entry *fdb;
268 int hash = br_mac_hash(addr);
271 if (!is_valid_ether_addr(addr))
272 return -EADDRNOTAVAIL;
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)) {
281 printk(KERN_INFO "%s: attempt to add"
282 " interface with same source address.\n",
284 else if (net_ratelimit())
285 printk(KERN_WARNING "%s: received packet with "
286 " own address as source address\n",
293 if (likely(!fdb->is_static || is_local)) {
294 /* move to end of age list */
295 list_del(&fdb->age_list);
302 fdb = kmem_cache_alloc(br_fdb_cache, GFP_ATOMIC);
303 if (unlikely(fdb == NULL)) {
308 memcpy(fdb->addr.addr, addr, ETH_ALEN);
309 atomic_set(&fdb->use_count, 1);
310 hlist_add_head(&fdb->hlist, &br->hash[hash]);
312 if (!timer_pending(&br->gc_timer)) {
313 br->gc_timer.expires = jiffies + hold_time(br);
314 add_timer(&br->gc_timer);
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);
324 write_unlock_bh(&br->hash_lock);