File: /usr/src/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.5 2000/11/08 05:16:40 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/spinlock.h>
18     #include <linux/if_bridge.h>
19     #include <asm/atomic.h>
20     #include <asm/uaccess.h>
21     #include "br_private.h"
22     
23     static __inline__ unsigned long __timeout(struct net_bridge *br)
24     {
25     	unsigned long timeout;
26     
27     	timeout = jiffies - br->ageing_time;
28     	if (br->topology_change)
29     		timeout = jiffies - br->forward_delay;
30     
31     	return timeout;
32     }
33     
34     static __inline__ int has_expired(struct net_bridge *br,
35     				  struct net_bridge_fdb_entry *fdb)
36     {
37     	if (!fdb->is_static &&
38     	    time_before_eq(fdb->ageing_timer, __timeout(br)))
39     		return 1;
40     
41     	return 0;
42     }
43     
44     static __inline__ void copy_fdb(struct __fdb_entry *ent, struct net_bridge_fdb_entry *f)
45     {
46     	memset(ent, 0, sizeof(struct __fdb_entry));
47     	memcpy(ent->mac_addr, f->addr.addr, ETH_ALEN);
48     	ent->port_no = f->dst?f->dst->port_no:0;
49     	ent->is_local = f->is_local;
50     	ent->ageing_timer_value = 0;
51     	if (!f->is_static)
52     		ent->ageing_timer_value = jiffies - f->ageing_timer;
53     }
54     
55     static __inline__ int br_mac_hash(unsigned char *mac)
56     {
57     	unsigned long x;
58     
59     	x = mac[0];
60     	x = (x << 2) ^ mac[1];
61     	x = (x << 2) ^ mac[2];
62     	x = (x << 2) ^ mac[3];
63     	x = (x << 2) ^ mac[4];
64     	x = (x << 2) ^ mac[5];
65     
66     	x ^= x >> 8;
67     
68     	return x & (BR_HASH_SIZE - 1);
69     }
70     
71     static __inline__ void __hash_link(struct net_bridge *br,
72     				   struct net_bridge_fdb_entry *ent,
73     				   int hash)
74     {
75     	ent->next_hash = br->hash[hash];
76     	if (ent->next_hash != NULL)
77     		ent->next_hash->pprev_hash = &ent->next_hash;
78     	br->hash[hash] = ent;
79     	ent->pprev_hash = &br->hash[hash];
80     }
81     
82     static __inline__ void __hash_unlink(struct net_bridge_fdb_entry *ent)
83     {
84     	*(ent->pprev_hash) = ent->next_hash;
85     	if (ent->next_hash != NULL)
86     		ent->next_hash->pprev_hash = ent->pprev_hash;
87     	ent->next_hash = NULL;
88     	ent->pprev_hash = NULL;
89     }
90     
91     
92     
93     void br_fdb_changeaddr(struct net_bridge_port *p, unsigned char *newaddr)
94     {
95     	struct net_bridge *br;
96     	int i;
97     
98     	br = p->br;
99     	write_lock_bh(&br->hash_lock);
100     	for (i=0;i<BR_HASH_SIZE;i++) {
101     		struct net_bridge_fdb_entry *f;
102     
103     		f = br->hash[i];
104     		while (f != NULL) {
105     			if (f->dst == p && f->is_local) {
106     				__hash_unlink(f);
107     				memcpy(f->addr.addr, newaddr, ETH_ALEN);
108     				__hash_link(br, f, br_mac_hash(newaddr));
109     				write_unlock_bh(&br->hash_lock);
110     				return;
111     			}
112     			f = f->next_hash;
113     		}
114     	}
115     	write_unlock_bh(&br->hash_lock);
116     }
117     
118     void br_fdb_cleanup(struct net_bridge *br)
119     {
120     	int i;
121     	unsigned long timeout;
122     
123     	timeout = __timeout(br);
124     
125     	write_lock_bh(&br->hash_lock);
126     	for (i=0;i<BR_HASH_SIZE;i++) {
127     		struct net_bridge_fdb_entry *f;
128     
129     		f = br->hash[i];
130     		while (f != NULL) {
131     			struct net_bridge_fdb_entry *g;
132     
133     			g = f->next_hash;
134     			if (!f->is_static &&
135     			    time_before_eq(f->ageing_timer, timeout)) {
136     				__hash_unlink(f);
137     				br_fdb_put(f);
138     			}
139     			f = g;
140     		}
141     	}
142     	write_unlock_bh(&br->hash_lock);
143     }
144     
145     void br_fdb_delete_by_port(struct net_bridge *br, struct net_bridge_port *p)
146     {
147     	int i;
148     
149     	write_lock_bh(&br->hash_lock);
150     	for (i=0;i<BR_HASH_SIZE;i++) {
151     		struct net_bridge_fdb_entry *f;
152     
153     		f = br->hash[i];
154     		while (f != NULL) {
155     			struct net_bridge_fdb_entry *g;
156     
157     			g = f->next_hash;
158     			if (f->dst == p) {
159     				__hash_unlink(f);
160     				br_fdb_put(f);
161     			}
162     			f = g;
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 net_bridge_fdb_entry *fdb;
171     
172     	read_lock_bh(&br->hash_lock);
173     	fdb = br->hash[br_mac_hash(addr)];
174     	while (fdb != NULL) {
175     		if (!memcmp(fdb->addr.addr, addr, ETH_ALEN)) {
176     			if (!has_expired(br, fdb)) {
177     				atomic_inc(&fdb->use_count);
178     				read_unlock_bh(&br->hash_lock);
179     				return fdb;
180     			}
181     
182     			read_unlock_bh(&br->hash_lock);
183     			return NULL;
184     		}
185     
186     		fdb = fdb->next_hash;
187     	}
188     
189     	read_unlock_bh(&br->hash_lock);
190     	return NULL;
191     }
192     
193     void br_fdb_put(struct net_bridge_fdb_entry *ent)
194     {
195     	if (atomic_dec_and_test(&ent->use_count))
196     		kfree(ent);
197     }
198     
199     int br_fdb_get_entries(struct net_bridge *br,
200     		       unsigned char *_buf,
201     		       int maxnum,
202     		       int offset)
203     {
204     	int i;
205     	int num;
206     	struct __fdb_entry *walk;
207     
208     	num = 0;
209     	walk = (struct __fdb_entry *)_buf;
210     
211     	read_lock_bh(&br->hash_lock);
212     	for (i=0;i<BR_HASH_SIZE;i++) {
213     		struct net_bridge_fdb_entry *f;
214     
215     		f = br->hash[i];
216     		while (f != NULL && num < maxnum) {
217     			struct __fdb_entry ent;
218     			int err;
219     			struct net_bridge_fdb_entry *g;
220     			struct net_bridge_fdb_entry **pp; 
221     
222     			if (has_expired(br, f)) {
223     				f = f->next_hash;
224     				continue;
225     			}
226     
227     			if (offset) {
228     				offset--;
229     				f = f->next_hash;
230     				continue;
231     			}
232     
233     			copy_fdb(&ent, f);
234     
235     			atomic_inc(&f->use_count);
236     			read_unlock_bh(&br->hash_lock);
237     			err = copy_to_user(walk, &ent, sizeof(struct __fdb_entry));
238     			read_lock_bh(&br->hash_lock);
239     
240     			g = f->next_hash;
241     			pp = f->pprev_hash;
242     			br_fdb_put(f);
243     
244     			if (err)
245     				goto out_fault;
246     
247     			if (g == NULL && pp == NULL)
248     				goto out_disappeared;
249     
250     			num++;
251     			walk++;
252     
253     			f = g;
254     		}
255     	}
256     
257      out:
258     	read_unlock_bh(&br->hash_lock);
259     	return num;
260     
261      out_disappeared:
262     	num = -EAGAIN;
263     	goto out;
264     
265      out_fault:
266     	num = -EFAULT;
267     	goto out;
268     }
269     
270     static __inline__ void __fdb_possibly_replace(struct net_bridge_fdb_entry *fdb,
271     					      struct net_bridge_port *source,
272     					      int is_local)
273     {
274     	if (!fdb->is_static || is_local) {
275     		fdb->dst = source;
276     		fdb->is_local = is_local;
277     		fdb->is_static = is_local;
278     		fdb->ageing_timer = jiffies;
279     	}
280     }
281     
282     void br_fdb_insert(struct net_bridge *br,
283     		   struct net_bridge_port *source,
284     		   unsigned char *addr,
285     		   int is_local)
286     {
287     	struct net_bridge_fdb_entry *fdb;
288     	int hash;
289     
290     	hash = br_mac_hash(addr);
291     
292     	write_lock_bh(&br->hash_lock);
293     	fdb = br->hash[hash];
294     	while (fdb != NULL) {
295     		if (!memcmp(fdb->addr.addr, addr, ETH_ALEN)) {
296     			__fdb_possibly_replace(fdb, source, is_local);
297     			write_unlock_bh(&br->hash_lock);
298     			return;
299     		}
300     
301     		fdb = fdb->next_hash;
302     	}
303     
304     	fdb = kmalloc(sizeof(*fdb), GFP_ATOMIC);
305     	if (fdb == NULL) {
306     		write_unlock_bh(&br->hash_lock);
307     		return;
308     	}
309     
310     	memcpy(fdb->addr.addr, addr, ETH_ALEN);
311     	atomic_set(&fdb->use_count, 1);
312     	fdb->dst = source;
313     	fdb->is_local = is_local;
314     	fdb->is_static = is_local;
315     	fdb->ageing_timer = jiffies;
316     
317     	__hash_link(br, fdb, hash);
318     
319     	write_unlock_bh(&br->hash_lock);
320     }
321