12e4e6a17SHarald Welte /* Kernel module to match connection tracking byte counter.
22e4e6a17SHarald Welte * GPL (C) 2002 Martin Devera (devik@cdi.cz).
32e4e6a17SHarald Welte */
48bee4badSJan Engelhardt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
52e4e6a17SHarald Welte #include <linux/module.h>
61977f032SJiri Slaby #include <linux/bitops.h>
72e4e6a17SHarald Welte #include <linux/skbuff.h>
86f6d6a1aSRoman Zippel #include <linux/math64.h>
92e4e6a17SHarald Welte #include <linux/netfilter/x_tables.h>
102e4e6a17SHarald Welte #include <linux/netfilter/xt_connbytes.h>
11587aa641SPatrick McHardy #include <net/netfilter/nf_conntrack.h>
1258401572SKrzysztof Piotr Oledzki #include <net/netfilter/nf_conntrack_acct.h>
132e4e6a17SHarald Welte
142e4e6a17SHarald Welte MODULE_LICENSE("GPL");
152e4e6a17SHarald Welte MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>");
162ae15b64SJan Engelhardt MODULE_DESCRIPTION("Xtables: Number of packets/bytes per connection matching");
172e4e6a17SHarald Welte MODULE_ALIAS("ipt_connbytes");
1873aaf935SJan Engelhardt MODULE_ALIAS("ip6t_connbytes");
192e4e6a17SHarald Welte
201d93a9cbSJan Engelhardt static bool
connbytes_mt(const struct sk_buff * skb,struct xt_action_param * par)2162fc8051SJan Engelhardt connbytes_mt(const struct sk_buff *skb, struct xt_action_param *par)
222e4e6a17SHarald Welte {
23f7108a20SJan Engelhardt const struct xt_connbytes_info *sinfo = par->matchinfo;
24a47362a2SJan Engelhardt const struct nf_conn *ct;
25587aa641SPatrick McHardy enum ip_conntrack_info ctinfo;
262e4e6a17SHarald Welte u_int64_t what = 0; /* initialize to make gcc happy */
27fb74a841SPatrick McHardy u_int64_t bytes = 0;
28fb74a841SPatrick McHardy u_int64_t pkts = 0;
29f7b13e43SHolger Eitzenberger const struct nf_conn_acct *acct;
3058401572SKrzysztof Piotr Oledzki const struct nf_conn_counter *counters;
312e4e6a17SHarald Welte
32587aa641SPatrick McHardy ct = nf_ct_get(skb, &ctinfo);
33587aa641SPatrick McHardy if (!ct)
341d93a9cbSJan Engelhardt return false;
3558401572SKrzysztof Piotr Oledzki
36f7b13e43SHolger Eitzenberger acct = nf_conn_acct_find(ct);
37f7b13e43SHolger Eitzenberger if (!acct)
3858401572SKrzysztof Piotr Oledzki return false;
392e4e6a17SHarald Welte
40f7b13e43SHolger Eitzenberger counters = acct->counter;
412e4e6a17SHarald Welte switch (sinfo->what) {
422e4e6a17SHarald Welte case XT_CONNBYTES_PKTS:
432e4e6a17SHarald Welte switch (sinfo->direction) {
442e4e6a17SHarald Welte case XT_CONNBYTES_DIR_ORIGINAL:
45b3e0bfa7SEric Dumazet what = atomic64_read(&counters[IP_CT_DIR_ORIGINAL].packets);
462e4e6a17SHarald Welte break;
472e4e6a17SHarald Welte case XT_CONNBYTES_DIR_REPLY:
48b3e0bfa7SEric Dumazet what = atomic64_read(&counters[IP_CT_DIR_REPLY].packets);
492e4e6a17SHarald Welte break;
502e4e6a17SHarald Welte case XT_CONNBYTES_DIR_BOTH:
51b3e0bfa7SEric Dumazet what = atomic64_read(&counters[IP_CT_DIR_ORIGINAL].packets);
52b3e0bfa7SEric Dumazet what += atomic64_read(&counters[IP_CT_DIR_REPLY].packets);
532e4e6a17SHarald Welte break;
542e4e6a17SHarald Welte }
552e4e6a17SHarald Welte break;
562e4e6a17SHarald Welte case XT_CONNBYTES_BYTES:
572e4e6a17SHarald Welte switch (sinfo->direction) {
582e4e6a17SHarald Welte case XT_CONNBYTES_DIR_ORIGINAL:
59b3e0bfa7SEric Dumazet what = atomic64_read(&counters[IP_CT_DIR_ORIGINAL].bytes);
602e4e6a17SHarald Welte break;
612e4e6a17SHarald Welte case XT_CONNBYTES_DIR_REPLY:
62b3e0bfa7SEric Dumazet what = atomic64_read(&counters[IP_CT_DIR_REPLY].bytes);
632e4e6a17SHarald Welte break;
642e4e6a17SHarald Welte case XT_CONNBYTES_DIR_BOTH:
65b3e0bfa7SEric Dumazet what = atomic64_read(&counters[IP_CT_DIR_ORIGINAL].bytes);
66b3e0bfa7SEric Dumazet what += atomic64_read(&counters[IP_CT_DIR_REPLY].bytes);
672e4e6a17SHarald Welte break;
682e4e6a17SHarald Welte }
692e4e6a17SHarald Welte break;
702e4e6a17SHarald Welte case XT_CONNBYTES_AVGPKT:
712e4e6a17SHarald Welte switch (sinfo->direction) {
722e4e6a17SHarald Welte case XT_CONNBYTES_DIR_ORIGINAL:
73b3e0bfa7SEric Dumazet bytes = atomic64_read(&counters[IP_CT_DIR_ORIGINAL].bytes);
74b3e0bfa7SEric Dumazet pkts = atomic64_read(&counters[IP_CT_DIR_ORIGINAL].packets);
752e4e6a17SHarald Welte break;
762e4e6a17SHarald Welte case XT_CONNBYTES_DIR_REPLY:
77b3e0bfa7SEric Dumazet bytes = atomic64_read(&counters[IP_CT_DIR_REPLY].bytes);
78b3e0bfa7SEric Dumazet pkts = atomic64_read(&counters[IP_CT_DIR_REPLY].packets);
792e4e6a17SHarald Welte break;
802e4e6a17SHarald Welte case XT_CONNBYTES_DIR_BOTH:
81b3e0bfa7SEric Dumazet bytes = atomic64_read(&counters[IP_CT_DIR_ORIGINAL].bytes) +
82b3e0bfa7SEric Dumazet atomic64_read(&counters[IP_CT_DIR_REPLY].bytes);
83b3e0bfa7SEric Dumazet pkts = atomic64_read(&counters[IP_CT_DIR_ORIGINAL].packets) +
84b3e0bfa7SEric Dumazet atomic64_read(&counters[IP_CT_DIR_REPLY].packets);
852e4e6a17SHarald Welte break;
862e4e6a17SHarald Welte }
87fb74a841SPatrick McHardy if (pkts != 0)
886f6d6a1aSRoman Zippel what = div64_u64(bytes, pkts);
892e4e6a17SHarald Welte break;
902e4e6a17SHarald Welte }
912e4e6a17SHarald Welte
920354b48fSFlorian Westphal if (sinfo->count.to >= sinfo->count.from)
937c4e36bcSJan Engelhardt return what <= sinfo->count.to && what >= sinfo->count.from;
940354b48fSFlorian Westphal else /* inverted */
950354b48fSFlorian Westphal return what < sinfo->count.to || what > sinfo->count.from;
962e4e6a17SHarald Welte }
972e4e6a17SHarald Welte
connbytes_mt_check(const struct xt_mtchk_param * par)98b0f38452SJan Engelhardt static int connbytes_mt_check(const struct xt_mtchk_param *par)
992e4e6a17SHarald Welte {
1009b4fce7aSJan Engelhardt const struct xt_connbytes_info *sinfo = par->matchinfo;
1014a5a5c73SJan Engelhardt int ret;
1022e4e6a17SHarald Welte
1032e4e6a17SHarald Welte if (sinfo->what != XT_CONNBYTES_PKTS &&
1042e4e6a17SHarald Welte sinfo->what != XT_CONNBYTES_BYTES &&
1052e4e6a17SHarald Welte sinfo->what != XT_CONNBYTES_AVGPKT)
106bd414ee6SJan Engelhardt return -EINVAL;
1072e4e6a17SHarald Welte
1082e4e6a17SHarald Welte if (sinfo->direction != XT_CONNBYTES_DIR_ORIGINAL &&
1092e4e6a17SHarald Welte sinfo->direction != XT_CONNBYTES_DIR_REPLY &&
1102e4e6a17SHarald Welte sinfo->direction != XT_CONNBYTES_DIR_BOTH)
111bd414ee6SJan Engelhardt return -EINVAL;
1122e4e6a17SHarald Welte
113ecb2421bSFlorian Westphal ret = nf_ct_netns_get(par->net, par->family);
114f95c74e3SJan Engelhardt if (ret < 0)
115b2606644SFlorian Westphal pr_info_ratelimited("cannot load conntrack support for proto=%u\n",
1168bee4badSJan Engelhardt par->family);
117a8756201STim Gardner
118a8756201STim Gardner /*
119a8756201STim Gardner * This filter cannot function correctly unless connection tracking
120a8756201STim Gardner * accounting is enabled, so complain in the hope that someone notices.
121a8756201STim Gardner */
122a8756201STim Gardner if (!nf_ct_acct_enabled(par->net)) {
123b167a37cSJoe Perches pr_warn("Forcing CT accounting to be enabled\n");
124a8756201STim Gardner nf_ct_set_acct(par->net, true);
125a8756201STim Gardner }
126a8756201STim Gardner
1274a5a5c73SJan Engelhardt return ret;
12811078c37SYasuyuki Kozakai }
12911078c37SYasuyuki Kozakai
connbytes_mt_destroy(const struct xt_mtdtor_param * par)1306be3d859SJan Engelhardt static void connbytes_mt_destroy(const struct xt_mtdtor_param *par)
13111078c37SYasuyuki Kozakai {
132ecb2421bSFlorian Westphal nf_ct_netns_put(par->net, par->family);
13311078c37SYasuyuki Kozakai }
13411078c37SYasuyuki Kozakai
13592f3b2b1SJan Engelhardt static struct xt_match connbytes_mt_reg __read_mostly = {
1362e4e6a17SHarald Welte .name = "connbytes",
13792f3b2b1SJan Engelhardt .revision = 0,
13892f3b2b1SJan Engelhardt .family = NFPROTO_UNSPEC,
139d3c5ee6dSJan Engelhardt .checkentry = connbytes_mt_check,
140d3c5ee6dSJan Engelhardt .match = connbytes_mt,
141d3c5ee6dSJan Engelhardt .destroy = connbytes_mt_destroy,
1425d04bff0SPatrick McHardy .matchsize = sizeof(struct xt_connbytes_info),
14392f3b2b1SJan Engelhardt .me = THIS_MODULE,
1442e4e6a17SHarald Welte };
1452e4e6a17SHarald Welte
connbytes_mt_init(void)146d3c5ee6dSJan Engelhardt static int __init connbytes_mt_init(void)
1472e4e6a17SHarald Welte {
14892f3b2b1SJan Engelhardt return xt_register_match(&connbytes_mt_reg);
1492e4e6a17SHarald Welte }
1502e4e6a17SHarald Welte
connbytes_mt_exit(void)151d3c5ee6dSJan Engelhardt static void __exit connbytes_mt_exit(void)
1522e4e6a17SHarald Welte {
15392f3b2b1SJan Engelhardt xt_unregister_match(&connbytes_mt_reg);
1542e4e6a17SHarald Welte }
1552e4e6a17SHarald Welte
156d3c5ee6dSJan Engelhardt module_init(connbytes_mt_init);
157d3c5ee6dSJan Engelhardt module_exit(connbytes_mt_exit);
158