summaryrefslogtreecommitdiff
path: root/net/netfilter/xt_connbytes.c
blob: 2aabdcea8707236244984357c0d079acc5ffcb39 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
/* 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_ratelimited("cannot load conntrack support for proto=%u\n",
				    par->family);
		return ret;
	}

	/*
	 * 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);