]> git.ipfire.org Git - people/arne_f/kernel.git/commitdiff
netfilter: layer7: ct memory optimization
authorArne Fitzenreiter <arne_f@ipfire.org>
Mon, 6 Mar 2017 08:15:09 +0000 (09:15 +0100)
committerArne Fitzenreiter <arne_f@ipfire.org>
Mon, 13 Nov 2017 06:24:18 +0000 (07:24 +0100)
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 <arne_f@ipfire.org>
net/netfilter/nf_conntrack_core.c
net/netfilter/xt_layer7.c

index 3185748997c01443049556b801a4dde2c480b283..871eaa2c35e71240a4bbca15965c37547b709cd6 100644 (file)
@@ -428,8 +428,6 @@ destroy_conntrack(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
index becbec0e8f9d1ff29bb56292ed44725028272689..ddf7fecc05c1331c7146218dc25822cdfe827bb4 100644 (file)
 #include "regexp/regexp.c"
 
 MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Matthew Strait <quadong@users.sf.net>, Ethan Sommer <sommere@users.sf.net>");
+MODULE_AUTHOR("Matthew Strait <quadong@users.sf.net>, Ethan Sommer <sommere@users.sf.net>, Arne Fitzenreiter <arne_f@ipfire.org>");
 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;
        }