blob: 834b736857cb2160b54951234b7f62609903f6fc [file] [log] [blame]
Patrick McHardy404bdbf2006-05-29 18:21:34 -07001/*
2 * Copyright (c) 2006 Patrick McHardy <kaber@trash.net>
Jan Engelhardt079aa882008-10-08 11:35:00 +02003 * Copyright © CC Computer Consultants GmbH, 2007 - 2008
Patrick McHardy404bdbf2006-05-29 18:21:34 -07004 *
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>
Jan Engelhardt6709dbb2007-02-07 15:11:19 -080016#include <linux/ip.h>
Jan Engelhardt079aa882008-10-08 11:35:00 +020017#include <linux/ipv6.h>
18#include <linux/module.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070019#include <linux/moduleparam.h>
Patrick McHardy404bdbf2006-05-29 18:21:34 -070020#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>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090030#include <linux/slab.h>
Eric W. Biederman457c4cb2007-09-12 12:01:34 +020031#include <net/net_namespace.h>
Alexey Dobriyan7d07d562010-01-18 08:31:00 +010032#include <net/netns/generic.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070033
Jan Engelhardt6709dbb2007-02-07 15:11:19 -080034#include <linux/netfilter/x_tables.h>
Jan Engelhardte948b202008-10-08 11:35:00 +020035#include <linux/netfilter/xt_recent.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070036
Patrick McHardy404bdbf2006-05-29 18:21:34 -070037MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
Jan Engelhardt079aa882008-10-08 11:35:00 +020038MODULE_AUTHOR("Jan Engelhardt <jengelh@computergmbh.de>");
Jan Engelhardt2ae15b62008-01-14 23:42:28 -080039MODULE_DESCRIPTION("Xtables: \"recently-seen\" host matching for IPv4");
Patrick McHardy404bdbf2006-05-29 18:21:34 -070040MODULE_LICENSE("GPL");
Jan Engelhardte948b202008-10-08 11:35:00 +020041MODULE_ALIAS("ipt_recent");
Jan Engelhardt079aa882008-10-08 11:35:00 +020042MODULE_ALIAS("ip6t_recent");
Linus Torvalds1da177e2005-04-16 15:20:36 -070043
Patrick McHardye7be6992006-01-05 12:19:46 -080044static 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;
Daniel De Graafb93ff782006-08-22 00:30:55 -070048static unsigned int ip_list_uid = 0;
49static unsigned int ip_list_gid = 0;
Patrick McHardye7be6992006-01-05 12:19:46 -080050module_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);
Daniel De Graafb93ff782006-08-22 00:30:55 -070054module_param(ip_list_uid, uint, 0400);
55module_param(ip_list_gid, uint, 0400);
Patrick McHardy404bdbf2006-05-29 18:21:34 -070056MODULE_PARM_DESC(ip_list_tot, "number of IPs to remember per list");
Jan Engelhardt98e6d2d2010-02-15 16:31:35 +010057MODULE_PARM_DESC(ip_pkt_list_tot, "number of packets per IP address to remember (max. 255)");
Patrick McHardy404bdbf2006-05-29 18:21:34 -070058MODULE_PARM_DESC(ip_list_hash_size, "size of hash table used to look up IPs");
Jan Engelhardt079aa882008-10-08 11:35:00 +020059MODULE_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");
Linus Torvalds1da177e2005-04-16 15:20:36 -070062
Patrick McHardy404bdbf2006-05-29 18:21:34 -070063struct recent_entry {
64 struct list_head list;
65 struct list_head lru_list;
Jan Engelhardt079aa882008-10-08 11:35:00 +020066 union nf_inet_addr addr;
67 u_int16_t family;
Patrick McHardy404bdbf2006-05-29 18:21:34 -070068 u_int8_t ttl;
69 u_int8_t index;
70 u_int16_t nstamps;
71 unsigned long stamps[0];
Linus Torvalds1da177e2005-04-16 15:20:36 -070072};
73
Patrick McHardy404bdbf2006-05-29 18:21:34 -070074struct recent_table {
75 struct list_head list;
Jan Engelhardte948b202008-10-08 11:35:00 +020076 char name[XT_RECENT_NAME_LEN];
Patrick McHardy404bdbf2006-05-29 18:21:34 -070077 unsigned int refcnt;
78 unsigned int entries;
79 struct list_head lru_list;
80 struct list_head iphash[0];
Linus Torvalds1da177e2005-04-16 15:20:36 -070081};
82
Alexey Dobriyan7d07d562010-01-18 08:31:00 +010083struct 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
Linus Torvalds1da177e2005-04-16 15:20:36 -070099static DEFINE_SPINLOCK(recent_lock);
Patrick McHardya0e889b2006-06-09 12:17:41 -0700100static DEFINE_MUTEX(recent_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700101
102#ifdef CONFIG_PROC_FS
Jan Engelhardt079aa882008-10-08 11:35:00 +0200103static const struct file_operations recent_old_fops, recent_mt_fops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700104#endif
105
Jan Engelhardt294188a2010-01-04 16:28:38 +0100106static u_int32_t hash_rnd __read_mostly;
107static bool hash_rnd_inited __read_mostly;
Patrick McHardy404bdbf2006-05-29 18:21:34 -0700108
Jan Engelhardt294188a2010-01-04 16:28:38 +0100109static inline unsigned int recent_entry_hash4(const union nf_inet_addr *addr)
Patrick McHardy404bdbf2006-05-29 18:21:34 -0700110{
Jan Engelhardt079aa882008-10-08 11:35:00 +0200111 return jhash_1word((__force u32)addr->ip, hash_rnd) &
112 (ip_list_hash_size - 1);
113}
114
Jan Engelhardt294188a2010-01-04 16:28:38 +0100115static inline unsigned int recent_entry_hash6(const union nf_inet_addr *addr)
Jan Engelhardt079aa882008-10-08 11:35:00 +0200116{
Jan Engelhardt079aa882008-10-08 11:35:00 +0200117 return jhash2((u32 *)addr->ip6, ARRAY_SIZE(addr->ip6), hash_rnd) &
118 (ip_list_hash_size - 1);
Patrick McHardy404bdbf2006-05-29 18:21:34 -0700119}
120
121static struct recent_entry *
Jan Engelhardt079aa882008-10-08 11:35:00 +0200122recent_entry_lookup(const struct recent_table *table,
123 const union nf_inet_addr *addrp, u_int16_t family,
124 u_int8_t ttl)
Patrick McHardy404bdbf2006-05-29 18:21:34 -0700125{
126 struct recent_entry *e;
127 unsigned int h;
128
Jan Engelhardtee999d82008-10-08 11:35:01 +0200129 if (family == NFPROTO_IPV4)
Jan Engelhardt079aa882008-10-08 11:35:00 +0200130 h = recent_entry_hash4(addrp);
131 else
132 h = recent_entry_hash6(addrp);
133
Patrick McHardy404bdbf2006-05-29 18:21:34 -0700134 list_for_each_entry(e, &table->iphash[h], list)
Jan Engelhardt079aa882008-10-08 11:35:00 +0200135 if (e->family == family &&
136 memcmp(&e->addr, addrp, sizeof(e->addr)) == 0 &&
137 (ttl == e->ttl || ttl == 0 || e->ttl == 0))
Patrick McHardy404bdbf2006-05-29 18:21:34 -0700138 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 *
Jan Engelhardt079aa882008-10-08 11:35:00 +0200151recent_entry_init(struct recent_table *t, const union nf_inet_addr *addr,
152 u_int16_t family, u_int8_t ttl)
Patrick McHardy404bdbf2006-05-29 18:21:34 -0700153{
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;
Jan Engelhardt079aa882008-10-08 11:35:00 +0200164 memcpy(&e->addr, addr, sizeof(e->addr));
Patrick McHardy404bdbf2006-05-29 18:21:34 -0700165 e->ttl = ttl;
166 e->stamps[0] = jiffies;
167 e->nstamps = 1;
168 e->index = 1;
Jan Engelhardt079aa882008-10-08 11:35:00 +0200169 e->family = family;
Jan Engelhardtee999d82008-10-08 11:35:01 +0200170 if (family == NFPROTO_IPV4)
Jan Engelhardt079aa882008-10-08 11:35:00 +0200171 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)]);
Patrick McHardy404bdbf2006-05-29 18:21:34 -0700174 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{
Tim Gardner2c085222010-02-23 14:55:21 +0100181 e->index %= ip_pkt_list_tot;
Patrick McHardy404bdbf2006-05-29 18:21:34 -0700182 e->stamps[e->index++] = jiffies;
183 if (e->index > e->nstamps)
184 e->nstamps = e->index;
Patrick McHardy404bdbf2006-05-29 18:21:34 -0700185 list_move_tail(&e->lru_list, &t->lru_list);
186}
187
Alexey Dobriyan7d07d562010-01-18 08:31:00 +0100188static struct recent_table *recent_table_lookup(struct recent_net *recent_net,
189 const char *name)
Patrick McHardy404bdbf2006-05-29 18:21:34 -0700190{
191 struct recent_table *t;
192
Alexey Dobriyan7d07d562010-01-18 08:31:00 +0100193 list_for_each_entry(t, &recent_net->tables, list)
Patrick McHardy404bdbf2006-05-29 18:21:34 -0700194 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
Jan Engelhardt7c4e36b2007-07-07 22:19:08 -0700204 for (i = 0; i < ip_list_hash_size; i++)
Patrick McHardy404bdbf2006-05-29 18:21:34 -0700205 list_for_each_entry_safe(e, next, &t->iphash[i], list)
206 recent_entry_remove(t, e);
Patrick McHardy404bdbf2006-05-29 18:21:34 -0700207}
208
Jan Engelhardt1d93a9c2007-07-07 22:15:35 -0700209static bool
Jan Engelhardtf7108a22008-10-08 11:35:18 +0200210recent_mt(const struct sk_buff *skb, const struct xt_match_param *par)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700211{
Alexey Dobriyan7d07d562010-01-18 08:31:00 +0100212 struct net *net = dev_net(par->in ? par->in : par->out);
213 struct recent_net *recent_net = recent_pernet(net);
Jan Engelhardtf7108a22008-10-08 11:35:18 +0200214 const struct xt_recent_mtinfo *info = par->matchinfo;
Patrick McHardy404bdbf2006-05-29 18:21:34 -0700215 struct recent_table *t;
216 struct recent_entry *e;
Jan Engelhardt079aa882008-10-08 11:35:00 +0200217 union nf_inet_addr addr = {};
Patrick McHardy404bdbf2006-05-29 18:21:34 -0700218 u_int8_t ttl;
Jan Engelhardt1d93a9c2007-07-07 22:15:35 -0700219 bool ret = info->invert;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700220
Jan Engelhardtf7108a22008-10-08 11:35:18 +0200221 if (par->match->family == NFPROTO_IPV4) {
Jan Engelhardt079aa882008-10-08 11:35:00 +0200222 const struct iphdr *iph = ip_hdr(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700223
Jan Engelhardt079aa882008-10-08 11:35:00 +0200224 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
Patrick McHardy404bdbf2006-05-29 18:21:34 -0700241 /* use TTL as seen before forwarding */
Jan Engelhardtf7108a22008-10-08 11:35:18 +0200242 if (par->out != NULL && skb->sk == NULL)
Patrick McHardy404bdbf2006-05-29 18:21:34 -0700243 ttl++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700244
Linus Torvalds1da177e2005-04-16 15:20:36 -0700245 spin_lock_bh(&recent_lock);
Alexey Dobriyan7d07d562010-01-18 08:31:00 +0100246 t = recent_table_lookup(recent_net, info->name);
Jan Engelhardtf7108a22008-10-08 11:35:18 +0200247 e = recent_entry_lookup(t, &addr, par->match->family,
Jan Engelhardt079aa882008-10-08 11:35:00 +0200248 (info->check_set & XT_RECENT_TTL) ? ttl : 0);
Patrick McHardy404bdbf2006-05-29 18:21:34 -0700249 if (e == NULL) {
Jan Engelhardte948b202008-10-08 11:35:00 +0200250 if (!(info->check_set & XT_RECENT_SET))
Patrick McHardy404bdbf2006-05-29 18:21:34 -0700251 goto out;
Jan Engelhardtf7108a22008-10-08 11:35:18 +0200252 e = recent_entry_init(t, &addr, par->match->family, ttl);
Patrick McHardy404bdbf2006-05-29 18:21:34 -0700253 if (e == NULL)
Jan Engelhardtf7108a22008-10-08 11:35:18 +0200254 *par->hotdrop = true;
Jan Engelhardt1d93a9c2007-07-07 22:15:35 -0700255 ret = !ret;
Patrick McHardy404bdbf2006-05-29 18:21:34 -0700256 goto out;
257 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700258
Jan Engelhardte948b202008-10-08 11:35:00 +0200259 if (info->check_set & XT_RECENT_SET)
Jan Engelhardt1d93a9c2007-07-07 22:15:35 -0700260 ret = !ret;
Jan Engelhardte948b202008-10-08 11:35:00 +0200261 else if (info->check_set & XT_RECENT_REMOVE) {
Patrick McHardy404bdbf2006-05-29 18:21:34 -0700262 recent_entry_remove(t, e);
Jan Engelhardt1d93a9c2007-07-07 22:15:35 -0700263 ret = !ret;
Jan Engelhardte948b202008-10-08 11:35:00 +0200264 } else if (info->check_set & (XT_RECENT_CHECK | XT_RECENT_UPDATE)) {
Patrick McHardy855304a2008-01-31 04:09:46 -0800265 unsigned long time = jiffies - info->seconds * HZ;
Patrick McHardy404bdbf2006-05-29 18:21:34 -0700266 unsigned int i, hits = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700267
Patrick McHardy404bdbf2006-05-29 18:21:34 -0700268 for (i = 0; i < e->nstamps; i++) {
Patrick McHardy855304a2008-01-31 04:09:46 -0800269 if (info->seconds && time_after(time, e->stamps[i]))
Patrick McHardy404bdbf2006-05-29 18:21:34 -0700270 continue;
Patrick McHardyef169152010-03-22 18:25:20 +0100271 if (!info->hit_count || ++hits >= info->hit_count) {
Jan Engelhardt1d93a9c2007-07-07 22:15:35 -0700272 ret = !ret;
Patrick McHardy404bdbf2006-05-29 18:21:34 -0700273 break;
274 }
275 }
276 }
277
Jan Engelhardte948b202008-10-08 11:35:00 +0200278 if (info->check_set & XT_RECENT_SET ||
279 (info->check_set & XT_RECENT_UPDATE && ret)) {
Patrick McHardy404bdbf2006-05-29 18:21:34 -0700280 recent_entry_update(t, e);
281 e->ttl = ttl;
282 }
283out:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700284 spin_unlock_bh(&recent_lock);
Patrick McHardy404bdbf2006-05-29 18:21:34 -0700285 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700286}
287
Jan Engelhardt9b4fce72008-10-08 11:35:18 +0200288static bool recent_mt_check(const struct xt_mtchk_param *par)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700289{
Alexey Dobriyan7d07d562010-01-18 08:31:00 +0100290 struct recent_net *recent_net = recent_pernet(par->net);
Jan Engelhardt9b4fce72008-10-08 11:35:18 +0200291 const struct xt_recent_mtinfo *info = par->matchinfo;
Patrick McHardy404bdbf2006-05-29 18:21:34 -0700292 struct recent_table *t;
Alexey Dobriyanb0ceb562008-11-20 09:57:01 +0100293#ifdef CONFIG_PROC_FS
294 struct proc_dir_entry *pde;
295#endif
Patrick McHardy404bdbf2006-05-29 18:21:34 -0700296 unsigned i;
Jan Engelhardtccb79bd2007-07-07 22:16:00 -0700297 bool ret = false;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700298
Jan Engelhardt294188a2010-01-04 16:28:38 +0100299 if (unlikely(!hash_rnd_inited)) {
300 get_random_bytes(&hash_rnd, sizeof(hash_rnd));
301 hash_rnd_inited = true;
302 }
Patrick McHardy404bdbf2006-05-29 18:21:34 -0700303 if (hweight8(info->check_set &
Jan Engelhardte948b202008-10-08 11:35:00 +0200304 (XT_RECENT_SET | XT_RECENT_REMOVE |
305 XT_RECENT_CHECK | XT_RECENT_UPDATE)) != 1)
Jan Engelhardtccb79bd2007-07-07 22:16:00 -0700306 return false;
Jan Engelhardte948b202008-10-08 11:35:00 +0200307 if ((info->check_set & (XT_RECENT_SET | XT_RECENT_REMOVE)) &&
Patrick McHardy404bdbf2006-05-29 18:21:34 -0700308 (info->seconds || info->hit_count))
Jan Engelhardtccb79bd2007-07-07 22:16:00 -0700309 return false;
Jan Engelhardt98e6d2d2010-02-15 16:31:35 +0100310 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);
Daniel Hokka Zakrissond0ebf132008-03-20 15:07:10 -0700314 return false;
Jan Engelhardt98e6d2d2010-02-15 16:31:35 +0100315 }
Patrick McHardy404bdbf2006-05-29 18:21:34 -0700316 if (info->name[0] == '\0' ||
Jan Engelhardte948b202008-10-08 11:35:00 +0200317 strnlen(info->name, XT_RECENT_NAME_LEN) == XT_RECENT_NAME_LEN)
Jan Engelhardtccb79bd2007-07-07 22:16:00 -0700318 return false;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700319
Patrick McHardya0e889b2006-06-09 12:17:41 -0700320 mutex_lock(&recent_mutex);
Alexey Dobriyan7d07d562010-01-18 08:31:00 +0100321 t = recent_table_lookup(recent_net, info->name);
Patrick McHardy404bdbf2006-05-29 18:21:34 -0700322 if (t != NULL) {
323 t->refcnt++;
Jan Engelhardtccb79bd2007-07-07 22:16:00 -0700324 ret = true;
Patrick McHardy404bdbf2006-05-29 18:21:34 -0700325 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700326 }
327
Patrick McHardy404bdbf2006-05-29 18:21:34 -0700328 t = kzalloc(sizeof(*t) + sizeof(t->iphash[0]) * ip_list_hash_size,
Patrick McHardya0e889b2006-06-09 12:17:41 -0700329 GFP_KERNEL);
Patrick McHardy404bdbf2006-05-29 18:21:34 -0700330 if (t == NULL)
331 goto out;
Patrick McHardy2b2283d2006-06-09 12:18:17 -0700332 t->refcnt = 1;
Patrick McHardy404bdbf2006-05-29 18:21:34 -0700333 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]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700337#ifdef CONFIG_PROC_FS
Alexey Dobriyan7d07d562010-01-18 08:31:00 +0100338 pde = proc_create_data(t->name, ip_list_perms, recent_net->xt_recent,
Alexey Dobriyanb09eec12008-10-20 03:33:49 -0700339 &recent_mt_fops, t);
Alexey Dobriyanb0ceb562008-11-20 09:57:01 +0100340 if (pde == NULL) {
Patrick McHardy404bdbf2006-05-29 18:21:34 -0700341 kfree(t);
342 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700343 }
Alexey Dobriyanb0ceb562008-11-20 09:57:01 +0100344 pde->uid = ip_list_uid;
345 pde->gid = ip_list_gid;
Jan Engelhardt079aa882008-10-08 11:35:00 +0200346#ifdef CONFIG_NETFILTER_XT_MATCH_RECENT_PROC_COMPAT
Alexey Dobriyan7d07d562010-01-18 08:31:00 +0100347 pde = proc_create_data(t->name, ip_list_perms, recent_net->ipt_recent,
Alexey Dobriyanb09eec12008-10-20 03:33:49 -0700348 &recent_old_fops, t);
Alexey Dobriyanb0ceb562008-11-20 09:57:01 +0100349 if (pde == NULL) {
Alexey Dobriyan7d07d562010-01-18 08:31:00 +0100350 remove_proc_entry(t->name, recent_net->xt_recent);
Jan Engelhardt079aa882008-10-08 11:35:00 +0200351 kfree(t);
352 goto out;
353 }
Alexey Dobriyanb0ceb562008-11-20 09:57:01 +0100354 pde->uid = ip_list_uid;
355 pde->gid = ip_list_gid;
Jan Engelhardt079aa882008-10-08 11:35:00 +0200356#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700357#endif
Patrick McHardya0e889b2006-06-09 12:17:41 -0700358 spin_lock_bh(&recent_lock);
Alexey Dobriyan7d07d562010-01-18 08:31:00 +0100359 list_add_tail(&t->list, &recent_net->tables);
Patrick McHardya0e889b2006-06-09 12:17:41 -0700360 spin_unlock_bh(&recent_lock);
Jan Engelhardtccb79bd2007-07-07 22:16:00 -0700361 ret = true;
Patrick McHardy404bdbf2006-05-29 18:21:34 -0700362out:
Patrick McHardya0e889b2006-06-09 12:17:41 -0700363 mutex_unlock(&recent_mutex);
Patrick McHardy404bdbf2006-05-29 18:21:34 -0700364 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700365}
366
Jan Engelhardt6be3d852008-10-08 11:35:19 +0200367static void recent_mt_destroy(const struct xt_mtdtor_param *par)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700368{
Alexey Dobriyan7d07d562010-01-18 08:31:00 +0100369 struct recent_net *recent_net = recent_pernet(par->net);
Jan Engelhardt6be3d852008-10-08 11:35:19 +0200370 const struct xt_recent_mtinfo *info = par->matchinfo;
Patrick McHardy404bdbf2006-05-29 18:21:34 -0700371 struct recent_table *t;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700372
Patrick McHardya0e889b2006-06-09 12:17:41 -0700373 mutex_lock(&recent_mutex);
Alexey Dobriyan7d07d562010-01-18 08:31:00 +0100374 t = recent_table_lookup(recent_net, info->name);
Patrick McHardy404bdbf2006-05-29 18:21:34 -0700375 if (--t->refcnt == 0) {
Patrick McHardya0e889b2006-06-09 12:17:41 -0700376 spin_lock_bh(&recent_lock);
Patrick McHardy404bdbf2006-05-29 18:21:34 -0700377 list_del(&t->list);
Patrick McHardya0e889b2006-06-09 12:17:41 -0700378 spin_unlock_bh(&recent_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700379#ifdef CONFIG_PROC_FS
Jan Engelhardt079aa882008-10-08 11:35:00 +0200380#ifdef CONFIG_NETFILTER_XT_MATCH_RECENT_PROC_COMPAT
Alexey Dobriyan7d07d562010-01-18 08:31:00 +0100381 remove_proc_entry(t->name, recent_net->ipt_recent);
Jan Engelhardt079aa882008-10-08 11:35:00 +0200382#endif
Alexey Dobriyan7d07d562010-01-18 08:31:00 +0100383 remove_proc_entry(t->name, recent_net->xt_recent);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700384#endif
Pavel Emelyanova8ddc912008-07-31 00:38:31 -0700385 recent_table_flush(t);
Patrick McHardy404bdbf2006-05-29 18:21:34 -0700386 kfree(t);
387 }
Patrick McHardya0e889b2006-06-09 12:17:41 -0700388 mutex_unlock(&recent_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700389}
390
Patrick McHardy404bdbf2006-05-29 18:21:34 -0700391#ifdef CONFIG_PROC_FS
392struct recent_iter_state {
Jan Engelhardt079aa882008-10-08 11:35:00 +0200393 const struct recent_table *table;
Patrick McHardy404bdbf2006-05-29 18:21:34 -0700394 unsigned int bucket;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700395};
396
Patrick McHardy404bdbf2006-05-29 18:21:34 -0700397static void *recent_seq_start(struct seq_file *seq, loff_t *pos)
Patrick McHardy855304a2008-01-31 04:09:46 -0800398 __acquires(recent_lock)
Patrick McHardy404bdbf2006-05-29 18:21:34 -0700399{
400 struct recent_iter_state *st = seq->private;
Jan Engelhardta47362a2007-07-07 22:16:55 -0700401 const struct recent_table *t = st->table;
Patrick McHardy404bdbf2006-05-29 18:21:34 -0700402 struct recent_entry *e;
403 loff_t p = *pos;
404
405 spin_lock_bh(&recent_lock);
406
Jan Engelhardt7c4e36b2007-07-07 22:19:08 -0700407 for (st->bucket = 0; st->bucket < ip_list_hash_size; st->bucket++)
408 list_for_each_entry(e, &t->iphash[st->bucket], list)
Patrick McHardy404bdbf2006-05-29 18:21:34 -0700409 if (p-- == 0)
410 return e;
Patrick McHardy404bdbf2006-05-29 18:21:34 -0700411 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;
Jan Engelhardt3cf93c92008-04-14 09:56:05 +0200417 const struct recent_table *t = st->table;
Jan Engelhardt079aa882008-10-08 11:35:00 +0200418 const struct recent_entry *e = v;
419 const struct list_head *head = e->list.next;
Patrick McHardy404bdbf2006-05-29 18:21:34 -0700420
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)
Patrick McHardy855304a2008-01-31 04:09:46 -0800431 __releases(recent_lock)
Patrick McHardy404bdbf2006-05-29 18:21:34 -0700432{
433 spin_unlock_bh(&recent_lock);
434}
435
436static int recent_seq_show(struct seq_file *seq, void *v)
437{
Jan Engelhardt3cf93c92008-04-14 09:56:05 +0200438 const struct recent_entry *e = v;
Patrick McHardy404bdbf2006-05-29 18:21:34 -0700439 unsigned int i;
440
441 i = (e->index - 1) % ip_pkt_list_tot;
Jan Engelhardtee999d82008-10-08 11:35:01 +0200442 if (e->family == NFPROTO_IPV4)
Harvey Harrison14d5e832008-10-31 00:54:29 -0700443 seq_printf(seq, "src=%pI4 ttl: %u last_seen: %lu oldest_pkt: %u",
444 &e->addr.ip, e->ttl, e->stamps[i], e->index);
Jan Engelhardt079aa882008-10-08 11:35:00 +0200445 else
Harvey Harrison5b095d9892008-10-29 12:52:50 -0700446 seq_printf(seq, "src=%pI6 ttl: %u last_seen: %lu oldest_pkt: %u",
Harvey Harrison38ff4fa2008-10-28 16:08:13 -0700447 &e->addr.in6, e->ttl, e->stamps[i], e->index);
Patrick McHardy404bdbf2006-05-29 18:21:34 -0700448 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
Philippe De Muyter56b3d972007-07-10 23:07:31 -0700454static const struct seq_operations recent_seq_ops = {
Patrick McHardy404bdbf2006-05-29 18:21:34 -0700455 .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);
Patrick McHardy404bdbf2006-05-29 18:21:34 -0700464 struct recent_iter_state *st;
Patrick McHardy404bdbf2006-05-29 18:21:34 -0700465
Pavel Emelyanove2da5912007-10-10 02:29:58 -0700466 st = __seq_open_private(file, &recent_seq_ops, sizeof(*st));
Patrick McHardy404bdbf2006-05-29 18:21:34 -0700467 if (st == NULL)
468 return -ENOMEM;
Jesper Juhl3af8e312007-08-07 18:10:54 -0700469
Patrick McHardy404bdbf2006-05-29 18:21:34 -0700470 st->table = pde->data;
Pavel Emelyanove2da5912007-10-10 02:29:58 -0700471 return 0;
Patrick McHardy404bdbf2006-05-29 18:21:34 -0700472}
473
Jan Engelhardt079aa882008-10-08 11:35:00 +0200474#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)
Patrick McHardy404bdbf2006-05-29 18:21:34 -0700490{
Jan Engelhardt3cf93c92008-04-14 09:56:05 +0200491 const struct proc_dir_entry *pde = PDE(file->f_path.dentry->d_inode);
Patrick McHardy404bdbf2006-05-29 18:21:34 -0700492 struct recent_table *t = pde->data;
493 struct recent_entry *e;
494 char buf[sizeof("+255.255.255.255")], *c = buf;
Jan Engelhardt37e55cf2009-04-24 17:05:21 +0200495 union nf_inet_addr addr = {};
Patrick McHardy404bdbf2006-05-29 18:21:34 -0700496 int add;
497
498 if (size > sizeof(buf))
499 size = sizeof(buf);
500 if (copy_from_user(buf, input, size))
501 return -EFAULT;
Jan Engelhardt079aa882008-10-08 11:35:00 +0200502
André Goddard Rosae7d28602009-12-14 18:01:06 -0800503 c = skip_spaces(c);
Patrick McHardy404bdbf2006-05-29 18:21:34 -0700504
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 }
Jan Engelhardt37e55cf2009-04-24 17:05:21 +0200526 addr.ip = in_aton(c);
Patrick McHardy404bdbf2006-05-29 18:21:34 -0700527
528 spin_lock_bh(&recent_lock);
Jan Engelhardt37e55cf2009-04-24 17:05:21 +0200529 e = recent_entry_lookup(t, &addr, NFPROTO_IPV4, 0);
Patrick McHardy404bdbf2006-05-29 18:21:34 -0700530 if (e == NULL) {
531 if (add)
Jan Engelhardt37e55cf2009-04-24 17:05:21 +0200532 recent_entry_init(t, &addr, NFPROTO_IPV4, 0);
Patrick McHardy404bdbf2006-05-29 18:21:34 -0700533 } 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
Jan Engelhardt079aa882008-10-08 11:35:00 +0200543static const struct file_operations recent_old_fops = {
544 .open = recent_old_seq_open,
Patrick McHardy404bdbf2006-05-29 18:21:34 -0700545 .read = seq_read,
Jan Engelhardt079aa882008-10-08 11:35:00 +0200546 .write = recent_old_proc_write,
Patrick McHardy404bdbf2006-05-29 18:21:34 -0700547 .release = seq_release_private,
548 .owner = THIS_MODULE,
549};
Jan Engelhardt079aa882008-10-08 11:35:00 +0200550#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;
Josef Drexler325fb5b2009-02-24 14:53:12 +0100561 union nf_inet_addr addr = {};
Jan Engelhardt079aa882008-10-08 11:35:00 +0200562 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) {
Jan Engelhardtee999d82008-10-08 11:35:01 +0200595 family = NFPROTO_IPV6;
Jan Engelhardt079aa882008-10-08 11:35:00 +0200596 succ = in6_pton(c, size, (void *)&addr, '\n', NULL);
597 } else {
Jan Engelhardtee999d82008-10-08 11:35:01 +0200598 family = NFPROTO_IPV4;
Jan Engelhardt079aa882008-10-08 11:35:00 +0200599 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};
Alexey Dobriyan7d07d562010-01-18 08:31:00 +0100632
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}
Patrick McHardy404bdbf2006-05-29 18:21:34 -0700666#endif /* CONFIG_PROC_FS */
667
Alexey Dobriyan7d07d562010-01-18 08:31:00 +0100668static 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
Jan Engelhardt079aa882008-10-08 11:35:00 +0200691static struct xt_match recent_mt_reg[] __read_mostly = {
692 {
693 .name = "recent",
694 .revision = 0,
Jan Engelhardtee999d82008-10-08 11:35:01 +0200695 .family = NFPROTO_IPV4,
Jan Engelhardt079aa882008-10-08 11:35:00 +0200696 .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,
Jan Engelhardtee999d82008-10-08 11:35:01 +0200705 .family = NFPROTO_IPV6,
Jan Engelhardt079aa882008-10-08 11:35:00 +0200706 .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 },
Patrick McHardy404bdbf2006-05-29 18:21:34 -0700712};
713
Jan Engelhardtd3c5ee62007-12-04 23:24:03 -0800714static int __init recent_mt_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700715{
Patrick McHardy404bdbf2006-05-29 18:21:34 -0700716 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700717
Patrick McHardy404bdbf2006-05-29 18:21:34 -0700718 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);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700721
Alexey Dobriyan7d07d562010-01-18 08:31:00 +0100722 err = register_pernet_subsys(&recent_net_ops);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700723 if (err)
Patrick McHardy404bdbf2006-05-29 18:21:34 -0700724 return err;
Alexey Dobriyan7d07d562010-01-18 08:31:00 +0100725 err = xt_register_matches(recent_mt_reg, ARRAY_SIZE(recent_mt_reg));
726 if (err)
727 unregister_pernet_subsys(&recent_net_ops);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700728 return err;
729}
730
Jan Engelhardtd3c5ee62007-12-04 23:24:03 -0800731static void __exit recent_mt_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700732{
Jan Engelhardt079aa882008-10-08 11:35:00 +0200733 xt_unregister_matches(recent_mt_reg, ARRAY_SIZE(recent_mt_reg));
Alexey Dobriyan7d07d562010-01-18 08:31:00 +0100734 unregister_pernet_subsys(&recent_net_ops);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700735}
736
Jan Engelhardtd3c5ee62007-12-04 23:24:03 -0800737module_init(recent_mt_init);
738module_exit(recent_mt_exit);