diff options
| -rw-r--r-- | target/linux/generic-2.6/patches-2.6.35/100-netfilter_layer7_2.21.patch | 2132 | ||||
| -rw-r--r-- | target/linux/generic-2.6/patches-2.6.35/101-netfilter_layer7_pktmatch.patch | 108 | 
2 files changed, 2240 insertions, 0 deletions
diff --git a/target/linux/generic-2.6/patches-2.6.35/100-netfilter_layer7_2.21.patch b/target/linux/generic-2.6/patches-2.6.35/100-netfilter_layer7_2.21.patch new file mode 100644 index 000000000..fa1274c3b --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.35/100-netfilter_layer7_2.21.patch @@ -0,0 +1,2132 @@ +--- /dev/null ++++ b/include/linux/netfilter/xt_layer7.h +@@ -0,0 +1,13 @@ ++#ifndef _XT_LAYER7_H ++#define _XT_LAYER7_H ++ ++#define MAX_PATTERN_LEN 8192 ++#define MAX_PROTOCOL_LEN 256 ++ ++struct xt_layer7_info { ++    char protocol[MAX_PROTOCOL_LEN]; ++    char pattern[MAX_PATTERN_LEN]; ++    u_int8_t invert; ++}; ++ ++#endif /* _XT_LAYER7_H */ +--- a/include/net/netfilter/nf_conntrack.h ++++ b/include/net/netfilter/nf_conntrack.h +@@ -116,6 +116,22 @@ struct nf_conn { + 	u_int32_t secmark; + #endif +  ++#if defined(CONFIG_NETFILTER_XT_MATCH_LAYER7) || \ ++    defined(CONFIG_NETFILTER_XT_MATCH_LAYER7_MODULE) ++	struct { ++		/* ++		 * e.g. "http". NULL before decision. "unknown" after decision ++		 * if no match. ++		 */ ++		char *app_proto; ++		/* ++		 * application layer data so far. NULL after match decision. ++		 */ ++		char *app_data; ++		unsigned int app_data_len; ++	} layer7; ++#endif ++ + 	/* Storage reserved for other modules: */ + 	union nf_conntrack_proto proto; +  +--- a/net/netfilter/Kconfig ++++ b/net/netfilter/Kconfig +@@ -921,6 +921,27 @@ config NETFILTER_XT_MATCH_STATE +  + 	  To compile it as a module, choose M here.  If unsure, say N. +  ++config NETFILTER_XT_MATCH_LAYER7 ++	tristate '"layer7" match support' ++	depends on NETFILTER_XTABLES ++	depends on EXPERIMENTAL && (IP_NF_CONNTRACK || NF_CONNTRACK) ++       depends on NF_CT_ACCT ++	help ++	  Say Y if you want to be able to classify connections (and their ++	  packets) based on regular expression matching of their application ++	  layer data.   This is one way to classify applications such as ++	  peer-to-peer filesharing systems that do not always use the same ++	  port. ++ ++	  To compile it as a module, choose M here.  If unsure, say N. ++ ++config NETFILTER_XT_MATCH_LAYER7_DEBUG ++        bool 'Layer 7 debugging output' ++        depends on NETFILTER_XT_MATCH_LAYER7 ++        help ++          Say Y to get lots of debugging output. ++ ++ + config NETFILTER_XT_MATCH_STATISTIC + 	tristate '"statistic" match support' + 	depends on NETFILTER_ADVANCED +--- a/net/netfilter/Makefile ++++ b/net/netfilter/Makefile +@@ -91,6 +91,7 @@ obj-$(CONFIG_NETFILTER_XT_MATCH_RECENT)  + obj-$(CONFIG_NETFILTER_XT_MATCH_SCTP) += xt_sctp.o + obj-$(CONFIG_NETFILTER_XT_MATCH_SOCKET) += xt_socket.o + obj-$(CONFIG_NETFILTER_XT_MATCH_STATE) += xt_state.o ++obj-$(CONFIG_NETFILTER_XT_MATCH_LAYER7) += xt_layer7.o + obj-$(CONFIG_NETFILTER_XT_MATCH_STATISTIC) += xt_statistic.o + obj-$(CONFIG_NETFILTER_XT_MATCH_STRING) += xt_string.o + obj-$(CONFIG_NETFILTER_XT_MATCH_TCPMSS) += xt_tcpmss.o +--- a/net/netfilter/nf_conntrack_core.c ++++ b/net/netfilter/nf_conntrack_core.c +@@ -202,6 +202,14 @@ destroy_conntrack(struct nf_conntrack *n + 	 * too. */ + 	nf_ct_remove_expectations(ct); +  ++	#if defined(CONFIG_NETFILTER_XT_MATCH_LAYER7) || defined(CONFIG_NETFILTER_XT_MATCH_LAYER7_MODULE) ++	if(ct->layer7.app_proto) ++		kfree(ct->layer7.app_proto); ++	if(ct->layer7.app_data) ++	kfree(ct->layer7.app_data); ++	#endif ++ ++ + 	/* We overload first tuple to link into unconfirmed list. */ + 	if (!nf_ct_is_confirmed(ct)) { + 		BUG_ON(hlist_nulls_unhashed(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode)); +--- a/net/netfilter/nf_conntrack_standalone.c ++++ b/net/netfilter/nf_conntrack_standalone.c +@@ -178,6 +178,12 @@ static int ct_seq_show(struct seq_file * + 		goto release; + #endif +  ++#if defined(CONFIG_NETFILTER_XT_MATCH_LAYER7) || defined(CONFIG_NETFILTER_XT_MATCH_LAYER7_MODULE) ++	if(ct->layer7.app_proto && ++           seq_printf(s, "l7proto=%s ", ct->layer7.app_proto)) ++		return -ENOSPC; ++#endif ++ + 	if (seq_printf(s, "use=%u\n", atomic_read(&ct->ct_general.use))) + 		goto release; +  +--- /dev/null ++++ b/net/netfilter/regexp/regexp.c +@@ -0,0 +1,1197 @@ ++/* ++ * regcomp and regexec -- regsub and regerror are elsewhere ++ * @(#)regexp.c	1.3 of 18 April 87 ++ * ++ *	Copyright (c) 1986 by University of Toronto. ++ *	Written by Henry Spencer.  Not derived from licensed software. ++ * ++ *	Permission is granted to anyone to use this software for any ++ *	purpose on any computer system, and to redistribute it freely, ++ *	subject to the following restrictions: ++ * ++ *	1. The author is not responsible for the consequences of use of ++ *		this software, no matter how awful, even if they arise ++ *		from defects in it. ++ * ++ *	2. The origin of this software must not be misrepresented, either ++ *		by explicit claim or by omission. ++ * ++ *	3. Altered versions must be plainly marked as such, and must not ++ *		be misrepresented as being the original software. ++ * ++ * Beware that some of this code is subtly aware of the way operator ++ * precedence is structured in regular expressions.  Serious changes in ++ * regular-expression syntax might require a total rethink. ++ * ++ * This code was modified by Ethan Sommer to work within the kernel ++ * (it now uses kmalloc etc..) ++ * ++ * Modified slightly by Matthew Strait to use more modern C. ++ */ ++ ++#include "regexp.h" ++#include "regmagic.h" ++ ++/* added by ethan and matt.  Lets it work in both kernel and user space. ++(So iptables can use it, for instance.)  Yea, it goes both ways... */ ++#if __KERNEL__ ++  #define malloc(foo) kmalloc(foo,GFP_ATOMIC) ++#else ++  #define printk(format,args...) printf(format,##args) ++#endif ++ ++void regerror(char * s) ++{ ++        printk("<3>Regexp: %s\n", s); ++        /* NOTREACHED */ ++} ++ ++/* ++ * The "internal use only" fields in regexp.h are present to pass info from ++ * compile to execute that permits the execute phase to run lots faster on ++ * simple cases.  They are: ++ * ++ * regstart	char that must begin a match; '\0' if none obvious ++ * reganch	is the match anchored (at beginning-of-line only)? ++ * regmust	string (pointer into program) that match must include, or NULL ++ * regmlen	length of regmust string ++ * ++ * Regstart and reganch permit very fast decisions on suitable starting points ++ * for a match, cutting down the work a lot.  Regmust permits fast rejection ++ * of lines that cannot possibly match.  The regmust tests are costly enough ++ * that regcomp() supplies a regmust only if the r.e. contains something ++ * potentially expensive (at present, the only such thing detected is * or + ++ * at the start of the r.e., which can involve a lot of backup).  Regmlen is ++ * supplied because the test in regexec() needs it and regcomp() is computing ++ * it anyway. ++ */ ++ ++/* ++ * Structure for regexp "program".  This is essentially a linear encoding ++ * of a nondeterministic finite-state machine (aka syntax charts or ++ * "railroad normal form" in parsing technology).  Each node is an opcode ++ * plus a "next" pointer, possibly plus an operand.  "Next" pointers of ++ * all nodes except BRANCH implement concatenation; a "next" pointer with ++ * a BRANCH on both ends of it is connecting two alternatives.  (Here we ++ * have one of the subtle syntax dependencies:  an individual BRANCH (as ++ * opposed to a collection of them) is never concatenated with anything ++ * because of operator precedence.)  The operand of some types of node is ++ * a literal string; for others, it is a node leading into a sub-FSM.  In ++ * particular, the operand of a BRANCH node is the first node of the branch. ++ * (NB this is *not* a tree structure:  the tail of the branch connects ++ * to the thing following the set of BRANCHes.)  The opcodes are: ++ */ ++ ++/* definition	number	opnd?	meaning */ ++#define	END	0	/* no	End of program. */ ++#define	BOL	1	/* no	Match "" at beginning of line. */ ++#define	EOL	2	/* no	Match "" at end of line. */ ++#define	ANY	3	/* no	Match any one character. */ ++#define	ANYOF	4	/* str	Match any character in this string. */ ++#define	ANYBUT	5	/* str	Match any character not in this string. */ ++#define	BRANCH	6	/* node	Match this alternative, or the next... */ ++#define	BACK	7	/* no	Match "", "next" ptr points backward. */ ++#define	EXACTLY	8	/* str	Match this string. */ ++#define	NOTHING	9	/* no	Match empty string. */ ++#define	STAR	10	/* node	Match this (simple) thing 0 or more times. */ ++#define	PLUS	11	/* node	Match this (simple) thing 1 or more times. */ ++#define	OPEN	20	/* no	Mark this point in input as start of #n. */ ++			/*	OPEN+1 is number 1, etc. */ ++#define	CLOSE	30	/* no	Analogous to OPEN. */ ++ ++/* ++ * Opcode notes: ++ * ++ * BRANCH	The set of branches constituting a single choice are hooked ++ *		together with their "next" pointers, since precedence prevents ++ *		anything being concatenated to any individual branch.  The ++ *		"next" pointer of the last BRANCH in a choice points to the ++ *		thing following the whole choice.  This is also where the ++ *		final "next" pointer of each individual branch points; each ++ *		branch starts with the operand node of a BRANCH node. ++ * ++ * BACK		Normal "next" pointers all implicitly point forward; BACK ++ *		exists to make loop structures possible. ++ * ++ * STAR,PLUS	'?', and complex '*' and '+', are implemented as circular ++ *		BRANCH structures using BACK.  Simple cases (one character ++ *		per match) are implemented with STAR and PLUS for speed ++ *		and to minimize recursive plunges. ++ * ++ * OPEN,CLOSE	...are numbered at compile time. ++ */ ++ ++/* ++ * A node is one char of opcode followed by two chars of "next" pointer. ++ * "Next" pointers are stored as two 8-bit pieces, high order first.  The ++ * value is a positive offset from the opcode of the node containing it. ++ * An operand, if any, simply follows the node.  (Note that much of the ++ * code generation knows about this implicit relationship.) ++ * ++ * Using two bytes for the "next" pointer is vast overkill for most things, ++ * but allows patterns to get big without disasters. ++ */ ++#define	OP(p)	(*(p)) ++#define	NEXT(p)	(((*((p)+1)&0377)<<8) + (*((p)+2)&0377)) ++#define	OPERAND(p)	((p) + 3) ++ ++/* ++ * See regmagic.h for one further detail of program structure. ++ */ ++ ++ ++/* ++ * Utility definitions. ++ */ ++#ifndef CHARBITS ++#define	UCHARAT(p)	((int)*(unsigned char *)(p)) ++#else ++#define	UCHARAT(p)	((int)*(p)&CHARBITS) ++#endif ++ ++#define	FAIL(m)	{ regerror(m); return(NULL); } ++#define	ISMULT(c)	((c) == '*' || (c) == '+' || (c) == '?') ++#define	META	"^$.[()|?+*\\" ++ ++/* ++ * Flags to be passed up and down. ++ */ ++#define	HASWIDTH	01	/* Known never to match null string. */ ++#define	SIMPLE		02	/* Simple enough to be STAR/PLUS operand. */ ++#define	SPSTART		04	/* Starts with * or +. */ ++#define	WORST		0	/* Worst case. */ ++ ++/* ++ * Global work variables for regcomp(). ++ */ ++struct match_globals { ++char *reginput;		/* String-input pointer. */ ++char *regbol;		/* Beginning of input, for ^ check. */ ++char **regstartp;	/* Pointer to startp array. */ ++char **regendp;		/* Ditto for endp. */ ++char *regparse;		/* Input-scan pointer. */ ++int regnpar;		/* () count. */ ++char regdummy; ++char *regcode;		/* Code-emit pointer; ®dummy = don't. */ ++long regsize;		/* Code size. */ ++}; ++ ++/* ++ * Forward declarations for regcomp()'s friends. ++ */ ++#ifndef STATIC ++#define	STATIC	static ++#endif ++STATIC char *reg(struct match_globals *g, int paren,int *flagp); ++STATIC char *regbranch(struct match_globals *g, int *flagp); ++STATIC char *regpiece(struct match_globals *g, int *flagp); ++STATIC char *regatom(struct match_globals *g, int *flagp); ++STATIC char *regnode(struct match_globals *g, char op); ++STATIC char *regnext(struct match_globals *g, char *p); ++STATIC void regc(struct match_globals *g, char b); ++STATIC void reginsert(struct match_globals *g, char op, char *opnd); ++STATIC void regtail(struct match_globals *g, char *p, char *val); ++STATIC void regoptail(struct match_globals *g, char *p, char *val); ++ ++ ++__kernel_size_t my_strcspn(const char *s1,const char *s2) ++{ ++        char *scan1; ++        char *scan2; ++        int count; ++ ++        count = 0; ++        for (scan1 = (char *)s1; *scan1 != '\0'; scan1++) { ++                for (scan2 = (char *)s2; *scan2 != '\0';)       /* ++ moved down. */ ++                        if (*scan1 == *scan2++) ++                                return(count); ++                count++; ++        } ++        return(count); ++} ++ ++/* ++ - regcomp - compile a regular expression into internal code ++ * ++ * We can't allocate space until we know how big the compiled form will be, ++ * but we can't compile it (and thus know how big it is) until we've got a ++ * place to put the code.  So we cheat:  we compile it twice, once with code ++ * generation turned off and size counting turned on, and once "for real". ++ * This also means that we don't allocate space until we are sure that the ++ * thing really will compile successfully, and we never have to move the ++ * code and thus invalidate pointers into it.  (Note that it has to be in ++ * one piece because free() must be able to free it all.) ++ * ++ * Beware that the optimization-preparation code in here knows about some ++ * of the structure of the compiled regexp. ++ */ ++regexp * ++regcomp(char *exp,int *patternsize) ++{ ++	register regexp *r; ++	register char *scan; ++	register char *longest; ++	register int len; ++	int flags; ++	struct match_globals g; ++	 ++	/* commented out by ethan ++	   extern char *malloc(); ++	*/ ++ ++	if (exp == NULL) ++		FAIL("NULL argument"); ++ ++	/* First pass: determine size, legality. */ ++	g.regparse = exp; ++	g.regnpar = 1; ++	g.regsize = 0L; ++	g.regcode = &g.regdummy; ++	regc(&g, MAGIC); ++	if (reg(&g, 0, &flags) == NULL) ++		return(NULL); ++ ++	/* Small enough for pointer-storage convention? */ ++	if (g.regsize >= 32767L)		/* Probably could be 65535L. */ ++		FAIL("regexp too big"); ++ ++	/* Allocate space. */ ++	*patternsize=sizeof(regexp) + (unsigned)g.regsize; ++	r = (regexp *)malloc(sizeof(regexp) + (unsigned)g.regsize); ++	if (r == NULL) ++		FAIL("out of space"); ++ ++	/* Second pass: emit code. */ ++	g.regparse = exp; ++	g.regnpar = 1; ++	g.regcode = r->program; ++	regc(&g, MAGIC); ++	if (reg(&g, 0, &flags) == NULL) ++		return(NULL); ++ ++	/* Dig out information for optimizations. */ ++	r->regstart = '\0';	/* Worst-case defaults. */ ++	r->reganch = 0; ++	r->regmust = NULL; ++	r->regmlen = 0; ++	scan = r->program+1;			/* First BRANCH. */ ++	if (OP(regnext(&g, scan)) == END) {		/* Only one top-level choice. */ ++		scan = OPERAND(scan); ++ ++		/* Starting-point info. */ ++		if (OP(scan) == EXACTLY) ++			r->regstart = *OPERAND(scan); ++		else if (OP(scan) == BOL) ++			r->reganch++; ++ ++		/* ++		 * If there's something expensive in the r.e., find the ++		 * longest literal string that must appear and make it the ++		 * regmust.  Resolve ties in favor of later strings, since ++		 * the regstart check works with the beginning of the r.e. ++		 * and avoiding duplication strengthens checking.  Not a ++		 * strong reason, but sufficient in the absence of others. ++		 */ ++		if (flags&SPSTART) { ++			longest = NULL; ++			len = 0; ++			for (; scan != NULL; scan = regnext(&g, scan)) ++				if (OP(scan) == EXACTLY && strlen(OPERAND(scan)) >= len) { ++					longest = OPERAND(scan); ++					len = strlen(OPERAND(scan)); ++				} ++			r->regmust = longest; ++			r->regmlen = len; ++		} ++	} ++ ++	return(r); ++} ++ ++/* ++ - reg - regular expression, i.e. main body or parenthesized thing ++ * ++ * Caller must absorb opening parenthesis. ++ * ++ * Combining parenthesis handling with the base level of regular expression ++ * is a trifle forced, but the need to tie the tails of the branches to what ++ * follows makes it hard to avoid. ++ */ ++static char * ++reg(struct match_globals *g, int paren, int *flagp /* Parenthesized? */ ) ++{ ++	register char *ret; ++	register char *br; ++	register char *ender; ++	register int parno = 0; /* 0 makes gcc happy */ ++	int flags; ++ ++	*flagp = HASWIDTH;	/* Tentatively. */ ++ ++	/* Make an OPEN node, if parenthesized. */ ++	if (paren) { ++		if (g->regnpar >= NSUBEXP) ++			FAIL("too many ()"); ++		parno = g->regnpar; ++		g->regnpar++; ++		ret = regnode(g, OPEN+parno); ++	} else ++		ret = NULL; ++ ++	/* Pick up the branches, linking them together. */ ++	br = regbranch(g, &flags); ++	if (br == NULL) ++		return(NULL); ++	if (ret != NULL) ++		regtail(g, ret, br);	/* OPEN -> first. */ ++	else ++		ret = br; ++	if (!(flags&HASWIDTH)) ++		*flagp &= ~HASWIDTH; ++	*flagp |= flags&SPSTART; ++	while (*g->regparse == '|') { ++		g->regparse++; ++		br = regbranch(g, &flags); ++		if (br == NULL) ++			return(NULL); ++		regtail(g, ret, br);	/* BRANCH -> BRANCH. */ ++		if (!(flags&HASWIDTH)) ++			*flagp &= ~HASWIDTH; ++		*flagp |= flags&SPSTART; ++	} ++ ++	/* Make a closing node, and hook it on the end. */ ++	ender = regnode(g, (paren) ? CLOSE+parno : END);	 ++	regtail(g, ret, ender); ++ ++	/* Hook the tails of the branches to the closing node. */ ++	for (br = ret; br != NULL; br = regnext(g, br)) ++		regoptail(g, br, ender); ++ ++	/* Check for proper termination. */ ++	if (paren && *g->regparse++ != ')') { ++		FAIL("unmatched ()"); ++	} else if (!paren && *g->regparse != '\0') { ++		if (*g->regparse == ')') { ++			FAIL("unmatched ()"); ++		} else ++			FAIL("junk on end");	/* "Can't happen". */ ++		/* NOTREACHED */ ++	} ++ ++	return(ret); ++} ++ ++/* ++ - regbranch - one alternative of an | operator ++ * ++ * Implements the concatenation operator. ++ */ ++static char * ++regbranch(struct match_globals *g, int *flagp) ++{ ++	register char *ret; ++	register char *chain; ++	register char *latest; ++	int flags; ++ ++	*flagp = WORST;		/* Tentatively. */ ++ ++	ret = regnode(g, BRANCH); ++	chain = NULL; ++	while (*g->regparse != '\0' && *g->regparse != '|' && *g->regparse != ')') { ++		latest = regpiece(g, &flags); ++		if (latest == NULL) ++			return(NULL); ++		*flagp |= flags&HASWIDTH; ++		if (chain == NULL)	/* First piece. */ ++			*flagp |= flags&SPSTART; ++		else ++			regtail(g, chain, latest); ++		chain = latest; ++	} ++	if (chain == NULL)	/* Loop ran zero times. */ ++		(void) regnode(g, NOTHING); ++ ++	return(ret); ++} ++ ++/* ++ - regpiece - something followed by possible [*+?] ++ * ++ * Note that the branching code sequences used for ? and the general cases ++ * of * and + are somewhat optimized:  they use the same NOTHING node as ++ * both the endmarker for their branch list and the body of the last branch. ++ * It might seem that this node could be dispensed with entirely, but the ++ * endmarker role is not redundant. ++ */ ++static char * ++regpiece(struct match_globals *g, int *flagp) ++{ ++	register char *ret; ++	register char op; ++	register char *next; ++	int flags; ++ ++	ret = regatom(g, &flags); ++	if (ret == NULL) ++		return(NULL); ++ ++	op = *g->regparse; ++	if (!ISMULT(op)) { ++		*flagp = flags; ++		return(ret); ++	} ++ ++	if (!(flags&HASWIDTH) && op != '?') ++		FAIL("*+ operand could be empty"); ++	*flagp = (op != '+') ? (WORST|SPSTART) : (WORST|HASWIDTH); ++ ++	if (op == '*' && (flags&SIMPLE)) ++		reginsert(g, STAR, ret); ++	else if (op == '*') { ++		/* Emit x* as (x&|), where & means "self". */ ++		reginsert(g, BRANCH, ret);			/* Either x */ ++		regoptail(g, ret, regnode(g, BACK));		/* and loop */ ++		regoptail(g, ret, ret);			/* back */ ++		regtail(g, ret, regnode(g, BRANCH));		/* or */ ++		regtail(g, ret, regnode(g, NOTHING));		/* null. */ ++	} else if (op == '+' && (flags&SIMPLE)) ++		reginsert(g, PLUS, ret); ++	else if (op == '+') { ++		/* Emit x+ as x(&|), where & means "self". */ ++		next = regnode(g, BRANCH);			/* Either */ ++		regtail(g, ret, next); ++		regtail(g, regnode(g, BACK), ret);		/* loop back */ ++		regtail(g, next, regnode(g, BRANCH));		/* or */ ++		regtail(g, ret, regnode(g, NOTHING));		/* null. */ ++	} else if (op == '?') { ++		/* Emit x? as (x|) */ ++		reginsert(g, BRANCH, ret);			/* Either x */ ++		regtail(g, ret, regnode(g, BRANCH));		/* or */ ++		next = regnode(g, NOTHING);		/* null. */ ++		regtail(g, ret, next); ++		regoptail(g, ret, next); ++	} ++	g->regparse++; ++	if (ISMULT(*g->regparse)) ++		FAIL("nested *?+"); ++ ++	return(ret); ++} ++ ++/* ++ - regatom - the lowest level ++ * ++ * Optimization:  gobbles an entire sequence of ordinary characters so that ++ * it can turn them into a single node, which is smaller to store and ++ * faster to run.  Backslashed characters are exceptions, each becoming a ++ * separate node; the code is simpler that way and it's not worth fixing. ++ */ ++static char * ++regatom(struct match_globals *g, int *flagp) ++{ ++	register char *ret; ++	int flags; ++ ++	*flagp = WORST;		/* Tentatively. */ ++ ++	switch (*g->regparse++) { ++	case '^': ++		ret = regnode(g, BOL); ++		break; ++	case '$': ++		ret = regnode(g, EOL); ++		break; ++	case '.': ++		ret = regnode(g, ANY); ++		*flagp |= HASWIDTH|SIMPLE; ++		break; ++	case '[': { ++			register int class; ++			register int classend; ++ ++			if (*g->regparse == '^') {	/* Complement of range. */ ++				ret = regnode(g, ANYBUT); ++				g->regparse++; ++			} else ++				ret = regnode(g, ANYOF); ++			if (*g->regparse == ']' || *g->regparse == '-') ++				regc(g, *g->regparse++); ++			while (*g->regparse != '\0' && *g->regparse != ']') { ++				if (*g->regparse == '-') { ++					g->regparse++; ++					if (*g->regparse == ']' || *g->regparse == '\0') ++						regc(g, '-'); ++					else { ++						class = UCHARAT(g->regparse-2)+1; ++						classend = UCHARAT(g->regparse); ++						if (class > classend+1) ++							FAIL("invalid [] range"); ++						for (; class <= classend; class++) ++							regc(g, class); ++						g->regparse++; ++					} ++				} else ++					regc(g, *g->regparse++); ++			} ++			regc(g, '\0'); ++			if (*g->regparse != ']') ++				FAIL("unmatched []"); ++			g->regparse++; ++			*flagp |= HASWIDTH|SIMPLE; ++		} ++		break; ++	case '(': ++		ret = reg(g, 1, &flags); ++		if (ret == NULL) ++			return(NULL); ++		*flagp |= flags&(HASWIDTH|SPSTART); ++		break; ++	case '\0': ++	case '|': ++	case ')': ++		FAIL("internal urp");	/* Supposed to be caught earlier. */ ++		break; ++	case '?': ++	case '+': ++	case '*': ++		FAIL("?+* follows nothing"); ++		break; ++	case '\\': ++		if (*g->regparse == '\0') ++			FAIL("trailing \\"); ++		ret = regnode(g, EXACTLY); ++		regc(g, *g->regparse++); ++		regc(g, '\0'); ++		*flagp |= HASWIDTH|SIMPLE; ++		break; ++	default: { ++			register int len; ++			register char ender; ++ ++			g->regparse--; ++			len = my_strcspn((const char *)g->regparse, (const char *)META); ++			if (len <= 0) ++				FAIL("internal disaster"); ++			ender = *(g->regparse+len); ++			if (len > 1 && ISMULT(ender)) ++				len--;		/* Back off clear of ?+* operand. */ ++			*flagp |= HASWIDTH; ++			if (len == 1) ++				*flagp |= SIMPLE; ++			ret = regnode(g, EXACTLY); ++			while (len > 0) { ++				regc(g, *g->regparse++); ++				len--; ++			} ++			regc(g, '\0'); ++		} ++		break; ++	} ++ ++	return(ret); ++} ++ ++/* ++ - regnode - emit a node ++ */ ++static char *			/* Location. */ ++regnode(struct match_globals *g, char op) ++{ ++	register char *ret; ++	register char *ptr; ++ ++	ret = g->regcode; ++	if (ret == &g->regdummy) { ++		g->regsize += 3; ++		return(ret); ++	} ++ ++	ptr = ret; ++	*ptr++ = op; ++	*ptr++ = '\0';		/* Null "next" pointer. */ ++	*ptr++ = '\0'; ++	g->regcode = ptr; ++ ++	return(ret); ++} ++ ++/* ++ - regc - emit (if appropriate) a byte of code ++ */ ++static void ++regc(struct match_globals *g, char b) ++{ ++	if (g->regcode != &g->regdummy) ++		*g->regcode++ = b; ++	else ++		g->regsize++; ++} ++ ++/* ++ - reginsert - insert an operator in front of already-emitted operand ++ * ++ * Means relocating the operand. ++ */ ++static void ++reginsert(struct match_globals *g, char op, char* opnd) ++{ ++	register char *src; ++	register char *dst; ++	register char *place; ++ ++	if (g->regcode == &g->regdummy) { ++		g->regsize += 3; ++		return; ++	} ++ ++	src = g->regcode; ++	g->regcode += 3; ++	dst = g->regcode; ++	while (src > opnd) ++		*--dst = *--src; ++ ++	place = opnd;		/* Op node, where operand used to be. */ ++	*place++ = op; ++	*place++ = '\0'; ++	*place++ = '\0'; ++} ++ ++/* ++ - regtail - set the next-pointer at the end of a node chain ++ */ ++static void ++regtail(struct match_globals *g, char *p, char *val) ++{ ++	register char *scan; ++	register char *temp; ++	register int offset; ++ ++	if (p == &g->regdummy) ++		return; ++ ++	/* Find last node. */ ++	scan = p; ++	for (;;) { ++		temp = regnext(g, scan); ++		if (temp == NULL) ++			break; ++		scan = temp; ++	} ++ ++	if (OP(scan) == BACK) ++		offset = scan - val; ++	else ++		offset = val - scan; ++	*(scan+1) = (offset>>8)&0377; ++	*(scan+2) = offset&0377; ++} ++ ++/* ++ - regoptail - regtail on operand of first argument; nop if operandless ++ */ ++static void ++regoptail(struct match_globals *g, char *p, char *val) ++{ ++	/* "Operandless" and "op != BRANCH" are synonymous in practice. */ ++	if (p == NULL || p == &g->regdummy || OP(p) != BRANCH) ++		return; ++	regtail(g, OPERAND(p), val); ++} ++ ++/* ++ * regexec and friends ++ */ ++ ++ ++/* ++ * Forwards. ++ */ ++STATIC int regtry(struct match_globals *g, regexp *prog, char *string); ++STATIC int regmatch(struct match_globals *g, char *prog); ++STATIC int regrepeat(struct match_globals *g, char *p); ++ ++#ifdef DEBUG ++int regnarrate = 0; ++void regdump(); ++STATIC char *regprop(char *op); ++#endif ++ ++/* ++ - regexec - match a regexp against a string ++ */ ++int ++regexec(regexp *prog, char *string) ++{ ++	register char *s; ++	struct match_globals g; ++ ++	/* Be paranoid... */ ++	if (prog == NULL || string == NULL) { ++		printk("<3>Regexp: NULL parameter\n"); ++		return(0); ++	} ++ ++	/* Check validity of program. */ ++	if (UCHARAT(prog->program) != MAGIC) { ++		printk("<3>Regexp: corrupted program\n"); ++		return(0); ++	} ++ ++	/* If there is a "must appear" string, look for it. */ ++	if (prog->regmust != NULL) { ++		s = string; ++		while ((s = strchr(s, prog->regmust[0])) != NULL) { ++			if (strncmp(s, prog->regmust, prog->regmlen) == 0) ++				break;	/* Found it. */ ++			s++; ++		} ++		if (s == NULL)	/* Not present. */ ++			return(0); ++	} ++ ++	/* Mark beginning of line for ^ . */ ++	g.regbol = string; ++ ++	/* Simplest case:  anchored match need be tried only once. */ ++	if (prog->reganch) ++		return(regtry(&g, prog, string)); ++ ++	/* Messy cases:  unanchored match. */ ++	s = string; ++	if (prog->regstart != '\0') ++		/* We know what char it must start with. */ ++		while ((s = strchr(s, prog->regstart)) != NULL) { ++			if (regtry(&g, prog, s)) ++				return(1); ++			s++; ++		} ++	else ++		/* We don't -- general case. */ ++		do { ++			if (regtry(&g, prog, s)) ++				return(1); ++		} while (*s++ != '\0'); ++ ++	/* Failure. */ ++	return(0); ++} ++ ++/* ++ - regtry - try match at specific point ++ */ ++static int			/* 0 failure, 1 success */ ++regtry(struct match_globals *g, regexp *prog, char *string) ++{ ++	register int i; ++	register char **sp; ++	register char **ep; ++ ++	g->reginput = string; ++	g->regstartp = prog->startp; ++	g->regendp = prog->endp; ++ ++	sp = prog->startp; ++	ep = prog->endp; ++	for (i = NSUBEXP; i > 0; i--) { ++		*sp++ = NULL; ++		*ep++ = NULL; ++	} ++	if (regmatch(g, prog->program + 1)) { ++		prog->startp[0] = string; ++		prog->endp[0] = g->reginput; ++		return(1); ++	} else ++		return(0); ++} ++ ++/* ++ - regmatch - main matching routine ++ * ++ * Conceptually the strategy is simple:  check to see whether the current ++ * node matches, call self recursively to see whether the rest matches, ++ * and then act accordingly.  In practice we make some effort to avoid ++ * recursion, in particular by going through "ordinary" nodes (that don't ++ * need to know whether the rest of the match failed) by a loop instead of ++ * by recursion. ++ */ ++static int			/* 0 failure, 1 success */ ++regmatch(struct match_globals *g, char *prog) ++{ ++	register char *scan = prog; /* Current node. */ ++	char *next;		    /* Next node. */ ++ ++#ifdef DEBUG ++	if (scan != NULL && regnarrate) ++		fprintf(stderr, "%s(\n", regprop(scan)); ++#endif ++	while (scan != NULL) { ++#ifdef DEBUG ++		if (regnarrate) ++			fprintf(stderr, "%s...\n", regprop(scan)); ++#endif ++		next = regnext(g, scan); ++ ++		switch (OP(scan)) { ++		case BOL: ++			if (g->reginput != g->regbol) ++				return(0); ++			break; ++		case EOL: ++			if (*g->reginput != '\0') ++				return(0); ++			break; ++		case ANY: ++			if (*g->reginput == '\0') ++				return(0); ++			g->reginput++; ++			break; ++		case EXACTLY: { ++				register int len; ++				register char *opnd; ++ ++				opnd = OPERAND(scan); ++				/* Inline the first character, for speed. */ ++				if (*opnd != *g->reginput) ++					return(0); ++				len = strlen(opnd); ++				if (len > 1 && strncmp(opnd, g->reginput, len) != 0) ++					return(0); ++				g->reginput += len; ++			} ++			break; ++		case ANYOF: ++			if (*g->reginput == '\0' || strchr(OPERAND(scan), *g->reginput) == NULL) ++				return(0); ++			g->reginput++; ++			break; ++		case ANYBUT: ++			if (*g->reginput == '\0' || strchr(OPERAND(scan), *g->reginput) != NULL) ++				return(0); ++			g->reginput++; ++			break; ++		case NOTHING: ++		case BACK: ++			break; ++		case OPEN+1: ++		case OPEN+2: ++		case OPEN+3: ++		case OPEN+4: ++		case OPEN+5: ++		case OPEN+6: ++		case OPEN+7: ++		case OPEN+8: ++		case OPEN+9: { ++				register int no; ++				register char *save; ++ ++				no = OP(scan) - OPEN; ++				save = g->reginput; ++ ++				if (regmatch(g, next)) { ++					/* ++					 * Don't set startp if some later ++					 * invocation of the same parentheses ++					 * already has. ++					 */ ++					if (g->regstartp[no] == NULL) ++						g->regstartp[no] = save; ++					return(1); ++				} else ++					return(0); ++			} ++			break; ++		case CLOSE+1: ++		case CLOSE+2: ++		case CLOSE+3: ++		case CLOSE+4: ++		case CLOSE+5: ++		case CLOSE+6: ++		case CLOSE+7: ++		case CLOSE+8: ++		case CLOSE+9: ++			{ ++				register int no; ++				register char *save; ++ ++				no = OP(scan) - CLOSE; ++				save = g->reginput; ++ ++				if (regmatch(g, next)) { ++					/* ++					 * Don't set endp if some later ++					 * invocation of the same parentheses ++					 * already has. ++					 */ ++					if (g->regendp[no] == NULL) ++						g->regendp[no] = save; ++					return(1); ++				} else ++					return(0); ++			} ++			break; ++		case BRANCH: { ++				register char *save; ++ ++				if (OP(next) != BRANCH)		/* No choice. */ ++					next = OPERAND(scan);	/* Avoid recursion. */ ++				else { ++					do { ++						save = g->reginput; ++						if (regmatch(g, OPERAND(scan))) ++							return(1); ++						g->reginput = save; ++						scan = regnext(g, scan); ++					} while (scan != NULL && OP(scan) == BRANCH); ++					return(0); ++					/* NOTREACHED */ ++				} ++			} ++			break; ++		case STAR: ++		case PLUS: { ++				register char nextch; ++				register int no; ++				register char *save; ++				register int min; ++ ++				/* ++				 * Lookahead to avoid useless match attempts ++				 * when we know what character comes next. ++				 */ ++				nextch = '\0'; ++				if (OP(next) == EXACTLY) ++					nextch = *OPERAND(next); ++				min = (OP(scan) == STAR) ? 0 : 1; ++				save = g->reginput; ++				no = regrepeat(g, OPERAND(scan)); ++				while (no >= min) { ++					/* If it could work, try it. */ ++					if (nextch == '\0' || *g->reginput == nextch) ++						if (regmatch(g, next)) ++							return(1); ++					/* Couldn't or didn't -- back up. */ ++					no--; ++					g->reginput = save + no; ++				} ++				return(0); ++			} ++			break; ++		case END: ++			return(1);	/* Success! */ ++			break; ++		default: ++			printk("<3>Regexp: memory corruption\n"); ++			return(0); ++			break; ++		} ++ ++		scan = next; ++	} ++ ++	/* ++	 * We get here only if there's trouble -- normally "case END" is ++	 * the terminating point. ++	 */ ++	printk("<3>Regexp: corrupted pointers\n"); ++	return(0); ++} ++ ++/* ++ - regrepeat - repeatedly match something simple, report how many ++ */ ++static int ++regrepeat(struct match_globals *g, char *p) ++{ ++	register int count = 0; ++	register char *scan; ++	register char *opnd; ++ ++	scan = g->reginput; ++	opnd = OPERAND(p); ++	switch (OP(p)) { ++	case ANY: ++		count = strlen(scan); ++		scan += count; ++		break; ++	case EXACTLY: ++		while (*opnd == *scan) { ++			count++; ++			scan++; ++		} ++		break; ++	case ANYOF: ++		while (*scan != '\0' && strchr(opnd, *scan) != NULL) { ++			count++; ++			scan++; ++		} ++		break; ++	case ANYBUT: ++		while (*scan != '\0' && strchr(opnd, *scan) == NULL) { ++			count++; ++			scan++; ++		} ++		break; ++	default:		/* Oh dear.  Called inappropriately. */ ++		printk("<3>Regexp: internal foulup\n"); ++		count = 0;	/* Best compromise. */ ++		break; ++	} ++	g->reginput = scan; ++ ++	return(count); ++} ++ ++/* ++ - regnext - dig the "next" pointer out of a node ++ */ ++static char* ++regnext(struct match_globals *g, char *p) ++{ ++	register int offset; ++ ++	if (p == &g->regdummy) ++		return(NULL); ++ ++	offset = NEXT(p); ++	if (offset == 0) ++		return(NULL); ++ ++	if (OP(p) == BACK) ++		return(p-offset); ++	else ++		return(p+offset); ++} ++ ++#ifdef DEBUG ++ ++STATIC char *regprop(); ++ ++/* ++ - regdump - dump a regexp onto stdout in vaguely comprehensible form ++ */ ++void ++regdump(regexp *r) ++{ ++	register char *s; ++	register char op = EXACTLY;	/* Arbitrary non-END op. */ ++	register char *next; ++	/* extern char *strchr(); */ ++ ++ ++	s = r->program + 1; ++	while (op != END) {	/* While that wasn't END last time... */ ++		op = OP(s); ++		printf("%2d%s", s-r->program, regprop(s));	/* Where, what. */ ++		next = regnext(s); ++		if (next == NULL)		/* Next ptr. */ ++			printf("(0)"); ++		else ++			printf("(%d)", (s-r->program)+(next-s)); ++		s += 3; ++		if (op == ANYOF || op == ANYBUT || op == EXACTLY) { ++			/* Literal string, where present. */ ++			while (*s != '\0') { ++				putchar(*s); ++				s++; ++			} ++			s++; ++		} ++		putchar('\n'); ++	} ++ ++	/* Header fields of interest. */ ++	if (r->regstart != '\0') ++		printf("start `%c' ", r->regstart); ++	if (r->reganch) ++		printf("anchored "); ++	if (r->regmust != NULL) ++		printf("must have \"%s\"", r->regmust); ++	printf("\n"); ++} ++ ++/* ++ - regprop - printable representation of opcode ++ */ ++static char * ++regprop(char *op) ++{ ++#define BUFLEN 50 ++	register char *p; ++	static char buf[BUFLEN]; ++ ++	strcpy(buf, ":"); ++ ++	switch (OP(op)) { ++	case BOL: ++		p = "BOL"; ++		break; ++	case EOL: ++		p = "EOL"; ++		break; ++	case ANY: ++		p = "ANY"; ++		break; ++	case ANYOF: ++		p = "ANYOF"; ++		break; ++	case ANYBUT: ++		p = "ANYBUT"; ++		break; ++	case BRANCH: ++		p = "BRANCH"; ++		break; ++	case EXACTLY: ++		p = "EXACTLY"; ++		break; ++	case NOTHING: ++		p = "NOTHING"; ++		break; ++	case BACK: ++		p = "BACK"; ++		break; ++	case END: ++		p = "END"; ++		break; ++	case OPEN+1: ++	case OPEN+2: ++	case OPEN+3: ++	case OPEN+4: ++	case OPEN+5: ++	case OPEN+6: ++	case OPEN+7: ++	case OPEN+8: ++	case OPEN+9: ++		snprintf(buf+strlen(buf),BUFLEN-strlen(buf), "OPEN%d", OP(op)-OPEN); ++		p = NULL; ++		break; ++	case CLOSE+1: ++	case CLOSE+2: ++	case CLOSE+3: ++	case CLOSE+4: ++	case CLOSE+5: ++	case CLOSE+6: ++	case CLOSE+7: ++	case CLOSE+8: ++	case CLOSE+9: ++		snprintf(buf+strlen(buf),BUFLEN-strlen(buf), "CLOSE%d", OP(op)-CLOSE); ++		p = NULL; ++		break; ++	case STAR: ++		p = "STAR"; ++		break; ++	case PLUS: ++		p = "PLUS"; ++		break; ++	default: ++		printk("<3>Regexp: corrupted opcode\n"); ++		break; ++	} ++	if (p != NULL) ++		strncat(buf, p, BUFLEN-strlen(buf)); ++	return(buf); ++} ++#endif ++ ++ +--- /dev/null ++++ b/net/netfilter/regexp/regexp.h +@@ -0,0 +1,41 @@ ++/* ++ * Definitions etc. for regexp(3) routines. ++ * ++ * Caveat:  this is V8 regexp(3) [actually, a reimplementation thereof], ++ * not the System V one. ++ */ ++ ++#ifndef REGEXP_H ++#define REGEXP_H ++ ++ ++/* ++http://www.opensource.apple.com/darwinsource/10.3/expect-1/expect/expect.h , ++which contains a version of this library, says: ++ ++ * ++ * NSUBEXP must be at least 10, and no greater than 117 or the parser ++ * will not work properly. ++ * ++ ++However, it looks rather like this library is limited to 10.  If you think ++otherwise, let us know. ++*/ ++ ++#define NSUBEXP  10 ++typedef struct regexp { ++	char *startp[NSUBEXP]; ++	char *endp[NSUBEXP]; ++	char regstart;		/* Internal use only. */ ++	char reganch;		/* Internal use only. */ ++	char *regmust;		/* Internal use only. */ ++	int regmlen;		/* Internal use only. */ ++	char program[1];	/* Unwarranted chumminess with compiler. */ ++} regexp; ++ ++regexp * regcomp(char *exp, int *patternsize); ++int regexec(regexp *prog, char *string); ++void regsub(regexp *prog, char *source, char *dest); ++void regerror(char *s); ++ ++#endif +--- /dev/null ++++ b/net/netfilter/regexp/regmagic.h +@@ -0,0 +1,5 @@ ++/* ++ * The first byte of the regexp internal "program" is actually this magic ++ * number; the start node begins in the second byte. ++ */ ++#define	MAGIC	0234 +--- /dev/null ++++ b/net/netfilter/regexp/regsub.c +@@ -0,0 +1,95 @@ ++/* ++ * regsub ++ * @(#)regsub.c	1.3 of 2 April 86 ++ * ++ *	Copyright (c) 1986 by University of Toronto. ++ *	Written by Henry Spencer.  Not derived from licensed software. ++ * ++ *	Permission is granted to anyone to use this software for any ++ *	purpose on any computer system, and to redistribute it freely, ++ *	subject to the following restrictions: ++ * ++ *	1. The author is not responsible for the consequences of use of ++ *		this software, no matter how awful, even if they arise ++ *		from defects in it. ++ * ++ *	2. The origin of this software must not be misrepresented, either ++ *		by explicit claim or by omission. ++ * ++ *	3. Altered versions must be plainly marked as such, and must not ++ *		be misrepresented as being the original software. ++ * ++ * ++ * This code was modified by Ethan Sommer to work within the kernel ++ * (it now uses kmalloc etc..) ++ * ++ */ ++#include "regexp.h" ++#include "regmagic.h" ++#include <linux/string.h> ++ ++ ++#ifndef CHARBITS ++#define	UCHARAT(p)	((int)*(unsigned char *)(p)) ++#else ++#define	UCHARAT(p)	((int)*(p)&CHARBITS) ++#endif ++ ++#if 0 ++//void regerror(char * s) ++//{ ++//        printk("regexp(3): %s", s); ++//        /* NOTREACHED */ ++//} ++#endif ++ ++/* ++ - regsub - perform substitutions after a regexp match ++ */ ++void ++regsub(regexp * prog, char * source, char * dest) ++{ ++	register char *src; ++	register char *dst; ++	register char c; ++	register int no; ++	register int len; ++	 ++	/* Not necessary and gcc doesn't like it -MLS */ ++	/*extern char *strncpy();*/ ++ ++	if (prog == NULL || source == NULL || dest == NULL) { ++		regerror("NULL parm to regsub"); ++		return; ++	} ++	if (UCHARAT(prog->program) != MAGIC) { ++		regerror("damaged regexp fed to regsub"); ++		return; ++	} ++ ++	src = source; ++	dst = dest; ++	while ((c = *src++) != '\0') { ++		if (c == '&') ++			no = 0; ++		else if (c == '\\' && '0' <= *src && *src <= '9') ++			no = *src++ - '0'; ++		else ++			no = -1; ++ ++		if (no < 0) {	/* Ordinary character. */ ++			if (c == '\\' && (*src == '\\' || *src == '&')) ++				c = *src++; ++			*dst++ = c; ++		} else if (prog->startp[no] != NULL && prog->endp[no] != NULL) { ++			len = prog->endp[no] - prog->startp[no]; ++			(void) strncpy(dst, prog->startp[no], len); ++			dst += len; ++			if (len != 0 && *(dst-1) == '\0') {	/* strncpy hit NUL. */ ++				regerror("damaged match string"); ++				return; ++			} ++		} ++	} ++	*dst++ = '\0'; ++} +--- /dev/null ++++ b/net/netfilter/xt_layer7.c +@@ -0,0 +1,666 @@ ++/* ++  Kernel module to match application layer (OSI layer 7) data in connections. ++ ++  http://l7-filter.sf.net ++ ++  (C) 2003-2009 Matthew Strait and Ethan Sommer. ++ ++  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. ++  http://www.gnu.org/licenses/gpl.txt ++ ++  Based on ipt_string.c (C) 2000 Emmanuel Roger <winfield@freegates.be>, ++  xt_helper.c (C) 2002 Harald Welte and cls_layer7.c (C) 2003 Matthew Strait, ++  Ethan Sommer, Justin Levandoski. ++*/ ++ ++#include <linux/spinlock.h> ++#include <linux/version.h> ++#include <net/ip.h> ++#include <net/tcp.h> ++#include <linux/module.h> ++#include <linux/skbuff.h> ++#include <linux/netfilter.h> ++#include <net/netfilter/nf_conntrack.h> ++#include <net/netfilter/nf_conntrack_core.h> ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) ++#include <net/netfilter/nf_conntrack_extend.h> ++#include <net/netfilter/nf_conntrack_acct.h> ++#endif ++#include <linux/netfilter/x_tables.h> ++#include <linux/netfilter/xt_layer7.h> ++#include <linux/ctype.h> ++#include <linux/proc_fs.h> ++ ++#include "regexp/regexp.c" ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Matthew Strait <quadong@users.sf.net>, Ethan Sommer <sommere@users.sf.net>"); ++MODULE_DESCRIPTION("iptables application layer match module"); ++MODULE_ALIAS("ipt_layer7"); ++MODULE_VERSION("2.21"); ++ ++static int maxdatalen = 2048; // this is the default ++module_param(maxdatalen, int, 0444); ++MODULE_PARM_DESC(maxdatalen, "maximum bytes of data looked at by l7-filter"); ++#ifdef CONFIG_NETFILTER_XT_MATCH_LAYER7_DEBUG ++	#define DPRINTK(format,args...) printk(format,##args) ++#else ++	#define DPRINTK(format,args...) ++#endif ++ ++/* Number of packets whose data we look at. ++This can be modified through /proc/net/layer7_numpackets */ ++static int num_packets = 10; ++ ++static struct pattern_cache { ++	char * regex_string; ++	regexp * pattern; ++	struct pattern_cache * next; ++} * first_pattern_cache = NULL; ++ ++DEFINE_SPINLOCK(l7_lock); ++ ++static int total_acct_packets(struct nf_conn *ct) ++{ ++#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 26) ++	BUG_ON(ct == NULL); ++	return (ct->counters[IP_CT_DIR_ORIGINAL].packets + ct->counters[IP_CT_DIR_REPLY].packets); ++#else ++	struct nf_conn_counter *acct; ++ ++	BUG_ON(ct == NULL); ++	acct = nf_conn_acct_find(ct); ++	if (!acct) ++		return 0; ++	return (acct[IP_CT_DIR_ORIGINAL].packets + acct[IP_CT_DIR_REPLY].packets); ++#endif ++} ++ ++#ifdef CONFIG_IP_NF_MATCH_LAYER7_DEBUG ++/* Converts an unfriendly string into a friendly one by ++replacing unprintables with periods and all whitespace with " ". */ ++static char * friendly_print(unsigned char * s) ++{ ++	char * f = kmalloc(strlen(s) + 1, GFP_ATOMIC); ++	int i; ++ ++	if(!f) { ++		if (net_ratelimit()) ++			printk(KERN_ERR "layer7: out of memory in " ++					"friendly_print, bailing.\n"); ++		return NULL; ++	} ++ ++	for(i = 0; i < strlen(s); i++){ ++		if(isprint(s[i]) && s[i] < 128)	f[i] = s[i]; ++		else if(isspace(s[i]))		f[i] = ' '; ++		else 				f[i] = '.'; ++	} ++	f[i] = '\0'; ++	return f; ++} ++ ++static char dec2hex(int i) ++{ ++	switch (i) { ++		case 0 ... 9: ++			return (i + '0'); ++			break; ++		case 10 ... 15: ++			return (i - 10 + 'a'); ++			break; ++		default: ++			if (net_ratelimit()) ++				printk("layer7: Problem in dec2hex\n"); ++			return '\0'; ++	} ++} ++ ++static char * hex_print(unsigned char * s) ++{ ++	char * g = kmalloc(strlen(s)*3 + 1, GFP_ATOMIC); ++	int i; ++ ++	if(!g) { ++	       if (net_ratelimit()) ++			printk(KERN_ERR "layer7: out of memory in hex_print, " ++					"bailing.\n"); ++	       return NULL; ++	} ++ ++	for(i = 0; i < strlen(s); i++) { ++		g[i*3    ] = dec2hex(s[i]/16); ++		g[i*3 + 1] = dec2hex(s[i]%16); ++		g[i*3 + 2] = ' '; ++	} ++	g[i*3] = '\0'; ++ ++	return g; ++} ++#endif // DEBUG ++ ++/* Use instead of regcomp.  As we expect to be seeing the same regexps over and ++over again, it make sense to cache the results. */ ++static regexp * compile_and_cache(const char * regex_string,  ++                                  const char * protocol) ++{ ++	struct pattern_cache * node               = first_pattern_cache; ++	struct pattern_cache * last_pattern_cache = first_pattern_cache; ++	struct pattern_cache * tmp; ++	unsigned int len; ++ ++	while (node != NULL) { ++		if (!strcmp(node->regex_string, regex_string)) ++		return node->pattern; ++ ++		last_pattern_cache = node;/* points at the last non-NULL node */ ++		node = node->next; ++	} ++ ++	/* If we reach the end of the list, then we have not yet cached ++	   the pattern for this regex. Let's do that now. ++	   Be paranoid about running out of memory to avoid list corruption. */ ++	tmp = kmalloc(sizeof(struct pattern_cache), GFP_ATOMIC); ++ ++	if(!tmp) { ++		if (net_ratelimit()) ++			printk(KERN_ERR "layer7: out of memory in " ++					"compile_and_cache, bailing.\n"); ++		return NULL; ++	} ++ ++	tmp->regex_string  = kmalloc(strlen(regex_string) + 1, GFP_ATOMIC); ++	tmp->pattern       = kmalloc(sizeof(struct regexp),    GFP_ATOMIC); ++	tmp->next = NULL; ++ ++	if(!tmp->regex_string || !tmp->pattern) { ++		if (net_ratelimit()) ++			printk(KERN_ERR "layer7: out of memory in " ++					"compile_and_cache, bailing.\n"); ++		kfree(tmp->regex_string); ++		kfree(tmp->pattern); ++		kfree(tmp); ++		return NULL; ++	} ++ ++	/* Ok.  The new node is all ready now. */ ++	node = tmp; ++ ++	if(first_pattern_cache == NULL) /* list is empty */ ++		first_pattern_cache = node; /* make node the beginning */ ++	else ++		last_pattern_cache->next = node; /* attach node to the end */ ++ ++	/* copy the string and compile the regex */ ++	len = strlen(regex_string); ++	DPRINTK("About to compile this: \"%s\"\n", regex_string); ++	node->pattern = regcomp((char *)regex_string, &len); ++	if ( !node->pattern ) { ++		if (net_ratelimit()) ++			printk(KERN_ERR "layer7: Error compiling regexp " ++					"\"%s\" (%s)\n",  ++					regex_string, protocol); ++		/* pattern is now cached as NULL, so we won't try again. */ ++	} ++ ++	strcpy(node->regex_string, regex_string); ++	return node->pattern; ++} ++ ++static int can_handle(const struct sk_buff *skb) ++{ ++	if(!ip_hdr(skb)) /* not IP */ ++		return 0; ++	if(ip_hdr(skb)->protocol != IPPROTO_TCP && ++	   ip_hdr(skb)->protocol != IPPROTO_UDP && ++	   ip_hdr(skb)->protocol != IPPROTO_ICMP) ++		return 0; ++	return 1; ++} ++ ++/* Returns offset the into the skb->data that the application data starts */ ++static int app_data_offset(const struct sk_buff *skb) ++{ ++	/* In case we are ported somewhere (ebtables?) where ip_hdr(skb) ++	isn't set, this can be gotten from 4*(skb->data[0] & 0x0f) as well. */ ++	int ip_hl = 4*ip_hdr(skb)->ihl; ++ ++	if( ip_hdr(skb)->protocol == IPPROTO_TCP ) { ++		/* 12 == offset into TCP header for the header length field. ++		Can't get this with skb->h.th->doff because the tcphdr ++		struct doesn't get set when routing (this is confirmed to be ++		true in Netfilter as well as QoS.) */ ++		int tcp_hl = 4*(skb->data[ip_hl + 12] >> 4); ++ ++		return ip_hl + tcp_hl; ++	} else if( ip_hdr(skb)->protocol == IPPROTO_UDP  ) { ++		return ip_hl + 8; /* UDP header is always 8 bytes */ ++	} else if( ip_hdr(skb)->protocol == IPPROTO_ICMP ) { ++		return ip_hl + 8; /* ICMP header is 8 bytes */ ++	} else { ++		if (net_ratelimit()) ++			printk(KERN_ERR "layer7: tried to handle unknown " ++					"protocol!\n"); ++		return ip_hl + 8; /* something reasonable */ ++	} ++} ++ ++/* handles whether there's a match when we aren't appending data anymore */ ++static int match_no_append(struct nf_conn * conntrack,  ++                           struct nf_conn * master_conntrack,  ++                           enum ip_conntrack_info ctinfo, ++                           enum ip_conntrack_info master_ctinfo, ++                           const struct xt_layer7_info * info) ++{ ++	/* If we're in here, throw the app data away */ ++	if(master_conntrack->layer7.app_data != NULL) { ++ ++	#ifdef CONFIG_IP_NF_MATCH_LAYER7_DEBUG ++		if(!master_conntrack->layer7.app_proto) { ++			char * f =  ++			  friendly_print(master_conntrack->layer7.app_data); ++			char * g =  ++			  hex_print(master_conntrack->layer7.app_data); ++			DPRINTK("\nl7-filter gave up after %d bytes " ++				"(%d packets):\n%s\n", ++				strlen(f), total_acct_packets(master_conntrack), f); ++			kfree(f); ++			DPRINTK("In hex: %s\n", g); ++			kfree(g); ++		} ++	#endif ++ ++		kfree(master_conntrack->layer7.app_data); ++		master_conntrack->layer7.app_data = NULL; /* don't free again */ ++	} ++ ++	if(master_conntrack->layer7.app_proto){ ++		/* Here child connections set their .app_proto (for /proc) */ ++		if(!conntrack->layer7.app_proto) { ++			conntrack->layer7.app_proto =  ++			  kmalloc(strlen(master_conntrack->layer7.app_proto)+1,  ++			    GFP_ATOMIC); ++			if(!conntrack->layer7.app_proto){ ++				if (net_ratelimit()) ++					printk(KERN_ERR "layer7: out of memory " ++							"in match_no_append, " ++							"bailing.\n"); ++				return 1; ++			} ++			strcpy(conntrack->layer7.app_proto,  ++				master_conntrack->layer7.app_proto); ++		} ++ ++		return (!strcmp(master_conntrack->layer7.app_proto,  ++				info->protocol)); ++	} ++	else { ++		/* If not classified, set to "unknown" to distinguish from ++		connections that are still being tested. */ ++		master_conntrack->layer7.app_proto =  ++			kmalloc(strlen("unknown")+1, GFP_ATOMIC); ++		if(!master_conntrack->layer7.app_proto){ ++			if (net_ratelimit()) ++				printk(KERN_ERR "layer7: out of memory in " ++						"match_no_append, bailing.\n"); ++			return 1; ++		} ++		strcpy(master_conntrack->layer7.app_proto, "unknown"); ++		return 0; ++	} ++} ++ ++/* add the new app data to the conntrack.  Return number of bytes added. */ ++static int add_data(struct nf_conn * master_conntrack, ++                    char * app_data, int appdatalen) ++{ ++	int length = 0, i; ++	int oldlength = master_conntrack->layer7.app_data_len; ++ ++	/* This is a fix for a race condition by Deti Fliegl. However, I'm not  ++	   clear on whether the race condition exists or whether this really  ++	   fixes it.  I might just be being dense... Anyway, if it's not really  ++	   a fix, all it does is waste a very small amount of time. */ ++	if(!master_conntrack->layer7.app_data) return 0; ++ ++	/* Strip nulls. Make everything lower case (our regex lib doesn't ++	do case insensitivity).  Add it to the end of the current data. */ ++	for(i = 0; i < maxdatalen-oldlength-1 && ++		   i < appdatalen; i++) { ++		if(app_data[i] != '\0') { ++			/* the kernel version of tolower mungs 'upper ascii' */ ++			master_conntrack->layer7.app_data[length+oldlength] = ++				isascii(app_data[i])?  ++					tolower(app_data[i]) : app_data[i]; ++			length++; ++		} ++	} ++ ++	master_conntrack->layer7.app_data[length+oldlength] = '\0'; ++	master_conntrack->layer7.app_data_len = length + oldlength; ++ ++	return length; ++} ++ ++/* taken from drivers/video/modedb.c */ ++static int my_atoi(const char *s) ++{ ++	int val = 0; ++ ++	for (;; s++) { ++		switch (*s) { ++			case '0'...'9': ++			val = 10*val+(*s-'0'); ++			break; ++		default: ++			return val; ++		} ++	} ++} ++ ++/* write out num_packets to userland. */ ++static int layer7_read_proc(char* page, char ** start, off_t off, int count, ++                            int* eof, void * data) ++{ ++	if(num_packets > 99 && net_ratelimit()) ++		printk(KERN_ERR "layer7: NOT REACHED. num_packets too big\n"); ++ ++	page[0] = num_packets/10 + '0'; ++	page[1] = num_packets%10 + '0'; ++	page[2] = '\n'; ++	page[3] = '\0'; ++ ++	*eof=1; ++ ++	return 3; ++} ++ ++/* Read in num_packets from userland */ ++static int layer7_write_proc(struct file* file, const char* buffer, ++                             unsigned long count, void *data) ++{ ++	char * foo = kmalloc(count, GFP_ATOMIC); ++ ++	if(!foo){ ++		if (net_ratelimit()) ++			printk(KERN_ERR "layer7: out of memory, bailing. " ++					"num_packets unchanged.\n"); ++		return count; ++	} ++ ++	if(copy_from_user(foo, buffer, count)) { ++		return -EFAULT; ++	} ++ ++ ++	num_packets = my_atoi(foo); ++	kfree (foo); ++ ++	/* This has an arbitrary limit to make the math easier. I'm lazy. ++	But anyway, 99 is a LOT! If you want more, you're doing it wrong! */ ++	if(num_packets > 99) { ++		printk(KERN_WARNING "layer7: num_packets can't be > 99.\n"); ++		num_packets = 99; ++	} else if(num_packets < 1) { ++		printk(KERN_WARNING "layer7: num_packets can't be < 1.\n"); ++		num_packets = 1; ++	} ++ ++	return count; ++} ++ ++static bool ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28) ++match(const struct sk_buff *skbin, const struct xt_match_param *par) ++#else ++match(const struct sk_buff *skbin, ++      const struct net_device *in, ++      const struct net_device *out, ++      const struct xt_match *match, ++      const void *matchinfo, ++      int offset, ++      unsigned int protoff, ++      bool *hotdrop) ++#endif ++{ ++	/* sidestep const without getting a compiler warning... */ ++	struct sk_buff * skb = (struct sk_buff *)skbin;  ++ ++	const struct xt_layer7_info * info =  ++	#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28) ++		par->matchinfo; ++	#else ++		matchinfo; ++	#endif ++ ++	enum ip_conntrack_info master_ctinfo, ctinfo; ++	struct nf_conn *master_conntrack, *conntrack; ++	unsigned char * app_data; ++	unsigned int pattern_result, appdatalen; ++	regexp * comppattern; ++ ++	/* Be paranoid/incompetent - lock the entire match function. */ ++	spin_lock_bh(&l7_lock); ++ ++	if(!can_handle(skb)){ ++		DPRINTK("layer7: This is some protocol I can't handle.\n"); ++		spin_unlock_bh(&l7_lock); ++		return info->invert; ++	} ++ ++	/* Treat parent & all its children together as one connection, except ++	for the purpose of setting conntrack->layer7.app_proto in the actual ++	connection. This makes /proc/net/ip_conntrack more satisfying. */ ++	if(!(conntrack = nf_ct_get(skb, &ctinfo)) || ++	   !(master_conntrack=nf_ct_get(skb,&master_ctinfo))){ ++		DPRINTK("layer7: couldn't get conntrack.\n"); ++		spin_unlock_bh(&l7_lock); ++		return info->invert; ++	} ++ ++	/* Try to get a master conntrack (and its master etc) for FTP, etc. */ ++	while (master_ct(master_conntrack) != NULL) ++		master_conntrack = master_ct(master_conntrack); ++ ++	/* if we've classified it or seen too many packets */ ++	if(total_acct_packets(master_conntrack) > num_packets || ++	   master_conntrack->layer7.app_proto) { ++ ++		pattern_result = match_no_append(conntrack, master_conntrack,  ++						 ctinfo, master_ctinfo, info); ++ ++		/* skb->cb[0] == seen. Don't do things twice if there are  ++		multiple l7 rules. I'm not sure that using cb for this purpose  ++		is correct, even though it says "put your private variables  ++		there". But it doesn't look like it is being used for anything ++		else in the skbs that make it here. */ ++		skb->cb[0] = 1; /* marking it seen here's probably irrelevant */ ++ ++		spin_unlock_bh(&l7_lock); ++		return (pattern_result ^ info->invert); ++	} ++ ++	if(skb_is_nonlinear(skb)){ ++		if(skb_linearize(skb) != 0){ ++			if (net_ratelimit()) ++				printk(KERN_ERR "layer7: failed to linearize " ++						"packet, bailing.\n"); ++			spin_unlock_bh(&l7_lock); ++			return info->invert; ++		} ++	} ++ ++	/* now that the skb is linearized, it's safe to set these. */ ++	app_data = skb->data + app_data_offset(skb); ++	appdatalen = skb_tail_pointer(skb) - app_data; ++ ++	/* the return value gets checked later, when we're ready to use it */ ++	comppattern = compile_and_cache(info->pattern, info->protocol); ++ ++	/* On the first packet of a connection, allocate space for app data */ ++	if(total_acct_packets(master_conntrack) == 1 && !skb->cb[0] &&  ++	   !master_conntrack->layer7.app_data){ ++		master_conntrack->layer7.app_data =  ++			kmalloc(maxdatalen, GFP_ATOMIC); ++		if(!master_conntrack->layer7.app_data){ ++			if (net_ratelimit()) ++				printk(KERN_ERR "layer7: out of memory in " ++						"match, bailing.\n"); ++			spin_unlock_bh(&l7_lock); ++			return info->invert; ++		} ++ ++		master_conntrack->layer7.app_data[0] = '\0'; ++	} ++ ++	/* Can be here, but unallocated, if numpackets is increased near ++	the beginning of a connection */ ++	if(master_conntrack->layer7.app_data == NULL){ ++		spin_unlock_bh(&l7_lock); ++		return info->invert; /* unmatched */ ++	} ++ ++	if(!skb->cb[0]){ ++		int newbytes; ++		newbytes = add_data(master_conntrack, app_data, appdatalen); ++ ++		if(newbytes == 0) { /* didn't add any data */ ++			skb->cb[0] = 1; ++			/* Didn't match before, not going to match now */ ++			spin_unlock_bh(&l7_lock); ++			return info->invert; ++		} ++	} ++ ++	/* If looking for "unknown", then never match.  "Unknown" means that ++	we've given up; we're still trying with these packets. */ ++	if(!strcmp(info->protocol, "unknown")) { ++		pattern_result = 0; ++	/* If looking for "unset", then always match. "Unset" means that we ++	haven't yet classified the connection. */ ++	} else if(!strcmp(info->protocol, "unset")) { ++		pattern_result = 2; ++		DPRINTK("layer7: matched unset: not yet classified " ++			"(%d/%d packets)\n", ++                        total_acct_packets(master_conntrack), num_packets); ++	/* If the regexp failed to compile, don't bother running it */ ++	} else if(comppattern &&  ++		  regexec(comppattern, master_conntrack->layer7.app_data)){ ++		DPRINTK("layer7: matched %s\n", info->protocol); ++		pattern_result = 1; ++	} else pattern_result = 0; ++ ++	if(pattern_result == 1) { ++		master_conntrack->layer7.app_proto =  ++			kmalloc(strlen(info->protocol)+1, GFP_ATOMIC); ++		if(!master_conntrack->layer7.app_proto){ ++			if (net_ratelimit()) ++				printk(KERN_ERR "layer7: out of memory in " ++						"match, bailing.\n"); ++			spin_unlock_bh(&l7_lock); ++			return (pattern_result ^ info->invert); ++		} ++		strcpy(master_conntrack->layer7.app_proto, info->protocol); ++	} else if(pattern_result > 1) { /* cleanup from "unset" */ ++		pattern_result = 1; ++	} ++ ++	/* mark the packet seen */ ++	skb->cb[0] = 1; ++ ++	spin_unlock_bh(&l7_lock); ++	return (pattern_result ^ info->invert); ++} ++ ++// load nf_conntrack_ipv4 ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28) ++static bool check(const struct xt_mtchk_param *par) ++{ ++        if (nf_ct_l3proto_try_module_get(par->match->family) < 0) { ++                printk(KERN_WARNING "can't load conntrack support for " ++                                    "proto=%d\n", par->match->family); ++#else ++static bool check(const char *tablename, const void *inf, ++		 const struct xt_match *match, void *matchinfo, ++		 unsigned int hook_mask) ++{ ++        if (nf_ct_l3proto_try_module_get(match->family) < 0) { ++                printk(KERN_WARNING "can't load conntrack support for " ++                                    "proto=%d\n", match->family); ++#endif ++                return 0; ++        } ++	return 1; ++} ++ ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28) ++	static void destroy(const struct xt_mtdtor_param *par) ++	{ ++		nf_ct_l3proto_module_put(par->match->family); ++	} ++#else ++	static void destroy(const struct xt_match *match, void *matchinfo) ++	{ ++		nf_ct_l3proto_module_put(match->family); ++	} ++#endif ++ ++static struct xt_match xt_layer7_match[] __read_mostly = { ++{ ++	.name		= "layer7", ++	.family		= AF_INET, ++	.checkentry	= check, ++	.match		= match, ++	.destroy	= destroy, ++	.matchsize	= sizeof(struct xt_layer7_info), ++	.me		= THIS_MODULE ++} ++}; ++ ++static void layer7_cleanup_proc(void) ++{ ++	remove_proc_entry("layer7_numpackets", init_net.proc_net); ++} ++ ++/* register the proc file */ ++static void layer7_init_proc(void) ++{ ++	struct proc_dir_entry* entry; ++	entry = create_proc_entry("layer7_numpackets", 0644, init_net.proc_net); ++	entry->read_proc = layer7_read_proc; ++	entry->write_proc = layer7_write_proc; ++} ++ ++static int __init xt_layer7_init(void) ++{ ++	need_conntrack(); ++ ++	layer7_init_proc(); ++	if(maxdatalen < 1) { ++		printk(KERN_WARNING "layer7: maxdatalen can't be < 1, " ++			"using 1\n"); ++		maxdatalen = 1; ++	} ++	/* This is not a hard limit.  It's just here to prevent people from ++	bringing their slow machines to a grinding halt. */ ++	else if(maxdatalen > 65536) { ++		printk(KERN_WARNING "layer7: maxdatalen can't be > 65536, " ++			"using 65536\n"); ++		maxdatalen = 65536; ++	} ++	return xt_register_matches(xt_layer7_match, ++				   ARRAY_SIZE(xt_layer7_match)); ++} ++ ++static void __exit xt_layer7_fini(void) ++{ ++	layer7_cleanup_proc(); ++	xt_unregister_matches(xt_layer7_match, ARRAY_SIZE(xt_layer7_match)); ++} ++ ++module_init(xt_layer7_init); ++module_exit(xt_layer7_fini); diff --git a/target/linux/generic-2.6/patches-2.6.35/101-netfilter_layer7_pktmatch.patch b/target/linux/generic-2.6/patches-2.6.35/101-netfilter_layer7_pktmatch.patch new file mode 100644 index 000000000..f65e301fd --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.35/101-netfilter_layer7_pktmatch.patch @@ -0,0 +1,108 @@ +--- a/include/linux/netfilter/xt_layer7.h ++++ b/include/linux/netfilter/xt_layer7.h +@@ -8,6 +8,7 @@ struct xt_layer7_info { +     char protocol[MAX_PROTOCOL_LEN]; +     char pattern[MAX_PATTERN_LEN]; +     u_int8_t invert; ++    u_int8_t pkt; + }; +  + #endif /* _XT_LAYER7_H */ +--- a/net/netfilter/xt_layer7.c ++++ b/net/netfilter/xt_layer7.c +@@ -314,33 +314,35 @@ static int match_no_append(struct nf_con + } +  + /* add the new app data to the conntrack.  Return number of bytes added. */ +-static int add_data(struct nf_conn * master_conntrack, +-                    char * app_data, int appdatalen) ++static int add_datastr(char *target, int offset, char *app_data, int len) + { + 	int length = 0, i; +-	int oldlength = master_conntrack->layer7.app_data_len; +- +-	/* This is a fix for a race condition by Deti Fliegl. However, I'm not  +-	   clear on whether the race condition exists or whether this really  +-	   fixes it.  I might just be being dense... Anyway, if it's not really  +-	   a fix, all it does is waste a very small amount of time. */ +-	if(!master_conntrack->layer7.app_data) return 0; ++	if (!target) return 0; +  + 	/* Strip nulls. Make everything lower case (our regex lib doesn't + 	do case insensitivity).  Add it to the end of the current data. */ +-	for(i = 0; i < maxdatalen-oldlength-1 && +-		   i < appdatalen; i++) { ++ 	for(i = 0; i < maxdatalen-offset-1 && i < len; i++) { + 		if(app_data[i] != '\0') { + 			/* the kernel version of tolower mungs 'upper ascii' */ +-			master_conntrack->layer7.app_data[length+oldlength] = ++			target[length+offset] = + 				isascii(app_data[i])?  + 					tolower(app_data[i]) : app_data[i]; + 			length++; + 		} + 	} ++	target[length+offset] = '\0'; ++ ++	return length; ++} ++ ++/* add the new app data to the conntrack.  Return number of bytes added. */ ++static int add_data(struct nf_conn * master_conntrack, ++                    char * app_data, int appdatalen) ++{ ++	int length; +  +-	master_conntrack->layer7.app_data[length+oldlength] = '\0'; +-	master_conntrack->layer7.app_data_len = length + oldlength; ++	length = add_datastr(master_conntrack->layer7.app_data, master_conntrack->layer7.app_data_len, app_data, appdatalen); ++	master_conntrack->layer7.app_data_len += length; +  + 	return length; + } +@@ -438,7 +440,7 @@ match(const struct sk_buff *skbin, +  + 	enum ip_conntrack_info master_ctinfo, ctinfo; + 	struct nf_conn *master_conntrack, *conntrack; +-	unsigned char * app_data; ++	unsigned char *app_data, *tmp_data; + 	unsigned int pattern_result, appdatalen; + 	regexp * comppattern; +  +@@ -466,8 +468,8 @@ match(const struct sk_buff *skbin, + 		master_conntrack = master_ct(master_conntrack); +  + 	/* if we've classified it or seen too many packets */ +-	if(total_acct_packets(master_conntrack) > num_packets || +-	   master_conntrack->layer7.app_proto) { ++	if(!info->pkt && (total_acct_packets(master_conntrack) > num_packets || ++	   master_conntrack->layer7.app_proto)) { +  + 		pattern_result = match_no_append(conntrack, master_conntrack,  + 						 ctinfo, master_ctinfo, info); +@@ -500,6 +502,25 @@ match(const struct sk_buff *skbin, + 	/* the return value gets checked later, when we're ready to use it */ + 	comppattern = compile_and_cache(info->pattern, info->protocol); +  ++	if (info->pkt) { ++		tmp_data = kmalloc(maxdatalen, GFP_ATOMIC); ++		if(!tmp_data){ ++			if (net_ratelimit()) ++				printk(KERN_ERR "layer7: out of memory in match, bailing.\n"); ++			return info->invert; ++		} ++ ++		tmp_data[0] = '\0'; ++		add_datastr(tmp_data, 0, app_data, appdatalen); ++		pattern_result = ((comppattern && regexec(comppattern, tmp_data)) ? 1 : 0); ++ ++		kfree(tmp_data); ++		tmp_data = NULL; ++		spin_unlock_bh(&l7_lock); ++ ++		return (pattern_result ^ info->invert); ++	} ++ + 	/* On the first packet of a connection, allocate space for app data */ + 	if(total_acct_packets(master_conntrack) == 1 && !skb->cb[0] &&  + 	   !master_conntrack->layer7.app_data){  | 
