From: Arne Fitzenreiter Date: Mon, 6 Mar 2017 08:15:09 +0000 (+0100) Subject: netfilter: layer7: ct memory optimization X-Git-Url: http://git.ipfire.org/gitweb/gitweb.cgi?a=commitdiff_plain;h=b2277fb1e1d78881295f73e3f4ac8fcfe0ab0e4f;p=people%2Fms%2Flinux.git netfilter: layer7: ct memory optimization The original code reserves new memory for every ct entry for the protocol name. Now the strings are cached and all ct entries points to the same strings in the cace. This use less memory if many connections are there. Signed-off-by: Arne Fitzenreiter --- diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index 7cf2b36ca40c..e0a4be0abce5 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -602,8 +602,6 @@ void nf_ct_destroy(struct nf_conntrack *nfct) 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 diff --git a/net/netfilter/xt_layer7.c b/net/netfilter/xt_layer7.c index becbec0e8f9d..ddf7fecc05c1 100644 --- a/net/netfilter/xt_layer7.c +++ b/net/netfilter/xt_layer7.c @@ -37,10 +37,10 @@ #include "regexp/regexp.c" MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Matthew Strait , Ethan Sommer "); +MODULE_AUTHOR("Matthew Strait , Ethan Sommer , Arne Fitzenreiter "); MODULE_DESCRIPTION("iptables application layer match module"); MODULE_ALIAS("ipt_layer7"); -MODULE_VERSION("2.23"); +MODULE_VERSION("2.30"); static int maxdatalen = 2048; // this is the default module_param(maxdatalen, int, 0444); @@ -61,6 +61,11 @@ static struct pattern_cache { struct pattern_cache * next; } * first_pattern_cache = NULL; +static struct proto_cache { + char * proto_string; + struct proto_cache * next; +} * first_proto_cache = NULL; + DEFINE_SPINLOCK(l7_lock); static int total_acct_packets(struct nf_conn *ct) @@ -210,6 +215,55 @@ static regexp * compile_and_cache(const char * regex_string, return node->pattern; } +static char * get_protostr_ptr(const char * protocol) +{ + struct proto_cache * node = first_proto_cache; + struct proto_cache * last_proto_cache = first_proto_cache; + struct proto_cache * tmp; + + while (node != NULL) { + if (!strcmp(node->proto_string, protocol)) + return node->proto_string; + + last_proto_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 protocol + Be paranoid about running out of memory to avoid list corruption. */ + tmp = kmalloc(sizeof(struct proto_cache), GFP_ATOMIC); + + if(!tmp) { + if (net_ratelimit()) + printk(KERN_ERR "layer7: out of memory in " + "proto_cache add, bailing.\n"); + return NULL; + } + + tmp->proto_string = kmalloc(strlen(protocol) + 1 , GFP_ATOMIC); + tmp->next = NULL; + + if(!tmp->proto_string) { + if (net_ratelimit()) + printk(KERN_ERR "layer7: out of memory in " + "proto_cache add, bailing.\n"); + kfree(tmp->proto_string); + kfree(tmp); + return NULL; + } + + /* Ok. The new node is all ready now. */ + node = tmp; + + if(first_proto_cache == NULL) /* list is empty */ + first_proto_cache = node; /* make node the beginning */ + else + last_proto_cache->next = node; /* attach node to the end */ + + strcpy(node->proto_string, protocol); + return node->proto_string; +} + static int can_handle(const struct sk_buff *skb) { if(!ip_hdr(skb)) /* not IP */ @@ -280,18 +334,7 @@ static int match_no_append(struct nf_conn * conntrack, 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); + conntrack->layer7.app_proto = master_conntrack->layer7.app_proto; } return (!strcmp(master_conntrack->layer7.app_proto, @@ -300,15 +343,7 @@ static int match_no_append(struct nf_conn * conntrack, 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"); + master_conntrack->layer7.app_proto = get_protostr_ptr("unknown"); return 0; } } @@ -508,7 +543,6 @@ match(const struct sk_buff *skbin, 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 */ @@ -536,16 +570,7 @@ match(const struct sk_buff *skbin, } 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); + master_conntrack->layer7.app_proto=get_protostr_ptr(info->protocol); } else if(pattern_result > 1) { /* cleanup from "unset" */ pattern_result = 1; }