xref: /openbmc/linux/net/netfilter/xt_connbytes.c (revision 4470bbc7)
12e4e6a17SHarald Welte /* Kernel module to match connection tracking byte counter.
22e4e6a17SHarald Welte  * GPL (C) 2002 Martin Devera (devik@cdi.cz).
32e4e6a17SHarald Welte  *
42e4e6a17SHarald Welte  * 2004-07-20 Harald Welte <laforge@netfilter.org>
52e4e6a17SHarald Welte  * 	- reimplemented to use per-connection accounting counters
62e4e6a17SHarald Welte  * 	- add functionality to match number of packets
72e4e6a17SHarald Welte  * 	- add functionality to match average packet size
82e4e6a17SHarald Welte  * 	- add support to match directions seperately
92e4e6a17SHarald Welte  * 2005-10-16 Harald Welte <laforge@netfilter.org>
102e4e6a17SHarald Welte  * 	- Port to x_tables
112e4e6a17SHarald Welte  *
122e4e6a17SHarald Welte  */
132e4e6a17SHarald Welte #include <linux/module.h>
142e4e6a17SHarald Welte #include <linux/skbuff.h>
152e4e6a17SHarald Welte #include <net/netfilter/nf_conntrack_compat.h>
162e4e6a17SHarald Welte #include <linux/netfilter/x_tables.h>
172e4e6a17SHarald Welte #include <linux/netfilter/xt_connbytes.h>
182e4e6a17SHarald Welte 
192e4e6a17SHarald Welte #include <asm/div64.h>
202e4e6a17SHarald Welte #include <asm/bitops.h>
212e4e6a17SHarald Welte 
222e4e6a17SHarald Welte MODULE_LICENSE("GPL");
232e4e6a17SHarald Welte MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>");
242e4e6a17SHarald Welte MODULE_DESCRIPTION("iptables match for matching number of pkts/bytes per connection");
252e4e6a17SHarald Welte MODULE_ALIAS("ipt_connbytes");
262e4e6a17SHarald Welte 
272e4e6a17SHarald Welte /* 64bit divisor, dividend and result. dynamic precision */
282e4e6a17SHarald Welte static u_int64_t div64_64(u_int64_t dividend, u_int64_t divisor)
292e4e6a17SHarald Welte {
302e4e6a17SHarald Welte 	u_int32_t d = divisor;
312e4e6a17SHarald Welte 
322e4e6a17SHarald Welte 	if (divisor > 0xffffffffULL) {
332e4e6a17SHarald Welte 		unsigned int shift = fls(divisor >> 32);
342e4e6a17SHarald Welte 
352e4e6a17SHarald Welte 		d = divisor >> shift;
362e4e6a17SHarald Welte 		dividend >>= shift;
372e4e6a17SHarald Welte 	}
382e4e6a17SHarald Welte 
392e4e6a17SHarald Welte 	do_div(dividend, d);
402e4e6a17SHarald Welte 	return dividend;
412e4e6a17SHarald Welte }
422e4e6a17SHarald Welte 
432e4e6a17SHarald Welte static int
442e4e6a17SHarald Welte match(const struct sk_buff *skb,
452e4e6a17SHarald Welte       const struct net_device *in,
462e4e6a17SHarald Welte       const struct net_device *out,
47c4986734SPatrick McHardy       const struct xt_match *match,
482e4e6a17SHarald Welte       const void *matchinfo,
492e4e6a17SHarald Welte       int offset,
502e4e6a17SHarald Welte       unsigned int protoff,
512e4e6a17SHarald Welte       int *hotdrop)
522e4e6a17SHarald Welte {
532e4e6a17SHarald Welte 	const struct xt_connbytes_info *sinfo = matchinfo;
542e4e6a17SHarald Welte 	u_int64_t what = 0;	/* initialize to make gcc happy */
552e4e6a17SHarald Welte 	const struct ip_conntrack_counter *counters;
562e4e6a17SHarald Welte 
572e4e6a17SHarald Welte 	if (!(counters = nf_ct_get_counters(skb)))
582e4e6a17SHarald Welte 		return 0; /* no match */
592e4e6a17SHarald Welte 
602e4e6a17SHarald Welte 	switch (sinfo->what) {
612e4e6a17SHarald Welte 	case XT_CONNBYTES_PKTS:
622e4e6a17SHarald Welte 		switch (sinfo->direction) {
632e4e6a17SHarald Welte 		case XT_CONNBYTES_DIR_ORIGINAL:
642e4e6a17SHarald Welte 			what = counters[IP_CT_DIR_ORIGINAL].packets;
652e4e6a17SHarald Welte 			break;
662e4e6a17SHarald Welte 		case XT_CONNBYTES_DIR_REPLY:
672e4e6a17SHarald Welte 			what = counters[IP_CT_DIR_REPLY].packets;
682e4e6a17SHarald Welte 			break;
692e4e6a17SHarald Welte 		case XT_CONNBYTES_DIR_BOTH:
702e4e6a17SHarald Welte 			what = counters[IP_CT_DIR_ORIGINAL].packets;
712e4e6a17SHarald Welte 			what += counters[IP_CT_DIR_REPLY].packets;
722e4e6a17SHarald Welte 			break;
732e4e6a17SHarald Welte 		}
742e4e6a17SHarald Welte 		break;
752e4e6a17SHarald Welte 	case XT_CONNBYTES_BYTES:
762e4e6a17SHarald Welte 		switch (sinfo->direction) {
772e4e6a17SHarald Welte 		case XT_CONNBYTES_DIR_ORIGINAL:
782e4e6a17SHarald Welte 			what = counters[IP_CT_DIR_ORIGINAL].bytes;
792e4e6a17SHarald Welte 			break;
802e4e6a17SHarald Welte 		case XT_CONNBYTES_DIR_REPLY:
812e4e6a17SHarald Welte 			what = counters[IP_CT_DIR_REPLY].bytes;
822e4e6a17SHarald Welte 			break;
832e4e6a17SHarald Welte 		case XT_CONNBYTES_DIR_BOTH:
842e4e6a17SHarald Welte 			what = counters[IP_CT_DIR_ORIGINAL].bytes;
852e4e6a17SHarald Welte 			what += counters[IP_CT_DIR_REPLY].bytes;
862e4e6a17SHarald Welte 			break;
872e4e6a17SHarald Welte 		}
882e4e6a17SHarald Welte 		break;
892e4e6a17SHarald Welte 	case XT_CONNBYTES_AVGPKT:
902e4e6a17SHarald Welte 		switch (sinfo->direction) {
912e4e6a17SHarald Welte 		case XT_CONNBYTES_DIR_ORIGINAL:
922e4e6a17SHarald Welte 			what = div64_64(counters[IP_CT_DIR_ORIGINAL].bytes,
932e4e6a17SHarald Welte 					counters[IP_CT_DIR_ORIGINAL].packets);
942e4e6a17SHarald Welte 			break;
952e4e6a17SHarald Welte 		case XT_CONNBYTES_DIR_REPLY:
962e4e6a17SHarald Welte 			what = div64_64(counters[IP_CT_DIR_REPLY].bytes,
972e4e6a17SHarald Welte 					counters[IP_CT_DIR_REPLY].packets);
982e4e6a17SHarald Welte 			break;
992e4e6a17SHarald Welte 		case XT_CONNBYTES_DIR_BOTH:
1002e4e6a17SHarald Welte 			{
1012e4e6a17SHarald Welte 				u_int64_t bytes;
1022e4e6a17SHarald Welte 				u_int64_t pkts;
1032e4e6a17SHarald Welte 				bytes = counters[IP_CT_DIR_ORIGINAL].bytes +
1042e4e6a17SHarald Welte 					counters[IP_CT_DIR_REPLY].bytes;
1052e4e6a17SHarald Welte 				pkts = counters[IP_CT_DIR_ORIGINAL].packets+
1062e4e6a17SHarald Welte 					counters[IP_CT_DIR_REPLY].packets;
1072e4e6a17SHarald Welte 
1082e4e6a17SHarald Welte 				/* FIXME_THEORETICAL: what to do if sum
1092e4e6a17SHarald Welte 				 * overflows ? */
1102e4e6a17SHarald Welte 
1112e4e6a17SHarald Welte 				what = div64_64(bytes, pkts);
1122e4e6a17SHarald Welte 			}
1132e4e6a17SHarald Welte 			break;
1142e4e6a17SHarald Welte 		}
1152e4e6a17SHarald Welte 		break;
1162e4e6a17SHarald Welte 	}
1172e4e6a17SHarald Welte 
1182e4e6a17SHarald Welte 	if (sinfo->count.to)
1192e4e6a17SHarald Welte 		return (what <= sinfo->count.to && what >= sinfo->count.from);
1202e4e6a17SHarald Welte 	else
1212e4e6a17SHarald Welte 		return (what >= sinfo->count.from);
1222e4e6a17SHarald Welte }
1232e4e6a17SHarald Welte 
1242e4e6a17SHarald Welte static int check(const char *tablename,
1252e4e6a17SHarald Welte 		 const void *ip,
126c4986734SPatrick McHardy 		 const struct xt_match *match,
1272e4e6a17SHarald Welte 		 void *matchinfo,
1282e4e6a17SHarald Welte 		 unsigned int matchsize,
1292e4e6a17SHarald Welte 		 unsigned int hook_mask)
1302e4e6a17SHarald Welte {
1312e4e6a17SHarald Welte 	const struct xt_connbytes_info *sinfo = matchinfo;
1322e4e6a17SHarald Welte 
1332e4e6a17SHarald Welte 	if (sinfo->what != XT_CONNBYTES_PKTS &&
1342e4e6a17SHarald Welte 	    sinfo->what != XT_CONNBYTES_BYTES &&
1352e4e6a17SHarald Welte 	    sinfo->what != XT_CONNBYTES_AVGPKT)
1362e4e6a17SHarald Welte 		return 0;
1372e4e6a17SHarald Welte 
1382e4e6a17SHarald Welte 	if (sinfo->direction != XT_CONNBYTES_DIR_ORIGINAL &&
1392e4e6a17SHarald Welte 	    sinfo->direction != XT_CONNBYTES_DIR_REPLY &&
1402e4e6a17SHarald Welte 	    sinfo->direction != XT_CONNBYTES_DIR_BOTH)
1412e4e6a17SHarald Welte 		return 0;
1422e4e6a17SHarald Welte 
1432e4e6a17SHarald Welte 	return 1;
1442e4e6a17SHarald Welte }
1452e4e6a17SHarald Welte 
1464470bbc7SPatrick McHardy static struct xt_match xt_connbytes_match = {
1474470bbc7SPatrick McHardy 	{
1482e4e6a17SHarald Welte 		.name		= "connbytes",
149a45049c5SPablo Neira Ayuso 		.family		= AF_INET,
1505d04bff0SPatrick McHardy 		.checkentry	= check,
1514470bbc7SPatrick McHardy 		.match		= match,
1525d04bff0SPatrick McHardy 		.matchsize	= sizeof(struct xt_connbytes_info),
1532e4e6a17SHarald Welte 		.me		= THIS_MODULE
1544470bbc7SPatrick McHardy 	},
1554470bbc7SPatrick McHardy 	{
1564470bbc7SPatrick McHardy 		.name		= "connbytes",
1574470bbc7SPatrick McHardy 		.family		= AF_INET6,
1584470bbc7SPatrick McHardy 		.checkentry	= check,
1594470bbc7SPatrick McHardy 		.match		= match,
1604470bbc7SPatrick McHardy 		.matchsize	= sizeof(struct xt_connbytes_info),
1614470bbc7SPatrick McHardy 		.me		= THIS_MODULE
1624470bbc7SPatrick McHardy 	},
1632e4e6a17SHarald Welte };
1642e4e6a17SHarald Welte 
16565b4b4e8SAndrew Morton static int __init xt_connbytes_init(void)
1662e4e6a17SHarald Welte {
1674470bbc7SPatrick McHardy 	return xt_register_matches(xt_connbytes_match,
1684470bbc7SPatrick McHardy 				   ARRAY_SIZE(xt_connbytes_match));
1692e4e6a17SHarald Welte }
1702e4e6a17SHarald Welte 
17165b4b4e8SAndrew Morton static void __exit xt_connbytes_fini(void)
1722e4e6a17SHarald Welte {
1734470bbc7SPatrick McHardy 	xt_unregister_matches(xt_connbytes_match,
1744470bbc7SPatrick McHardy 			      ARRAY_SIZE(xt_connbytes_match));
1752e4e6a17SHarald Welte }
1762e4e6a17SHarald Welte 
17765b4b4e8SAndrew Morton module_init(xt_connbytes_init);
17865b4b4e8SAndrew Morton module_exit(xt_connbytes_fini);
179