added mtd driver
[linux-2.4.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.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
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                         /* 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 }