Permalink
Cannot retrieve contributors at this time
Fetching contributors…
| diff -uNr linux-2.6.35.3.old/include/linux/netfilter/xt_layer7.h linux-2.6.35.3.new/include/linux/netfilter/xt_layer7.h | |
| --- linux-2.6.35.3.old/include/linux/netfilter/xt_layer7.h 1969-12-31 19:30:00.000000000 -0430 | |
| +++ linux-2.6.35.3.new/include/linux/netfilter/xt_layer7.h 2010-09-09 09:41:33.000000000 -0400 | |
| @@ -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 */ | |
| diff -uNr linux-2.6.35.3.old/include/net/netfilter/nf_conntrack.h linux-2.6.35.3.new/include/net/netfilter/nf_conntrack.h | |
| --- linux-2.6.35.3.old/include/net/netfilter/nf_conntrack.h 2010-08-20 14:55:55.000000000 -0400 | |
| +++ linux-2.6.35.3.new/include/net/netfilter/nf_conntrack.h 2010-09-09 09:41:33.000000000 -0400 | |
| @@ -116,6 +116,22 @@ | |
| 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; | |
| diff -uNr linux-2.6.35.3.old/net/netfilter/Kconfig linux-2.6.35.3.new/net/netfilter/Kconfig | |
| --- linux-2.6.35.3.old/net/netfilter/Kconfig 2010-08-20 14:55:55.000000000 -0400 | |
| +++ linux-2.6.35.3.new/net/netfilter/Kconfig 2010-09-09 09:41:33.000000000 -0400 | |
| @@ -921,6 +921,27 @@ | |
| 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 | |
| diff -uNr linux-2.6.35.3.old/net/netfilter/Makefile linux-2.6.35.3.new/net/netfilter/Makefile | |
| --- linux-2.6.35.3.old/net/netfilter/Makefile 2010-08-20 14:55:55.000000000 -0400 | |
| +++ linux-2.6.35.3.new/net/netfilter/Makefile 2010-09-09 09:41:33.000000000 -0400 | |
| @@ -91,6 +91,7 @@ | |
| 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 | |
| diff -uNr linux-2.6.35.3.old/net/netfilter/nf_conntrack_core.c linux-2.6.35.3.new/net/netfilter/nf_conntrack_core.c | |
| --- linux-2.6.35.3.old/net/netfilter/nf_conntrack_core.c 2010-08-20 14:55:55.000000000 -0400 | |
| +++ linux-2.6.35.3.new/net/netfilter/nf_conntrack_core.c 2010-09-09 09:41:33.000000000 -0400 | |
| @@ -202,6 +202,14 @@ | |
| * 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)); | |
| diff -uNr linux-2.6.35.3.old/net/netfilter/nf_conntrack_standalone.c linux-2.6.35.3.new/net/netfilter/nf_conntrack_standalone.c | |
| --- linux-2.6.35.3.old/net/netfilter/nf_conntrack_standalone.c 2010-08-20 14:55:55.000000000 -0400 | |
| +++ linux-2.6.35.3.new/net/netfilter/nf_conntrack_standalone.c 2010-09-09 09:41:33.000000000 -0400 | |
| @@ -178,6 +178,12 @@ | |
| 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; | |
| diff -uNr linux-2.6.35.3.old/net/netfilter/regexp/regexp.c linux-2.6.35.3.new/net/netfilter/regexp/regexp.c | |
| --- linux-2.6.35.3.old/net/netfilter/regexp/regexp.c 1969-12-31 19:30:00.000000000 -0430 | |
| +++ linux-2.6.35.3.new/net/netfilter/regexp/regexp.c 2010-09-09 09:41:33.000000000 -0400 | |
| @@ -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 | |
| + | |
| + | |
| diff -uNr linux-2.6.35.3.old/net/netfilter/regexp/regexp.h linux-2.6.35.3.new/net/netfilter/regexp/regexp.h | |
| --- linux-2.6.35.3.old/net/netfilter/regexp/regexp.h 1969-12-31 19:30:00.000000000 -0430 | |
| +++ linux-2.6.35.3.new/net/netfilter/regexp/regexp.h 2010-09-09 09:41:33.000000000 -0400 | |
| @@ -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 | |
| diff -uNr linux-2.6.35.3.old/net/netfilter/regexp/regmagic.h linux-2.6.35.3.new/net/netfilter/regexp/regmagic.h | |
| --- linux-2.6.35.3.old/net/netfilter/regexp/regmagic.h 1969-12-31 19:30:00.000000000 -0430 | |
| +++ linux-2.6.35.3.new/net/netfilter/regexp/regmagic.h 2010-09-09 09:41:33.000000000 -0400 | |
| @@ -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 | |
| diff -uNr linux-2.6.35.3.old/net/netfilter/regexp/regsub.c linux-2.6.35.3.new/net/netfilter/regexp/regsub.c | |
| --- linux-2.6.35.3.old/net/netfilter/regexp/regsub.c 1969-12-31 19:30:00.000000000 -0430 | |
| +++ linux-2.6.35.3.new/net/netfilter/regexp/regsub.c 2010-09-09 09:41:33.000000000 -0400 | |
| @@ -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'; | |
| +} | |
| diff -uNr linux-2.6.35.3.old/net/netfilter/xt_layer7.c linux-2.6.35.3.new/net/netfilter/xt_layer7.c | |
| --- linux-2.6.35.3.old/net/netfilter/xt_layer7.c 1969-12-31 19:30:00.000000000 -0430 | |
| +++ linux-2.6.35.3.new/net/netfilter/xt_layer7.c 2010-09-09 09:42:30.000000000 -0400 | |
| @@ -0,0 +1,679 @@ | |
| +/* | |
| + 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.23"); | |
| + | |
| +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, 35) | |
| +match(const struct sk_buff *skbin, struct xt_action_param *par) | |
| +#elif 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, 35) | |
| +static int | |
| +#else | |
| +static bool | |
| +#endif | |
| +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28) | |
| +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 | |
| +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 | |
| +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35) | |
| + return -EINVAL; | |
| + } | |
| + return 0; | |
| +#else | |
| + return 0; | |
| + } | |
| + return 1; | |
| +#endif | |
| +} | |
| + | |
| + | |
| +#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); |