]>
Commit | Line | Data |
---|---|---|
9697c820 AF |
1 | /* |
2 | Kernel module to match application layer (OSI layer 7) data in connections. | |
3 | ||
4 | http://l7-filter.sf.net | |
5 | ||
6 | (C) 2003-2009 Matthew Strait and Ethan Sommer. | |
7 | ||
8 | This program is free software; you can redistribute it and/or | |
9 | modify it under the terms of the GNU General Public License | |
10 | as published by the Free Software Foundation; either version | |
11 | 2 of the License, or (at your option) any later version. | |
12 | http://www.gnu.org/licenses/gpl.txt | |
13 | ||
14 | Based on ipt_string.c (C) 2000 Emmanuel Roger <winfield@freegates.be>, | |
15 | xt_helper.c (C) 2002 Harald Welte and cls_layer7.c (C) 2003 Matthew Strait, | |
16 | Ethan Sommer, Justin Levandoski. | |
17 | */ | |
18 | ||
19 | #include <linux/spinlock.h> | |
20 | #include <linux/version.h> | |
21 | #include <net/ip.h> | |
22 | #include <net/tcp.h> | |
23 | #include <linux/module.h> | |
24 | #include <linux/skbuff.h> | |
25 | #include <linux/netfilter.h> | |
26 | #include <net/netfilter/nf_conntrack.h> | |
27 | #include <net/netfilter/nf_conntrack_core.h> | |
28 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) | |
29 | #include <net/netfilter/nf_conntrack_extend.h> | |
30 | #include <net/netfilter/nf_conntrack_acct.h> | |
31 | #endif | |
32 | #include <linux/netfilter/x_tables.h> | |
33 | #include <linux/netfilter/xt_layer7.h> | |
34 | #include <linux/ctype.h> | |
35 | #include <linux/proc_fs.h> | |
36 | ||
37 | #include "regexp/regexp.c" | |
38 | ||
39 | MODULE_LICENSE("GPL"); | |
e6cddf5d | 40 | MODULE_AUTHOR("Matthew Strait <quadong@users.sf.net>, Ethan Sommer <sommere@users.sf.net>, Arne Fitzenreiter <arne_f@ipfire.org>"); |
9697c820 AF |
41 | MODULE_DESCRIPTION("iptables application layer match module"); |
42 | MODULE_ALIAS("ipt_layer7"); | |
e6cddf5d | 43 | MODULE_VERSION("2.30"); |
9697c820 AF |
44 | |
45 | static int maxdatalen = 2048; // this is the default | |
46 | module_param(maxdatalen, int, 0444); | |
47 | MODULE_PARM_DESC(maxdatalen, "maximum bytes of data looked at by l7-filter"); | |
48 | #ifdef CONFIG_NETFILTER_XT_MATCH_LAYER7_DEBUG | |
49 | #define DPRINTK(format,args...) printk(format,##args) | |
50 | #else | |
51 | #define DPRINTK(format,args...) | |
52 | #endif | |
53 | ||
54 | /* Number of packets whose data we look at. | |
55 | This can be modified through /proc/net/layer7_numpackets */ | |
56 | static int num_packets = 10; | |
57 | ||
58 | static struct pattern_cache { | |
59 | char * regex_string; | |
60 | regexp * pattern; | |
61 | struct pattern_cache * next; | |
62 | } * first_pattern_cache = NULL; | |
63 | ||
e6cddf5d AF |
64 | static struct proto_cache { |
65 | char * proto_string; | |
66 | struct proto_cache * next; | |
67 | } * first_proto_cache = NULL; | |
68 | ||
9697c820 AF |
69 | DEFINE_SPINLOCK(l7_lock); |
70 | ||
71 | static int total_acct_packets(struct nf_conn *ct) | |
72 | { | |
73 | #if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 26) | |
74 | BUG_ON(ct == NULL); | |
75 | return (ct->counters[IP_CT_DIR_ORIGINAL].packets + ct->counters[IP_CT_DIR_REPLY].packets); | |
76 | #else | |
77 | struct nf_conn_counter *acct; | |
78 | ||
79 | BUG_ON(ct == NULL); | |
80 | acct = nf_conn_acct_find(ct); | |
81 | if (!acct) | |
82 | return 0; | |
83 | return (atomic64_read(&acct[IP_CT_DIR_ORIGINAL].packets) + atomic64_read(&acct[IP_CT_DIR_REPLY].packets)); | |
84 | #endif | |
85 | } | |
86 | ||
87 | #ifdef CONFIG_IP_NF_MATCH_LAYER7_DEBUG | |
88 | /* Converts an unfriendly string into a friendly one by | |
89 | replacing unprintables with periods and all whitespace with " ". */ | |
90 | static char * friendly_print(unsigned char * s) | |
91 | { | |
92 | char * f = kmalloc(strlen(s) + 1, GFP_ATOMIC); | |
93 | int i; | |
94 | ||
95 | if(!f) { | |
96 | if (net_ratelimit()) | |
97 | printk(KERN_ERR "layer7: out of memory in " | |
98 | "friendly_print, bailing.\n"); | |
99 | return NULL; | |
100 | } | |
101 | ||
102 | for(i = 0; i < strlen(s); i++){ | |
103 | if(isprint(s[i]) && s[i] < 128) f[i] = s[i]; | |
104 | else if(isspace(s[i])) f[i] = ' '; | |
105 | else f[i] = '.'; | |
106 | } | |
107 | f[i] = '\0'; | |
108 | return f; | |
109 | } | |
110 | ||
111 | static char dec2hex(int i) | |
112 | { | |
113 | switch (i) { | |
114 | case 0 ... 9: | |
115 | return (i + '0'); | |
116 | break; | |
117 | case 10 ... 15: | |
118 | return (i - 10 + 'a'); | |
119 | break; | |
120 | default: | |
121 | if (net_ratelimit()) | |
122 | printk("layer7: Problem in dec2hex\n"); | |
123 | return '\0'; | |
124 | } | |
125 | } | |
126 | ||
127 | static char * hex_print(unsigned char * s) | |
128 | { | |
129 | char * g = kmalloc(strlen(s)*3 + 1, GFP_ATOMIC); | |
130 | int i; | |
131 | ||
132 | if(!g) { | |
133 | if (net_ratelimit()) | |
134 | printk(KERN_ERR "layer7: out of memory in hex_print, " | |
135 | "bailing.\n"); | |
136 | return NULL; | |
137 | } | |
138 | ||
139 | for(i = 0; i < strlen(s); i++) { | |
140 | g[i*3 ] = dec2hex(s[i]/16); | |
141 | g[i*3 + 1] = dec2hex(s[i]%16); | |
142 | g[i*3 + 2] = ' '; | |
143 | } | |
144 | g[i*3] = '\0'; | |
145 | ||
146 | return g; | |
147 | } | |
148 | #endif // DEBUG | |
149 | ||
150 | /* Use instead of regcomp. As we expect to be seeing the same regexps over and | |
151 | over again, it make sense to cache the results. */ | |
152 | static regexp * compile_and_cache(const char * regex_string, | |
153 | const char * protocol) | |
154 | { | |
155 | struct pattern_cache * node = first_pattern_cache; | |
156 | struct pattern_cache * last_pattern_cache = first_pattern_cache; | |
157 | struct pattern_cache * tmp; | |
158 | unsigned int len; | |
159 | ||
160 | while (node != NULL) { | |
161 | if (!strcmp(node->regex_string, regex_string)) | |
162 | return node->pattern; | |
163 | ||
164 | last_pattern_cache = node;/* points at the last non-NULL node */ | |
165 | node = node->next; | |
166 | } | |
167 | ||
168 | /* If we reach the end of the list, then we have not yet cached | |
169 | the pattern for this regex. Let's do that now. | |
170 | Be paranoid about running out of memory to avoid list corruption. */ | |
171 | tmp = kmalloc(sizeof(struct pattern_cache), GFP_ATOMIC); | |
172 | ||
173 | if(!tmp) { | |
174 | if (net_ratelimit()) | |
175 | printk(KERN_ERR "layer7: out of memory in " | |
176 | "compile_and_cache, bailing.\n"); | |
177 | return NULL; | |
178 | } | |
179 | ||
180 | tmp->regex_string = kmalloc(strlen(regex_string) + 1, GFP_ATOMIC); | |
181 | tmp->pattern = kmalloc(sizeof(struct regexp), GFP_ATOMIC); | |
182 | tmp->next = NULL; | |
183 | ||
184 | if(!tmp->regex_string || !tmp->pattern) { | |
185 | if (net_ratelimit()) | |
186 | printk(KERN_ERR "layer7: out of memory in " | |
187 | "compile_and_cache, bailing.\n"); | |
188 | kfree(tmp->regex_string); | |
189 | kfree(tmp->pattern); | |
190 | kfree(tmp); | |
191 | return NULL; | |
192 | } | |
193 | ||
194 | /* Ok. The new node is all ready now. */ | |
195 | node = tmp; | |
196 | ||
197 | if(first_pattern_cache == NULL) /* list is empty */ | |
198 | first_pattern_cache = node; /* make node the beginning */ | |
199 | else | |
200 | last_pattern_cache->next = node; /* attach node to the end */ | |
201 | ||
202 | /* copy the string and compile the regex */ | |
203 | len = strlen(regex_string); | |
204 | DPRINTK("About to compile this: \"%s\"\n", regex_string); | |
205 | node->pattern = regcomp((char *)regex_string, &len); | |
206 | if ( !node->pattern ) { | |
207 | if (net_ratelimit()) | |
208 | printk(KERN_ERR "layer7: Error compiling regexp " | |
209 | "\"%s\" (%s)\n", | |
210 | regex_string, protocol); | |
211 | /* pattern is now cached as NULL, so we won't try again. */ | |
212 | } | |
213 | ||
214 | strcpy(node->regex_string, regex_string); | |
215 | return node->pattern; | |
216 | } | |
217 | ||
e6cddf5d AF |
218 | static char * get_protostr_ptr(const char * protocol) |
219 | { | |
220 | struct proto_cache * node = first_proto_cache; | |
221 | struct proto_cache * last_proto_cache = first_proto_cache; | |
222 | struct proto_cache * tmp; | |
223 | ||
224 | while (node != NULL) { | |
225 | if (!strcmp(node->proto_string, protocol)) | |
226 | return node->proto_string; | |
227 | ||
228 | last_proto_cache = node;/* points at the last non-NULL node */ | |
229 | node = node->next; | |
230 | } | |
231 | ||
232 | /* If we reach the end of the list, then we have not yet cached protocol | |
233 | Be paranoid about running out of memory to avoid list corruption. */ | |
234 | tmp = kmalloc(sizeof(struct proto_cache), GFP_ATOMIC); | |
235 | ||
236 | if(!tmp) { | |
237 | if (net_ratelimit()) | |
238 | printk(KERN_ERR "layer7: out of memory in " | |
239 | "proto_cache add, bailing.\n"); | |
240 | return NULL; | |
241 | } | |
242 | ||
243 | tmp->proto_string = kmalloc(strlen(protocol) + 1 , GFP_ATOMIC); | |
244 | tmp->next = NULL; | |
245 | ||
246 | if(!tmp->proto_string) { | |
247 | if (net_ratelimit()) | |
248 | printk(KERN_ERR "layer7: out of memory in " | |
249 | "proto_cache add, bailing.\n"); | |
250 | kfree(tmp->proto_string); | |
251 | kfree(tmp); | |
252 | return NULL; | |
253 | } | |
254 | ||
255 | /* Ok. The new node is all ready now. */ | |
256 | node = tmp; | |
257 | ||
258 | if(first_proto_cache == NULL) /* list is empty */ | |
259 | first_proto_cache = node; /* make node the beginning */ | |
260 | else | |
261 | last_proto_cache->next = node; /* attach node to the end */ | |
262 | ||
263 | strcpy(node->proto_string, protocol); | |
264 | return node->proto_string; | |
265 | } | |
266 | ||
9697c820 AF |
267 | static int can_handle(const struct sk_buff *skb) |
268 | { | |
269 | if(!ip_hdr(skb)) /* not IP */ | |
270 | return 0; | |
271 | if(ip_hdr(skb)->protocol != IPPROTO_TCP && | |
272 | ip_hdr(skb)->protocol != IPPROTO_UDP && | |
273 | ip_hdr(skb)->protocol != IPPROTO_ICMP) | |
274 | return 0; | |
275 | return 1; | |
276 | } | |
277 | ||
278 | /* Returns offset the into the skb->data that the application data starts */ | |
279 | static int app_data_offset(const struct sk_buff *skb) | |
280 | { | |
281 | /* In case we are ported somewhere (ebtables?) where ip_hdr(skb) | |
282 | isn't set, this can be gotten from 4*(skb->data[0] & 0x0f) as well. */ | |
283 | int ip_hl = 4*ip_hdr(skb)->ihl; | |
284 | ||
285 | if( ip_hdr(skb)->protocol == IPPROTO_TCP ) { | |
286 | /* 12 == offset into TCP header for the header length field. | |
287 | Can't get this with skb->h.th->doff because the tcphdr | |
288 | struct doesn't get set when routing (this is confirmed to be | |
289 | true in Netfilter as well as QoS.) */ | |
290 | int tcp_hl = 4*(skb->data[ip_hl + 12] >> 4); | |
291 | ||
292 | return ip_hl + tcp_hl; | |
293 | } else if( ip_hdr(skb)->protocol == IPPROTO_UDP ) { | |
294 | return ip_hl + 8; /* UDP header is always 8 bytes */ | |
295 | } else if( ip_hdr(skb)->protocol == IPPROTO_ICMP ) { | |
296 | return ip_hl + 8; /* ICMP header is 8 bytes */ | |
297 | } else { | |
298 | if (net_ratelimit()) | |
299 | printk(KERN_ERR "layer7: tried to handle unknown " | |
300 | "protocol!\n"); | |
301 | return ip_hl + 8; /* something reasonable */ | |
302 | } | |
303 | } | |
304 | ||
305 | /* handles whether there's a match when we aren't appending data anymore */ | |
306 | static int match_no_append(struct nf_conn * conntrack, | |
307 | struct nf_conn * master_conntrack, | |
308 | enum ip_conntrack_info ctinfo, | |
309 | enum ip_conntrack_info master_ctinfo, | |
310 | const struct xt_layer7_info * info) | |
311 | { | |
312 | /* If we're in here, throw the app data away */ | |
313 | if(master_conntrack->layer7.app_data != NULL) { | |
314 | ||
315 | #ifdef CONFIG_IP_NF_MATCH_LAYER7_DEBUG | |
316 | if(!master_conntrack->layer7.app_proto) { | |
317 | char * f = | |
318 | friendly_print(master_conntrack->layer7.app_data); | |
319 | char * g = | |
320 | hex_print(master_conntrack->layer7.app_data); | |
321 | DPRINTK("\nl7-filter gave up after %d bytes " | |
322 | "(%d packets):\n%s\n", | |
323 | strlen(f), total_acct_packets(master_conntrack), f); | |
324 | kfree(f); | |
325 | DPRINTK("In hex: %s\n", g); | |
326 | kfree(g); | |
327 | } | |
328 | #endif | |
329 | ||
330 | kfree(master_conntrack->layer7.app_data); | |
331 | master_conntrack->layer7.app_data = NULL; /* don't free again */ | |
332 | } | |
333 | ||
334 | if(master_conntrack->layer7.app_proto){ | |
335 | /* Here child connections set their .app_proto (for /proc) */ | |
336 | if(!conntrack->layer7.app_proto) { | |
e6cddf5d | 337 | conntrack->layer7.app_proto = master_conntrack->layer7.app_proto; |
9697c820 AF |
338 | } |
339 | ||
340 | return (!strcmp(master_conntrack->layer7.app_proto, | |
341 | info->protocol)); | |
342 | } | |
343 | else { | |
344 | /* If not classified, set to "unknown" to distinguish from | |
345 | connections that are still being tested. */ | |
e6cddf5d | 346 | master_conntrack->layer7.app_proto = get_protostr_ptr("unknown"); |
9697c820 AF |
347 | return 0; |
348 | } | |
349 | } | |
350 | ||
351 | /* add the new app data to the conntrack. Return number of bytes added. */ | |
352 | static int add_data(struct nf_conn * master_conntrack, | |
353 | char * app_data, int appdatalen) | |
354 | { | |
355 | int length = 0, i; | |
356 | int oldlength = master_conntrack->layer7.app_data_len; | |
357 | ||
358 | /* This is a fix for a race condition by Deti Fliegl. However, I'm not | |
359 | clear on whether the race condition exists or whether this really | |
360 | fixes it. I might just be being dense... Anyway, if it's not really | |
361 | a fix, all it does is waste a very small amount of time. */ | |
362 | if(!master_conntrack->layer7.app_data) return 0; | |
363 | ||
364 | /* Strip nulls. Make everything lower case (our regex lib doesn't | |
365 | do case insensitivity). Add it to the end of the current data. */ | |
366 | for(i = 0; i < maxdatalen-oldlength-1 && | |
367 | i < appdatalen; i++) { | |
368 | if(app_data[i] != '\0') { | |
369 | /* the kernel version of tolower mungs 'upper ascii' */ | |
370 | master_conntrack->layer7.app_data[length+oldlength] = | |
371 | isascii(app_data[i])? | |
372 | tolower(app_data[i]) : app_data[i]; | |
373 | length++; | |
374 | } | |
375 | } | |
376 | ||
377 | master_conntrack->layer7.app_data[length+oldlength] = '\0'; | |
378 | master_conntrack->layer7.app_data_len = length + oldlength; | |
379 | ||
380 | return length; | |
381 | } | |
382 | ||
383 | /* taken from drivers/video/modedb.c */ | |
384 | static int my_atoi(const char *s) | |
385 | { | |
386 | int val = 0; | |
387 | ||
388 | for (;; s++) { | |
389 | switch (*s) { | |
390 | case '0'...'9': | |
391 | val = 10*val+(*s-'0'); | |
392 | break; | |
393 | default: | |
394 | return val; | |
395 | } | |
396 | } | |
397 | } | |
398 | ||
399 | static int layer7_numpackets_proc_show(struct seq_file *s, void *p) { | |
400 | seq_printf(s, "%d\n", num_packets); | |
401 | ||
402 | return 0; | |
403 | } | |
404 | ||
405 | static int layer7_numpackets_proc_open(struct inode *inode, struct file *file) { | |
406 | return single_open(file, layer7_numpackets_proc_show, NULL); | |
407 | } | |
408 | ||
409 | /* Read in num_packets from userland */ | |
410 | static ssize_t layer7_numpackets_write_proc(struct file* file, const char __user *buffer, | |
411 | size_t count, loff_t *data) { | |
412 | char value[1024]; | |
413 | int new_num_packets; | |
414 | ||
415 | if (copy_from_user(&value, buffer, sizeof(value))) | |
416 | return -EFAULT; | |
417 | ||
418 | new_num_packets = my_atoi(value); | |
419 | ||
420 | if ((new_num_packets < 1) || (new_num_packets > 99)) { | |
421 | printk(KERN_WARNING "layer7: numpackets must be between 1 and 99\n"); | |
422 | return -EFAULT; | |
423 | } | |
424 | ||
425 | num_packets = new_num_packets; | |
426 | ||
427 | return count; | |
428 | } | |
429 | ||
430 | static bool | |
431 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35) | |
432 | match(const struct sk_buff *skbin, struct xt_action_param *par) | |
433 | #elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28) | |
434 | match(const struct sk_buff *skbin, const struct xt_match_param *par) | |
435 | #else | |
436 | match(const struct sk_buff *skbin, | |
437 | const struct net_device *in, | |
438 | const struct net_device *out, | |
439 | const struct xt_match *match, | |
440 | const void *matchinfo, | |
441 | int offset, | |
442 | unsigned int protoff, | |
443 | bool *hotdrop) | |
444 | #endif | |
445 | { | |
446 | /* sidestep const without getting a compiler warning... */ | |
447 | struct sk_buff * skb = (struct sk_buff *)skbin; | |
448 | ||
449 | const struct xt_layer7_info * info = | |
450 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28) | |
451 | par->matchinfo; | |
452 | #else | |
453 | matchinfo; | |
454 | #endif | |
455 | ||
456 | enum ip_conntrack_info master_ctinfo, ctinfo; | |
457 | struct nf_conn *master_conntrack, *conntrack; | |
458 | unsigned char * app_data; | |
459 | unsigned int pattern_result, appdatalen; | |
460 | regexp * comppattern; | |
461 | ||
462 | /* Be paranoid/incompetent - lock the entire match function. */ | |
463 | spin_lock_bh(&l7_lock); | |
464 | ||
465 | if(!can_handle(skb)){ | |
466 | DPRINTK("layer7: This is some protocol I can't handle.\n"); | |
467 | spin_unlock_bh(&l7_lock); | |
468 | return info->invert; | |
469 | } | |
470 | ||
471 | /* Treat parent & all its children together as one connection, except | |
472 | for the purpose of setting conntrack->layer7.app_proto in the actual | |
473 | connection. This makes /proc/net/ip_conntrack more satisfying. */ | |
474 | if(!(conntrack = nf_ct_get(skb, &ctinfo)) || | |
475 | !(master_conntrack=nf_ct_get(skb,&master_ctinfo))){ | |
476 | DPRINTK("layer7: couldn't get conntrack.\n"); | |
477 | spin_unlock_bh(&l7_lock); | |
478 | return info->invert; | |
479 | } | |
480 | ||
481 | /* Try to get a master conntrack (and its master etc) for FTP, etc. */ | |
482 | while (master_ct(master_conntrack) != NULL) | |
483 | master_conntrack = master_ct(master_conntrack); | |
484 | ||
485 | /* if we've classified it or seen too many packets */ | |
486 | if(total_acct_packets(master_conntrack) > num_packets || | |
487 | master_conntrack->layer7.app_proto) { | |
488 | ||
489 | pattern_result = match_no_append(conntrack, master_conntrack, | |
490 | ctinfo, master_ctinfo, info); | |
491 | ||
492 | /* skb->cb[0] == seen. Don't do things twice if there are | |
493 | multiple l7 rules. I'm not sure that using cb for this purpose | |
494 | is correct, even though it says "put your private variables | |
495 | there". But it doesn't look like it is being used for anything | |
496 | else in the skbs that make it here. */ | |
497 | skb->cb[0] = 1; /* marking it seen here's probably irrelevant */ | |
498 | ||
499 | spin_unlock_bh(&l7_lock); | |
500 | return (pattern_result ^ info->invert); | |
501 | } | |
502 | ||
503 | if(skb_is_nonlinear(skb)){ | |
504 | if(skb_linearize(skb) != 0){ | |
505 | if (net_ratelimit()) | |
506 | printk(KERN_ERR "layer7: failed to linearize " | |
507 | "packet, bailing.\n"); | |
508 | spin_unlock_bh(&l7_lock); | |
509 | return info->invert; | |
510 | } | |
511 | } | |
512 | ||
513 | /* now that the skb is linearized, it's safe to set these. */ | |
514 | app_data = skb->data + app_data_offset(skb); | |
515 | appdatalen = skb_tail_pointer(skb) - app_data; | |
516 | ||
517 | /* the return value gets checked later, when we're ready to use it */ | |
518 | comppattern = compile_and_cache(info->pattern, info->protocol); | |
519 | ||
520 | /* On the first packet of a connection, allocate space for app data */ | |
521 | if(total_acct_packets(master_conntrack) == 1 && !skb->cb[0] && | |
522 | !master_conntrack->layer7.app_data){ | |
523 | master_conntrack->layer7.app_data = | |
524 | kmalloc(maxdatalen, GFP_ATOMIC); | |
525 | if(!master_conntrack->layer7.app_data){ | |
526 | if (net_ratelimit()) | |
527 | printk(KERN_ERR "layer7: out of memory in " | |
528 | "match, bailing.\n"); | |
529 | spin_unlock_bh(&l7_lock); | |
530 | return info->invert; | |
531 | } | |
532 | ||
533 | master_conntrack->layer7.app_data[0] = '\0'; | |
534 | } | |
535 | ||
536 | /* Can be here, but unallocated, if numpackets is increased near | |
537 | the beginning of a connection */ | |
538 | if(master_conntrack->layer7.app_data == NULL){ | |
539 | spin_unlock_bh(&l7_lock); | |
540 | return info->invert; /* unmatched */ | |
541 | } | |
542 | ||
543 | if(!skb->cb[0]){ | |
544 | int newbytes; | |
545 | newbytes = add_data(master_conntrack, app_data, appdatalen); | |
9697c820 AF |
546 | if(newbytes == 0) { /* didn't add any data */ |
547 | skb->cb[0] = 1; | |
548 | /* Didn't match before, not going to match now */ | |
549 | spin_unlock_bh(&l7_lock); | |
550 | return info->invert; | |
551 | } | |
552 | } | |
553 | ||
554 | /* If looking for "unknown", then never match. "Unknown" means that | |
555 | we've given up; we're still trying with these packets. */ | |
556 | if(!strcmp(info->protocol, "unknown")) { | |
557 | pattern_result = 0; | |
558 | /* If looking for "unset", then always match. "Unset" means that we | |
559 | haven't yet classified the connection. */ | |
560 | } else if(!strcmp(info->protocol, "unset")) { | |
561 | pattern_result = 2; | |
562 | DPRINTK("layer7: matched unset: not yet classified " | |
563 | "(%d/%d packets)\n", | |
564 | total_acct_packets(master_conntrack), num_packets); | |
565 | /* If the regexp failed to compile, don't bother running it */ | |
566 | } else if(comppattern && | |
567 | regexec(comppattern, master_conntrack->layer7.app_data)){ | |
568 | DPRINTK("layer7: matched %s\n", info->protocol); | |
569 | pattern_result = 1; | |
570 | } else pattern_result = 0; | |
571 | ||
572 | if(pattern_result == 1) { | |
e6cddf5d | 573 | master_conntrack->layer7.app_proto=get_protostr_ptr(info->protocol); |
9697c820 AF |
574 | } else if(pattern_result > 1) { /* cleanup from "unset" */ |
575 | pattern_result = 1; | |
576 | } | |
577 | ||
578 | /* mark the packet seen */ | |
579 | skb->cb[0] = 1; | |
580 | ||
581 | spin_unlock_bh(&l7_lock); | |
582 | return (pattern_result ^ info->invert); | |
583 | } | |
584 | ||
585 | // load nf_conntrack_ipv4 | |
586 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35) | |
587 | static int | |
588 | #else | |
589 | static bool | |
590 | #endif | |
591 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28) | |
592 | check(const struct xt_mtchk_param *par) | |
593 | { | |
594 | if (nf_ct_l3proto_try_module_get(par->match->family) < 0) { | |
595 | printk(KERN_WARNING "can't load conntrack support for " | |
596 | "proto=%d\n", par->match->family); | |
597 | #else | |
598 | check(const char *tablename, const void *inf, | |
599 | const struct xt_match *match, void *matchinfo, | |
600 | unsigned int hook_mask) | |
601 | { | |
602 | if (nf_ct_l3proto_try_module_get(match->family) < 0) { | |
603 | printk(KERN_WARNING "can't load conntrack support for " | |
604 | "proto=%d\n", match->family); | |
605 | #endif | |
606 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35) | |
607 | return -EINVAL; | |
608 | } | |
609 | return 0; | |
610 | #else | |
611 | return 0; | |
612 | } | |
613 | return 1; | |
614 | #endif | |
615 | } | |
616 | ||
617 | ||
618 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28) | |
619 | static void destroy(const struct xt_mtdtor_param *par) | |
620 | { | |
621 | nf_ct_l3proto_module_put(par->match->family); | |
622 | } | |
623 | #else | |
624 | static void destroy(const struct xt_match *match, void *matchinfo) | |
625 | { | |
626 | nf_ct_l3proto_module_put(match->family); | |
627 | } | |
628 | #endif | |
629 | ||
630 | static struct xt_match xt_layer7_match[] __read_mostly = { | |
631 | { | |
632 | .name = "layer7", | |
c59158ee | 633 | .family = NFPROTO_IPV4, |
9697c820 AF |
634 | .checkentry = check, |
635 | .match = match, | |
636 | .destroy = destroy, | |
637 | .matchsize = sizeof(struct xt_layer7_info), | |
638 | .me = THIS_MODULE | |
639 | } | |
640 | }; | |
641 | ||
642 | static const struct file_operations layer7_numpackets_proc_fops = { | |
643 | .owner = THIS_MODULE, | |
644 | .open = layer7_numpackets_proc_open, | |
645 | .read = seq_read, | |
646 | .llseek = seq_lseek, | |
647 | .release = single_release, | |
648 | .write = layer7_numpackets_write_proc, | |
649 | }; | |
650 | ||
651 | static int __init xt_layer7_init(void) | |
652 | { | |
653 | need_conntrack(); | |
654 | ||
655 | // Register proc interface | |
656 | proc_create_data("layer7_numpackets", 0644, | |
657 | init_net.proc_net, &layer7_numpackets_proc_fops, NULL); | |
658 | ||
659 | if(maxdatalen < 1) { | |
660 | printk(KERN_WARNING "layer7: maxdatalen can't be < 1, " | |
661 | "using 1\n"); | |
662 | maxdatalen = 1; | |
663 | } | |
664 | /* This is not a hard limit. It's just here to prevent people from | |
665 | bringing their slow machines to a grinding halt. */ | |
666 | else if(maxdatalen > 65536) { | |
667 | printk(KERN_WARNING "layer7: maxdatalen can't be > 65536, " | |
668 | "using 65536\n"); | |
669 | maxdatalen = 65536; | |
670 | } | |
671 | return xt_register_matches(xt_layer7_match, | |
672 | ARRAY_SIZE(xt_layer7_match)); | |
673 | } | |
674 | ||
675 | static void __exit xt_layer7_fini(void) | |
676 | { | |
677 | remove_proc_entry("layer7_numpackets", init_net.proc_net); | |
678 | xt_unregister_matches(xt_layer7_match, ARRAY_SIZE(xt_layer7_match)); | |
679 | } | |
680 | ||
681 | module_init(xt_layer7_init); | |
682 | module_exit(xt_layer7_fini); | |
683 |