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