1 /* Kernel module to match a string into a packet.
3 * Copyright (C) 2000 Emmanuel Roger <winfield@freegates.be>
6 * 19.02.2002: Gianni Tedesco <gianni@ecsc.co.uk>
7 * Fixed SMP re-entrancy problem using per-cpu data areas
8 * for the skip/shift tables.
9 * 02.05.2001: Gianni Tedesco <gianni@ecsc.co.uk>
10 * Fixed kernel panic, due to overrunning boyer moore string
11 * tables. Also slightly tweaked heuristic for deciding what
13 * 27.01.2001: Gianni Tedesco <gianni@ecsc.co.uk>
14 * Implemented Boyer Moore Sublinear search algorithm
15 * alongside the existing linear search based on memcmp().
16 * Also a quick check to decide which method to use on a per
20 /* Kernel module to match a http header string into a packet.
22 * Copyright (C) 2003, CyberTAN Corporation
23 * All Rights Reserved.
26 * This is kernel module for web content inspection. It was derived from
27 * 'string' match module, declared as above.
29 * The module follows the Netfilter framework, called extended packet
33 /* Linux Kernel 2.6 Port ( 2.4 ipt-> 2.6 xt)
34 * Copyright (C) 2008, Ralink Technology Corporation.
35 * All Rights Reserved.
38 #include <linux/module.h>
39 #include <linux/skbuff.h>
40 #include <linux/netfilter/x_tables.h>
42 #include <linux/tcp.h>
45 #define BM_MAX_NLEN 256
46 #define BM_MAX_HLEN 1024
49 #define BLK_ACTIVE 0x02
50 #define BLK_COOKIE 0x04
51 #define BLK_PROXY 0x08
53 typedef char *(*proc_ipt_search
) (char *, char *, int, int);
55 struct ipt_webstr_info
{
56 char string
[BM_MAX_NLEN
];
70 #define isdigit(x) ((x) >= '0' && (x) <= '9')
71 #define isupper(x) (((unsigned)(x) >= 'A') && ((unsigned)(x) <= 'Z'))
72 #define islower(x) (((unsigned)(x) >= 'a') && ((unsigned)(x) <= 'z'))
73 #define isalpha(x) (isupper(x) || islower(x))
74 #define toupper(x) (isupper(x) ? (x) : (x) - 'a' + 'A')
75 #define tolower(x) (isupper(x) ? ((x) - 'A' + 'a') : (x))
77 #define split(word, wordlist, next, delim) \
78 for (next = wordlist, \
79 strncpy(word, next, sizeof(word)), \
80 word[(next=strstr(next, delim)) ? strstr(word, delim) - word : sizeof(word) - 1] = '\0', \
81 next = next ? next + sizeof(delim) - 1 : NULL ; \
84 strncpy(word, next, sizeof(word)), \
85 word[(next=strstr(next, delim)) ? strstr(word, delim) - word : sizeof(word) - 1] = '\0', \
86 next = next ? next + sizeof(delim) - 1 : NULL)
90 /* Flags for get_http_info() */
91 #define HTTP_HOST 0x01
93 /* Flags for mangle_http_header() */
94 #define HTTP_COOKIE 0x04
97 #define SPARQ_LOG printk
99 #define SPARQ_LOG(format, args...)
102 typedef struct httpinfo
{
103 char host
[BUFSIZE
+ 1];
105 char url
[BUFSIZE
+ 1];
109 /* Return 1 for match, 0 for accept, -1 for partial. */
110 static int find_pattern2(const char *data
, size_t dlen
,
111 const char *pattern
, size_t plen
,
113 unsigned int *numoff
,
114 unsigned int *numlen
)
118 *numoff
= *numlen
= 0;
120 SPARQ_LOG("%s: pattern = '%s', dlen = %u\n",__FUNCTION__
, pattern
, dlen
);
124 if (dlen
<= plen
) { /* Short packet: try for partial? */
125 if (strnicmp(data
, pattern
, dlen
) == 0)
130 for (i
= 0; i
<= (dlen
- plen
); i
++) {
131 /* DFA : \r\n\r\n :: 1234 */
132 if (*(data
+ i
) == '\r') {
133 if (!(state
% 2)) state
++; /* forwarding move */
134 else state
= 0; /* reset */
136 else if (*(data
+ i
) == '\n') {
137 if (state
% 2) state
++;
145 /* pattern compare */
146 if (memcmp(data
+ i
, pattern
, plen
) != 0)
149 /* Here, it means patten match!! */
151 for (j
= *numoff
, k
= 0; data
[j
] != term
; j
++, k
++)
152 if (j
> dlen
) return -1 ; /* no terminal char */
161 static int mangle_http_header(const struct sk_buff
*skb
, int flags
)
163 struct iphdr
*iph
= (skb
)->nh
.iph
;
164 struct tcphdr
*tcph
= (void *)iph
+ iph
->ihl
*4;
165 unsigned char *data
= (void *)tcph
+ tcph
->doff
*4;
166 unsigned int datalen
= (skb
)->len
- (iph
->ihl
*4) - (tcph
->doff
*4);
168 int found
, offset
, len
;
172 SPARQ_LOG("%s: seq=%u\n", __FUNCTION__
, ntohl(tcph
->seq
));
174 /* Basic checking, is it HTTP packet? */
176 return ret
; /* Not enough length, ignore it */
177 if (memcmp(data
, "GET ", sizeof("GET ") - 1) != 0 &&
178 memcmp(data
, "POST ", sizeof("POST ") - 1) != 0 &&
179 memcmp(data
, "HEAD ", sizeof("HEAD ") - 1) != 0) //zg add 2006.09.28 for cdrouter3.3 item 186(cdrouter_urlfilter_15)
180 return ret
; /* Pass it */
182 /* COOKIE modification */
183 if (flags
& HTTP_COOKIE
) {
184 found
= find_pattern2(data
, datalen
, "Cookie: ",
185 sizeof("Cookie: ")-1, '\r', &offset
, &len
);
188 offset
-= (sizeof("Cookie: ") - 1);
189 /* Swap the 2rd and 4th bit */
190 c
= *(data
+ offset
+ 2) ;
191 *(data
+ offset
+ 2) = *(data
+ offset
+ 4) ;
192 *(data
+ offset
+ 4) = c
;
201 static int get_http_info(const struct sk_buff
*skb
, int flags
, httpinfo_t
*info
)
203 struct iphdr
*iph
= ip_hdr(skb
);
204 struct tcphdr
*tcph
= (void *)iph
+ iph
->ihl
*4;
205 unsigned char *data
= (void *)tcph
+ tcph
->doff
*4;
206 unsigned int datalen
= (skb
)->len
- (iph
->ihl
*4) - (tcph
->doff
*4);
209 int hostlen
, pathlen
;
213 SPARQ_LOG("%s: seq=%u\n", __FUNCTION__
, ntohl(tcph
->seq
));
215 /* Basic checking, is it HTTP packet? */
217 return ret
; /* Not enough length, ignore it */
218 if (memcmp(data
, "GET ", sizeof("GET ") - 1) != 0 &&
219 memcmp(data
, "POST ", sizeof("POST ") - 1) != 0 &&
220 memcmp(data
, "HEAD ", sizeof("HEAD ") - 1) != 0) //zg add 2006.09.28 for cdrouter3.3 item 186(cdrouter_urlfilter_15)
221 return ret
; /* Pass it */
223 if (!(flags
& (HTTP_HOST
| HTTP_URL
)))
226 /* find the 'Host: ' value */
227 found
= find_pattern2(data
, datalen
, "Host: ",
228 sizeof("Host: ") - 1, '\r', &offset
, &hostlen
);
229 SPARQ_LOG("Host found=%d\n", found
);
231 if (!found
|| !hostlen
)
234 ret
++; /* Host found, increase the return value */
235 hostlen
= (hostlen
< BUFSIZE
) ? hostlen
: BUFSIZE
;
236 strncpy(info
->host
, data
+ offset
, hostlen
);
237 *(info
->host
+ hostlen
) = 0; /* null-terminated */
238 info
->hostlen
= hostlen
;
239 SPARQ_LOG("HOST=%s, hostlen=%d\n", info
->host
, info
->hostlen
);
241 if (!(flags
& HTTP_URL
))
244 /* find the 'GET ' or 'POST ' or 'HEAD ' value */
245 found
= find_pattern2(data
, datalen
, "GET ",
246 sizeof("GET ") - 1, '\r', &offset
, &pathlen
);
248 found
= find_pattern2(data
, datalen
, "POST ",
249 sizeof("POST ") - 1, '\r', &offset
, &pathlen
);
250 /******* zg add 2006.09.28 for cdrouter3.3 item 186(cdrouter_urlfilter_15) ******/
252 found
= find_pattern2(data
, datalen
, "HEAD ",
253 sizeof("HEAD ") - 1, '\r', &offset
, &pathlen
);
254 /************************* zg end 2006.09.28 ****************************/
255 SPARQ_LOG("GET/POST found=%d\n", found
);
257 if (!found
|| (pathlen
-= (sizeof(" HTTP/x.x") - 1)) <= 0)/* ignor this field */
260 ret
++; /* GET/POST/HEAD found, increase the return value */
261 pathlen
= ((pathlen
+ hostlen
) < BUFSIZE
) ? pathlen
: BUFSIZE
- hostlen
;
262 strncpy(info
->url
, info
->host
, hostlen
);
263 strncpy(info
->url
+ hostlen
, data
+ offset
, pathlen
);
264 *(info
->url
+ hostlen
+ pathlen
) = 0; /* null-terminated */
265 info
->urllen
= hostlen
+ pathlen
;
266 SPARQ_LOG("URL=%s, urllen=%d\n", info
->url
, info
->urllen
);
271 /* Linear string search based on memcmp() */
272 static char *search_linear (char *needle
, char *haystack
, int needle_len
, int haystack_len
)
274 char *k
= haystack
+ (haystack_len
-needle_len
);
277 SPARQ_LOG("%s: haystack=%s, needle=%s\n", __FUNCTION__
, t
, needle
);
279 //SPARQ_LOG("%s: haystack=%s, needle=%s\n", __FUNCTION__, t, needle);
280 if (strnicmp(t
, needle
, needle_len
) == 0) return t
;
281 //if ( memcmp(t, needle, needle_len) == 0 ) return t;
288 static bool match(const struct sk_buff
*skb
, struct xt_action_param
*par
)
290 const struct ipt_webstr_info
*info
= par
->matchinfo
;
291 struct iphdr
*ip
= ip_hdr(skb
);
292 proc_ipt_search search
=search_linear
;
294 char token
[] = "< >";
295 char *wordlist
= (char *)&info
->string
;
302 if (!ip
|| info
->len
< 1)
305 SPARQ_LOG("\n************************************************\n"
306 "%s: type=%s\n", __FUNCTION__
, (info
->type
== IPT_WEBSTR_URL
)
307 ? "IPT_WEBSTR_URL" : (info
->type
== IPT_WEBSTR_HOST
)
308 ? "IPT_WEBSTR_HOST" : "IPT_WEBSTR_CONTENT" );
310 /* Determine the flags value for get_http_info(), and mangle packet
314 case IPT_WEBSTR_URL
: /* fall through */
317 case IPT_WEBSTR_HOST
:
321 case IPT_WEBSTR_CONTENT
:
322 opt
= simple_strtol(wordlist
, (char **)NULL
, 10);
323 SPARQ_LOG("%s: string=%s, opt=%#lx\n", __FUNCTION__
, wordlist
, opt
);
325 if (opt
& (BLK_JAVA
| BLK_ACTIVE
| BLK_PROXY
))
330 // Could we modify the packet payload in a "match" module? --YY@Ralink
331 if (opt
& BLK_COOKIE
)
332 mangle_http_header(skb
, HTTP_COOKIE
);
337 printk("%s: Sorry! Cannot find this match option.\n", __FILE__
);
341 /* Get the http header info */
342 if (get_http_info(skb
, flags
, &htinfo
) < 1)
345 /* Check if the http header content contains the forbidden keyword */
346 if (info
->type
== IPT_WEBSTR_HOST
|| info
->type
== IPT_WEBSTR_URL
) {
347 int nlen
= 0, hlen
= 0;
348 char needle
[BUFSIZE
], *haystack
= NULL
;
351 if (info
->type
== IPT_WEBSTR_HOST
) {
352 haystack
= htinfo
.host
;
353 hlen
= htinfo
.hostlen
;
356 haystack
= htinfo
.url
;
357 hlen
= htinfo
.urllen
;
359 split(needle
, wordlist
, next
, token
) {
360 nlen
= strlen(needle
);
361 SPARQ_LOG("keyword=%s, nlen=%d, hlen=%d\n", needle
, nlen
, hlen
);
362 if (!nlen
|| !hlen
|| nlen
> hlen
) continue;
363 if (search(needle
, haystack
, nlen
, hlen
) != NULL
) {
369 else { /* IPT_WEBSTR_CONTENT */
372 if (opt
& BLK_JAVA
) {
373 vicelen
= sizeof(".js") - 1;
374 if (strnicmp(htinfo
.url
+ htinfo
.urllen
- vicelen
, ".js", vicelen
) == 0) {
375 SPARQ_LOG("%s: MATCH....java\n", __FUNCTION__
);
379 vicelen
= sizeof(".class") - 1;
380 if (strnicmp(htinfo
.url
+ htinfo
.urllen
- vicelen
, ".class", vicelen
) == 0) {
381 SPARQ_LOG("%s: MATCH....java\n", __FUNCTION__
);
386 if (opt
& BLK_ACTIVE
){
387 vicelen
= sizeof(".ocx") - 1;
388 if (strnicmp(htinfo
.url
+ htinfo
.urllen
- vicelen
, ".ocx", vicelen
) == 0) {
389 SPARQ_LOG("%s: MATCH....activex\n", __FUNCTION__
);
393 vicelen
= sizeof(".cab") - 1;
394 if (strnicmp(htinfo
.url
+ htinfo
.urllen
- vicelen
, ".cab", vicelen
) == 0) {
395 SPARQ_LOG("%s: MATCH....activex\n", __FUNCTION__
);
400 if (opt
& BLK_PROXY
){
401 if (strnicmp(htinfo
.url
+ htinfo
.hostlen
, "http://", sizeof("http://") - 1) == 0) {
402 SPARQ_LOG("%s: MATCH....proxy\n", __FUNCTION__
);
410 SPARQ_LOG("%s: Verdict =======> %s \n",__FUNCTION__
411 , found
? "DROP" : "ACCEPT");
413 return (found
^ info
->invert
);
416 static int checkentry(const struct xt_mtchk_param
*par
)
419 if (matchsize
!= IPT_ALIGN(sizeof(struct ipt_webstr_info
)))
425 static struct xt_match xt_webstr_match
[] = {
430 .checkentry
= checkentry
,
431 .matchsize
= sizeof(struct ipt_webstr_info
),
438 .checkentry
= checkentry
,
439 .matchsize
= sizeof(struct ipt_webstr_info
),
445 static int __init
init(void)
447 return xt_register_matches(xt_webstr_match
, ARRAY_SIZE(xt_webstr_match
));
450 static void __exit
fini(void)
452 xt_unregister_matches(xt_webstr_match
, ARRAY_SIZE(xt_webstr_match
));