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.34 738 lines 19 kB view raw
1/* 2 * Copyright (c) 2006 Patrick McHardy <kaber@trash.net> 3 * Copyright © CC Computer Consultants GmbH, 2007 - 2008 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License version 2 as 7 * published by the Free Software Foundation. 8 * 9 * This is a replacement of the old ipt_recent module, which carried the 10 * following copyright notice: 11 * 12 * Author: Stephen Frost <sfrost@snowman.net> 13 * Copyright 2002-2003, Stephen Frost, 2.5.x port by laforge@netfilter.org 14 */ 15#include <linux/init.h> 16#include <linux/ip.h> 17#include <linux/ipv6.h> 18#include <linux/module.h> 19#include <linux/moduleparam.h> 20#include <linux/proc_fs.h> 21#include <linux/seq_file.h> 22#include <linux/string.h> 23#include <linux/ctype.h> 24#include <linux/list.h> 25#include <linux/random.h> 26#include <linux/jhash.h> 27#include <linux/bitops.h> 28#include <linux/skbuff.h> 29#include <linux/inet.h> 30#include <linux/slab.h> 31#include <net/net_namespace.h> 32#include <net/netns/generic.h> 33 34#include <linux/netfilter/x_tables.h> 35#include <linux/netfilter/xt_recent.h> 36 37MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>"); 38MODULE_AUTHOR("Jan Engelhardt <jengelh@computergmbh.de>"); 39MODULE_DESCRIPTION("Xtables: \"recently-seen\" host matching for IPv4"); 40MODULE_LICENSE("GPL"); 41MODULE_ALIAS("ipt_recent"); 42MODULE_ALIAS("ip6t_recent"); 43 44static unsigned int ip_list_tot = 100; 45static unsigned int ip_pkt_list_tot = 20; 46static unsigned int ip_list_hash_size = 0; 47static unsigned int ip_list_perms = 0644; 48static unsigned int ip_list_uid = 0; 49static unsigned int ip_list_gid = 0; 50module_param(ip_list_tot, uint, 0400); 51module_param(ip_pkt_list_tot, uint, 0400); 52module_param(ip_list_hash_size, uint, 0400); 53module_param(ip_list_perms, uint, 0400); 54module_param(ip_list_uid, uint, 0400); 55module_param(ip_list_gid, uint, 0400); 56MODULE_PARM_DESC(ip_list_tot, "number of IPs to remember per list"); 57MODULE_PARM_DESC(ip_pkt_list_tot, "number of packets per IP address to remember (max. 255)"); 58MODULE_PARM_DESC(ip_list_hash_size, "size of hash table used to look up IPs"); 59MODULE_PARM_DESC(ip_list_perms, "permissions on /proc/net/xt_recent/* files"); 60MODULE_PARM_DESC(ip_list_uid,"owner of /proc/net/xt_recent/* files"); 61MODULE_PARM_DESC(ip_list_gid,"owning group of /proc/net/xt_recent/* files"); 62 63struct recent_entry { 64 struct list_head list; 65 struct list_head lru_list; 66 union nf_inet_addr addr; 67 u_int16_t family; 68 u_int8_t ttl; 69 u_int8_t index; 70 u_int16_t nstamps; 71 unsigned long stamps[0]; 72}; 73 74struct recent_table { 75 struct list_head list; 76 char name[XT_RECENT_NAME_LEN]; 77 unsigned int refcnt; 78 unsigned int entries; 79 struct list_head lru_list; 80 struct list_head iphash[0]; 81}; 82 83struct recent_net { 84 struct list_head tables; 85#ifdef CONFIG_PROC_FS 86 struct proc_dir_entry *xt_recent; 87#ifdef CONFIG_NETFILTER_XT_MATCH_RECENT_PROC_COMPAT 88 struct proc_dir_entry *ipt_recent; 89#endif 90#endif 91}; 92 93static int recent_net_id; 94static inline struct recent_net *recent_pernet(struct net *net) 95{ 96 return net_generic(net, recent_net_id); 97} 98 99static DEFINE_SPINLOCK(recent_lock); 100static DEFINE_MUTEX(recent_mutex); 101 102#ifdef CONFIG_PROC_FS 103static const struct file_operations recent_old_fops, recent_mt_fops; 104#endif 105 106static u_int32_t hash_rnd __read_mostly; 107static bool hash_rnd_inited __read_mostly; 108 109static inline unsigned int recent_entry_hash4(const union nf_inet_addr *addr) 110{ 111 return jhash_1word((__force u32)addr->ip, hash_rnd) & 112 (ip_list_hash_size - 1); 113} 114 115static inline unsigned int recent_entry_hash6(const union nf_inet_addr *addr) 116{ 117 return jhash2((u32 *)addr->ip6, ARRAY_SIZE(addr->ip6), hash_rnd) & 118 (ip_list_hash_size - 1); 119} 120 121static struct recent_entry * 122recent_entry_lookup(const struct recent_table *table, 123 const union nf_inet_addr *addrp, u_int16_t family, 124 u_int8_t ttl) 125{ 126 struct recent_entry *e; 127 unsigned int h; 128 129 if (family == NFPROTO_IPV4) 130 h = recent_entry_hash4(addrp); 131 else 132 h = recent_entry_hash6(addrp); 133 134 list_for_each_entry(e, &table->iphash[h], list) 135 if (e->family == family && 136 memcmp(&e->addr, addrp, sizeof(e->addr)) == 0 && 137 (ttl == e->ttl || ttl == 0 || e->ttl == 0)) 138 return e; 139 return NULL; 140} 141 142static void recent_entry_remove(struct recent_table *t, struct recent_entry *e) 143{ 144 list_del(&e->list); 145 list_del(&e->lru_list); 146 kfree(e); 147 t->entries--; 148} 149 150static struct recent_entry * 151recent_entry_init(struct recent_table *t, const union nf_inet_addr *addr, 152 u_int16_t family, u_int8_t ttl) 153{ 154 struct recent_entry *e; 155 156 if (t->entries >= ip_list_tot) { 157 e = list_entry(t->lru_list.next, struct recent_entry, lru_list); 158 recent_entry_remove(t, e); 159 } 160 e = kmalloc(sizeof(*e) + sizeof(e->stamps[0]) * ip_pkt_list_tot, 161 GFP_ATOMIC); 162 if (e == NULL) 163 return NULL; 164 memcpy(&e->addr, addr, sizeof(e->addr)); 165 e->ttl = ttl; 166 e->stamps[0] = jiffies; 167 e->nstamps = 1; 168 e->index = 1; 169 e->family = family; 170 if (family == NFPROTO_IPV4) 171 list_add_tail(&e->list, &t->iphash[recent_entry_hash4(addr)]); 172 else 173 list_add_tail(&e->list, &t->iphash[recent_entry_hash6(addr)]); 174 list_add_tail(&e->lru_list, &t->lru_list); 175 t->entries++; 176 return e; 177} 178 179static void recent_entry_update(struct recent_table *t, struct recent_entry *e) 180{ 181 e->index %= ip_pkt_list_tot; 182 e->stamps[e->index++] = jiffies; 183 if (e->index > e->nstamps) 184 e->nstamps = e->index; 185 list_move_tail(&e->lru_list, &t->lru_list); 186} 187 188static struct recent_table *recent_table_lookup(struct recent_net *recent_net, 189 const char *name) 190{ 191 struct recent_table *t; 192 193 list_for_each_entry(t, &recent_net->tables, list) 194 if (!strcmp(t->name, name)) 195 return t; 196 return NULL; 197} 198 199static void recent_table_flush(struct recent_table *t) 200{ 201 struct recent_entry *e, *next; 202 unsigned int i; 203 204 for (i = 0; i < ip_list_hash_size; i++) 205 list_for_each_entry_safe(e, next, &t->iphash[i], list) 206 recent_entry_remove(t, e); 207} 208 209static bool 210recent_mt(const struct sk_buff *skb, const struct xt_match_param *par) 211{ 212 struct net *net = dev_net(par->in ? par->in : par->out); 213 struct recent_net *recent_net = recent_pernet(net); 214 const struct xt_recent_mtinfo *info = par->matchinfo; 215 struct recent_table *t; 216 struct recent_entry *e; 217 union nf_inet_addr addr = {}; 218 u_int8_t ttl; 219 bool ret = info->invert; 220 221 if (par->match->family == NFPROTO_IPV4) { 222 const struct iphdr *iph = ip_hdr(skb); 223 224 if (info->side == XT_RECENT_DEST) 225 addr.ip = iph->daddr; 226 else 227 addr.ip = iph->saddr; 228 229 ttl = iph->ttl; 230 } else { 231 const struct ipv6hdr *iph = ipv6_hdr(skb); 232 233 if (info->side == XT_RECENT_DEST) 234 memcpy(&addr.in6, &iph->daddr, sizeof(addr.in6)); 235 else 236 memcpy(&addr.in6, &iph->saddr, sizeof(addr.in6)); 237 238 ttl = iph->hop_limit; 239 } 240 241 /* use TTL as seen before forwarding */ 242 if (par->out != NULL && skb->sk == NULL) 243 ttl++; 244 245 spin_lock_bh(&recent_lock); 246 t = recent_table_lookup(recent_net, info->name); 247 e = recent_entry_lookup(t, &addr, par->match->family, 248 (info->check_set & XT_RECENT_TTL) ? ttl : 0); 249 if (e == NULL) { 250 if (!(info->check_set & XT_RECENT_SET)) 251 goto out; 252 e = recent_entry_init(t, &addr, par->match->family, ttl); 253 if (e == NULL) 254 *par->hotdrop = true; 255 ret = !ret; 256 goto out; 257 } 258 259 if (info->check_set & XT_RECENT_SET) 260 ret = !ret; 261 else if (info->check_set & XT_RECENT_REMOVE) { 262 recent_entry_remove(t, e); 263 ret = !ret; 264 } else if (info->check_set & (XT_RECENT_CHECK | XT_RECENT_UPDATE)) { 265 unsigned long time = jiffies - info->seconds * HZ; 266 unsigned int i, hits = 0; 267 268 for (i = 0; i < e->nstamps; i++) { 269 if (info->seconds && time_after(time, e->stamps[i])) 270 continue; 271 if (!info->hit_count || ++hits >= info->hit_count) { 272 ret = !ret; 273 break; 274 } 275 } 276 } 277 278 if (info->check_set & XT_RECENT_SET || 279 (info->check_set & XT_RECENT_UPDATE && ret)) { 280 recent_entry_update(t, e); 281 e->ttl = ttl; 282 } 283out: 284 spin_unlock_bh(&recent_lock); 285 return ret; 286} 287 288static bool recent_mt_check(const struct xt_mtchk_param *par) 289{ 290 struct recent_net *recent_net = recent_pernet(par->net); 291 const struct xt_recent_mtinfo *info = par->matchinfo; 292 struct recent_table *t; 293#ifdef CONFIG_PROC_FS 294 struct proc_dir_entry *pde; 295#endif 296 unsigned i; 297 bool ret = false; 298 299 if (unlikely(!hash_rnd_inited)) { 300 get_random_bytes(&hash_rnd, sizeof(hash_rnd)); 301 hash_rnd_inited = true; 302 } 303 if (hweight8(info->check_set & 304 (XT_RECENT_SET | XT_RECENT_REMOVE | 305 XT_RECENT_CHECK | XT_RECENT_UPDATE)) != 1) 306 return false; 307 if ((info->check_set & (XT_RECENT_SET | XT_RECENT_REMOVE)) && 308 (info->seconds || info->hit_count)) 309 return false; 310 if (info->hit_count > ip_pkt_list_tot) { 311 pr_info(KBUILD_MODNAME ": hitcount (%u) is larger than " 312 "packets to be remembered (%u)\n", 313 info->hit_count, ip_pkt_list_tot); 314 return false; 315 } 316 if (info->name[0] == '\0' || 317 strnlen(info->name, XT_RECENT_NAME_LEN) == XT_RECENT_NAME_LEN) 318 return false; 319 320 mutex_lock(&recent_mutex); 321 t = recent_table_lookup(recent_net, info->name); 322 if (t != NULL) { 323 t->refcnt++; 324 ret = true; 325 goto out; 326 } 327 328 t = kzalloc(sizeof(*t) + sizeof(t->iphash[0]) * ip_list_hash_size, 329 GFP_KERNEL); 330 if (t == NULL) 331 goto out; 332 t->refcnt = 1; 333 strcpy(t->name, info->name); 334 INIT_LIST_HEAD(&t->lru_list); 335 for (i = 0; i < ip_list_hash_size; i++) 336 INIT_LIST_HEAD(&t->iphash[i]); 337#ifdef CONFIG_PROC_FS 338 pde = proc_create_data(t->name, ip_list_perms, recent_net->xt_recent, 339 &recent_mt_fops, t); 340 if (pde == NULL) { 341 kfree(t); 342 goto out; 343 } 344 pde->uid = ip_list_uid; 345 pde->gid = ip_list_gid; 346#ifdef CONFIG_NETFILTER_XT_MATCH_RECENT_PROC_COMPAT 347 pde = proc_create_data(t->name, ip_list_perms, recent_net->ipt_recent, 348 &recent_old_fops, t); 349 if (pde == NULL) { 350 remove_proc_entry(t->name, recent_net->xt_recent); 351 kfree(t); 352 goto out; 353 } 354 pde->uid = ip_list_uid; 355 pde->gid = ip_list_gid; 356#endif 357#endif 358 spin_lock_bh(&recent_lock); 359 list_add_tail(&t->list, &recent_net->tables); 360 spin_unlock_bh(&recent_lock); 361 ret = true; 362out: 363 mutex_unlock(&recent_mutex); 364 return ret; 365} 366 367static void recent_mt_destroy(const struct xt_mtdtor_param *par) 368{ 369 struct recent_net *recent_net = recent_pernet(par->net); 370 const struct xt_recent_mtinfo *info = par->matchinfo; 371 struct recent_table *t; 372 373 mutex_lock(&recent_mutex); 374 t = recent_table_lookup(recent_net, info->name); 375 if (--t->refcnt == 0) { 376 spin_lock_bh(&recent_lock); 377 list_del(&t->list); 378 spin_unlock_bh(&recent_lock); 379#ifdef CONFIG_PROC_FS 380#ifdef CONFIG_NETFILTER_XT_MATCH_RECENT_PROC_COMPAT 381 remove_proc_entry(t->name, recent_net->ipt_recent); 382#endif 383 remove_proc_entry(t->name, recent_net->xt_recent); 384#endif 385 recent_table_flush(t); 386 kfree(t); 387 } 388 mutex_unlock(&recent_mutex); 389} 390 391#ifdef CONFIG_PROC_FS 392struct recent_iter_state { 393 const struct recent_table *table; 394 unsigned int bucket; 395}; 396 397static void *recent_seq_start(struct seq_file *seq, loff_t *pos) 398 __acquires(recent_lock) 399{ 400 struct recent_iter_state *st = seq->private; 401 const struct recent_table *t = st->table; 402 struct recent_entry *e; 403 loff_t p = *pos; 404 405 spin_lock_bh(&recent_lock); 406 407 for (st->bucket = 0; st->bucket < ip_list_hash_size; st->bucket++) 408 list_for_each_entry(e, &t->iphash[st->bucket], list) 409 if (p-- == 0) 410 return e; 411 return NULL; 412} 413 414static void *recent_seq_next(struct seq_file *seq, void *v, loff_t *pos) 415{ 416 struct recent_iter_state *st = seq->private; 417 const struct recent_table *t = st->table; 418 const struct recent_entry *e = v; 419 const struct list_head *head = e->list.next; 420 421 while (head == &t->iphash[st->bucket]) { 422 if (++st->bucket >= ip_list_hash_size) 423 return NULL; 424 head = t->iphash[st->bucket].next; 425 } 426 (*pos)++; 427 return list_entry(head, struct recent_entry, list); 428} 429 430static void recent_seq_stop(struct seq_file *s, void *v) 431 __releases(recent_lock) 432{ 433 spin_unlock_bh(&recent_lock); 434} 435 436static int recent_seq_show(struct seq_file *seq, void *v) 437{ 438 const struct recent_entry *e = v; 439 unsigned int i; 440 441 i = (e->index - 1) % ip_pkt_list_tot; 442 if (e->family == NFPROTO_IPV4) 443 seq_printf(seq, "src=%pI4 ttl: %u last_seen: %lu oldest_pkt: %u", 444 &e->addr.ip, e->ttl, e->stamps[i], e->index); 445 else 446 seq_printf(seq, "src=%pI6 ttl: %u last_seen: %lu oldest_pkt: %u", 447 &e->addr.in6, e->ttl, e->stamps[i], e->index); 448 for (i = 0; i < e->nstamps; i++) 449 seq_printf(seq, "%s %lu", i ? "," : "", e->stamps[i]); 450 seq_printf(seq, "\n"); 451 return 0; 452} 453 454static const struct seq_operations recent_seq_ops = { 455 .start = recent_seq_start, 456 .next = recent_seq_next, 457 .stop = recent_seq_stop, 458 .show = recent_seq_show, 459}; 460 461static int recent_seq_open(struct inode *inode, struct file *file) 462{ 463 struct proc_dir_entry *pde = PDE(inode); 464 struct recent_iter_state *st; 465 466 st = __seq_open_private(file, &recent_seq_ops, sizeof(*st)); 467 if (st == NULL) 468 return -ENOMEM; 469 470 st->table = pde->data; 471 return 0; 472} 473 474#ifdef CONFIG_NETFILTER_XT_MATCH_RECENT_PROC_COMPAT 475static int recent_old_seq_open(struct inode *inode, struct file *filp) 476{ 477 static bool warned_of_old; 478 479 if (unlikely(!warned_of_old)) { 480 printk(KERN_INFO KBUILD_MODNAME ": Use of /proc/net/ipt_recent" 481 " is deprecated; use /proc/net/xt_recent.\n"); 482 warned_of_old = true; 483 } 484 return recent_seq_open(inode, filp); 485} 486 487static ssize_t recent_old_proc_write(struct file *file, 488 const char __user *input, 489 size_t size, loff_t *loff) 490{ 491 const struct proc_dir_entry *pde = PDE(file->f_path.dentry->d_inode); 492 struct recent_table *t = pde->data; 493 struct recent_entry *e; 494 char buf[sizeof("+255.255.255.255")], *c = buf; 495 union nf_inet_addr addr = {}; 496 int add; 497 498 if (size > sizeof(buf)) 499 size = sizeof(buf); 500 if (copy_from_user(buf, input, size)) 501 return -EFAULT; 502 503 c = skip_spaces(c); 504 505 if (size - (c - buf) < 5) 506 return c - buf; 507 if (!strncmp(c, "clear", 5)) { 508 c += 5; 509 spin_lock_bh(&recent_lock); 510 recent_table_flush(t); 511 spin_unlock_bh(&recent_lock); 512 return c - buf; 513 } 514 515 switch (*c) { 516 case '-': 517 add = 0; 518 c++; 519 break; 520 case '+': 521 c++; 522 default: 523 add = 1; 524 break; 525 } 526 addr.ip = in_aton(c); 527 528 spin_lock_bh(&recent_lock); 529 e = recent_entry_lookup(t, &addr, NFPROTO_IPV4, 0); 530 if (e == NULL) { 531 if (add) 532 recent_entry_init(t, &addr, NFPROTO_IPV4, 0); 533 } else { 534 if (add) 535 recent_entry_update(t, e); 536 else 537 recent_entry_remove(t, e); 538 } 539 spin_unlock_bh(&recent_lock); 540 return size; 541} 542 543static const struct file_operations recent_old_fops = { 544 .open = recent_old_seq_open, 545 .read = seq_read, 546 .write = recent_old_proc_write, 547 .release = seq_release_private, 548 .owner = THIS_MODULE, 549}; 550#endif 551 552static ssize_t 553recent_mt_proc_write(struct file *file, const char __user *input, 554 size_t size, loff_t *loff) 555{ 556 const struct proc_dir_entry *pde = PDE(file->f_path.dentry->d_inode); 557 struct recent_table *t = pde->data; 558 struct recent_entry *e; 559 char buf[sizeof("+b335:1d35:1e55:dead:c0de:1715:5afe:c0de")]; 560 const char *c = buf; 561 union nf_inet_addr addr = {}; 562 u_int16_t family; 563 bool add, succ; 564 565 if (size == 0) 566 return 0; 567 if (size > sizeof(buf)) 568 size = sizeof(buf); 569 if (copy_from_user(buf, input, size) != 0) 570 return -EFAULT; 571 572 /* Strict protocol! */ 573 if (*loff != 0) 574 return -ESPIPE; 575 switch (*c) { 576 case '/': /* flush table */ 577 spin_lock_bh(&recent_lock); 578 recent_table_flush(t); 579 spin_unlock_bh(&recent_lock); 580 return size; 581 case '-': /* remove address */ 582 add = false; 583 break; 584 case '+': /* add address */ 585 add = true; 586 break; 587 default: 588 printk(KERN_INFO KBUILD_MODNAME ": Need +ip, -ip or /\n"); 589 return -EINVAL; 590 } 591 592 ++c; 593 --size; 594 if (strnchr(c, size, ':') != NULL) { 595 family = NFPROTO_IPV6; 596 succ = in6_pton(c, size, (void *)&addr, '\n', NULL); 597 } else { 598 family = NFPROTO_IPV4; 599 succ = in4_pton(c, size, (void *)&addr, '\n', NULL); 600 } 601 602 if (!succ) { 603 printk(KERN_INFO KBUILD_MODNAME ": illegal address written " 604 "to procfs\n"); 605 return -EINVAL; 606 } 607 608 spin_lock_bh(&recent_lock); 609 e = recent_entry_lookup(t, &addr, family, 0); 610 if (e == NULL) { 611 if (add) 612 recent_entry_init(t, &addr, family, 0); 613 } else { 614 if (add) 615 recent_entry_update(t, e); 616 else 617 recent_entry_remove(t, e); 618 } 619 spin_unlock_bh(&recent_lock); 620 /* Note we removed one above */ 621 *loff += size + 1; 622 return size + 1; 623} 624 625static const struct file_operations recent_mt_fops = { 626 .open = recent_seq_open, 627 .read = seq_read, 628 .write = recent_mt_proc_write, 629 .release = seq_release_private, 630 .owner = THIS_MODULE, 631}; 632 633static int __net_init recent_proc_net_init(struct net *net) 634{ 635 struct recent_net *recent_net = recent_pernet(net); 636 637 recent_net->xt_recent = proc_mkdir("xt_recent", net->proc_net); 638 if (!recent_net->xt_recent) 639 return -ENOMEM; 640#ifdef CONFIG_NETFILTER_XT_MATCH_RECENT_PROC_COMPAT 641 recent_net->ipt_recent = proc_mkdir("ipt_recent", net->proc_net); 642 if (!recent_net->ipt_recent) { 643 proc_net_remove(net, "xt_recent"); 644 return -ENOMEM; 645 } 646#endif 647 return 0; 648} 649 650static void __net_exit recent_proc_net_exit(struct net *net) 651{ 652#ifdef CONFIG_NETFILTER_XT_MATCH_RECENT_PROC_COMPAT 653 proc_net_remove(net, "ipt_recent"); 654#endif 655 proc_net_remove(net, "xt_recent"); 656} 657#else 658static inline int recent_proc_net_init(struct net *net) 659{ 660 return 0; 661} 662 663static inline void recent_proc_net_exit(struct net *net) 664{ 665} 666#endif /* CONFIG_PROC_FS */ 667 668static int __net_init recent_net_init(struct net *net) 669{ 670 struct recent_net *recent_net = recent_pernet(net); 671 672 INIT_LIST_HEAD(&recent_net->tables); 673 return recent_proc_net_init(net); 674} 675 676static void __net_exit recent_net_exit(struct net *net) 677{ 678 struct recent_net *recent_net = recent_pernet(net); 679 680 BUG_ON(!list_empty(&recent_net->tables)); 681 recent_proc_net_exit(net); 682} 683 684static struct pernet_operations recent_net_ops = { 685 .init = recent_net_init, 686 .exit = recent_net_exit, 687 .id = &recent_net_id, 688 .size = sizeof(struct recent_net), 689}; 690 691static struct xt_match recent_mt_reg[] __read_mostly = { 692 { 693 .name = "recent", 694 .revision = 0, 695 .family = NFPROTO_IPV4, 696 .match = recent_mt, 697 .matchsize = sizeof(struct xt_recent_mtinfo), 698 .checkentry = recent_mt_check, 699 .destroy = recent_mt_destroy, 700 .me = THIS_MODULE, 701 }, 702 { 703 .name = "recent", 704 .revision = 0, 705 .family = NFPROTO_IPV6, 706 .match = recent_mt, 707 .matchsize = sizeof(struct xt_recent_mtinfo), 708 .checkentry = recent_mt_check, 709 .destroy = recent_mt_destroy, 710 .me = THIS_MODULE, 711 }, 712}; 713 714static int __init recent_mt_init(void) 715{ 716 int err; 717 718 if (!ip_list_tot || !ip_pkt_list_tot || ip_pkt_list_tot > 255) 719 return -EINVAL; 720 ip_list_hash_size = 1 << fls(ip_list_tot); 721 722 err = register_pernet_subsys(&recent_net_ops); 723 if (err) 724 return err; 725 err = xt_register_matches(recent_mt_reg, ARRAY_SIZE(recent_mt_reg)); 726 if (err) 727 unregister_pernet_subsys(&recent_net_ops); 728 return err; 729} 730 731static void __exit recent_mt_exit(void) 732{ 733 xt_unregister_matches(recent_mt_reg, ARRAY_SIZE(recent_mt_reg)); 734 unregister_pernet_subsys(&recent_net_ops); 735} 736 737module_init(recent_mt_init); 738module_exit(recent_mt_exit);