ipt_recent.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507
  1. /*
  2. * Copyright (c) 2006 Patrick McHardy <kaber@trash.net>
  3. *
  4. * This program is free software; you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License version 2 as
  6. * published by the Free Software Foundation.
  7. *
  8. * This is a replacement of the old ipt_recent module, which carried the
  9. * following copyright notice:
  10. *
  11. * Author: Stephen Frost <sfrost@snowman.net>
  12. * Copyright 2002-2003, Stephen Frost, 2.5.x port by laforge@netfilter.org
  13. */
  14. #include <linux/init.h>
  15. #include <linux/ip.h>
  16. #include <linux/moduleparam.h>
  17. #include <linux/proc_fs.h>
  18. #include <linux/seq_file.h>
  19. #include <linux/string.h>
  20. #include <linux/ctype.h>
  21. #include <linux/list.h>
  22. #include <linux/random.h>
  23. #include <linux/jhash.h>
  24. #include <linux/bitops.h>
  25. #include <linux/skbuff.h>
  26. #include <linux/inet.h>
  27. #include <linux/netfilter/x_tables.h>
  28. #include <linux/netfilter_ipv4/ipt_recent.h>
  29. MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
  30. MODULE_DESCRIPTION("IP tables recently seen matching module");
  31. MODULE_LICENSE("GPL");
  32. static unsigned int ip_list_tot = 100;
  33. static unsigned int ip_pkt_list_tot = 20;
  34. static unsigned int ip_list_hash_size = 0;
  35. static unsigned int ip_list_perms = 0644;
  36. static unsigned int ip_list_uid = 0;
  37. static unsigned int ip_list_gid = 0;
  38. module_param(ip_list_tot, uint, 0400);
  39. module_param(ip_pkt_list_tot, uint, 0400);
  40. module_param(ip_list_hash_size, uint, 0400);
  41. module_param(ip_list_perms, uint, 0400);
  42. module_param(ip_list_uid, uint, 0400);
  43. module_param(ip_list_gid, uint, 0400);
  44. MODULE_PARM_DESC(ip_list_tot, "number of IPs to remember per list");
  45. MODULE_PARM_DESC(ip_pkt_list_tot, "number of packets per IP to remember (max. 255)");
  46. MODULE_PARM_DESC(ip_list_hash_size, "size of hash table used to look up IPs");
  47. MODULE_PARM_DESC(ip_list_perms, "permissions on /proc/net/ipt_recent/* files");
  48. MODULE_PARM_DESC(ip_list_uid,"owner of /proc/net/ipt_recent/* files");
  49. MODULE_PARM_DESC(ip_list_gid,"owning group of /proc/net/ipt_recent/* files");
  50. struct recent_entry {
  51. struct list_head list;
  52. struct list_head lru_list;
  53. __be32 addr;
  54. u_int8_t ttl;
  55. u_int8_t index;
  56. u_int16_t nstamps;
  57. unsigned long stamps[0];
  58. };
  59. struct recent_table {
  60. struct list_head list;
  61. char name[IPT_RECENT_NAME_LEN];
  62. #ifdef CONFIG_PROC_FS
  63. struct proc_dir_entry *proc;
  64. #endif
  65. unsigned int refcnt;
  66. unsigned int entries;
  67. struct list_head lru_list;
  68. struct list_head iphash[0];
  69. };
  70. static LIST_HEAD(tables);
  71. static DEFINE_SPINLOCK(recent_lock);
  72. static DEFINE_MUTEX(recent_mutex);
  73. #ifdef CONFIG_PROC_FS
  74. static struct proc_dir_entry *proc_dir;
  75. static const struct file_operations recent_fops;
  76. #endif
  77. static u_int32_t hash_rnd;
  78. static int hash_rnd_initted;
  79. static unsigned int recent_entry_hash(__be32 addr)
  80. {
  81. if (!hash_rnd_initted) {
  82. get_random_bytes(&hash_rnd, 4);
  83. hash_rnd_initted = 1;
  84. }
  85. return jhash_1word((__force u32)addr, hash_rnd) & (ip_list_hash_size - 1);
  86. }
  87. static struct recent_entry *
  88. recent_entry_lookup(const struct recent_table *table, __be32 addr, u_int8_t ttl)
  89. {
  90. struct recent_entry *e;
  91. unsigned int h;
  92. h = recent_entry_hash(addr);
  93. list_for_each_entry(e, &table->iphash[h], list)
  94. if (e->addr == addr && (ttl == e->ttl || !ttl || !e->ttl))
  95. return e;
  96. return NULL;
  97. }
  98. static void recent_entry_remove(struct recent_table *t, struct recent_entry *e)
  99. {
  100. list_del(&e->list);
  101. list_del(&e->lru_list);
  102. kfree(e);
  103. t->entries--;
  104. }
  105. static struct recent_entry *
  106. recent_entry_init(struct recent_table *t, __be32 addr, u_int8_t ttl)
  107. {
  108. struct recent_entry *e;
  109. if (t->entries >= ip_list_tot) {
  110. e = list_entry(t->lru_list.next, struct recent_entry, lru_list);
  111. recent_entry_remove(t, e);
  112. }
  113. e = kmalloc(sizeof(*e) + sizeof(e->stamps[0]) * ip_pkt_list_tot,
  114. GFP_ATOMIC);
  115. if (e == NULL)
  116. return NULL;
  117. e->addr = addr;
  118. e->ttl = ttl;
  119. e->stamps[0] = jiffies;
  120. e->nstamps = 1;
  121. e->index = 1;
  122. list_add_tail(&e->list, &t->iphash[recent_entry_hash(addr)]);
  123. list_add_tail(&e->lru_list, &t->lru_list);
  124. t->entries++;
  125. return e;
  126. }
  127. static void recent_entry_update(struct recent_table *t, struct recent_entry *e)
  128. {
  129. e->stamps[e->index++] = jiffies;
  130. if (e->index > e->nstamps)
  131. e->nstamps = e->index;
  132. e->index %= ip_pkt_list_tot;
  133. list_move_tail(&e->lru_list, &t->lru_list);
  134. }
  135. static struct recent_table *recent_table_lookup(const char *name)
  136. {
  137. struct recent_table *t;
  138. list_for_each_entry(t, &tables, list)
  139. if (!strcmp(t->name, name))
  140. return t;
  141. return NULL;
  142. }
  143. static void recent_table_flush(struct recent_table *t)
  144. {
  145. struct recent_entry *e, *next;
  146. unsigned int i;
  147. for (i = 0; i < ip_list_hash_size; i++) {
  148. list_for_each_entry_safe(e, next, &t->iphash[i], list)
  149. recent_entry_remove(t, e);
  150. }
  151. }
  152. static int
  153. ipt_recent_match(const struct sk_buff *skb,
  154. const struct net_device *in, const struct net_device *out,
  155. const struct xt_match *match, const void *matchinfo,
  156. int offset, unsigned int protoff, int *hotdrop)
  157. {
  158. const struct ipt_recent_info *info = matchinfo;
  159. struct recent_table *t;
  160. struct recent_entry *e;
  161. __be32 addr;
  162. u_int8_t ttl;
  163. int ret = info->invert;
  164. if (info->side == IPT_RECENT_DEST)
  165. addr = skb->nh.iph->daddr;
  166. else
  167. addr = skb->nh.iph->saddr;
  168. ttl = skb->nh.iph->ttl;
  169. /* use TTL as seen before forwarding */
  170. if (out && !skb->sk)
  171. ttl++;
  172. spin_lock_bh(&recent_lock);
  173. t = recent_table_lookup(info->name);
  174. e = recent_entry_lookup(t, addr,
  175. info->check_set & IPT_RECENT_TTL ? ttl : 0);
  176. if (e == NULL) {
  177. if (!(info->check_set & IPT_RECENT_SET))
  178. goto out;
  179. e = recent_entry_init(t, addr, ttl);
  180. if (e == NULL)
  181. *hotdrop = 1;
  182. ret ^= 1;
  183. goto out;
  184. }
  185. if (info->check_set & IPT_RECENT_SET)
  186. ret ^= 1;
  187. else if (info->check_set & IPT_RECENT_REMOVE) {
  188. recent_entry_remove(t, e);
  189. ret ^= 1;
  190. } else if (info->check_set & (IPT_RECENT_CHECK | IPT_RECENT_UPDATE)) {
  191. unsigned long t = jiffies - info->seconds * HZ;
  192. unsigned int i, hits = 0;
  193. for (i = 0; i < e->nstamps; i++) {
  194. if (info->seconds && time_after(t, e->stamps[i]))
  195. continue;
  196. if (++hits >= info->hit_count) {
  197. ret ^= 1;
  198. break;
  199. }
  200. }
  201. }
  202. if (info->check_set & IPT_RECENT_SET ||
  203. (info->check_set & IPT_RECENT_UPDATE && ret)) {
  204. recent_entry_update(t, e);
  205. e->ttl = ttl;
  206. }
  207. out:
  208. spin_unlock_bh(&recent_lock);
  209. return ret;
  210. }
  211. static int
  212. ipt_recent_checkentry(const char *tablename, const void *ip,
  213. const struct xt_match *match, void *matchinfo,
  214. unsigned int hook_mask)
  215. {
  216. const struct ipt_recent_info *info = matchinfo;
  217. struct recent_table *t;
  218. unsigned i;
  219. int ret = 0;
  220. if (hweight8(info->check_set &
  221. (IPT_RECENT_SET | IPT_RECENT_REMOVE |
  222. IPT_RECENT_CHECK | IPT_RECENT_UPDATE)) != 1)
  223. return 0;
  224. if ((info->check_set & (IPT_RECENT_SET | IPT_RECENT_REMOVE)) &&
  225. (info->seconds || info->hit_count))
  226. return 0;
  227. if (info->name[0] == '\0' ||
  228. strnlen(info->name, IPT_RECENT_NAME_LEN) == IPT_RECENT_NAME_LEN)
  229. return 0;
  230. mutex_lock(&recent_mutex);
  231. t = recent_table_lookup(info->name);
  232. if (t != NULL) {
  233. t->refcnt++;
  234. ret = 1;
  235. goto out;
  236. }
  237. t = kzalloc(sizeof(*t) + sizeof(t->iphash[0]) * ip_list_hash_size,
  238. GFP_KERNEL);
  239. if (t == NULL)
  240. goto out;
  241. t->refcnt = 1;
  242. strcpy(t->name, info->name);
  243. INIT_LIST_HEAD(&t->lru_list);
  244. for (i = 0; i < ip_list_hash_size; i++)
  245. INIT_LIST_HEAD(&t->iphash[i]);
  246. #ifdef CONFIG_PROC_FS
  247. t->proc = create_proc_entry(t->name, ip_list_perms, proc_dir);
  248. if (t->proc == NULL) {
  249. kfree(t);
  250. goto out;
  251. }
  252. t->proc->proc_fops = &recent_fops;
  253. t->proc->uid = ip_list_uid;
  254. t->proc->gid = ip_list_gid;
  255. t->proc->data = t;
  256. #endif
  257. spin_lock_bh(&recent_lock);
  258. list_add_tail(&t->list, &tables);
  259. spin_unlock_bh(&recent_lock);
  260. ret = 1;
  261. out:
  262. mutex_unlock(&recent_mutex);
  263. return ret;
  264. }
  265. static void
  266. ipt_recent_destroy(const struct xt_match *match, void *matchinfo)
  267. {
  268. const struct ipt_recent_info *info = matchinfo;
  269. struct recent_table *t;
  270. mutex_lock(&recent_mutex);
  271. t = recent_table_lookup(info->name);
  272. if (--t->refcnt == 0) {
  273. spin_lock_bh(&recent_lock);
  274. list_del(&t->list);
  275. spin_unlock_bh(&recent_lock);
  276. recent_table_flush(t);
  277. #ifdef CONFIG_PROC_FS
  278. remove_proc_entry(t->name, proc_dir);
  279. #endif
  280. kfree(t);
  281. }
  282. mutex_unlock(&recent_mutex);
  283. }
  284. #ifdef CONFIG_PROC_FS
  285. struct recent_iter_state {
  286. struct recent_table *table;
  287. unsigned int bucket;
  288. };
  289. static void *recent_seq_start(struct seq_file *seq, loff_t *pos)
  290. {
  291. struct recent_iter_state *st = seq->private;
  292. struct recent_table *t = st->table;
  293. struct recent_entry *e;
  294. loff_t p = *pos;
  295. spin_lock_bh(&recent_lock);
  296. for (st->bucket = 0; st->bucket < ip_list_hash_size; st->bucket++) {
  297. list_for_each_entry(e, &t->iphash[st->bucket], list) {
  298. if (p-- == 0)
  299. return e;
  300. }
  301. }
  302. return NULL;
  303. }
  304. static void *recent_seq_next(struct seq_file *seq, void *v, loff_t *pos)
  305. {
  306. struct recent_iter_state *st = seq->private;
  307. struct recent_table *t = st->table;
  308. struct recent_entry *e = v;
  309. struct list_head *head = e->list.next;
  310. while (head == &t->iphash[st->bucket]) {
  311. if (++st->bucket >= ip_list_hash_size)
  312. return NULL;
  313. head = t->iphash[st->bucket].next;
  314. }
  315. (*pos)++;
  316. return list_entry(head, struct recent_entry, list);
  317. }
  318. static void recent_seq_stop(struct seq_file *s, void *v)
  319. {
  320. spin_unlock_bh(&recent_lock);
  321. }
  322. static int recent_seq_show(struct seq_file *seq, void *v)
  323. {
  324. struct recent_entry *e = v;
  325. unsigned int i;
  326. i = (e->index - 1) % ip_pkt_list_tot;
  327. seq_printf(seq, "src=%u.%u.%u.%u ttl: %u last_seen: %lu oldest_pkt: %u",
  328. NIPQUAD(e->addr), e->ttl, e->stamps[i], e->index);
  329. for (i = 0; i < e->nstamps; i++)
  330. seq_printf(seq, "%s %lu", i ? "," : "", e->stamps[i]);
  331. seq_printf(seq, "\n");
  332. return 0;
  333. }
  334. static struct seq_operations recent_seq_ops = {
  335. .start = recent_seq_start,
  336. .next = recent_seq_next,
  337. .stop = recent_seq_stop,
  338. .show = recent_seq_show,
  339. };
  340. static int recent_seq_open(struct inode *inode, struct file *file)
  341. {
  342. struct proc_dir_entry *pde = PDE(inode);
  343. struct seq_file *seq;
  344. struct recent_iter_state *st;
  345. int ret;
  346. st = kzalloc(sizeof(*st), GFP_KERNEL);
  347. if (st == NULL)
  348. return -ENOMEM;
  349. ret = seq_open(file, &recent_seq_ops);
  350. if (ret)
  351. kfree(st);
  352. st->table = pde->data;
  353. seq = file->private_data;
  354. seq->private = st;
  355. return ret;
  356. }
  357. static ssize_t recent_proc_write(struct file *file, const char __user *input,
  358. size_t size, loff_t *loff)
  359. {
  360. struct proc_dir_entry *pde = PDE(file->f_path.dentry->d_inode);
  361. struct recent_table *t = pde->data;
  362. struct recent_entry *e;
  363. char buf[sizeof("+255.255.255.255")], *c = buf;
  364. __be32 addr;
  365. int add;
  366. if (size > sizeof(buf))
  367. size = sizeof(buf);
  368. if (copy_from_user(buf, input, size))
  369. return -EFAULT;
  370. while (isspace(*c))
  371. c++;
  372. if (size - (c - buf) < 5)
  373. return c - buf;
  374. if (!strncmp(c, "clear", 5)) {
  375. c += 5;
  376. spin_lock_bh(&recent_lock);
  377. recent_table_flush(t);
  378. spin_unlock_bh(&recent_lock);
  379. return c - buf;
  380. }
  381. switch (*c) {
  382. case '-':
  383. add = 0;
  384. c++;
  385. break;
  386. case '+':
  387. c++;
  388. default:
  389. add = 1;
  390. break;
  391. }
  392. addr = in_aton(c);
  393. spin_lock_bh(&recent_lock);
  394. e = recent_entry_lookup(t, addr, 0);
  395. if (e == NULL) {
  396. if (add)
  397. recent_entry_init(t, addr, 0);
  398. } else {
  399. if (add)
  400. recent_entry_update(t, e);
  401. else
  402. recent_entry_remove(t, e);
  403. }
  404. spin_unlock_bh(&recent_lock);
  405. return size;
  406. }
  407. static const struct file_operations recent_fops = {
  408. .open = recent_seq_open,
  409. .read = seq_read,
  410. .write = recent_proc_write,
  411. .release = seq_release_private,
  412. .owner = THIS_MODULE,
  413. };
  414. #endif /* CONFIG_PROC_FS */
  415. static struct xt_match recent_match = {
  416. .name = "recent",
  417. .family = AF_INET,
  418. .match = ipt_recent_match,
  419. .matchsize = sizeof(struct ipt_recent_info),
  420. .checkentry = ipt_recent_checkentry,
  421. .destroy = ipt_recent_destroy,
  422. .me = THIS_MODULE,
  423. };
  424. static int __init ipt_recent_init(void)
  425. {
  426. int err;
  427. if (!ip_list_tot || !ip_pkt_list_tot || ip_pkt_list_tot > 255)
  428. return -EINVAL;
  429. ip_list_hash_size = 1 << fls(ip_list_tot);
  430. err = xt_register_match(&recent_match);
  431. #ifdef CONFIG_PROC_FS
  432. if (err)
  433. return err;
  434. proc_dir = proc_mkdir("ipt_recent", proc_net);
  435. if (proc_dir == NULL) {
  436. xt_unregister_match(&recent_match);
  437. err = -ENOMEM;
  438. }
  439. #endif
  440. return err;
  441. }
  442. static void __exit ipt_recent_exit(void)
  443. {
  444. BUG_ON(!list_empty(&tables));
  445. xt_unregister_match(&recent_match);
  446. #ifdef CONFIG_PROC_FS
  447. remove_proc_entry("ipt_recent", proc_net);
  448. #endif
  449. }
  450. module_init(ipt_recent_init);
  451. module_exit(ipt_recent_exit);