forked from torvalds/linux
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[NETFILTER]: Add new iptables "connbytes" match
This patch ads a new "connbytes" match that utilizes the CONFIG_NF_CT_ACCT per-connection byte and packet counters. Using it you can do things like packet classification on average packet size within a connection. Signed-off-by: Harald Welte <[email protected]> Signed-off-by: David S. Miller <[email protected]>
- Loading branch information
Showing
4 changed files
with
202 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
#ifndef _IPT_CONNBYTES_H | ||
#define _IPT_CONNBYTES_H | ||
|
||
enum ipt_connbytes_what { | ||
IPT_CONNBYTES_WHAT_PKTS, | ||
IPT_CONNBYTES_WHAT_BYTES, | ||
IPT_CONNBYTES_WHAT_AVGPKT, | ||
}; | ||
|
||
enum ipt_connbytes_direction { | ||
IPT_CONNBYTES_DIR_ORIGINAL, | ||
IPT_CONNBYTES_DIR_REPLY, | ||
IPT_CONNBYTES_DIR_BOTH, | ||
}; | ||
|
||
struct ipt_connbytes_info | ||
{ | ||
struct { | ||
aligned_u64 from; /* count to be matched */ | ||
aligned_u64 to; /* count to be matched */ | ||
} count; | ||
u_int8_t what; /* ipt_connbytes_what */ | ||
u_int8_t direction; /* ipt_connbytes_direction */ | ||
}; | ||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
/* Kernel module to match connection tracking byte counter. | ||
* GPL (C) 2002 Martin Devera ([email protected]). | ||
* | ||
* 2004-07-20 Harald Welte <[email protected]> | ||
* - reimplemented to use per-connection accounting counters | ||
* - add functionality to match number of packets | ||
* - add functionality to match average packet size | ||
* - add support to match directions seperately | ||
* | ||
*/ | ||
#include <linux/module.h> | ||
#include <linux/skbuff.h> | ||
#include <linux/netfilter_ipv4/ip_conntrack.h> | ||
#include <linux/netfilter_ipv4/ip_tables.h> | ||
#include <linux/netfilter_ipv4/ipt_connbytes.h> | ||
|
||
#include <asm/div64.h> | ||
#include <asm/bitops.h> | ||
|
||
MODULE_LICENSE("GPL"); | ||
MODULE_AUTHOR("Harald Welte <[email protected]>"); | ||
MODULE_DESCRIPTION("iptables match for matching number of pkts/bytes per connection"); | ||
|
||
/* 64bit divisor, dividend and result. dynamic precision */ | ||
static u_int64_t div64_64(u_int64_t divisor, u_int64_t dividend) | ||
{ | ||
u_int64_t result = divisor; | ||
|
||
if (dividend > 0xffffffff) { | ||
int first_bit = find_first_bit((unsigned long *) ÷nd, sizeof(dividend)); | ||
/* calculate number of bits to shift. shift exactly enough | ||
* bits to make dividend fit in 32bits. */ | ||
int num_shift = (64 - 32 - first_bit); | ||
/* first bit has to be < 32, since dividend was > 0xffffffff */ | ||
result = result >> num_shift; | ||
dividend = dividend >> num_shift; | ||
} | ||
|
||
do_div(divisor, dividend); | ||
|
||
return divisor; | ||
} | ||
|
||
static int | ||
match(const struct sk_buff *skb, | ||
const struct net_device *in, | ||
const struct net_device *out, | ||
const void *matchinfo, | ||
int offset, | ||
int *hotdrop) | ||
{ | ||
const struct ipt_connbytes_info *sinfo = matchinfo; | ||
enum ip_conntrack_info ctinfo; | ||
struct ip_conntrack *ct; | ||
u_int64_t what = 0; /* initialize to make gcc happy */ | ||
|
||
if (!(ct = ip_conntrack_get((struct sk_buff *)skb, &ctinfo))) | ||
return 0; /* no match */ | ||
|
||
switch (sinfo->what) { | ||
case IPT_CONNBYTES_WHAT_PKTS: | ||
switch (sinfo->direction) { | ||
case IPT_CONNBYTES_DIR_ORIGINAL: | ||
what = ct->counters[IP_CT_DIR_ORIGINAL].packets; | ||
break; | ||
case IPT_CONNBYTES_DIR_REPLY: | ||
what = ct->counters[IP_CT_DIR_REPLY].packets; | ||
break; | ||
case IPT_CONNBYTES_DIR_BOTH: | ||
what = ct->counters[IP_CT_DIR_ORIGINAL].packets; | ||
what += ct->counters[IP_CT_DIR_REPLY].packets; | ||
break; | ||
} | ||
break; | ||
case IPT_CONNBYTES_WHAT_BYTES: | ||
switch (sinfo->direction) { | ||
case IPT_CONNBYTES_DIR_ORIGINAL: | ||
what = ct->counters[IP_CT_DIR_ORIGINAL].bytes; | ||
break; | ||
case IPT_CONNBYTES_DIR_REPLY: | ||
what = ct->counters[IP_CT_DIR_REPLY].bytes; | ||
break; | ||
case IPT_CONNBYTES_DIR_BOTH: | ||
what = ct->counters[IP_CT_DIR_ORIGINAL].bytes; | ||
what += ct->counters[IP_CT_DIR_REPLY].bytes; | ||
break; | ||
} | ||
break; | ||
case IPT_CONNBYTES_WHAT_AVGPKT: | ||
switch (sinfo->direction) { | ||
case IPT_CONNBYTES_DIR_ORIGINAL: | ||
what = div64_64(ct->counters[IP_CT_DIR_ORIGINAL].bytes, | ||
ct->counters[IP_CT_DIR_ORIGINAL].packets); | ||
break; | ||
case IPT_CONNBYTES_DIR_REPLY: | ||
what = div64_64(ct->counters[IP_CT_DIR_REPLY].bytes, | ||
ct->counters[IP_CT_DIR_REPLY].packets); | ||
break; | ||
case IPT_CONNBYTES_DIR_BOTH: | ||
{ | ||
u_int64_t bytes; | ||
u_int64_t pkts; | ||
bytes = ct->counters[IP_CT_DIR_ORIGINAL].bytes + | ||
ct->counters[IP_CT_DIR_REPLY].bytes; | ||
pkts = ct->counters[IP_CT_DIR_ORIGINAL].packets+ | ||
ct->counters[IP_CT_DIR_REPLY].packets; | ||
|
||
/* FIXME_THEORETICAL: what to do if sum | ||
* overflows ? */ | ||
|
||
what = div64_64(bytes, pkts); | ||
} | ||
break; | ||
} | ||
break; | ||
} | ||
|
||
if (sinfo->count.to) | ||
return (what <= sinfo->count.to && what >= sinfo->count.from); | ||
else | ||
return (what >= sinfo->count.from); | ||
} | ||
|
||
static int check(const char *tablename, | ||
const struct ipt_ip *ip, | ||
void *matchinfo, | ||
unsigned int matchsize, | ||
unsigned int hook_mask) | ||
{ | ||
const struct ipt_connbytes_info *sinfo = matchinfo; | ||
|
||
if (matchsize != IPT_ALIGN(sizeof(struct ipt_connbytes_info))) | ||
return 0; | ||
|
||
if (sinfo->what != IPT_CONNBYTES_WHAT_PKTS && | ||
sinfo->what != IPT_CONNBYTES_WHAT_BYTES && | ||
sinfo->what != IPT_CONNBYTES_WHAT_AVGPKT) | ||
return 0; | ||
|
||
if (sinfo->direction != IPT_CONNBYTES_DIR_ORIGINAL && | ||
sinfo->direction != IPT_CONNBYTES_DIR_REPLY && | ||
sinfo->direction != IPT_CONNBYTES_DIR_BOTH) | ||
return 0; | ||
|
||
return 1; | ||
} | ||
|
||
static struct ipt_match state_match = { | ||
.name = "connbytes", | ||
.match = &match, | ||
.checkentry = &check, | ||
.me = THIS_MODULE | ||
}; | ||
|
||
static int __init init(void) | ||
{ | ||
return ipt_register_match(&state_match); | ||
} | ||
|
||
static void __exit fini(void) | ||
{ | ||
ipt_unregister_match(&state_match); | ||
} | ||
|
||
module_init(init); | ||
module_exit(fini); |