[PATCH 1/2] RFC: Add structure extension stuff for conntrack
(ct_extend)
Rusty Russell
rusty at rustcorp.com.au
Wed Dec 29 07:43:42 CET 2004
Name: Add a structure extension framework for connection tracking.
Status: Tested on 2.6.10-rc2-bk13
Signed-off-by: Rusty Russell <rusty at rustcorp.com.au>
Create a ct_extend structure for adding random crap to structures
(intended for the ip_conntrack structures which hold a lot of extra
fields).
Don't use it for anything yet.
Index: linux-2.6.10-bk1-Netfilter/net/ipv4/netfilter/ct_extend.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.10-bk1-Netfilter/net/ipv4/netfilter/ct_extend.c 2004-12-28 16:13:09.373275704 +1100
@@ -0,0 +1,226 @@
+/* Structure dynamic extension infrastructure
+ * Copyright (C) 2004 Rusty Russell IBM Corporation
+ *
+ * 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 <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/netfilter_ipv4/ct_extend.h>
+
+static struct ct_extend_type *ct_extend_types[CTE_MAX];
+static spinlock_t ct_extend_type_lock = SPIN_LOCK_UNLOCKED;
+
+/* Horrible trick to figure out smallest amount work kmallocing. */
+#define CACHE(x) (x) + 0 *
+enum {
+ CT_EXTEND_MIN_SIZE =
+#include <linux/kmalloc_sizes.h>
+ 1 };
+#undef CACHE
+
+/* Find data offset of nth entry. */
+static unsigned int extend_offset(const struct ct_extend *ext, unsigned int n)
+{
+ unsigned int i, off;
+
+ off = sizeof(*ext);
+ for (i = 0; i < n; i++) {
+ struct ct_extend_type *t = ct_extend_types[ext->type[i]];
+ off = ALIGN(off, t->align) + t->len;
+ }
+ return ALIGN(off, ct_extend_types[ext->type[n]]->align)-sizeof(*ext);
+}
+
+void *ct_extend_find(const struct ct_extend *ext, u8 type)
+{
+ u8 *p;
+
+ if (!ext)
+ return NULL;
+
+ p = memchr(ext->type, type, sizeof(ext->type));
+ if (!p)
+ return NULL;
+
+ return (void *)ext->data + extend_offset(ext, p - ext->type);
+}
+
+static unsigned int first_ext(const struct ct_extend *ext, unsigned int *i)
+{
+ unsigned int off = sizeof(*ext);
+
+ *i = 0;
+ if (ext->type[*i] == 0xFF)
+ return 0;
+ return ALIGN(off, ct_extend_types[ext->type[*i]]->align);
+}
+
+static unsigned int next_ext(const struct ct_extend *ext,
+ unsigned int *i,
+ unsigned int prev)
+{
+ if (ext->type[*i + 1] == 0xFF)
+ return 0;
+ prev += ct_extend_types[ext->type[(*i)++]]->len;
+ return ALIGN(prev, ct_extend_types[ext->type[*i]]->align);
+}
+
+void ct_extend_free(struct ct_extend *ext)
+{
+ unsigned int i, off;
+
+ for (off = first_ext(ext, &i); off; off = next_ext(ext, &i, off)) {
+ if (ct_extend_types[ext->type[i]]->free)
+ ct_extend_types[ext->type[i]]->free(ext,
+ off-sizeof(*ext));
+ }
+ kfree(ext);
+}
+
+static unsigned int ct_extend_datalen(const struct ct_extend *ext)
+{
+ unsigned int i, off, dlen = 0;
+
+ for (off = first_ext(ext, &i); off; off = next_ext(ext, &i, off))
+ dlen = off-sizeof(*ext) + ct_extend_types[ext->type[i]]->len;
+
+ return dlen;
+}
+
+static void *ct_extend_create(struct ct_extend **ext,
+ enum ct_ext_type type, const void *data,
+ int gfp)
+{
+ unsigned int len;
+
+ len = ALIGN(sizeof(**ext), ct_extend_types[type]->align);
+ *ext = kmalloc(max_t(int, CT_EXTEND_MIN_SIZE,
+ len + ct_extend_types[type]->len), gfp);
+ if (!*ext)
+ return NULL;
+
+ (*ext)->type[0] = type;
+ memset((*ext)->type+1, 0xFF, MAX_CT_EXTENDS-1);
+ memcpy((void *)(*ext) + len, data, ct_extend_types[type]->len);
+ return (void *)(*ext) + len;
+}
+
+void *ct_extend_add(struct ct_extend **ext,
+ enum ct_ext_type type, const void *data, int gfp_mask)
+{
+ struct ct_extend *old;
+ int i, len, newlen, newoff;
+
+ if (!*ext)
+ return ct_extend_create(ext, type, data, gfp_mask);
+
+ old = *ext;
+ len = sizeof(**ext) + ct_extend_datalen(old);
+ newoff = ALIGN(len, ct_extend_types[type]->align);
+ newlen = newoff + ct_extend_types[type]->len;
+
+ if (newlen >= CT_EXTEND_MIN_SIZE) {
+ int off;
+ *ext = kmalloc(max_t(int, CT_EXTEND_MIN_SIZE, newlen),
+ gfp_mask);
+ if (!*ext)
+ return NULL;
+
+ **ext = *old;
+ /* Move entries one at a time: eg. for linked list. */
+ for (off = first_ext(old, &i);
+ off;
+ off = next_ext(old, &i, off)) {
+ struct ct_extend_type *t
+ = ct_extend_types[old->type[i]];
+ memcpy((void *)*ext + off, (void *)old + off, t->len);
+ if (t->move)
+ t->move(*ext, off - sizeof(*ext));
+ }
+ kfree(old);
+ }
+
+ /* Find free slot. */
+ for (i = 0; i < MAX_CT_EXTENDS && (*ext)->type[i] != 0xFF; i++);
+ BUG_ON(i == MAX_CT_EXTENDS);
+
+ memcpy((void *)(*ext) + newoff, data, ct_extend_types[type]->len);
+ (*ext)->type[i] = type;
+ return (void *)(*ext) + newoff;
+}
+
+struct ct_extend *ct_extend_del(struct ct_extend *ext,
+ enum ct_ext_type type)
+{
+ unsigned int i, newi, off, newoff;
+ enum ct_ext_type *p;
+
+ if (!ext)
+ return ext;
+
+ p = memchr(ext->type, type, sizeof(ext->type));
+ if (!p)
+ return ext;
+
+ /* If last one, simply drop free and return NULL. */
+ if (ext->type[1] == 0xFF) {
+ ct_extend_free(ext);
+ return NULL;
+ }
+
+ if (ct_extend_types[type]->free)
+ ct_extend_types[type]->free(ext,
+ extend_offset(ext, p-ext->type));
+
+ newoff = sizeof(*ext);
+ newi = 0;
+ for (off = first_ext(ext, &i); off; off = next_ext(ext, &i, off)) {
+ struct ct_extend_type *t = ct_extend_types[ext->type[i]];
+
+ /* Don't copy the one we're deleting. */
+ if (i == (p - ext->type))
+ continue;
+
+ /* Move type information and actual data. */
+ ext->type[newi] = ext->type[i];
+ newoff = ALIGN(newoff, t->align);
+ BUG_ON(newoff > off);
+ memmove((void *)ext + newoff, (void *)ext + off, t->len);
+ if (ct_extend_types[ext->type[newi]]->move)
+ ct_extend_types[ext->type[newi]]->move(ext,
+ newoff
+ - sizeof(*ext));
+ newoff += t->len;
+ newi++;
+ }
+ while (newi < MAX_CT_EXTENDS)
+ ext->type[newi++] = 0xFF;
+ return ext;
+}
+
+void register_ct_extend_type(struct ct_extend_type *ext)
+{
+ spin_lock_irq(&ct_extend_type_lock);
+ BUG_ON(ct_extend_types[ext->type]);
+ ct_extend_types[ext->type] = ext;
+ spin_unlock_irq(&ct_extend_type_lock);
+}
+
+void unregister_ct_extend_type(struct ct_extend_type *ext)
+{
+ spin_lock_irq(&ct_extend_type_lock);
+ BUG_ON(ct_extend_types[ext->type] != ext);
+ ct_extend_types[ext->type] = NULL;
+ spin_unlock_irq(&ct_extend_type_lock);
+}
+
+EXPORT_SYMBOL(ct_extend_find);
+EXPORT_SYMBOL(ct_extend_destroy);
+EXPORT_SYMBOL(ct_extend_add);
+EXPORT_SYMBOL(ct_extend_del);
+EXPORT_SYMBOL_GPL(register_ct_extend_type);
+EXPORT_SYMBOL_GPL(unregister_ct_extend_type);
Index: linux-2.6.10-bk1-Netfilter/net/ipv4/netfilter/Makefile
===================================================================
--- linux-2.6.10-bk1-Netfilter.orig/net/ipv4/netfilter/Makefile 2004-12-28 16:13:03.216211720 +1100
+++ linux-2.6.10-bk1-Netfilter/net/ipv4/netfilter/Makefile 2004-12-28 16:13:34.904394384 +1100
@@ -3,7 +3,7 @@
#
# objects for the standalone - connection tracking / NAT
-ip_conntrack-objs := ip_conntrack_standalone.o ip_conntrack_core.o ip_conntrack_proto_generic.o ip_conntrack_proto_tcp.o ip_conntrack_proto_udp.o ip_conntrack_proto_icmp.o
+ip_conntrack-objs := ip_conntrack_standalone.o ip_conntrack_core.o ip_conntrack_proto_generic.o ip_conntrack_proto_tcp.o ip_conntrack_proto_udp.o ip_conntrack_proto_icmp.o ct_extend.o
iptable_nat-objs := ip_nat_standalone.o ip_nat_rule.o ip_nat_core.o ip_nat_helper.o ip_nat_proto_unknown.o ip_nat_proto_tcp.o ip_nat_proto_udp.o ip_nat_proto_icmp.o
# connection tracking
Index: linux-2.6.10-bk1-Netfilter/include/linux/netfilter_ipv4/ct_extend.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.10-bk1-Netfilter/include/linux/netfilter_ipv4/ct_extend.h 2004-12-28 16:13:09.374275552 +1100
@@ -0,0 +1,51 @@
+#ifndef _LINUX_CT_EXTEND_H
+#define _LINUX_CT_EXTEND_H
+#include <asm/atomic.h>
+
+enum ct_ext_type
+{
+ CTE_MAX,
+} __attribute__((packed));
+
+/* Maximum number of ct_extend entries on one structure. */
+#define MAX_CT_EXTENDS 4
+
+/* Extensions: optional stuff which isn't permanently in struct. */
+struct ct_extend {
+ enum ct_ext_type type[MAX_CT_EXTENDS];
+ char data[0];
+};
+
+void *ct_extend_find(const struct ct_extend *ext, enum ct_ext_type type);
+void ct_extend_free(struct ct_extend *ext);
+
+/*
+ * Add this type, copy data: returns pointer to copied data or NULL.
+ * Assumes ct_extend is not shared. Will not call ->move().
+ */
+void *ct_extend_add(struct ct_extend **ext,
+ enum ct_ext_type type, const void *data,
+ int gfp_mask);
+
+/* Assumes ct_extend is not shared: if all deleted, will return NULL */
+struct ct_extend *ct_extend_del(struct ct_extend *ext,
+ enum ct_ext_type type);
+
+
+struct ct_extend_type
+{
+ /* Length and min alignment. */
+ u16 len;
+ u16 align;
+
+ /* Free operation (can be NULL). */
+ void (*free)(struct ct_extend *ext, unsigned int dataoff);
+ /* Move operation (can be NULL). Has already been moved. */
+ void (*move)(struct ct_extend *ext, unsigned int dataoff);
+
+ enum ct_ext_type type;
+};
+
+void register_ct_extend_type(struct ct_extend_type *);
+void unregister_ct_extend_type(struct ct_extend_type *);
+#endif /* _LINUX_CT_EXTEND_H */
--
A bad analogy is like a leaky screwdriver -- Richard Braakman
More information about the netfilter-devel
mailing list