📄 100-netfilter_layer7_2.17.patch
字号:
Index: linux-2.6.21.7/net/netfilter/Kconfig===================================================================--- linux-2.6.21.7.orig/net/netfilter/Kconfig+++ linux-2.6.21.7/net/netfilter/Kconfig@@ -640,6 +640,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_XTABLESIndex: linux-2.6.21.7/net/netfilter/Makefile===================================================================--- linux-2.6.21.7.orig/net/netfilter/Makefile+++ linux-2.6.21.7/net/netfilter/Makefile@@ -68,6 +68,7 @@ obj-$(CONFIG_NETFILTER_XT_MATCH_QUOTA) + obj-$(CONFIG_NETFILTER_XT_MATCH_REALM) += xt_realm.o obj-$(CONFIG_NETFILTER_XT_MATCH_SCTP) += xt_sctp.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.oIndex: linux-2.6.21.7/net/netfilter/xt_layer7.c===================================================================--- /dev/null+++ linux-2.6.21.7/net/netfilter/xt_layer7.c@@ -0,0 +1,634 @@+/*+ Kernel module to match application layer (OSI layer 7) data in connections.++ http://l7-filter.sf.net++ (C) 2003, 2004, 2005, 2006, 2007 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>+#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.17");++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++#define TOTAL_PACKETS master_conntrack->counters[IP_CT_DIR_ORIGINAL].packets + \+ master_conntrack->counters[IP_CT_DIR_REPLY].packets++/* 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);++#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_PACKETS, 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 int+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,+ int *hotdrop)+{+ /* sidestep const without getting a compiler warning... */+ struct sk_buff * skb = (struct sk_buff *)skbin; ++ const struct xt_layer7_info * info = matchinfo;+ 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_PACKETS > 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_PACKETS == 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())
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -