1/* Kernel module to match connection tracking byte counter.
2 * GPL (C) 2002 Martin Devera (devik@cdi.cz).
3 */
4#include <linux/module.h>
5#include <linux/skbuff.h>
6#include <linux/netfilter/x_tables.h>
7#include <linux/netfilter/xt_connbytes.h>
8#include <net/netfilter/nf_conntrack.h>
9
10#include <asm/div64.h>
11#include <asm/bitops.h>
12
13MODULE_LICENSE("GPL");
14MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>");
15MODULE_DESCRIPTION("iptables match for matching number of pkts/bytes per connection");
16MODULE_ALIAS("ipt_connbytes");
17
18static int
19match(const struct sk_buff *skb,
20      const struct net_device *in,
21      const struct net_device *out,
22      const struct xt_match *match,
23      const void *matchinfo,
24      int offset,
25      unsigned int protoff,
26      int *hotdrop)
27{
28	const struct xt_connbytes_info *sinfo = matchinfo;
29	struct nf_conn *ct;
30	enum ip_conntrack_info ctinfo;
31	u_int64_t what = 0;	/* initialize to make gcc happy */
32	u_int64_t bytes = 0;
33	u_int64_t pkts = 0;
34	const struct ip_conntrack_counter *counters;
35
36	ct = nf_ct_get(skb, &ctinfo);
37	if (!ct)
38		return 0;
39	counters = ct->counters;
40
41	switch (sinfo->what) {
42	case XT_CONNBYTES_PKTS:
43		switch (sinfo->direction) {
44		case XT_CONNBYTES_DIR_ORIGINAL:
45			what = counters[IP_CT_DIR_ORIGINAL].packets;
46			break;
47		case XT_CONNBYTES_DIR_REPLY:
48			what = counters[IP_CT_DIR_REPLY].packets;
49			break;
50		case XT_CONNBYTES_DIR_BOTH:
51			what = counters[IP_CT_DIR_ORIGINAL].packets;
52			what += counters[IP_CT_DIR_REPLY].packets;
53			break;
54		}
55		break;
56	case XT_CONNBYTES_BYTES:
57		switch (sinfo->direction) {
58		case XT_CONNBYTES_DIR_ORIGINAL:
59			what = counters[IP_CT_DIR_ORIGINAL].bytes;
60			break;
61		case XT_CONNBYTES_DIR_REPLY:
62			what = counters[IP_CT_DIR_REPLY].bytes;
63			break;
64		case XT_CONNBYTES_DIR_BOTH:
65			what = counters[IP_CT_DIR_ORIGINAL].bytes;
66			what += counters[IP_CT_DIR_REPLY].bytes;
67			break;
68		}
69		break;
70	case XT_CONNBYTES_AVGPKT:
71		switch (sinfo->direction) {
72		case XT_CONNBYTES_DIR_ORIGINAL:
73			bytes = counters[IP_CT_DIR_ORIGINAL].bytes;
74			pkts  = counters[IP_CT_DIR_ORIGINAL].packets;
75			break;
76		case XT_CONNBYTES_DIR_REPLY:
77			bytes = counters[IP_CT_DIR_REPLY].bytes;
78			pkts  = counters[IP_CT_DIR_REPLY].packets;
79			break;
80		case XT_CONNBYTES_DIR_BOTH:
81			bytes = counters[IP_CT_DIR_ORIGINAL].bytes +
82				counters[IP_CT_DIR_REPLY].bytes;
83			pkts  = counters[IP_CT_DIR_ORIGINAL].packets +
84				counters[IP_CT_DIR_REPLY].packets;
85			break;
86		}
87		if (pkts != 0)
88			what = div64_64(bytes, pkts);
89		break;
90	}
91
92	if (sinfo->count.to)
93		return (what <= sinfo->count.to && what >= sinfo->count.from);
94	else
95		return (what >= sinfo->count.from);
96}
97
98static int check(const char *tablename,
99		 const void *ip,
100		 const struct xt_match *match,
101		 void *matchinfo,
102		 unsigned int hook_mask)
103{
104	const struct xt_connbytes_info *sinfo = matchinfo;
105
106	if (sinfo->what != XT_CONNBYTES_PKTS &&
107	    sinfo->what != XT_CONNBYTES_BYTES &&
108	    sinfo->what != XT_CONNBYTES_AVGPKT)
109		return 0;
110
111	if (sinfo->direction != XT_CONNBYTES_DIR_ORIGINAL &&
112	    sinfo->direction != XT_CONNBYTES_DIR_REPLY &&
113	    sinfo->direction != XT_CONNBYTES_DIR_BOTH)
114		return 0;
115
116	if (nf_ct_l3proto_try_module_get(match->family) < 0) {
117		printk(KERN_WARNING "can't load conntrack support for "
118				    "proto=%d\n", match->family);
119		return 0;
120	}
121
122	return 1;
123}
124
125static void
126destroy(const struct xt_match *match, void *matchinfo)
127{
128	nf_ct_l3proto_module_put(match->family);
129}
130
131static struct xt_match xt_connbytes_match[] = {
132	{
133		.name		= "connbytes",
134		.family		= AF_INET,
135		.checkentry	= check,
136		.match		= match,
137		.destroy	= destroy,
138		.matchsize	= sizeof(struct xt_connbytes_info),
139		.me		= THIS_MODULE
140	},
141	{
142		.name		= "connbytes",
143		.family		= AF_INET6,
144		.checkentry	= check,
145		.match		= match,
146		.destroy	= destroy,
147		.matchsize	= sizeof(struct xt_connbytes_info),
148		.me		= THIS_MODULE
149	},
150};
151
152static int __init xt_connbytes_init(void)
153{
154	return xt_register_matches(xt_connbytes_match,
155				   ARRAY_SIZE(xt_connbytes_match));
156}
157
158static void __exit xt_connbytes_fini(void)
159{
160	xt_unregister_matches(xt_connbytes_match,
161			      ARRAY_SIZE(xt_connbytes_match));
162}
163
164module_init(xt_connbytes_init);
165module_exit(xt_connbytes_fini);
166