mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-04 01:49:36 +00:00
![Florian Westphal](/assets/img/avatar_default.png)
currently aliased to try_module_get/_put. Will be changed in next patch when we add functions to make use of ->net argument to store usercount per l3proto tracker. This is needed to avoid registering the conntrack hooks in all netns and later only enable connection tracking in those that need conntrack. Signed-off-by: Florian Westphal <fw@strlen.de> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
157 lines
4.5 KiB
C
157 lines
4.5 KiB
C
/* Kernel module to match connection tracking byte counter.
|
|
* GPL (C) 2002 Martin Devera (devik@cdi.cz).
|
|
*/
|
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
#include <linux/module.h>
|
|
#include <linux/bitops.h>
|
|
#include <linux/skbuff.h>
|
|
#include <linux/math64.h>
|
|
#include <linux/netfilter/x_tables.h>
|
|
#include <linux/netfilter/xt_connbytes.h>
|
|
#include <net/netfilter/nf_conntrack.h>
|
|
#include <net/netfilter/nf_conntrack_acct.h>
|
|
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>");
|
|
MODULE_DESCRIPTION("Xtables: Number of packets/bytes per connection matching");
|
|
MODULE_ALIAS("ipt_connbytes");
|
|
MODULE_ALIAS("ip6t_connbytes");
|
|
|
|
static bool
|
|
connbytes_mt(const struct sk_buff *skb, struct xt_action_param *par)
|
|
{
|
|
const struct xt_connbytes_info *sinfo = par->matchinfo;
|
|
const struct nf_conn *ct;
|
|
enum ip_conntrack_info ctinfo;
|
|
u_int64_t what = 0; /* initialize to make gcc happy */
|
|
u_int64_t bytes = 0;
|
|
u_int64_t pkts = 0;
|
|
const struct nf_conn_acct *acct;
|
|
const struct nf_conn_counter *counters;
|
|
|
|
ct = nf_ct_get(skb, &ctinfo);
|
|
if (!ct)
|
|
return false;
|
|
|
|
acct = nf_conn_acct_find(ct);
|
|
if (!acct)
|
|
return false;
|
|
|
|
counters = acct->counter;
|
|
switch (sinfo->what) {
|
|
case XT_CONNBYTES_PKTS:
|
|
switch (sinfo->direction) {
|
|
case XT_CONNBYTES_DIR_ORIGINAL:
|
|
what = atomic64_read(&counters[IP_CT_DIR_ORIGINAL].packets);
|
|
break;
|
|
case XT_CONNBYTES_DIR_REPLY:
|
|
what = atomic64_read(&counters[IP_CT_DIR_REPLY].packets);
|
|
break;
|
|
case XT_CONNBYTES_DIR_BOTH:
|
|
what = atomic64_read(&counters[IP_CT_DIR_ORIGINAL].packets);
|
|
what += atomic64_read(&counters[IP_CT_DIR_REPLY].packets);
|
|
break;
|
|
}
|
|
break;
|
|
case XT_CONNBYTES_BYTES:
|
|
switch (sinfo->direction) {
|
|
case XT_CONNBYTES_DIR_ORIGINAL:
|
|
what = atomic64_read(&counters[IP_CT_DIR_ORIGINAL].bytes);
|
|
break;
|
|
case XT_CONNBYTES_DIR_REPLY:
|
|
what = atomic64_read(&counters[IP_CT_DIR_REPLY].bytes);
|
|
break;
|
|
case XT_CONNBYTES_DIR_BOTH:
|
|
what = atomic64_read(&counters[IP_CT_DIR_ORIGINAL].bytes);
|
|
what += atomic64_read(&counters[IP_CT_DIR_REPLY].bytes);
|
|
break;
|
|
}
|
|
break;
|
|
case XT_CONNBYTES_AVGPKT:
|
|
switch (sinfo->direction) {
|
|
case XT_CONNBYTES_DIR_ORIGINAL:
|
|
bytes = atomic64_read(&counters[IP_CT_DIR_ORIGINAL].bytes);
|
|
pkts = atomic64_read(&counters[IP_CT_DIR_ORIGINAL].packets);
|
|
break;
|
|
case XT_CONNBYTES_DIR_REPLY:
|
|
bytes = atomic64_read(&counters[IP_CT_DIR_REPLY].bytes);
|
|
pkts = atomic64_read(&counters[IP_CT_DIR_REPLY].packets);
|
|
break;
|
|
case XT_CONNBYTES_DIR_BOTH:
|
|
bytes = atomic64_read(&counters[IP_CT_DIR_ORIGINAL].bytes) +
|
|
atomic64_read(&counters[IP_CT_DIR_REPLY].bytes);
|
|
pkts = atomic64_read(&counters[IP_CT_DIR_ORIGINAL].packets) +
|
|
atomic64_read(&counters[IP_CT_DIR_REPLY].packets);
|
|
break;
|
|
}
|
|
if (pkts != 0)
|
|
what = div64_u64(bytes, pkts);
|
|
break;
|
|
}
|
|
|
|
if (sinfo->count.to >= sinfo->count.from)
|
|
return what <= sinfo->count.to && what >= sinfo->count.from;
|
|
else /* inverted */
|
|
return what < sinfo->count.to || what > sinfo->count.from;
|
|
}
|
|
|
|
static int connbytes_mt_check(const struct xt_mtchk_param *par)
|
|
{
|
|
const struct xt_connbytes_info *sinfo = par->matchinfo;
|
|
int ret;
|
|
|
|
if (sinfo->what != XT_CONNBYTES_PKTS &&
|
|
sinfo->what != XT_CONNBYTES_BYTES &&
|
|
sinfo->what != XT_CONNBYTES_AVGPKT)
|
|
return -EINVAL;
|
|
|
|
if (sinfo->direction != XT_CONNBYTES_DIR_ORIGINAL &&
|
|
sinfo->direction != XT_CONNBYTES_DIR_REPLY &&
|
|
sinfo->direction != XT_CONNBYTES_DIR_BOTH)
|
|
return -EINVAL;
|
|
|
|
ret = nf_ct_netns_get(par->net, par->family);
|
|
if (ret < 0)
|
|
pr_info("cannot load conntrack support for proto=%u\n",
|
|
par->family);
|
|
|
|
/*
|
|
* This filter cannot function correctly unless connection tracking
|
|
* accounting is enabled, so complain in the hope that someone notices.
|
|
*/
|
|
if (!nf_ct_acct_enabled(par->net)) {
|
|
pr_warn("Forcing CT accounting to be enabled\n");
|
|
nf_ct_set_acct(par->net, true);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void connbytes_mt_destroy(const struct xt_mtdtor_param *par)
|
|
{
|
|
nf_ct_netns_put(par->net, par->family);
|
|
}
|
|
|
|
static struct xt_match connbytes_mt_reg __read_mostly = {
|
|
.name = "connbytes",
|
|
.revision = 0,
|
|
.family = NFPROTO_UNSPEC,
|
|
.checkentry = connbytes_mt_check,
|
|
.match = connbytes_mt,
|
|
.destroy = connbytes_mt_destroy,
|
|
.matchsize = sizeof(struct xt_connbytes_info),
|
|
.me = THIS_MODULE,
|
|
};
|
|
|
|
static int __init connbytes_mt_init(void)
|
|
{
|
|
return xt_register_match(&connbytes_mt_reg);
|
|
}
|
|
|
|
static void __exit connbytes_mt_exit(void)
|
|
{
|
|
xt_unregister_match(&connbytes_mt_reg);
|
|
}
|
|
|
|
module_init(connbytes_mt_init);
|
|
module_exit(connbytes_mt_exit);
|