From: Diego Woitasen <diegows@linux.org.ar>
To: netfilter-devel@lists.netfilter.org
Subject: [PATCH] This add support for quota per destination ip to quota match.
Date: Fri, 27 Jun 2003 11:03:15 -0300 [thread overview]
Message-ID: <3EFC4EA3.7020505@linux.org.ar> (raw)
Please, I wan 't to hear commentaries about this. I 'm newbie...
diff -urN -X dontdiff
linux-2.4.20-orig/include/linux/netfilter_ipv4/ipt_quota.h
linux-2.4.20/include/linux/netfilter_ipv4/ipt_quota.h
--- linux-2.4.20-orig/include/linux/netfilter_ipv4/ipt_quota.h Wed Dec
31 21:00:00 1969
+++ linux-2.4.20/include/linux/netfilter_ipv4/ipt_quota.h Wed May 21
12:38:05 2003
@@ -0,0 +1,20 @@
+#ifndef _IPT_QUOTA_H
+#define _IPT_QUOTA_H
+
+#include <linux/list.h>
+
+/* print debug info in both kernel/netfilter module & iptable library */
+/* #define DEBUG_IPT_QUOTA */
+
+#define IPT_QUOTA 0x0001
+#define IPT_IPDSTQUOTA 0x0002
+#define IPT_QUOTA_FLAG_NEGATE 0x0004
+#define IPT_QUOTA_FLAG_COUNT_FULL 0x0009
+
+struct ipt_quota_info {
+ u_int64_t quota;
+ u_int16_t flags;
+ struct list_head *ipquota_hash;
+};
+
+#endif /*_IPT_QUOTA_H*/
diff -urN -X dontdiff linux-2.4.20-orig/net/ipv4/netfilter/ipt_quota.c
linux-2.4.20/net/ipv4/netfilter/ipt_quota.c
--- linux-2.4.20-orig/net/ipv4/netfilter/ipt_quota.c Wed Dec 31 21:00:00
1969
+++ linux-2.4.20/net/ipv4/netfilter/ipt_quota.c Wed May 21 12:38:17 2003
@@ -0,0 +1,381 @@
+/*
+ * netfilter module to enforce network quotas
+ *
+ * Sam Johnston <samj@samj.net>
+ *
+ * 20 May 2003: Diego Woitasen (diegows@linux.org.ar):
+ * - new quota per destination ip
+ * 29 Apr 2003: Brad Fisher (brad@info-link.net):
+ * - ! inverse match and add the option to count total packet size
+ * (headers + data)
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/vmalloc.h>
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv4/ipt_quota.h>
+#include <linux/netfilter_ipv4/ip_conntrack.h>
+#include <linux/proc_fs.h>
+#include <linux/netfilter_ipv4/lockhelp.h>
+#include <linux/sysctl.h>
+
+/* I don't need this DW */
+#define ASSERT_READ_LOCK(x)
+#define ASSERT_WRITE_LOCK(x)
+
+#include <linux/netfilter_ipv4/listhelp.h>
+
+#define QUOTA_KEY(ip) (ip % quota_hash_size)
+
+MODULE_LICENSE("GPL");
+
+static spinlock_t quota_lock = SPIN_LOCK_UNLOCKED;
+
+static int kmem = 0;
+static int vmem = 0;
+static int hashsize = 0;
+static int quota_hash_size = 0;
+static int ipquota_kmem_max = 0; /* max struct ipquota that could be
alloc. */
+
+static kmem_cache_t *ipquota_cache;
+
+MODULE_PARM(hashsize, "i");
+MODULE_PARM(ipquota_kmem_max, "i");
+
+struct ipquota {
+ struct list_head list;
+ u_int32_t ip;
+ u_int64_t quota;
+};
+
+#ifdef CONFIG_SYSCTL
+
+static struct ctl_table_header *ip_quota_sysctl_header;
+
+static ctl_table ipquota_table[] = {
+ {4048, "ipquota_kmem_max", &ipquota_kmem_max, sizeof (ipquota_kmem_max),
+ 0644, NULL, proc_dointvec}
+ ,
+ {0}
+};
+
+static ctl_table ipquota_table_dir[] = {
+ {NET_IPV4, "ipv4", NULL, 0, 0555, ipquota_table, 0, 0, 0, 0, 0},
+ {0}
+};
+
+static ctl_table ipquota_table_root[] = {
+ {CTL_NET, "net", NULL, 0, 0555, ipquota_table_dir, 0, 0, 0, 0, 0},
+ {0}
+};
+
+#endif
+
+static inline void *
+quota_alloc(void)
+{
+
+ if (kmem > ipquota_kmem_max)
+ return NULL;
+ kmem++;
+ return kmem_cache_alloc(ipquota_cache, GFP_ATOMIC);
+
+}
+
+static inline void
+quota_free(void *tmp_if)
+{
+
+ kmem--;
+ kmem_cache_free(ipquota_cache, tmp_if);
+
+}
+
+static inline void *
+quota_valloc(size_t size)
+{
+
+ vmem++;
+ return vmalloc(size);
+
+}
+
+static inline void
+quota_vfree(void *tmp_if)
+{
+
+ vmem--;
+ vfree(tmp_if);
+
+}
+
+static inline int
+quota_cmpfn(const struct ipquota *ipquota_tmp, u_int32_t ip)
+{
+
+ return ipquota_tmp->ip == ip;
+
+}
+
+static struct ipquota *
+quota_get(struct ipt_quota_info *info, u_int32_t ip)
+{
+
+ struct list_head *bucket;
+ struct ipquota *ipquota_tmp;
+
+ bucket = &info->ipquota_hash[QUOTA_KEY(ip)];
+
+ ipquota_tmp = LIST_FIND(bucket, quota_cmpfn, struct ipquota *, ip);
+
+ if (!ipquota_tmp) {
+ ipquota_tmp = quota_alloc();
+ if (!ipquota_tmp) {
+ printk(KERN_ERR
+ "unable to create ipquota item (quota_alloc),"
+ "ipquota_kmem_max reached");
+ return ipquota_tmp;
+ }
+
+ ipquota_tmp->ip = ip;
+ ipquota_tmp->quota = 0;
+ list_append(bucket, ipquota_tmp);
+ }
+ return ipquota_tmp;
+
+}
+
+static int
+quota_hash_init(struct ipt_quota_info *info)
+{
+
+ int i;
+
+ info->ipquota_hash =
+ quota_valloc(sizeof (struct list_head) * quota_hash_size);
+
+ if (!info->ipquota_hash) {
+ printk(KERN_ERR "Unable to create quota_hash\n");
+ return (-1);
+ }
+
+ for (i = 0; i < quota_hash_size; i++)
+ INIT_LIST_HEAD(&info->ipquota_hash[i]);
+
+ return (quota_hash_size);
+
+}
+
+static int
+quota_hash_destroy(struct ipt_quota_info *info)
+{
+
+ int i;
+
+ for (i = 0; i < quota_hash_size; i++) {
+ struct list_head *pos_tmp, *n_tmp;
+ list_for_each_safe(pos_tmp, n_tmp, &info->ipquota_hash[i]) {
+ list_del(pos_tmp);
+ quota_free(pos_tmp);
+ }
+
+ }
+ quota_vfree(info->ipquota_hash);
+
+}
+
+static int
+match(const struct sk_buff *skb,
+ const struct net_device *in,
+ const struct net_device *out,
+ const void *matchinfo,
+ int offset, const void *hdr, u_int16_t datalen, int *hotdrop)
+{
+
+ struct ipt_quota_info *q = (struct ipt_quota_info *) matchinfo;
+
+ if (q->flags & IPT_QUOTA_FLAG_COUNT_FULL)
+ datalen = skb->len;
+
+ spin_lock_bh("a_lock);
+
+ if (q->flags & IPT_QUOTA) {
+ if (q->quota >= datalen) {
+ /* we can afford this one */
+ q->quota -= datalen;
+ goto quota;
+ }
+ /* so we do not allow even small packets from now on */
+ q->quota = 0;
+ } else if (q->flags & IPT_IPDSTQUOTA) {
+
+ struct ipquota *ipquota_tmp;
+
+ ipquota_tmp = quota_get(q, skb->nh.iph->daddr);
+ if (!ipquota_tmp)
+ goto xquota;
+
+ if ((ipquota_tmp->quota + datalen) < q->quota) {
+ ipquota_tmp->quota += datalen;
+ goto quota;
+ } else
+ goto xquota;
+
+ }
+
+ xquota:
+ spin_unlock_bh("a_lock);
+
+#ifdef DEBUG_IPT_QUOTA
+ printk("IPT Quota Failed: max=%llu\n", q->quota);
+#endif
+
+ if (q->flags & IPT_QUOTA_FLAG_NEGATE)
+ return 1;
+ return 0;
+
+ quota:
+ spin_unlock_bh("a_lock);
+
+#ifdef DEBUG_IPT_QUOTA
+ printk("IPT Quota OK: %llu datlen %d \n", q->quota, datalen);
+#endif
+
+ if (q->flags & IPT_QUOTA_FLAG_NEGATE)
+ return 0;
+ return 1;
+
+}
+
+static int
+checkentry(const char *tablename,
+ const struct ipt_ip *ip,
+ void *matchinfo, unsigned int matchsize, unsigned int hook_mask)
+{
+
+ struct ipt_quota_info *info = (struct ipt_quota_info *) matchinfo;
+
+ /* TODO: spinlocks? sanity checks? */
+ if (matchsize != IPT_ALIGN(sizeof (struct ipt_quota_info)))
+ return 0;
+
+ if (info->flags & IPT_IPDSTQUOTA)
+ if (quota_hash_init(info) < 0)
+ return 0;
+
+ return 1;
+
+}
+
+static void
+destroy(void *matchinfo, unsigned int size)
+{
+
+ struct ipt_quota_info *q = (struct ipt_quota_info *)
+ matchinfo;
+
+ if (q->flags & IPT_IPDSTQUOTA) {
+ quota_hash_destroy(q);
+ }
+
+}
+
+static struct ipt_match quota_match = {
+ {NULL, NULL}, "quota", &match, &checkentry, &destroy, THIS_MODULE
+};
+
+int
+read_kmem(char *buf, char **start,
+ off_t offset, int count, int *eof, void *data)
+{
+
+ *start = NULL;
+ *eof = 1;
+ return (sprintf(buf, "%d\n", kmem));
+}
+
+int
+read_vmem(char *buf, char **start,
+ off_t offset, int count, int *eof, void *data)
+{
+
+ *start = NULL;
+ *eof = 1;
+ return (sprintf(buf, "%d\n", vmem));
+}
+
+static int __init
+init(void)
+{
+
+ ipquota_cache =
+ kmem_cache_create("ipquota", sizeof (struct ipquota), 0,
+ SLAB_HWCACHE_ALIGN, NULL, NULL);
+
+ if (!ipquota_cache) {
+ printk(KERN_ERR "Unable to create ipquota slab cache\n");
+ return -1;
+ }
+
+ /* Idea from tcp.c: use 1/16384 of memory. On i386: 32MB
+ * machine has 256 buckets. >= 1GB machines have 8192 buckets. */
+ if (hashsize) {
+ quota_hash_size = hashsize;
+ } else {
+ quota_hash_size = (((num_physpages << PAGE_SHIFT) / 16384)
+ / sizeof (struct list_head));
+ if (num_physpages > (1024 * 1024 * 1024 / PAGE_SIZE))
+ quota_hash_size = 8192;
+ if (quota_hash_size < 16)
+ quota_hash_size = 16;
+ }
+
+ if (!ipquota_kmem_max)
+ ipquota_kmem_max = 8 * quota_hash_size;
+
+ create_proc_read_entry("ipquota_kmem", 600, proc_net, read_kmem, NULL);
+ create_proc_read_entry("ipquota_vmem", 600, proc_net, read_vmem, NULL);
+
+#ifdef CONFIG_SYSCTL
+ ip_quota_sysctl_header = register_sysctl_table(ipquota_table_root, 0);
+ if (ip_quota_sysctl_header == NULL) {
+ kmem_cache_destroy(ipquota_cache);
+ remove_proc_entry("ipquota_kmem", NULL);
+ remove_proc_entry("ipquota_vmem", NULL);
+ return -1;
+ }
+#endif
+
+ printk(KERN_INFO
+ "quota/ipquota initialized (ipquota_kmem_max %d, buckets %d)\n",
+ ipquota_kmem_max, quota_hash_size);
+
+ return ipt_register_match("a_match);
+
+}
+
+static void __exit
+fini(void)
+{
+
+ remove_proc_entry("ipquota_kmem", NULL);
+ remove_proc_entry("ipquota_vmem", NULL);
+
+ kmem_cache_destroy(ipquota_cache);
+
+ ipt_unregister_match("a_match);
+
+#ifdef CONFIG_SYSCTL
+ unregister_sysctl_table(ip_quota_sysctl_header);
+#endif
+
+}
+
+module_init(init);
+module_exit(fini);
dep_tristate ' limit match support' CONFIG_IP_NF_MATCH_LIMIT
$CONFIG_IP_NF_IPTABLES
dep_tristate ' quota match support' CONFIG_IP_NF_MATCH_QUOTA
$CONFIG_IP_NF_IPTABLES
CONFIG_IP_NF_MATCH_LIMIT
quota match support
CONFIG_IP_NF_MATCH_QUOTA
This match implements network quotas and quotas per destination ip.
If you want to compile it as a module, say M here and read
Documentation/modules.txt. If unsure, say `N'.
Author: Sam Johnston <samj@samj.net>
Status: worksforme
This option adds CONFIG_IP_NF_MATCH_QUOTA, which implements network
quotas by decrementing a byte counter with each packet.
Also implements quota per destination ip address.
(Diego Woitasen <diegows@linux.org.ar>)
Supported options are:
--quota <bytes>
The quota in bytes.
--ipdstquota <bytes>
The quota per destination ip address in bytes
obj-$(CONFIG_IP_NF_MATCH_LIMIT) += ipt_limit.o
obj-$(CONFIG_IP_NF_MATCH_QUOTA) += ipt_quota.o
/*
* Shared library add-on to iptables to add quota support
*
* Sam Johnston <samj@samj.net>
*
* 2003/04/02 Diego Woitasen (diegows@linux.org.ar) add quota per dst ip
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <iptables.h>
#include <linux/netfilter_ipv4/ipt_quota.h>
#include <linux/netfilter_ipv4/ip_tables.h>
static struct option opts[] = {
{"quota", 1, 0, IPT_QUOTA},
{"ipdstquota", 1, 0, IPT_IPDSTQUOTA},
{"count-headers", 0, 0, IPT_QUOTA_FLAG_COUNT_FULL},
{0}
};
/* print usage */
static void
help (void)
{
printf ("quota options:\n\n"
" --quota [!] quota quota (bytes)\n"
" --ipdstquota [!] quota quota per ip (bytes)\n"
"\n"
" --count-headers count headers as well as data\n");
}
/* initialise match */
static void
init (struct ipt_entry_match *m, unsigned int *nfcache)
{
struct ipt_quota_info *info = (struct ipt_quota_info *) m->data;
info->flags = 0;
info->quota = 0;
info->ipquota_hash = NULL;
/* no can cache */
*nfcache |= NFC_UNKNOWN;
}
/* print matchinfo */
static void
print (const struct ipt_ip *ip, const struct ipt_entry_match *match,
int numeric)
{
struct ipt_quota_info *q = (struct ipt_quota_info *) match->data;
if (IPT_QUOTA & q->flags)
printf ("quota");
else if (IPT_IPDSTQUOTA & q->flags)
printf ("ipdstquota");
if (IPT_QUOTA_FLAG_NEGATE & q->flags)
printf (" ! ");
printf ("%llu bytes", (unsigned long long) q->quota);
if (IPT_QUOTA_FLAG_COUNT_FULL & q->flags)
printf (" (incl. headers)");
}
/* save matchinfo */
static void
save (const struct ipt_ip *ip, const struct ipt_entry_match *match)
{
struct ipt_quota_info *q = (struct ipt_quota_info *) match->data;
if (IPT_QUOTA & q->flags)
printf ("--quota");
else if (IPT_IPDSTQUOTA & q->flags)
printf ("--ipdstquota");
if (IPT_QUOTA_FLAG_NEGATE & q->flags)
printf (" ! ");
printf (" %llu", (unsigned long long) q->quota);
if (IPT_QUOTA_FLAG_COUNT_FULL & q->flags)
printf (" --count-headers");
}
/* parse quota option */
static int
parse_quota (const char *s, struct ipt_quota_info *quota, int c)
{
if (s && (*s == '-'))
exit_error (PARAMETER_PROBLEM, "quota invalid: '%s'\n", s);
quota->quota = strtoull (s, (char **) NULL, 10);
#ifdef DEBUG_IPT_QUOTA
printf ("Quota: %llu\n", quota->quota);
#endif
if (quota->quota == -1)
exit_error (PARAMETER_PROBLEM, "quota invalid: '%s'\n", s);
else
return 1;
}
/* parse all options, returning true if we found any for us */
static int
parse (int c, char **argv, int invert, unsigned int *flags,
const struct ipt_entry *entry,
unsigned int *nfcache, struct ipt_entry_match **match)
{
struct ipt_quota_info *info = (struct ipt_quota_info *) (*match)->data;
switch (c)
{
case IPT_QUOTA_FLAG_COUNT_FULL:
if (check_inverse (optarg, &invert, NULL, 0))
exit_error (PARAMETER_PROBLEM, "count-headers: unexpected '!'");
info->flags |= IPT_QUOTA_FLAG_COUNT_FULL;
break;
case IPT_QUOTA:
case IPT_IPDSTQUOTA:
if ((info->flags & IPT_QUOTA) && (info->flags & IPT_IPDSTQUOTA))
exit_error (PARAMETER_PROBLEM,
"syntax error: you can't use --quota and --ipdstquota");
check_inverse (optarg, &invert, &optind, 0);
if (invert)
info->flags |= IPT_QUOTA_FLAG_NEGATE;
parse_quota (argv[optind - 1], info, c);
info->flags |= (u_int16_t) c;
*flags = 1;
break;
default:
exit_error (PARAMETER_PROBLEM, "unknown option: try --help");
return 0;
}
return 1;
}
/* no final check */
static void
final_check (unsigned int flags)
{
if (flags != 1)
exit_error (PARAMETER_PROBLEM, "quota: missing arguments");
}
struct iptables_match quota = { NULL,
"quota",
NETFILTER_VERSION,
IPT_ALIGN (sizeof (struct ipt_quota_info)),
IPT_ALIGN (sizeof (struct ipt_quota_info)),
&help,
&init,
&parse,
&final_check,
&print,
&save,
opts
};
void
_init (void)
{
register_match ("a);
}
next reply other threads:[~2003-06-27 14:03 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2003-06-27 14:03 Diego Woitasen [this message]
2003-06-29 17:50 ` [PATCH] This add support for quota per destination ip to quota match Martin Josefsson
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=3EFC4EA3.7020505@linux.org.ar \
--to=diegows@linux.org.ar \
--cc=netfilter-devel@lists.netfilter.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.