Added the new version for dummynet.
[ipfw.git] / dummynet2 / ip_fw_lookup.c
diff --git a/dummynet2/ip_fw_lookup.c b/dummynet2/ip_fw_lookup.c
new file mode 100644 (file)
index 0000000..bf04cb6
--- /dev/null
@@ -0,0 +1,304 @@
+/*-
+ * Copyright (c) 2009 Luigi Rizzo Universita` di Pisa
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: user/luigi/ipfw3-head/sys/netinet/ipfw/ip_fw_table.c 200601 2009-12-16 10:48:40Z luigi $");
+
+/*
+ * Rule and pipe lookup support for ipfw.
+ *
+
+ipfw and dummynet need to quickly find objects (rules, pipes)
+that may be dynamically created or destroyed.
+To address the problem, we label each new object with a unique
+32-bit identifier whose low K bits are the index in a lookup
+table. All existing objects are referred by the lookup table,
+and identifiers are chosen so that for each slot there is
+at most one active object (whose identifier points to the slot).
+This is almost a hash table, except that we can pick the
+identifiers after looking at the table's occupation so
+we have a trivial hash function and are collision free.
+
+With this structure, operations are very fast and simple:
+- the table has N entries s[i] with two fields, 'id' and 'ptr',
+  with N <= M = 2^k (M is an upper bound to the size of the table);
+- initially, all slots have s[i].id = i, and the pointers
+  are used to build a freelist (tailq).
+- a slot is considered empty if ptr == NULL or s[0] <= ptr < s[N].
+  This is easy to detect and we can use ptr to build the freelist.
+- when a new object is created, we put it in the empty slot i at the
+  head of the freelist, and set the id to s[i].id;
+- when an object is destroyed, we append its slot i to the end
+  of the freelist, and set s[i].id += M (note M, not N).
+- on a lookup for id = X, we look at slot i = X & (M-1),
+  and consider the lookup successful only if the slot is not
+  empty and s[i].id == X;
+- wraps occur at most every F * 2^32/M operations, where F is
+  the number of free slots. Because F is usually a reasonable
+  fraction of M, we should not worry too much.
+- if the table fills up, we can extend it by increasing N
+- shrinking the table is more difficult as we might create
+  collisions during the rehashing.
+ *
+ */
+
+#include <sys/cdefs.h>
+#ifdef _KERNEL
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/rwlock.h>
+MALLOC_DEFINE(M_IPFW_LUT, "ipfw_lookup", "IpFw lookup");
+#define Malloc(n)      malloc(n, M_IPFW_LUT, M_WAITOK)
+#define Calloc(n)      calloc(n, M_IPFW_LUT, M_WAITOK | M_ZERO)
+#define Free(p)                free(p, M_IPFW_LUT)
+
+#define log(x, arg...)
+
+#else /* !_KERNEL */
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#define Malloc(n)      malloc(n)
+#define Calloc(n)      calloc(1, n)
+#define Free(p)                free(p)
+#define log(x, arg...) fprintf(stderr, "%s: " x "\n", __FUNCTION__, ##arg)
+#endif /* !_KERNEL */
+
+struct entry {
+       uint32_t        id;
+       struct entry    *ptr;
+};
+
+struct lookup_table {
+       int _size;
+       int used;
+       int mask; /* 2^k -1, used for hashing */
+       struct entry *f_head, *f_tail; /* freelist */
+       struct entry *  s;      /* slots, array of N entries */
+};
+
+static __inline int empty(struct lookup_table *head, const void *p)
+{
+       const struct entry *ep = p;
+       return (ep == NULL ||
+               (ep >= head->s && ep < &head->s[head->_size]));
+}
+
+/*
+ * init or reinit a table
+ */
+struct lookup_table *
+ipfw_lut_init(struct lookup_table *head, int new_size, int mask)
+{
+       int i;
+       struct entry *s;        /* the new slots */
+       struct entry *fh, *ft;  /* the freelist */
+
+       if (head != NULL) {
+               mask = head->mask;
+               if (new_size <= head->_size)
+                       return head;
+               if (new_size >= mask+1) {
+                       log("size larger than mask");
+                       return NULL;
+               }
+       } else {
+               log("old is null, initialize");
+               head = Calloc(sizeof(*head));
+               if (head == NULL)
+                       return NULL;
+               if (new_size >= mask)
+                       mask = new_size;
+               if (mask & (mask -1)) {
+                       for (i = 1; i < mask; i += i)
+                           ;
+                       log("mask %d not 2^k, round up to %d", mask, i);
+                       mask = i;
+               }
+               mask = head->mask = mask - 1;
+       }
+
+       s = Calloc(new_size * sizeof(*s));
+       if (s == NULL)
+               return NULL;
+       if (!head->s) {
+               head->s = s;
+               head->_size = 1;
+       }
+       fh = ft = NULL;
+       /* remap the entries, adjust the freelist */
+       for (i = 0; i < new_size; i++) {
+               s[i].id = (i >= head->_size) ? i : head->s[i].id;
+               if (i < head->_size && !empty(head, head->s[i].ptr)) {
+                       s[i].ptr = head->s[i].ptr;
+                       continue;
+               }
+               if (fh == NULL)
+                       fh = &s[i];
+               else
+                       ft->ptr = &s[i];
+               ft = &s[i];
+       }
+       head->f_head = fh;
+       head->f_tail = ft;
+
+       /* write lock on the structure, to protect the readers */
+       fh = head->s;
+       head->s = s;
+       head->_size = new_size;
+       /* release write lock */
+       if (fh != s)
+               Free(fh);
+       log("done");
+       return head;
+}
+
+/* insert returns the id */
+int
+ipfw_lut_insert(struct lookup_table *head, void *d)
+{
+       struct entry *e;
+
+       e = head->f_head;
+       if (e == NULL)
+               return -1;
+       head->f_head = e->ptr;
+       e->ptr = d;
+       head->used++;
+       return e->id;
+}
+
+/* delete, returns the original entry */
+void *
+ipfw_lut_delete(struct lookup_table *head, int id)
+{
+       int i = id & head->mask;
+       void *result;
+       struct entry *e;
+
+       if (i >= head->_size)
+               return NULL;
+       e = &head->s[i];
+       if (e->id != id)
+               return NULL;
+       result = e->ptr;
+       /* write lock to invalidate the entry to readers */
+       e->id += head->mask + 1; /* prepare for next insert */
+       e->ptr = NULL;
+       /* release write lock */
+       if (head->f_head == NULL)
+               head->f_head = e;
+       else
+               head->f_tail->ptr = e;
+       head->f_tail = e;
+       head->used--;
+       return result;
+}
+
+void *
+ipfw_lut_lookup(struct lookup_table *head, int id)
+{
+       int i = id & head->mask;
+       struct entry *e;
+
+       if (i >= head->_size)
+               return NULL;
+       e = &head->s[i];
+       return (e->id == id) ? e->ptr : NULL;
+}
+
+void
+ipfw_lut_dump(struct lookup_table *head)
+{
+       int i;
+
+       log("head %p size %d used %d freelist %d",
+           head, head->_size, head->used, head->f_head ?
+                   head->f_head - head->s : -1);
+       for (i = 0; i < head->_size; i++) {
+               struct entry *e = &head->s[i];
+               char ee = empty(head, e->ptr) ? 'E' : ' ';
+               log("%5d  %5d %c %p", i, e->id, ee,
+                   ee == 'E' && e->ptr != NULL ?
+                   (void *)((struct entry *)e->ptr - head->s) : e->ptr);
+       }
+}
+
+#ifndef _KERNEL
+void dump_p(struct lookup_table *p, int *map)
+{
+       int i;
+       for (i = 0; i < p->_size; i++) {
+           int id = (int)ipfw_lut_lookup(p, map[i]);
+           log("%3d: %3d: %c", map[i] % 64, i, id);
+       }
+}
+int main(int argc, char *argv[])
+{
+       int i, j, l;
+#define S 1000
+       int map[S];
+       struct lookup_table *p;
+       struct lookup_table *p1;
+       const char *m = "nel mezzo del cammin di nostra vita mi ritrovai"
+               " in una selva oscura e la diritta via era smarrita!";
+
+       fprintf(stderr, "testing lookup\n");
+
+       l = strlen(m);
+
+       p = ipfw_lut_init(NULL, 120, 33);
+
+       ipfw_lut_dump(p);
+       for (i = 0; i < l; i++) {
+           int x = m[i];
+           int id = ipfw_lut_insert(p, (void *)x);
+           //ipfw_lut_dump(p);
+           map[i] = id;
+           for (j=0; j < 10; j++) {
+                   id = ipfw_lut_insert(p, (void *)'a');
+                   // ipfw_lut_dump(p);
+                   ipfw_lut_delete(p, id);
+                   // ipfw_lut_dump(p);
+           }
+       //    ipfw_lut_dump(p);
+       } 
+       dump_p(p, map);
+       p1 = ipfw_lut_init(p, 23, 0);
+       if (!p1)
+               return 1;
+       dump_p(p1, map);
+       p1 = ipfw_lut_init(p1, 120, 0);
+       if (!p1)
+               return 1;
+       dump_p(p1, map);
+       return 0;
+}
+#endif
+/* end of file */