1/* Kernel module to match a string into a packet. 2 * 3 * Copyright (C) 2000 Emmanuel Roger <winfield@freegates.be> 4 * 5 * ChangeLog 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 12 * search algo to use. 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 17 * packet basis. 18 */ 19 20/* Kernel module to match a http header string into a packet. 21 * 22 * Copyright (C) 2003, CyberTAN Corporation 23 * All Rights Reserved. 24 * 25 * Description: 26 * This is kernel module for web content inspection. It was derived from 27 * 'string' match module, declared as above. 28 * 29 * The module follows the Netfilter framework, called extended packet 30 * matching modules. 31 */ 32 33/* Linux Kernel 2.6 Port ( 2.4 ipt-> 2.6 xt) 34 * Copyright (C) 2008, Ralink Technology Corporation. 35 * All Rights Reserved. 36 */ 37 38#include <linux/module.h> 39#include <linux/skbuff.h> 40#include <linux/netfilter/x_tables.h> 41#include <linux/ip.h> 42#include <linux/ipv6.h> 43#include <linux/tcp.h> 44#include <net/sock.h> 45 46#define BM_MAX_NLEN 256 47#define BM_MAX_HLEN 1024 48 49#define BLK_JAVA 0x01 50#define BLK_ACTIVE 0x02 51#define BLK_COOKIE 0x04 52#define BLK_PROXY 0x08 53 54typedef char *(*proc_ipt_search) (char *, char *, int, int); 55 56struct ipt_webstr_info { 57 char string[BM_MAX_NLEN]; 58 u_int16_t invert; 59 u_int16_t len; 60 u_int8_t type; 61}; 62 63enum xt_webstr_type 64{ 65 IPT_WEBSTR_HOST, 66 IPT_WEBSTR_URL, 67 IPT_WEBSTR_CONTENT 68}; 69 70 71#define isdigit(x) ((x) >= '0' && (x) <= '9') 72#define isupper(x) (((unsigned)(x) >= 'A') && ((unsigned)(x) <= 'Z')) 73#define islower(x) (((unsigned)(x) >= 'a') && ((unsigned)(x) <= 'z')) 74#define isalpha(x) (isupper(x) || islower(x)) 75#define toupper(x) (isupper(x) ? (x) : (x) - 'a' + 'A') 76#define tolower(x) (isupper(x) ? ((x) - 'A' + 'a') : (x)) 77 78#define split(word, wordlist, next, delim) \ 79 for (next = wordlist, \ 80 strncpy(word, next, sizeof(word)), \ 81 word[(next=strstr(next, delim)) ? strstr(word, delim) - word : sizeof(word) - 1] = '\0', \ 82 next = next ? next + sizeof(delim) - 1 : NULL ; \ 83 strlen(word); \ 84 next = next ? : "", \ 85 strncpy(word, next, sizeof(word)), \ 86 word[(next=strstr(next, delim)) ? strstr(word, delim) - word : sizeof(word) - 1] = '\0', \ 87 next = next ? next + sizeof(delim) - 1 : NULL) 88 89#define BUFSIZE 1024 90 91/* Flags for get_http_info() */ 92#define HTTP_HOST 0x01 93#define HTTP_URL 0x02 94/* Flags for mangle_http_header() */ 95#define HTTP_COOKIE 0x04 96 97#if 0 98#define SPARQ_LOG printk 99#else 100#define SPARQ_LOG(format, args...) 101#endif 102 103typedef struct httpinfo { 104 char host[BUFSIZE + 1]; 105 int hostlen; 106 char url[BUFSIZE + 1]; 107 int urllen; 108} httpinfo_t; 109 110/* Return 1 for match, 0 for accept, -1 for partial. */ 111static int find_pattern2(const char *data, size_t dlen, 112 const char *pattern, size_t plen, 113 char term, 114 unsigned int *numoff, 115 unsigned int *numlen) 116{ 117 size_t i, j, k; 118 int state = 0; 119 *numoff = *numlen = 0; 120 121 SPARQ_LOG("%s: pattern = '%s', dlen = %u\n",__FUNCTION__, pattern, dlen); 122 if (dlen == 0) 123 return 0; 124 125 if (dlen <= plen) { /* Short packet: try for partial? */ 126 if (strnicmp(data, pattern, dlen) == 0) 127 return -1; 128 else 129 return 0; 130 } 131 for (i = 0; i <= (dlen - plen); i++) { 132 /* DFA : \r\n\r\n :: 1234 */ 133 if (*(data + i) == '\r') { 134 if (!(state % 2)) state++; /* forwarding move */ 135 else state = 0; /* reset */ 136 } 137 else if (*(data + i) == '\n') { 138 if (state % 2) state++; 139 else state = 0; 140 } 141 else state = 0; 142 143 if (state >= 4) 144 break; 145 146 /* pattern compare */ 147 if (memcmp(data + i, pattern, plen ) != 0) 148 continue; 149 150 /* Here, it means patten match!! */ 151 *numoff=i + plen; 152 for (j = *numoff, k = 0; data[j] != term; j++, k++) 153 if (j > dlen) return -1 ; /* no terminal char */ 154 155 *numlen = k; 156 return 1; 157 } 158 return 0; 159} 160 161#if 0 162static int mangle_http_header(const struct sk_buff *skb, int flags) 163{ 164 struct iphdr *iph = (skb)->nh.iph; 165 struct tcphdr *tcph = (void *)iph + iph->ihl*4; 166 unsigned char *data = (void *)tcph + tcph->doff*4; 167 unsigned int datalen = (skb)->len - (iph->ihl*4) - (tcph->doff*4); 168 169 int found, offset, len; 170 int ret = 0; 171 172 173 SPARQ_LOG("%s: seq=%u\n", __FUNCTION__, ntohl(tcph->seq)); 174 175 /* Basic checking, is it HTTP packet? */ 176 if (datalen < 10) 177 return ret; /* Not enough length, ignore it */ 178 if (memcmp(data, "GET ", sizeof("GET ") - 1) != 0 && 179 memcmp(data, "POST ", sizeof("POST ") - 1) != 0 && 180 memcmp(data, "HEAD ", sizeof("HEAD ") - 1) != 0) //zg add 2006.09.28 for cdrouter3.3 item 186(cdrouter_urlfilter_15) 181 return ret; /* Pass it */ 182 183 /* COOKIE modification */ 184 if (flags & HTTP_COOKIE) { 185 found = find_pattern2(data, datalen, "Cookie: ", 186 sizeof("Cookie: ")-1, '\r', &offset, &len); 187 if (found) { 188 char c; 189 offset -= (sizeof("Cookie: ") - 1); 190 /* Swap the 2rd and 4th bit */ 191 c = *(data + offset + 2) ; 192 *(data + offset + 2) = *(data + offset + 4) ; 193 *(data + offset + 4) = c ; 194 ret++; 195 } 196 } 197 198 return ret; 199} 200#endif 201 202static int get_http_info(const struct sk_buff *skb, int flags, httpinfo_t *info) 203{ 204 struct iphdr *iph = ip_hdr(skb); 205 struct tcphdr *tcph = (void *)iph + iph->ihl*4; 206 unsigned char *data = (void *)tcph + tcph->doff*4; 207 unsigned int datalen = (skb)->len - (iph->ihl*4) - (tcph->doff*4); 208 209 int found, offset; 210 int hostlen, pathlen; 211 int ret = 0; 212 213 214 SPARQ_LOG("%s: seq=%u\n", __FUNCTION__, ntohl(tcph->seq)); 215 216 /* Basic checking, is it HTTP packet? */ 217 if (datalen < 10) 218 return ret; /* Not enough length, ignore it */ 219 if (memcmp(data, "GET ", sizeof("GET ") - 1) != 0 && 220 memcmp(data, "POST ", sizeof("POST ") - 1) != 0 && 221 memcmp(data, "HEAD ", sizeof("HEAD ") - 1) != 0) //zg add 2006.09.28 for cdrouter3.3 item 186(cdrouter_urlfilter_15) 222 return ret; /* Pass it */ 223 224 if (!(flags & (HTTP_HOST | HTTP_URL))) 225 return ret; 226 227 /* find the 'Host: ' value */ 228 found = find_pattern2(data, datalen, "Host: ", 229 sizeof("Host: ") - 1, '\r', &offset, &hostlen); 230 SPARQ_LOG("Host found=%d\n", found); 231 232 if (!found || !hostlen) 233 return ret; 234 235 ret++; /* Host found, increase the return value */ 236 hostlen = (hostlen < BUFSIZE) ? hostlen : BUFSIZE; 237 strncpy(info->host, data + offset, hostlen); 238 *(info->host + hostlen) = 0; /* null-terminated */ 239 info->hostlen = hostlen; 240 SPARQ_LOG("HOST=%s, hostlen=%d\n", info->host, info->hostlen); 241 242 if (!(flags & HTTP_URL)) 243 return ret; 244 245 /* find the 'GET ' or 'POST ' or 'HEAD ' value */ 246 found = find_pattern2(data, datalen, "GET ", 247 sizeof("GET ") - 1, '\r', &offset, &pathlen); 248 if (!found) 249 found = find_pattern2(data, datalen, "POST ", 250 sizeof("POST ") - 1, '\r', &offset, &pathlen); 251 /******* zg add 2006.09.28 for cdrouter3.3 item 186(cdrouter_urlfilter_15) ******/ 252 if (!found) 253 found = find_pattern2(data, datalen, "HEAD ", 254 sizeof("HEAD ") - 1, '\r', &offset, &pathlen); 255 /************************* zg end 2006.09.28 ****************************/ 256 SPARQ_LOG("GET/POST found=%d\n", found); 257 258 if (!found || (pathlen -= (sizeof(" HTTP/x.x") - 1)) <= 0)/* ignor this field */ 259 return ret; 260 261 ret++; /* GET/POST/HEAD found, increase the return value */ 262 pathlen = ((pathlen + hostlen) < BUFSIZE) ? pathlen : BUFSIZE - hostlen; 263 strncpy(info->url, info->host, hostlen); 264 strncpy(info->url + hostlen, data + offset, pathlen); 265 *(info->url + hostlen + pathlen) = 0; /* null-terminated */ 266 info->urllen = hostlen + pathlen; 267 SPARQ_LOG("URL=%s, urllen=%d\n", info->url, info->urllen); 268 269 return ret; 270} 271 272static int get_http_info6(const struct sk_buff *skb, int flags, httpinfo_t *info) 273{ 274 struct ipv6hdr *ip6h = ipv6_hdr(skb); 275 struct tcphdr *tcph = tcp_hdr(skb); 276 unsigned char *data = (void *)tcph + tcph->doff*4; 277 unsigned int datalen = ntohs(ip6h->payload_len); 278 279 int found, offset; 280 int hostlen, pathlen; 281 int ret = 0; 282 283 284 SPARQ_LOG("%s: seq=%u\n", __FUNCTION__, ntohl(tcph->seq)); 285 286 /* Basic checking, is it HTTP packet? */ 287 if (datalen < 10) 288 return ret; /* Not enough length, ignore it */ 289 if (memcmp(data, "GET ", sizeof("GET ") - 1) != 0 && 290 memcmp(data, "POST ", sizeof("POST ") - 1) != 0 && 291 memcmp(data, "HEAD ", sizeof("HEAD ") - 1) != 0) //zg add 2006.09.28 for cdrouter3.3 item 186(cdrouter_urlfilter_15) 292 return ret; /* Pass it */ 293 294 if (!(flags & (HTTP_HOST | HTTP_URL))) 295 return ret; 296 297 /* find the 'Host: ' value */ 298 found = find_pattern2(data, datalen, "Host: ", 299 sizeof("Host: ") - 1, '\r', &offset, &hostlen); 300 SPARQ_LOG("Host found=%d\n", found); 301 302 if (!found || !hostlen) 303 return ret; 304 305 ret++; /* Host found, increase the return value */ 306 hostlen = (hostlen < BUFSIZE) ? hostlen : BUFSIZE; 307 strncpy(info->host, data + offset, hostlen); 308 *(info->host + hostlen) = 0; /* null-terminated */ 309 info->hostlen = hostlen; 310 SPARQ_LOG("HOST=%s, hostlen=%d\n", info->host, info->hostlen); 311 312 if (!(flags & HTTP_URL)) 313 return ret; 314 315 /* find the 'GET ' or 'POST ' or 'HEAD ' value */ 316 found = find_pattern2(data, datalen, "GET ", 317 sizeof("GET ") - 1, '\r', &offset, &pathlen); 318 if (!found) 319 found = find_pattern2(data, datalen, "POST ", 320 sizeof("POST ") - 1, '\r', &offset, &pathlen); 321 /******* zg add 2006.09.28 for cdrouter3.3 item 186(cdrouter_urlfilter_15) ******/ 322 if (!found) 323 found = find_pattern2(data, datalen, "HEAD ", 324 sizeof("HEAD ") - 1, '\r', &offset, &pathlen); 325 /************************* zg end 2006.09.28 ****************************/ 326 SPARQ_LOG("GET/POST found=%d\n", found); 327 328 if (!found || (pathlen -= (sizeof(" HTTP/x.x") - 1)) <= 0)/* ignor this field */ 329 return ret; 330 331 ret++; /* GET/POST/HEAD found, increase the return value */ 332 pathlen = ((pathlen + hostlen) < BUFSIZE) ? pathlen : BUFSIZE - hostlen; 333 strncpy(info->url, info->host, hostlen); 334 strncpy(info->url + hostlen, data + offset, pathlen); 335 *(info->url + hostlen + pathlen) = 0; /* null-terminated */ 336 info->urllen = hostlen + pathlen; 337 SPARQ_LOG("URL=%s, urllen=%d\n", info->url, info->urllen); 338 339 return ret; 340} 341 342/* Linear string search based on memcmp() */ 343static char *search_linear (char *needle, char *haystack, int needle_len, int haystack_len) 344{ 345 char *k = haystack + (haystack_len-needle_len); 346 char *t = haystack; 347 348 SPARQ_LOG("%s: haystack=%s, needle=%s\n", __FUNCTION__, t, needle); 349 for(; t <= k; t++) { 350 //SPARQ_LOG("%s: haystack=%s, needle=%s\n", __FUNCTION__, t, needle); 351 if (strnicmp(t, needle, needle_len) == 0) return t; 352 //if ( memcmp(t, needle, needle_len) == 0 ) return t; 353 } 354 355 return NULL; 356} 357 358 359static bool match(const struct sk_buff *skb, struct xt_action_param *par) 360{ 361 const struct ipt_webstr_info *info = par->matchinfo; 362 struct iphdr *ip = ip_hdr(skb); 363 proc_ipt_search search=search_linear; 364 365 char token[] = "< >"; 366 char *wordlist = (char *)&info->string; 367 httpinfo_t htinfo; 368 int flags = 0; 369 int found = 0; 370 long int opt = 0; 371 372 373 if (!ip || info->len < 1) 374 return 0; 375 376 SPARQ_LOG("\n************************************************\n" 377 "%s: type=%s\n", __FUNCTION__, (info->type == IPT_WEBSTR_URL) 378 ? "IPT_WEBSTR_URL" : (info->type == IPT_WEBSTR_HOST) 379 ? "IPT_WEBSTR_HOST" : "IPT_WEBSTR_CONTENT" ); 380 381 /* Determine the flags value for get_http_info(), and mangle packet 382 * if needed. */ 383 switch(info->type) 384 { 385 case IPT_WEBSTR_URL: /* fall through */ 386 flags |= HTTP_URL; 387 388 case IPT_WEBSTR_HOST: 389 flags |= HTTP_HOST; 390 break; 391 392 case IPT_WEBSTR_CONTENT: 393 opt = simple_strtol(wordlist, (char **)NULL, 10); 394 SPARQ_LOG("%s: string=%s, opt=%#lx\n", __FUNCTION__, wordlist, opt); 395 396 if (opt & (BLK_JAVA | BLK_ACTIVE | BLK_PROXY)) 397 flags |= HTTP_URL; 398 if (opt & BLK_PROXY) 399 flags |= HTTP_HOST; 400#if 0 401 // Could we modify the packet payload in a "match" module? --YY@Ralink 402 if (opt & BLK_COOKIE) 403 mangle_http_header(skb, HTTP_COOKIE); 404#endif 405 break; 406 407 default: 408 printk("%s: Sorry! Cannot find this match option.\n", __FILE__); 409 return 0; 410 } 411 412 /* Get the http header info */ 413 if (get_http_info(skb, flags, &htinfo) < 1) 414 return 0; 415 416 /* Check if the http header content contains the forbidden keyword */ 417 if (info->type == IPT_WEBSTR_HOST || info->type == IPT_WEBSTR_URL) { 418 int nlen = 0, hlen = 0; 419 char needle[BUFSIZE], *haystack = NULL; 420 char *next; 421 422 if (info->type == IPT_WEBSTR_HOST) { 423 haystack = htinfo.host; 424 hlen = htinfo.hostlen; 425 } 426 else { 427 haystack = htinfo.url; 428 hlen = htinfo.urllen; 429 } 430 split(needle, wordlist, next, token) { 431 nlen = strlen(needle); 432 SPARQ_LOG("keyword=%s, nlen=%d, hlen=%d\n", needle, nlen, hlen); 433 if (!nlen || !hlen || nlen > hlen) continue; 434 if (search(needle, haystack, nlen, hlen) != NULL) { 435 found = 1; 436 break; 437 } 438 } 439 } 440 else { /* IPT_WEBSTR_CONTENT */ 441 int vicelen; 442 443 if (opt & BLK_JAVA) { 444 vicelen = sizeof(".js") - 1; 445 if (strnicmp(htinfo.url + htinfo.urllen - vicelen, ".js", vicelen) == 0) { 446 SPARQ_LOG("%s: MATCH....java\n", __FUNCTION__); 447 found = 1; 448 goto match_ret; 449 } 450 vicelen = sizeof(".class") - 1; 451 if (strnicmp(htinfo.url + htinfo.urllen - vicelen, ".class", vicelen) == 0) { 452 SPARQ_LOG("%s: MATCH....java\n", __FUNCTION__); 453 found = 1; 454 goto match_ret; 455 } 456 } 457 if (opt & BLK_ACTIVE){ 458 vicelen = sizeof(".ocx") - 1; 459 if (strnicmp(htinfo.url + htinfo.urllen - vicelen, ".ocx", vicelen) == 0) { 460 SPARQ_LOG("%s: MATCH....activex\n", __FUNCTION__); 461 found = 1; 462 goto match_ret; 463 } 464 vicelen = sizeof(".cab") - 1; 465 if (strnicmp(htinfo.url + htinfo.urllen - vicelen, ".cab", vicelen) == 0) { 466 SPARQ_LOG("%s: MATCH....activex\n", __FUNCTION__); 467 found = 1; 468 goto match_ret; 469 } 470 } 471 if (opt & BLK_PROXY){ 472 if (strnicmp(htinfo.url + htinfo.hostlen, "http://", sizeof("http://") - 1) == 0) { 473 SPARQ_LOG("%s: MATCH....proxy\n", __FUNCTION__); 474 found = 1; 475 goto match_ret; 476 } 477 } 478 } 479 480match_ret: 481 SPARQ_LOG("%s: Verdict =======> %s \n",__FUNCTION__ 482 , found ? "DROP" : "ACCEPT"); 483 484 return (found ^ info->invert); 485} 486 487static bool match6(const struct sk_buff *skb, struct xt_action_param *par) 488{ 489 const struct ipt_webstr_info *info = par->matchinfo; 490 struct ipv6hdr *ip6 = ipv6_hdr(skb); 491 proc_ipt_search search=search_linear; 492 493 char token[] = "< >"; 494 char *wordlist = (char *)&info->string; 495 httpinfo_t htinfo; 496 int flags = 0; 497 int found = 0; 498 long int opt = 0; 499 500 501 if (!ip6 || info->len < 1) 502 return 0; 503 504 SPARQ_LOG("\n************************************************\n" 505 "%s: type=%s\n", __FUNCTION__, (info->type == IPT_WEBSTR_URL) 506 ? "IPT_WEBSTR_URL" : (info->type == IPT_WEBSTR_HOST) 507 ? "IPT_WEBSTR_HOST" : "IPT_WEBSTR_CONTENT" ); 508 509 /* Determine the flags value for get_http_info(), and mangle packet 510 * if needed. */ 511 switch(info->type) 512 { 513 case IPT_WEBSTR_URL: /* fall through */ 514 flags |= HTTP_URL; 515 516 case IPT_WEBSTR_HOST: 517 flags |= HTTP_HOST; 518 break; 519 520 case IPT_WEBSTR_CONTENT: 521 opt = simple_strtol(wordlist, (char **)NULL, 10); 522 SPARQ_LOG("%s: string=%s, opt=%#lx\n", __FUNCTION__, wordlist, opt); 523 524 if (opt & (BLK_JAVA | BLK_ACTIVE | BLK_PROXY)) 525 flags |= HTTP_URL; 526 if (opt & BLK_PROXY) 527 flags |= HTTP_HOST; 528 break; 529 530 default: 531 printk("%s: Sorry! Cannot find this match option.\n", __FILE__); 532 return 0; 533 } 534 535 /* Get the http header info */ 536 if (get_http_info6(skb, flags, &htinfo) < 1) 537 return 0; 538 539 /* Check if the http header content contains the forbidden keyword */ 540 if (info->type == IPT_WEBSTR_HOST || info->type == IPT_WEBSTR_URL) { 541 int nlen = 0, hlen = 0; 542 char needle[BUFSIZE], *haystack = NULL; 543 char *next; 544 545 if (info->type == IPT_WEBSTR_HOST) { 546 haystack = htinfo.host; 547 hlen = htinfo.hostlen; 548 } 549 else { 550 haystack = htinfo.url; 551 hlen = htinfo.urllen; 552 } 553 split(needle, wordlist, next, token) { 554 nlen = strlen(needle); 555 SPARQ_LOG("keyword=%s, nlen=%d, hlen=%d\n", needle, nlen, hlen); 556 if (!nlen || !hlen || nlen > hlen) continue; 557 if (search(needle, haystack, nlen, hlen) != NULL) { 558 found = 1; 559 break; 560 } 561 } 562 } 563 else { /* IPT_WEBSTR_CONTENT */ 564 int vicelen; 565 566 if (opt & BLK_JAVA) { 567 vicelen = sizeof(".js") - 1; 568 if (strnicmp(htinfo.url + htinfo.urllen - vicelen, ".js", vicelen) == 0) { 569 SPARQ_LOG("%s: MATCH....java\n", __FUNCTION__); 570 found = 1; 571 goto match_ret; 572 } 573 vicelen = sizeof(".class") - 1; 574 if (strnicmp(htinfo.url + htinfo.urllen - vicelen, ".class", vicelen) == 0) { 575 SPARQ_LOG("%s: MATCH....java\n", __FUNCTION__); 576 found = 1; 577 goto match_ret; 578 } 579 } 580 if (opt & BLK_ACTIVE){ 581 vicelen = sizeof(".ocx") - 1; 582 if (strnicmp(htinfo.url + htinfo.urllen - vicelen, ".ocx", vicelen) == 0) { 583 SPARQ_LOG("%s: MATCH....activex\n", __FUNCTION__); 584 found = 1; 585 goto match_ret; 586 } 587 vicelen = sizeof(".cab") - 1; 588 if (strnicmp(htinfo.url + htinfo.urllen - vicelen, ".cab", vicelen) == 0) { 589 SPARQ_LOG("%s: MATCH....activex\n", __FUNCTION__); 590 found = 1; 591 goto match_ret; 592 } 593 } 594 if (opt & BLK_PROXY){ 595 if (strnicmp(htinfo.url + htinfo.hostlen, "http://", sizeof("http://") - 1) == 0) { 596 SPARQ_LOG("%s: MATCH....proxy\n", __FUNCTION__); 597 found = 1; 598 goto match_ret; 599 } 600 } 601 } 602 603match_ret: 604 SPARQ_LOG("%s: Verdict =======> %s \n",__FUNCTION__ 605 , found ? "DROP" : "ACCEPT"); 606 607 return (found ^ info->invert); 608} 609 610static int checkentry(const struct xt_mtchk_param *par) 611{ 612#if 0 613 if (matchsize != IPT_ALIGN(sizeof(struct ipt_webstr_info))) 614 return 0; 615#endif 616 return 0; 617} 618 619static struct xt_match xt_webstr_match[] = { 620 { 621 .name = "webstr", 622 .family = AF_INET, 623 .match = match, 624 .checkentry = checkentry, 625 .matchsize = sizeof(struct ipt_webstr_info), 626 .me = THIS_MODULE 627 }, 628 { 629 .name = "webstr", 630 .family = AF_INET6, 631 .match = match6, 632 .checkentry = checkentry, 633 .matchsize = sizeof(struct ipt_webstr_info), 634 .me = THIS_MODULE 635 }, 636 637}; 638 639static int __init init(void) 640{ 641 return xt_register_matches(xt_webstr_match, ARRAY_SIZE(xt_webstr_match)); 642} 643 644static void __exit fini(void) 645{ 646 xt_unregister_matches(xt_webstr_match, ARRAY_SIZE(xt_webstr_match)); 647} 648 649module_init(init); 650module_exit(fini); 651