Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v2.6.18 800 lines 18 kB view raw
1/* 2 * x_tables core - Backend for {ip,ip6,arp}_tables 3 * 4 * Copyright (C) 2006-2006 Harald Welte <laforge@netfilter.org> 5 * 6 * Based on existing ip_tables code which is 7 * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling 8 * Copyright (C) 2000-2005 Netfilter Core Team <coreteam@netfilter.org> 9 * 10 * This program is free software; you can redistribute it and/or modify 11 * it under the terms of the GNU General Public License version 2 as 12 * published by the Free Software Foundation. 13 * 14 */ 15 16#include <linux/kernel.h> 17#include <linux/socket.h> 18#include <linux/net.h> 19#include <linux/proc_fs.h> 20#include <linux/seq_file.h> 21#include <linux/string.h> 22#include <linux/vmalloc.h> 23#include <linux/mutex.h> 24 25#include <linux/netfilter/x_tables.h> 26#include <linux/netfilter_arp.h> 27 28 29MODULE_LICENSE("GPL"); 30MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>"); 31MODULE_DESCRIPTION("[ip,ip6,arp]_tables backend module"); 32 33#define SMP_ALIGN(x) (((x) + SMP_CACHE_BYTES-1) & ~(SMP_CACHE_BYTES-1)) 34 35struct xt_af { 36 struct mutex mutex; 37 struct list_head match; 38 struct list_head target; 39 struct list_head tables; 40 struct mutex compat_mutex; 41}; 42 43static struct xt_af *xt; 44 45#ifdef DEBUG_IP_FIREWALL_USER 46#define duprintf(format, args...) printk(format , ## args) 47#else 48#define duprintf(format, args...) 49#endif 50 51enum { 52 TABLE, 53 TARGET, 54 MATCH, 55}; 56 57static const char *xt_prefix[NPROTO] = { 58 [AF_INET] = "ip", 59 [AF_INET6] = "ip6", 60 [NF_ARP] = "arp", 61}; 62 63/* Registration hooks for targets. */ 64int 65xt_register_target(struct xt_target *target) 66{ 67 int ret, af = target->family; 68 69 ret = mutex_lock_interruptible(&xt[af].mutex); 70 if (ret != 0) 71 return ret; 72 list_add(&target->list, &xt[af].target); 73 mutex_unlock(&xt[af].mutex); 74 return ret; 75} 76EXPORT_SYMBOL(xt_register_target); 77 78void 79xt_unregister_target(struct xt_target *target) 80{ 81 int af = target->family; 82 83 mutex_lock(&xt[af].mutex); 84 LIST_DELETE(&xt[af].target, target); 85 mutex_unlock(&xt[af].mutex); 86} 87EXPORT_SYMBOL(xt_unregister_target); 88 89int 90xt_register_match(struct xt_match *match) 91{ 92 int ret, af = match->family; 93 94 ret = mutex_lock_interruptible(&xt[af].mutex); 95 if (ret != 0) 96 return ret; 97 98 list_add(&match->list, &xt[af].match); 99 mutex_unlock(&xt[af].mutex); 100 101 return ret; 102} 103EXPORT_SYMBOL(xt_register_match); 104 105void 106xt_unregister_match(struct xt_match *match) 107{ 108 int af = match->family; 109 110 mutex_lock(&xt[af].mutex); 111 LIST_DELETE(&xt[af].match, match); 112 mutex_unlock(&xt[af].mutex); 113} 114EXPORT_SYMBOL(xt_unregister_match); 115 116 117/* 118 * These are weird, but module loading must not be done with mutex 119 * held (since they will register), and we have to have a single 120 * function to use try_then_request_module(). 121 */ 122 123/* Find match, grabs ref. Returns ERR_PTR() on error. */ 124struct xt_match *xt_find_match(int af, const char *name, u8 revision) 125{ 126 struct xt_match *m; 127 int err = 0; 128 129 if (mutex_lock_interruptible(&xt[af].mutex) != 0) 130 return ERR_PTR(-EINTR); 131 132 list_for_each_entry(m, &xt[af].match, list) { 133 if (strcmp(m->name, name) == 0) { 134 if (m->revision == revision) { 135 if (try_module_get(m->me)) { 136 mutex_unlock(&xt[af].mutex); 137 return m; 138 } 139 } else 140 err = -EPROTOTYPE; /* Found something. */ 141 } 142 } 143 mutex_unlock(&xt[af].mutex); 144 return ERR_PTR(err); 145} 146EXPORT_SYMBOL(xt_find_match); 147 148/* Find target, grabs ref. Returns ERR_PTR() on error. */ 149struct xt_target *xt_find_target(int af, const char *name, u8 revision) 150{ 151 struct xt_target *t; 152 int err = 0; 153 154 if (mutex_lock_interruptible(&xt[af].mutex) != 0) 155 return ERR_PTR(-EINTR); 156 157 list_for_each_entry(t, &xt[af].target, list) { 158 if (strcmp(t->name, name) == 0) { 159 if (t->revision == revision) { 160 if (try_module_get(t->me)) { 161 mutex_unlock(&xt[af].mutex); 162 return t; 163 } 164 } else 165 err = -EPROTOTYPE; /* Found something. */ 166 } 167 } 168 mutex_unlock(&xt[af].mutex); 169 return ERR_PTR(err); 170} 171EXPORT_SYMBOL(xt_find_target); 172 173struct xt_target *xt_request_find_target(int af, const char *name, u8 revision) 174{ 175 struct xt_target *target; 176 177 target = try_then_request_module(xt_find_target(af, name, revision), 178 "%st_%s", xt_prefix[af], name); 179 if (IS_ERR(target) || !target) 180 return NULL; 181 return target; 182} 183EXPORT_SYMBOL_GPL(xt_request_find_target); 184 185static int match_revfn(int af, const char *name, u8 revision, int *bestp) 186{ 187 struct xt_match *m; 188 int have_rev = 0; 189 190 list_for_each_entry(m, &xt[af].match, list) { 191 if (strcmp(m->name, name) == 0) { 192 if (m->revision > *bestp) 193 *bestp = m->revision; 194 if (m->revision == revision) 195 have_rev = 1; 196 } 197 } 198 return have_rev; 199} 200 201static int target_revfn(int af, const char *name, u8 revision, int *bestp) 202{ 203 struct xt_target *t; 204 int have_rev = 0; 205 206 list_for_each_entry(t, &xt[af].target, list) { 207 if (strcmp(t->name, name) == 0) { 208 if (t->revision > *bestp) 209 *bestp = t->revision; 210 if (t->revision == revision) 211 have_rev = 1; 212 } 213 } 214 return have_rev; 215} 216 217/* Returns true or false (if no such extension at all) */ 218int xt_find_revision(int af, const char *name, u8 revision, int target, 219 int *err) 220{ 221 int have_rev, best = -1; 222 223 if (mutex_lock_interruptible(&xt[af].mutex) != 0) { 224 *err = -EINTR; 225 return 1; 226 } 227 if (target == 1) 228 have_rev = target_revfn(af, name, revision, &best); 229 else 230 have_rev = match_revfn(af, name, revision, &best); 231 mutex_unlock(&xt[af].mutex); 232 233 /* Nothing at all? Return 0 to try loading module. */ 234 if (best == -1) { 235 *err = -ENOENT; 236 return 0; 237 } 238 239 *err = best; 240 if (!have_rev) 241 *err = -EPROTONOSUPPORT; 242 return 1; 243} 244EXPORT_SYMBOL_GPL(xt_find_revision); 245 246int xt_check_match(const struct xt_match *match, unsigned short family, 247 unsigned int size, const char *table, unsigned int hook_mask, 248 unsigned short proto, int inv_proto) 249{ 250 if (XT_ALIGN(match->matchsize) != size) { 251 printk("%s_tables: %s match: invalid size %Zu != %u\n", 252 xt_prefix[family], match->name, 253 XT_ALIGN(match->matchsize), size); 254 return -EINVAL; 255 } 256 if (match->table && strcmp(match->table, table)) { 257 printk("%s_tables: %s match: only valid in %s table, not %s\n", 258 xt_prefix[family], match->name, match->table, table); 259 return -EINVAL; 260 } 261 if (match->hooks && (hook_mask & ~match->hooks) != 0) { 262 printk("%s_tables: %s match: bad hook_mask %u\n", 263 xt_prefix[family], match->name, hook_mask); 264 return -EINVAL; 265 } 266 if (match->proto && (match->proto != proto || inv_proto)) { 267 printk("%s_tables: %s match: only valid for protocol %u\n", 268 xt_prefix[family], match->name, match->proto); 269 return -EINVAL; 270 } 271 return 0; 272} 273EXPORT_SYMBOL_GPL(xt_check_match); 274 275#ifdef CONFIG_COMPAT 276int xt_compat_match(void *match, void **dstptr, int *size, int convert) 277{ 278 struct xt_match *m; 279 struct compat_xt_entry_match *pcompat_m; 280 struct xt_entry_match *pm; 281 u_int16_t msize; 282 int off, ret; 283 284 ret = 0; 285 m = ((struct xt_entry_match *)match)->u.kernel.match; 286 off = XT_ALIGN(m->matchsize) - COMPAT_XT_ALIGN(m->matchsize); 287 switch (convert) { 288 case COMPAT_TO_USER: 289 pm = (struct xt_entry_match *)match; 290 msize = pm->u.user.match_size; 291 if (copy_to_user(*dstptr, pm, msize)) { 292 ret = -EFAULT; 293 break; 294 } 295 msize -= off; 296 if (put_user(msize, (u_int16_t *)*dstptr)) 297 ret = -EFAULT; 298 *size -= off; 299 *dstptr += msize; 300 break; 301 case COMPAT_FROM_USER: 302 pcompat_m = (struct compat_xt_entry_match *)match; 303 pm = (struct xt_entry_match *)*dstptr; 304 msize = pcompat_m->u.user.match_size; 305 memcpy(pm, pcompat_m, msize); 306 msize += off; 307 pm->u.user.match_size = msize; 308 *size += off; 309 *dstptr += msize; 310 break; 311 case COMPAT_CALC_SIZE: 312 *size += off; 313 break; 314 default: 315 ret = -ENOPROTOOPT; 316 break; 317 } 318 return ret; 319} 320EXPORT_SYMBOL_GPL(xt_compat_match); 321#endif 322 323int xt_check_target(const struct xt_target *target, unsigned short family, 324 unsigned int size, const char *table, unsigned int hook_mask, 325 unsigned short proto, int inv_proto) 326{ 327 if (XT_ALIGN(target->targetsize) != size) { 328 printk("%s_tables: %s target: invalid size %Zu != %u\n", 329 xt_prefix[family], target->name, 330 XT_ALIGN(target->targetsize), size); 331 return -EINVAL; 332 } 333 if (target->table && strcmp(target->table, table)) { 334 printk("%s_tables: %s target: only valid in %s table, not %s\n", 335 xt_prefix[family], target->name, target->table, table); 336 return -EINVAL; 337 } 338 if (target->hooks && (hook_mask & ~target->hooks) != 0) { 339 printk("%s_tables: %s target: bad hook_mask %u\n", 340 xt_prefix[family], target->name, hook_mask); 341 return -EINVAL; 342 } 343 if (target->proto && (target->proto != proto || inv_proto)) { 344 printk("%s_tables: %s target: only valid for protocol %u\n", 345 xt_prefix[family], target->name, target->proto); 346 return -EINVAL; 347 } 348 return 0; 349} 350EXPORT_SYMBOL_GPL(xt_check_target); 351 352#ifdef CONFIG_COMPAT 353int xt_compat_target(void *target, void **dstptr, int *size, int convert) 354{ 355 struct xt_target *t; 356 struct compat_xt_entry_target *pcompat; 357 struct xt_entry_target *pt; 358 u_int16_t tsize; 359 int off, ret; 360 361 ret = 0; 362 t = ((struct xt_entry_target *)target)->u.kernel.target; 363 off = XT_ALIGN(t->targetsize) - COMPAT_XT_ALIGN(t->targetsize); 364 switch (convert) { 365 case COMPAT_TO_USER: 366 pt = (struct xt_entry_target *)target; 367 tsize = pt->u.user.target_size; 368 if (copy_to_user(*dstptr, pt, tsize)) { 369 ret = -EFAULT; 370 break; 371 } 372 tsize -= off; 373 if (put_user(tsize, (u_int16_t *)*dstptr)) 374 ret = -EFAULT; 375 *size -= off; 376 *dstptr += tsize; 377 break; 378 case COMPAT_FROM_USER: 379 pcompat = (struct compat_xt_entry_target *)target; 380 pt = (struct xt_entry_target *)*dstptr; 381 tsize = pcompat->u.user.target_size; 382 memcpy(pt, pcompat, tsize); 383 tsize += off; 384 pt->u.user.target_size = tsize; 385 *size += off; 386 *dstptr += tsize; 387 break; 388 case COMPAT_CALC_SIZE: 389 *size += off; 390 break; 391 default: 392 ret = -ENOPROTOOPT; 393 break; 394 } 395 return ret; 396} 397EXPORT_SYMBOL_GPL(xt_compat_target); 398#endif 399 400struct xt_table_info *xt_alloc_table_info(unsigned int size) 401{ 402 struct xt_table_info *newinfo; 403 int cpu; 404 405 /* Pedantry: prevent them from hitting BUG() in vmalloc.c --RR */ 406 if ((SMP_ALIGN(size) >> PAGE_SHIFT) + 2 > num_physpages) 407 return NULL; 408 409 newinfo = kzalloc(sizeof(struct xt_table_info), GFP_KERNEL); 410 if (!newinfo) 411 return NULL; 412 413 newinfo->size = size; 414 415 for_each_possible_cpu(cpu) { 416 if (size <= PAGE_SIZE) 417 newinfo->entries[cpu] = kmalloc_node(size, 418 GFP_KERNEL, 419 cpu_to_node(cpu)); 420 else 421 newinfo->entries[cpu] = vmalloc_node(size, 422 cpu_to_node(cpu)); 423 424 if (newinfo->entries[cpu] == NULL) { 425 xt_free_table_info(newinfo); 426 return NULL; 427 } 428 } 429 430 return newinfo; 431} 432EXPORT_SYMBOL(xt_alloc_table_info); 433 434void xt_free_table_info(struct xt_table_info *info) 435{ 436 int cpu; 437 438 for_each_possible_cpu(cpu) { 439 if (info->size <= PAGE_SIZE) 440 kfree(info->entries[cpu]); 441 else 442 vfree(info->entries[cpu]); 443 } 444 kfree(info); 445} 446EXPORT_SYMBOL(xt_free_table_info); 447 448/* Find table by name, grabs mutex & ref. Returns ERR_PTR() on error. */ 449struct xt_table *xt_find_table_lock(int af, const char *name) 450{ 451 struct xt_table *t; 452 453 if (mutex_lock_interruptible(&xt[af].mutex) != 0) 454 return ERR_PTR(-EINTR); 455 456 list_for_each_entry(t, &xt[af].tables, list) 457 if (strcmp(t->name, name) == 0 && try_module_get(t->me)) 458 return t; 459 mutex_unlock(&xt[af].mutex); 460 return NULL; 461} 462EXPORT_SYMBOL_GPL(xt_find_table_lock); 463 464void xt_table_unlock(struct xt_table *table) 465{ 466 mutex_unlock(&xt[table->af].mutex); 467} 468EXPORT_SYMBOL_GPL(xt_table_unlock); 469 470#ifdef CONFIG_COMPAT 471void xt_compat_lock(int af) 472{ 473 mutex_lock(&xt[af].compat_mutex); 474} 475EXPORT_SYMBOL_GPL(xt_compat_lock); 476 477void xt_compat_unlock(int af) 478{ 479 mutex_unlock(&xt[af].compat_mutex); 480} 481EXPORT_SYMBOL_GPL(xt_compat_unlock); 482#endif 483 484struct xt_table_info * 485xt_replace_table(struct xt_table *table, 486 unsigned int num_counters, 487 struct xt_table_info *newinfo, 488 int *error) 489{ 490 struct xt_table_info *oldinfo, *private; 491 492 /* Do the substitution. */ 493 write_lock_bh(&table->lock); 494 private = table->private; 495 /* Check inside lock: is the old number correct? */ 496 if (num_counters != private->number) { 497 duprintf("num_counters != table->private->number (%u/%u)\n", 498 num_counters, private->number); 499 write_unlock_bh(&table->lock); 500 *error = -EAGAIN; 501 return NULL; 502 } 503 oldinfo = private; 504 table->private = newinfo; 505 newinfo->initial_entries = oldinfo->initial_entries; 506 write_unlock_bh(&table->lock); 507 508 return oldinfo; 509} 510EXPORT_SYMBOL_GPL(xt_replace_table); 511 512int xt_register_table(struct xt_table *table, 513 struct xt_table_info *bootstrap, 514 struct xt_table_info *newinfo) 515{ 516 int ret; 517 struct xt_table_info *private; 518 519 ret = mutex_lock_interruptible(&xt[table->af].mutex); 520 if (ret != 0) 521 return ret; 522 523 /* Don't autoload: we'd eat our tail... */ 524 if (list_named_find(&xt[table->af].tables, table->name)) { 525 ret = -EEXIST; 526 goto unlock; 527 } 528 529 /* Simplifies replace_table code. */ 530 table->private = bootstrap; 531 rwlock_init(&table->lock); 532 if (!xt_replace_table(table, 0, newinfo, &ret)) 533 goto unlock; 534 535 private = table->private; 536 duprintf("table->private->number = %u\n", private->number); 537 538 /* save number of initial entries */ 539 private->initial_entries = private->number; 540 541 list_prepend(&xt[table->af].tables, table); 542 543 ret = 0; 544 unlock: 545 mutex_unlock(&xt[table->af].mutex); 546 return ret; 547} 548EXPORT_SYMBOL_GPL(xt_register_table); 549 550void *xt_unregister_table(struct xt_table *table) 551{ 552 struct xt_table_info *private; 553 554 mutex_lock(&xt[table->af].mutex); 555 private = table->private; 556 LIST_DELETE(&xt[table->af].tables, table); 557 mutex_unlock(&xt[table->af].mutex); 558 559 return private; 560} 561EXPORT_SYMBOL_GPL(xt_unregister_table); 562 563#ifdef CONFIG_PROC_FS 564static char *xt_proto_prefix[NPROTO] = { 565 [AF_INET] = "ip", 566 [AF_INET6] = "ip6", 567 [NF_ARP] = "arp", 568}; 569 570static struct list_head *xt_get_idx(struct list_head *list, struct seq_file *seq, loff_t pos) 571{ 572 struct list_head *head = list->next; 573 574 if (!head || list_empty(list)) 575 return NULL; 576 577 while (pos && (head = head->next)) { 578 if (head == list) 579 return NULL; 580 pos--; 581 } 582 return pos ? NULL : head; 583} 584 585static struct list_head *type2list(u_int16_t af, u_int16_t type) 586{ 587 struct list_head *list; 588 589 switch (type) { 590 case TARGET: 591 list = &xt[af].target; 592 break; 593 case MATCH: 594 list = &xt[af].match; 595 break; 596 case TABLE: 597 list = &xt[af].tables; 598 break; 599 default: 600 list = NULL; 601 break; 602 } 603 604 return list; 605} 606 607static void *xt_tgt_seq_start(struct seq_file *seq, loff_t *pos) 608{ 609 struct proc_dir_entry *pde = (struct proc_dir_entry *) seq->private; 610 u_int16_t af = (unsigned long)pde->data & 0xffff; 611 u_int16_t type = (unsigned long)pde->data >> 16; 612 struct list_head *list; 613 614 if (af >= NPROTO) 615 return NULL; 616 617 list = type2list(af, type); 618 if (!list) 619 return NULL; 620 621 if (mutex_lock_interruptible(&xt[af].mutex) != 0) 622 return NULL; 623 624 return xt_get_idx(list, seq, *pos); 625} 626 627static void *xt_tgt_seq_next(struct seq_file *seq, void *v, loff_t *pos) 628{ 629 struct proc_dir_entry *pde = seq->private; 630 u_int16_t af = (unsigned long)pde->data & 0xffff; 631 u_int16_t type = (unsigned long)pde->data >> 16; 632 struct list_head *list; 633 634 if (af >= NPROTO) 635 return NULL; 636 637 list = type2list(af, type); 638 if (!list) 639 return NULL; 640 641 (*pos)++; 642 return xt_get_idx(list, seq, *pos); 643} 644 645static void xt_tgt_seq_stop(struct seq_file *seq, void *v) 646{ 647 struct proc_dir_entry *pde = seq->private; 648 u_int16_t af = (unsigned long)pde->data & 0xffff; 649 650 mutex_unlock(&xt[af].mutex); 651} 652 653static int xt_name_seq_show(struct seq_file *seq, void *v) 654{ 655 char *name = (char *)v + sizeof(struct list_head); 656 657 if (strlen(name)) 658 return seq_printf(seq, "%s\n", name); 659 else 660 return 0; 661} 662 663static struct seq_operations xt_tgt_seq_ops = { 664 .start = xt_tgt_seq_start, 665 .next = xt_tgt_seq_next, 666 .stop = xt_tgt_seq_stop, 667 .show = xt_name_seq_show, 668}; 669 670static int xt_tgt_open(struct inode *inode, struct file *file) 671{ 672 int ret; 673 674 ret = seq_open(file, &xt_tgt_seq_ops); 675 if (!ret) { 676 struct seq_file *seq = file->private_data; 677 struct proc_dir_entry *pde = PDE(inode); 678 679 seq->private = pde; 680 } 681 682 return ret; 683} 684 685static struct file_operations xt_file_ops = { 686 .owner = THIS_MODULE, 687 .open = xt_tgt_open, 688 .read = seq_read, 689 .llseek = seq_lseek, 690 .release = seq_release, 691}; 692 693#define FORMAT_TABLES "_tables_names" 694#define FORMAT_MATCHES "_tables_matches" 695#define FORMAT_TARGETS "_tables_targets" 696 697#endif /* CONFIG_PROC_FS */ 698 699int xt_proto_init(int af) 700{ 701#ifdef CONFIG_PROC_FS 702 char buf[XT_FUNCTION_MAXNAMELEN]; 703 struct proc_dir_entry *proc; 704#endif 705 706 if (af >= NPROTO) 707 return -EINVAL; 708 709 710#ifdef CONFIG_PROC_FS 711 strlcpy(buf, xt_proto_prefix[af], sizeof(buf)); 712 strlcat(buf, FORMAT_TABLES, sizeof(buf)); 713 proc = proc_net_fops_create(buf, 0440, &xt_file_ops); 714 if (!proc) 715 goto out; 716 proc->data = (void *) ((unsigned long) af | (TABLE << 16)); 717 718 719 strlcpy(buf, xt_proto_prefix[af], sizeof(buf)); 720 strlcat(buf, FORMAT_MATCHES, sizeof(buf)); 721 proc = proc_net_fops_create(buf, 0440, &xt_file_ops); 722 if (!proc) 723 goto out_remove_tables; 724 proc->data = (void *) ((unsigned long) af | (MATCH << 16)); 725 726 strlcpy(buf, xt_proto_prefix[af], sizeof(buf)); 727 strlcat(buf, FORMAT_TARGETS, sizeof(buf)); 728 proc = proc_net_fops_create(buf, 0440, &xt_file_ops); 729 if (!proc) 730 goto out_remove_matches; 731 proc->data = (void *) ((unsigned long) af | (TARGET << 16)); 732#endif 733 734 return 0; 735 736#ifdef CONFIG_PROC_FS 737out_remove_matches: 738 strlcpy(buf, xt_proto_prefix[af], sizeof(buf)); 739 strlcat(buf, FORMAT_MATCHES, sizeof(buf)); 740 proc_net_remove(buf); 741 742out_remove_tables: 743 strlcpy(buf, xt_proto_prefix[af], sizeof(buf)); 744 strlcat(buf, FORMAT_TABLES, sizeof(buf)); 745 proc_net_remove(buf); 746out: 747 return -1; 748#endif 749} 750EXPORT_SYMBOL_GPL(xt_proto_init); 751 752void xt_proto_fini(int af) 753{ 754#ifdef CONFIG_PROC_FS 755 char buf[XT_FUNCTION_MAXNAMELEN]; 756 757 strlcpy(buf, xt_proto_prefix[af], sizeof(buf)); 758 strlcat(buf, FORMAT_TABLES, sizeof(buf)); 759 proc_net_remove(buf); 760 761 strlcpy(buf, xt_proto_prefix[af], sizeof(buf)); 762 strlcat(buf, FORMAT_TARGETS, sizeof(buf)); 763 proc_net_remove(buf); 764 765 strlcpy(buf, xt_proto_prefix[af], sizeof(buf)); 766 strlcat(buf, FORMAT_MATCHES, sizeof(buf)); 767 proc_net_remove(buf); 768#endif /*CONFIG_PROC_FS*/ 769} 770EXPORT_SYMBOL_GPL(xt_proto_fini); 771 772 773static int __init xt_init(void) 774{ 775 int i; 776 777 xt = kmalloc(sizeof(struct xt_af) * NPROTO, GFP_KERNEL); 778 if (!xt) 779 return -ENOMEM; 780 781 for (i = 0; i < NPROTO; i++) { 782 mutex_init(&xt[i].mutex); 783#ifdef CONFIG_COMPAT 784 mutex_init(&xt[i].compat_mutex); 785#endif 786 INIT_LIST_HEAD(&xt[i].target); 787 INIT_LIST_HEAD(&xt[i].match); 788 INIT_LIST_HEAD(&xt[i].tables); 789 } 790 return 0; 791} 792 793static void __exit xt_fini(void) 794{ 795 kfree(xt); 796} 797 798module_init(xt_init); 799module_exit(xt_fini); 800