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.2.1 2002/01/17 00:59:01 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 
__timeout(struct net_bridge * br)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 
has_expired(struct net_bridge * br,struct net_bridge_fdb_entry * fdb)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 
copy_fdb(struct __fdb_entry * ent,struct net_bridge_fdb_entry * f)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 
br_mac_hash(unsigned char * mac)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 
__hash_link(struct net_bridge * br,struct net_bridge_fdb_entry * ent,int hash)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 
__hash_unlink(struct net_bridge_fdb_entry * ent)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 
br_fdb_changeaddr(struct net_bridge_port * p,unsigned char * newaddr)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 
br_fdb_cleanup(struct net_bridge * br)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 
br_fdb_delete_by_port(struct net_bridge * br,struct net_bridge_port * p)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 
br_fdb_get(struct net_bridge * br,unsigned char * addr)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 
br_fdb_put(struct net_bridge_fdb_entry * ent)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 
br_fdb_get_entries(struct net_bridge * br,unsigned char * _buf,int maxnum,int offset)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 
__fdb_possibly_replace(struct net_bridge_fdb_entry * fdb,struct net_bridge_port * source,int is_local)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 
br_fdb_insert(struct net_bridge * br,struct net_bridge_port * source,unsigned char * addr,int is_local)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 			/* attempt to update an entry for a local interface */
297 			if (fdb->is_local) {
298 				if (is_local)
299 					printk(KERN_INFO "%s: attempt to add"
300 					       " interface with same source address.\n",
301 					       source->dev->name);
302 				else if (net_ratelimit())
303 					printk(KERN_WARNING "%s: received packet with "
304 					       " own address as source address\n",
305 					       source->dev->name);
306 				goto out;
307 			}
308 
309 			__fdb_possibly_replace(fdb, source, is_local);
310 			goto out;
311 		}
312 
313 		fdb = fdb->next_hash;
314 	}
315 
316 	fdb = kmalloc(sizeof(*fdb), GFP_ATOMIC);
317 	if (fdb == NULL)
318 		goto out;
319 
320 	memcpy(fdb->addr.addr, addr, ETH_ALEN);
321 	atomic_set(&fdb->use_count, 1);
322 	fdb->dst = source;
323 	fdb->is_local = is_local;
324 	fdb->is_static = is_local;
325 	fdb->ageing_timer = jiffies;
326 
327 	__hash_link(br, fdb, hash);
328 
329  out:
330 	write_unlock_bh(&br->hash_lock);
331 }
332