[PATCH 1/8] Netfilter: Add a structure extension framework for connection tracking.

Rusty Russell rusty at rustcorp.com.au
Wed Jan 12 21:53:09 CET 2005


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, but it will be protected by the
ip_conntrack_lock.  We can easily migrate this to RCU later.

Index: linux-2.6.10-bk14-Netfilter/net/ipv4/netfilter/ct_extend.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.10-bk14-Netfilter/net/ipv4/netfilter/ct_extend.c	2005-01-12 23:36:24.671917744 +1100
@@ -0,0 +1,223 @@
+/* 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 cte_types[CTE_MAX];
+static spinlock_t cte_type_lock = SPIN_LOCK_UNLOCKED;
+
+/* Horrible trick to figure out smallest amount worth 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 = &cte_types[ext->type[i]];
+		off = ALIGN(off, t->align) + t->len;
+	}
+	return ALIGN(off, cte_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, cte_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 += cte_types[ext->type[(*i)++]].len;
+	return ALIGN(prev, cte_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 (cte_types[ext->type[i]].free)
+			cte_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) + cte_types[ext->type[i]].len;
+
+	return dlen;
+}
+
+static void *cte_create(struct ct_extend **ext, enum ct_ext_type type, int gfp)
+{
+	unsigned int len;
+
+	len = ALIGN(sizeof(**ext), cte_types[type].align);
+	*ext = kmalloc(max_t(int, CT_EXTEND_MIN_SIZE,
+			     len + cte_types[type].len), gfp);
+	if (!*ext)
+		return NULL;
+ 
+	(*ext)->type[0] = type;
+	memset((*ext)->type+1, 0xFF, CTE_MAX-1);
+	return (void *)(*ext) + len;
+}
+
+void *__ct_extend_add(struct ct_extend **ext, enum ct_ext_type type, int gfp)
+{
+	struct ct_extend *new;
+	int i, len, newlen, newoff;
+
+	if (!*ext)
+		return cte_create(ext, type, gfp);
+
+	len = sizeof(*new) + ct_extend_datalen(*ext);
+	newoff = ALIGN(len, cte_types[type].align);
+	newlen = newoff + cte_types[type].len;
+
+	if (newlen >= CT_EXTEND_MIN_SIZE) {
+		int off;
+
+		new = kmalloc(newlen, gfp);
+		if (!new)
+			return NULL;
+		*new = **ext;
+
+		/* Copy one at a time, eg. two in same linked list. */
+		for (off = first_ext(new, &i);
+		     off;
+		     off = next_ext(new, &i, off)) {
+			struct ct_extend_type *t = &cte_types[new->type[i]];
+			memcpy((void *)new + off, (void *)*ext + off, t->len);
+			if (t->move)
+				t->move(new, off-sizeof(*new));
+		}
+		kfree(*ext);
+		*ext = new;
+	}
+
+	/* Find free slot. */
+	for (i = 0; i < CTE_MAX && (*ext)->type[i] != 0xFF; i++);
+	BUG_ON(i == CTE_MAX);
+
+	(*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, nextoff, nexti, 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 (cte_types[type].free)
+		cte_types[type].free(ext, extend_offset(ext, p-ext->type));
+
+	newoff = sizeof(*ext);
+	newi = 0;
+	for (off = first_ext(ext, &i); off; off = nextoff, i = nexti) {
+		struct ct_extend_type *t = &cte_types[ext->type[i]];
+
+		/* Figure out next offset before we blatt type. */
+		nexti = i;
+		nextoff = next_ext(ext, &nexti, off);
+
+		/* 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 (cte_types[ext->type[newi]].move)
+			cte_types[ext->type[newi]].move(ext,
+							newoff - sizeof(*ext));
+		newoff += t->len;
+		newi++;
+	}
+	while (newi < CTE_MAX)
+		ext->type[newi++] = 0xFF;
+	return ext;
+}
+
+void register_ct_extend_type(struct ct_extend_type *ext)
+{
+	spin_lock_irq(&cte_type_lock);
+	cte_types[ext->type] = *ext;
+	spin_unlock_irq(&cte_type_lock);
+}
+
+/* Presumably they don't care about the data any more, but we need to
+ * keep information about size and alignment so we can step across
+ * remaining instances. */
+void unregister_ct_extend_type(struct ct_extend_type *ext)
+{
+	spin_lock_irq(&cte_type_lock);
+	cte_types[ext->type].free = NULL;
+	cte_types[ext->type].move = NULL;
+	spin_unlock_irq(&cte_type_lock);
+}
+
+EXPORT_SYMBOL(ct_extend_find);
+EXPORT_SYMBOL(ct_extend_free);
+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-bk14-Netfilter/net/ipv4/netfilter/Makefile
===================================================================
--- linux-2.6.10-bk14-Netfilter.orig/net/ipv4/netfilter/Makefile	2005-01-12 13:09:20.000000000 +1100
+++ linux-2.6.10-bk14-Netfilter/net/ipv4/netfilter/Makefile	2005-01-12 14:17:33.000000000 +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-bk14-Netfilter/include/linux/netfilter_ipv4/ct_extend.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.10-bk14-Netfilter/include/linux/netfilter_ipv4/ct_extend.h	2005-01-12 23:39:17.013717808 +1100
@@ -0,0 +1,44 @@
+#ifndef _LINUX_CT_EXTEND_H
+#define _LINUX_CT_EXTEND_H
+#include <asm/atomic.h>
+
+enum ct_ext_type
+{
+	CTE_MAX,
+} __attribute__((packed));
+
+/* Extensions: optional stuff which isn't permanently in struct. */
+struct ct_extend {
+	enum ct_ext_type type[CTE_MAX];
+	char data[0];
+};
+
+#define ct_extend_find(ext, type) ((type##_TYPE *)__ct_extend_find(ext, type))
+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, returns pointer to data or NULL. */
+#define ct_extend_add(ext, type, gfp) \
+	((type##_TYPE *)__ct_extend_add(ext, type, gfp))
+void *__ct_extend_add(struct ct_extend **ext, enum ct_ext_type type, int gfp);
+
+/* 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 */




More information about the netfilter-devel mailing list