~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Linux Cross Reference
Linux/net/bridge/br_fdb.c

Version: ~ [ 2.4.0 ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  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 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

This page was automatically generated by the LXR engine.
Visit the LXR main site for more information.