| /* |
| 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_datastr(char *target, int offset, char *app_data, int len) |
| { |
| int length = 0, i; |
| 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-offset-1 && i < len; i++) { |
| if(app_data[i] != '\0') { |
| /* the kernel version of tolower mungs 'upper ascii' */ |
| 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; |
| |
| 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; |
| } |
| |
| /* 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, *tmp_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(!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); |
| |
| /* 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); |
| |
| 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){ |
| 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); |