Hi all, Here is a new string match. New kernel code, and updated userspace code.
Changes: o no more dependance on max() macro! (so works for 2.4.9 now) o skip/shift tables are now stored in matchinfo, making for tidier code and removing the need to generate skip/shift tables all the time. o due to above change it should now be AT LEAST 100 times faster o memcmp() matching removed alltogether o fix iptables-save quotation bug I am now working on a patch to the userspace libraries to allow snort-style binary data to be in the string eg: --string "|ef ff ff ff|/bin/sh" I have been testing this code extensively both in userspace and inside netfilter for a while now so it should be stable as a table. -- // Gianni Tedesco <[EMAIL PROTECTED]> 8646BE7D: 870E A2C9 8F60 3A3C 91B5 7669 8646 BE7D
diff -urN netfilter.orig/userspace/extensions/libipt_string.c netfilter/userspace/extensions/libipt_string.c --- netfilter.orig/userspace/extensions/libipt_string.c Thu Mar 14 11:35:58 2002 +++ netfilter/userspace/extensions/libipt_string.c Wed May 29 11:58:17 2002 @@ -22,11 +22,9 @@ help(void) { printf( -"STRING match v%s options:\n" -"--string [!] string Match a string in a packet\n", -NETFILTER_VERSION); - - fputc('\n', stdout); + "STRING match v%s options:\n" + "--string [!] string Match a string in a packet\n\n", + NETFILTER_VERSION); } static struct option opts[] = { @@ -35,23 +33,70 @@ }; /* Initialize the match. */ -static void -init(struct ipt_entry_match *m, unsigned int *nfcache) +static void init(struct ipt_entry_match *m, unsigned int *nfcache) { *nfcache |= NFC_UNKNOWN; } -static void -parse_string(const unsigned char *s, struct ipt_string_info *info) +/* Content matcher (generic string matching) */ +void bm_skip(char *x, int m, int *skip) +{ + int i; + + for (i = 0; i < 256; ++i) + skip[i] = m+1; + + while(m) + skip[(unsigned char)*x++]=m--; +} + +void bm_shift(char *ptrn, int plen, int *shift) +{ + int *sptr=shift+plen-1; + char *pptr=ptrn+plen-1; + char c; + + c=ptrn[plen-1]; + *sptr=1; + + while(sptr-- != shift) { + char *p1=ptrn+plen-2, *p2, *p3; + + do { + while(p1>=ptrn && *p1-- !=c ); + + p2=ptrn+plen-2; + p3=p1; + + while(p3>=ptrn && + *p3-- == *p2-- && + p2 >= pptr); + } + + while(p3>=ptrn && + *p3-- == *p2-- && + p2 >= pptr); + + *sptr=shift+plen-sptr+p2-p3; + + pptr--; + } +} + +static void parse_string(const unsigned char *s, struct ipt_string_info *info) { - if (strlen(s) <= BM_MAX_NLEN) strcpy(info->string, s); - else exit_error(PARAMETER_PROBLEM, "STRING too long `%s'", s); + if ( (info->len=strlen(s)) >= BM_MAX_NLEN) + exit_error(PARAMETER_PROBLEM, "STRING too long `%s'", s); + + /* Copy the string and setup the skip/shift tables */ + strcpy(info->string, s); + bm_shift(info->string, info->len, info->shift); + bm_skip(info->string, info->len, info->skip); } /* Function which parses command options; returns true if it ate an option */ -static int -parse(int c, char **argv, int invert, unsigned int *flags, +static int parse(int c, char **argv, int invert, unsigned int *flags, const struct ipt_entry *entry, unsigned int *nfcache, struct ipt_entry_match **match) @@ -62,9 +107,7 @@ case '1': check_inverse(optarg, &invert, &optind, 0); parse_string(argv[optind-1], stringinfo); - if (invert) - stringinfo->invert = 1; - stringinfo->len=strlen((char *)&stringinfo->string); + stringinfo->invert = (invert==0) ? 0 : 1; *flags = 1; break; @@ -74,27 +117,20 @@ return 1; } -static void -print_string(char string[], int invert, int numeric) +static void print_string(char string[], int invert, int numeric) { - if (invert) - fputc('!', stdout); - printf("%s ",string); + if (invert) fputc('!', stdout); + printf("\"%s\" ",string); } -/* Final check; must have specified --string. */ -static void -final_check(unsigned int flags) +/* Final check */ +static void final_check(unsigned int flags) { - if (!flags) - exit_error(PARAMETER_PROBLEM, - "STRING match: You must specify `--string'"); } /* Prints out the matchinfo. */ -static void -print(const struct ipt_ip *ip, +static void print(const struct ipt_ip *ip, const struct ipt_entry_match *match, int numeric) { @@ -104,28 +140,26 @@ } /* Saves the union ipt_matchinfo in parsable form to stdout. */ -static void -save(const struct ipt_ip *ip, const struct ipt_entry_match *match) +static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match) { printf("--string "); print_string(((struct ipt_string_info *)match->data)->string, ((struct ipt_string_info *)match->data)->invert, 0); } -static -struct iptables_match string -= { NULL, - "string", - NETFILTER_VERSION, - IPT_ALIGN(sizeof(struct ipt_string_info)), - IPT_ALIGN(sizeof(struct ipt_string_info)), - &help, - &init, - &parse, - &final_check, - &print, - &save, - opts +static struct iptables_match string= { + NULL, + "string", + NETFILTER_VERSION, + IPT_ALIGN(sizeof(struct ipt_string_info)), + IPT_ALIGN(sizeof(struct ipt_string_info)), + &help, + &init, + &parse, + &final_check, + &print, + &save, + opts }; void _init(void) diff -urN netfilter.orig/userspace/patch-o-matic/extra/string.patch netfilter/userspace/patch-o-matic/extra/string.patch --- netfilter.orig/userspace/patch-o-matic/extra/string.patch Tue Mar 19 07:54:57 2002 +++ netfilter/userspace/patch-o-matic/extra/string.patch Wed May 29 11:57:54 2002 @@ -1,37 +1,17 @@ -diff -urN linux.orig/include/linux/netfilter_ipv4/ipt_string.h linux/include/linux/netfilter_ipv4/ipt_string.h ---- linux.orig/include/linux/netfilter_ipv4/ipt_string.h Thu Jan 1 01:00:00 1970 -+++ linux/include/linux/netfilter_ipv4/ipt_string.h Thu Feb 21 20:26:23 2002 -@@ -0,0 +1,21 @@ -+#ifndef _IPT_STRING_H -+#define _IPT_STRING_H -+ -+/* *** PERFORMANCE TWEAK *** -+ * Packet size and search string threshold, -+ * above which sublinear searches is used. */ -+#define IPT_STRING_HAYSTACK_THRESH 100 -+#define IPT_STRING_NEEDLE_THRESH 20 -+ -+#define BM_MAX_NLEN 256 -+#define BM_MAX_HLEN 1024 -+ -+typedef char *(*proc_ipt_search) (char *, char *, int, int); -+ -+struct ipt_string_info { -+ char string[BM_MAX_NLEN]; -+ u_int16_t invert; -+ u_int16_t len; -+}; -+ -+#endif /* _IPT_STRING_H */ diff -urN linux.orig/net/ipv4/netfilter/ipt_string.c linux/net/ipv4/netfilter/ipt_string.c ---- linux.orig/net/ipv4/netfilter/ipt_string.c Thu Jan 1 01:00:00 1970 -+++ linux/net/ipv4/netfilter/ipt_string.c Thu Feb 21 20:26:09 2002 -@@ -0,0 +1,214 @@ +--- linux.orig/net/ipv4/netfilter/ipt_string.c Wed May 29 11:42:54 2002 ++++ linux/net/ipv4/netfilter/ipt_string.c Wed May 29 11:42:54 2002 +@@ -0,0 +1,111 @@ +/* Kernel module to match a string into a packet. + * + * Copyright (C) 2000 Emmanuel Roger <[EMAIL PROTECTED]> ++ * Copyright (c) 2002 Gianni Tedesco <[EMAIL PROTECTED]> + * + * ChangeLog ++ * 29.05.2002: Gianni Tedesco <[EMAIL PROTECTED]> ++ * Totally re-wrote, much more efficient. Use boyer-moore ++ * in all cases now. No more SMP problems - data is now ++ * stored in the matchinfo. + * 19.02.2002: Gianni Tedesco <[EMAIL PROTECTED]> + * Fixed SMP re-entrancy problem using per-cpu data areas + * for the skip/shift tables. @@ -55,77 +35,6 @@ +#include <linux/netfilter_ipv4/ip_tables.h> +#include <linux/netfilter_ipv4/ipt_string.h> + -+struct string_per_cpu { -+ int *skip; -+ int *shift; -+ int *len; -+}; -+ -+struct string_per_cpu *bm_string_data=NULL; -+ -+/* Boyer Moore Sublinear string search - VERY FAST */ -+char *search_sublinear (char *needle, char *haystack, int needle_len, int haystack_len) -+{ -+ int M1, right_end, sk, sh; -+ int ended, j, i; -+ -+ int *skip, *shift, *len; -+ -+ /* use data suitable for this CPU */ -+ shift=bm_string_data[smp_processor_id()].shift; -+ skip=bm_string_data[smp_processor_id()].skip; -+ len=bm_string_data[smp_processor_id()].len; -+ -+ /* Setup skip/shift tables */ -+ M1 = right_end = needle_len-1; -+ for (i = 0; i < BM_MAX_HLEN; i++) skip[i] = needle_len; -+ for (i = 0; needle[i]; i++) skip[needle[i]] = M1 - i; -+ -+ for (i = 1; i < needle_len; i++) { -+ for (j = 0; j < needle_len && needle[M1 - j] == needle[M1 - i - j]; j++); -+ len[i] = j; -+ } -+ -+ shift[0] = 1; -+ for (i = 1; i < needle_len; i++) shift[i] = needle_len; -+ for (i = M1; i > 0; i--) shift[len[i]] = i; -+ ended = 0; -+ -+ for (i = 0; i < needle_len; i++) { -+ if (len[i] == M1 - i) ended = i; -+ if (ended) shift[i] = ended; -+ } -+ -+ /* Do the search*/ -+ while (right_end < haystack_len) -+ { -+ for (i = 0; i < needle_len && haystack[right_end - i] == needle[M1 - i]; i++); -+ if (i == needle_len) { -+ return haystack+(right_end - M1); -+ } -+ -+ sk = skip[haystack[right_end - i]]; -+ sh = shift[i]; -+ right_end = max(right_end - i + sk, right_end + sh); -+ } -+ -+ return NULL; -+} -+ -+/* Linear string search based on memcmp() */ -+char *search_linear (char *needle, char *haystack, int needle_len, int haystack_len) -+{ -+ char *k = haystack + (haystack_len-needle_len); -+ char *t = haystack; -+ -+ while ( t++ < k ) { -+ if ( memcmp(t, needle, needle_len) == 0 ) return t; -+ } -+ -+ return NULL; -+} -+ -+ +static int +match(const struct sk_buff *skb, + const struct net_device *in, @@ -138,35 +47,40 @@ +{ + const struct ipt_string_info *info = matchinfo; + struct iphdr *ip = skb->nh.iph; -+ int hlen, nlen; -+ char *needle, *haystack; -+ proc_ipt_search search=search_linear; ++ int hlen; ++ char *haystack; ++ int b_idx; + + if ( !ip ) return 0; + -+ /* get lenghts, and validate them */ -+ nlen=info->len; -+ hlen=ntohs(ip->tot_len)-(ip->ihl*4); -+ if ( nlen > hlen ) return 0; -+ -+ needle=(char *)&info->string; -+ haystack=(char *)ip+(ip->ihl*4); -+ -+ /* The sublinear search comes in to its own -+ * on the larger packets */ -+ if ( (hlen>IPT_STRING_HAYSTACK_THRESH) && -+ (nlen>IPT_STRING_NEEDLE_THRESH) ) { -+ if ( hlen < BM_MAX_HLEN ) { -+ search=search_sublinear; -+ }else{ -+ if (net_ratelimit()) -+ printk(KERN_INFO "ipt_string: Packet too big " -+ "to attempt sublinear string search " -+ "(%d bytes)\n", hlen ); ++ /* get lengths, and validate them */ ++ b_idx=info->len; ++ hlen=ntohs(ip->tot_len)-(ip->ihl<<2); ++ if ( b_idx > hlen ) return 0; ++ ++ /* We search everything below the IP header, hrm... ++ * not sure about what we should be searching exactly ++ */ ++ haystack=((char *)ip)+(ip->ihl<<2); ++ ++ while(b_idx<=hlen) { ++ int p_idx=info->len; ++ int skip_stride,shift_stride; ++ ++ while(haystack[--b_idx]==info->string[--p_idx]) { ++ if(b_idx<0) return info->invert^0; ++ if(p_idx==0) return info->invert^1; + } ++ ++ skip_stride=info->skip[(unsigned char)haystack[b_idx]]; ++ shift_stride=info->shift[p_idx]; ++ ++ /* max() avoidance */ ++ b_idx += (skip_stride>shift_stride) ? ++ skip_stride : shift_stride; + } -+ -+ return ((search(needle, haystack, nlen, hlen)!=NULL) ^ info->invert); ++ ++ return 0 ^ info->invert; +} + +static int @@ -183,61 +97,37 @@ + return 1; +} + -+void string_freeup_data(void) -+{ -+ int c; -+ -+ if ( bm_string_data ) { -+ for(c=0; c<smp_num_cpus; c++) { -+ if ( bm_string_data[c].shift ) kfree(bm_string_data[c].shift); -+ if ( bm_string_data[c].skip ) kfree(bm_string_data[c].skip); -+ if ( bm_string_data[c].len ) kfree(bm_string_data[c].len); -+ } -+ kfree(bm_string_data); -+ } -+} -+ +static struct ipt_match string_match += { { NULL, NULL }, "string", &match, &checkentry, NULL, THIS_MODULE }; + +static int __init init(void) +{ -+ int c; -+ size_t tlen; -+ size_t alen; -+ -+ tlen=sizeof(struct string_per_cpu)*smp_num_cpus; -+ alen=sizeof(int)*BM_MAX_HLEN; -+ -+ /* allocate array of structures */ -+ if ( !(bm_string_data=kmalloc(tlen,GFP_KERNEL)) ) { -+ return 0; -+ } -+ -+ memset(bm_string_data, 0, tlen); -+ -+ /* allocate our skip/shift tables */ -+ for(c=0; c<smp_num_cpus; c++) { -+ if ( !(bm_string_data[c].shift=kmalloc(alen, GFP_KERNEL)) ) -+ goto alloc_fail; -+ if ( !(bm_string_data[c].skip=kmalloc(alen, GFP_KERNEL)) ) -+ goto alloc_fail; -+ if ( !(bm_string_data[c].len=kmalloc(alen, GFP_KERNEL)) ) -+ goto alloc_fail; -+ } -+ + return ipt_register_match(&string_match); -+ -+alloc_fail: -+ string_freeup_data(); -+ return 0; +} + +static void __exit fini(void) +{ + ipt_unregister_match(&string_match); -+ string_freeup_data(); +} + ++MODULE_LICENSE("GPL"); +module_init(init); +module_exit(fini); +diff -urN linux.orig/include/linux/netfilter_string.h linux/include/linux/netfilter_ipv4/ipt_string.h +--- linux.orig/include/linux/netfilter_ipv4/ipt_string.h Wed May 29 11:25:42 2002 ++++ linux/include/linux/netfilter_ipv4/ipt_string.h Wed May 29 11:25:42 2002 +@@ -0,0 +1,14 @@ ++#ifndef _IPT_STRING_H ++#define _IPT_STRING_H ++ ++#define BM_MAX_NLEN 256 ++ ++struct ipt_string_info { ++ u_int16_t invert; ++ u_int16_t len; ++ char string[BM_MAX_NLEN]; ++ int shift[BM_MAX_NLEN]; ++ int skip[256]; ++}; ++ ++#endif /* _IPT_STRING_H */ diff -urN netfilter.orig/userspace/patch-o-matic/extra/string.patch.help netfilter/userspace/patch-o-matic/extra/string.patch.help --- netfilter.orig/userspace/patch-o-matic/extra/string.patch.help Sun Oct 21 14:45:41 2001 +++ netfilter/userspace/patch-o-matic/extra/string.patch.help Wed May 29 12:04:23 2002 @@ -1,8 +1,5 @@ Author: Emmanuel Roger <[EMAIL PROTECTED]> -Status: Working, not with kernel 2.4.9 +Status: Working This patch adds CONFIG_IP_NF_MATCH_STRING which allows you to match a string in a whole packet. - -THIS PATCH DOES NOT WORK WITH KERNEL 2.4.9 !!! -
signature.asc
Description: This is a digitally signed message part