/* * bridge ethernet protocol database * * Authors: * Bart De Schuymer * * br_db.c, April, 2002 * * This code is stongly inspired on the iptables code which is * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ #include #include #include #include #include #include #include /* PF_BRIDGE */ #include /* rwlock_t */ #include #include /* copy_[to,from]_user */ #include /* multiprocessors */ #define BUGPRINT(format, args...) printk("kernel msg: brdb bug: please report to author: "format, ## args) /*#define BUGPRINT(format, args...)*/ #define MEMPRINT(format, args...) printk("kernel msg: brdb : out of memory: "format, ## args) /*#define MEMPRINT(format, args...)*/ /* database variables */ static __u16 allowdb = BRDB_NODB; static struct brdb_dbentry **flowdb = NULL; static unsigned int *dbsize; static unsigned int *dbnum; /* database lock */ static rwlock_t brdb_dblock; static inline int brdb_dev_check(char *entry, const struct net_device *device){ if (*entry == '\0') return 0; if (!device) return 1; return strncmp(entry, device->name, IFNAMSIZ); } static inline int brdb_proto_check(unsigned int a, unsigned int b){ if (a == b || ( a == IDENTIFY802_3 && ntohs(b) < 1536 )) return 0; return 1; } static unsigned int maintaindb (unsigned int hook, struct sk_buff **pskb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) { struct brdb_dbentry *hlp; int i, cpunr; unsigned short ethproto = ((**pskb).mac.ethernet)->h_proto; cpunr = cpu_number_map(smp_processor_id()); read_lock_bh(&brdb_dblock); if (allowdb == BRDB_NODB) {// must be after readlock read_unlock_bh(&brdb_dblock); return NF_ACCEPT; } hlp = flowdb[cpunr]; /* search for existing entry */ for (i = 0; i < dbnum[cpunr]; i++) { if (hlp->hook == hook && !brdb_proto_check(hlp->ethproto, ethproto) && !brdb_dev_check(hlp->in, in) && !brdb_dev_check(hlp->out, out)) { read_unlock_bh(&brdb_dblock); return NF_ACCEPT; } hlp++; } /* add new entry to database */ if (dbnum[cpunr] == dbsize[cpunr]) { dbsize[cpunr] *= 2; if ( !( hlp = (struct brdb_dbentry *) vmalloc(dbsize[cpunr] * sizeof(struct brdb_dbentry)) ) ) { dbsize[cpunr] /= 2; MEMPRINT("maintaindb && nomemory\n"); read_unlock_bh(&brdb_dblock); return NF_ACCEPT; } memcpy(hlp, flowdb[cpunr], dbnum[cpunr] * sizeof(struct brdb_dbentry)); vfree(flowdb[cpunr]); flowdb[cpunr] = hlp; } hlp = flowdb[cpunr] + dbnum[cpunr]; hlp->hook = hook; if (in) strncpy(hlp->in, in->name, IFNAMSIZ); else hlp->in[0] = '\0'; if (out) strncpy(hlp->out, out->name, IFNAMSIZ); else hlp->out[0] = '\0'; if (ntohs(ethproto) < 1536) hlp->ethproto = IDENTIFY802_3; else hlp->ethproto = ethproto; dbnum[cpunr]++; read_unlock_bh(&brdb_dblock); return NF_ACCEPT; } static int copy_db(void *user, int *len) { int i, j, nentries = 0, ret; struct brdb_dbentry *begin, *end1, *end2, *point, *point2; write_lock_bh(&brdb_dblock); for (i = 0; i < smp_num_cpus; i++) nentries += dbnum[i]; if (*len > nentries) return -EINVAL; if ( !(begin = (struct brdb_dbentry *) vmalloc((*len) * sizeof(struct brdb_dbentry))) ) return -ENOMEM; memcpy(begin, flowdb[0], dbnum[0] * sizeof(struct brdb_dbentry)); end1 = begin + dbnum[0]; for (i = 1; i < smp_num_cpus; i++) {/* cycle databases per cpu */ point2 = flowdb[i]; end2 = end1; for (j = 0; j < dbnum[i]; j++) {/* cycle entries of a cpu's database (point2) */ for (point = begin; point != end2; point++)/* cycle different entries we found so far */ if (point->hook == point2->hook && !strncmp(point->in, point2->in, IFNAMSIZ) && !strncmp(point->out, point2->out, IFNAMSIZ) && point->ethproto == point2->ethproto) goto out;/* already exists in a database of another cpu */ memcpy(end1, point2, sizeof(struct brdb_dbentry)); end1++; out: point2++; } } write_unlock_bh(&brdb_dblock); i = (int)( (char *)end1 - (char *)begin); *len = i < *len ? i : *len; if (copy_to_user(user, begin, *len * sizeof(struct brdb_dbentry)) != 0) ret = -EFAULT; else ret = 0; vfree(begin); return ret; } static int switch_nodb(void){ int i; if (!flowdb) BUGPRINT("switch_nodb && !flowdb\n"); for (i = 0; i < smp_num_cpus; i++) vfree(flowdb[i]); vfree(flowdb); if (!dbsize) BUGPRINT("switch_nodb && !dbsize\n"); vfree(dbsize); if (!dbnum) BUGPRINT("switch_nodb && !dbnum\n"); vfree(dbnum); flowdb = NULL; allowdb = BRDB_NODB; return 0; } static int switch_db(void) { int i, j; if (flowdb) BUGPRINT("switch_db && flowdb\n"); if ( !(flowdb = (struct brdb_dbentry **) vmalloc(smp_num_cpus * sizeof(struct brdb_dbentry *))) ) { MEMPRINT("switch_db && nomemory\n"); return -ENOMEM; } for (i = 0; i < smp_num_cpus; i++) if ( !(flowdb[i] = (struct brdb_dbentry *) vmalloc(INITIAL_DBSIZE * sizeof(struct brdb_dbentry))) ) goto sw_free1; else memset(flowdb[i], 0, INITIAL_DBSIZE * sizeof(struct brdb_dbentry)); if ( !(dbnum = (int*) vmalloc(smp_num_cpus * sizeof(int))) ) goto sw_free2; if ( !(dbsize = (int*) vmalloc(smp_num_cpus * sizeof(int))) ) goto sw_free3; for (i = 0; i < smp_num_cpus; i++) { dbnum[i] = 0; dbsize[i] = INITIAL_DBSIZE; } allowdb = BRDB_DB; return 0; sw_free3: MEMPRINT("switch_db && nomemory2\n"); vfree(dbnum); dbnum = NULL; sw_free2: MEMPRINT("switch_db && nomemory3\n"); sw_free1: MEMPRINT("switch_db && nomemory4\n"); for (j = 0; j