| 1 | --- a/net/ipv4/netfilter/Config.in |
| 2 | +++ b/net/ipv4/netfilter/Config.in |
| 3 | @@ -11,6 +11,8 @@ if [ "$CONFIG_IP_NF_CONNTRACK" != "n" ]; |
| 4 | dep_tristate ' Amanda protocol support' CONFIG_IP_NF_AMANDA $CONFIG_IP_NF_CONNTRACK |
| 5 | dep_tristate ' TFTP protocol support' CONFIG_IP_NF_TFTP $CONFIG_IP_NF_CONNTRACK |
| 6 | dep_tristate ' IRC protocol support' CONFIG_IP_NF_IRC $CONFIG_IP_NF_CONNTRACK |
| 7 | + dep_tristate ' Connection tracking flow accounting' CONFIG_IP_NF_CT_ACCT $CONFIG_IP_NF_CONNTRACK |
| 8 | + dep_tristate ' Connection byte counter support' CONFIG_IP_NF_MATCH_CONNBYTES $CONFIG_IP_NF_CT_ACCT $CONFIG_IP_NF_CONNTRACK $CONFIG_IP_NF_IPTABLES |
| 9 | fi |
| 10 | |
| 11 | if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then |
| 12 | --- a/net/ipv4/netfilter/Makefile |
| 13 | +++ b/net/ipv4/netfilter/Makefile |
| 14 | @@ -85,6 +85,7 @@ obj-$(CONFIG_IP_NF_MATCH_LENGTH) += ipt_ |
| 15 | obj-$(CONFIG_IP_NF_MATCH_TTL) += ipt_ttl.o |
| 16 | obj-$(CONFIG_IP_NF_MATCH_STATE) += ipt_state.o |
| 17 | obj-$(CONFIG_IP_NF_MATCH_CONNMARK) += ipt_connmark.o |
| 18 | +obj-$(CONFIG_IP_NF_MATCH_CONNBYTES) += ipt_connbytes.o |
| 19 | obj-$(CONFIG_IP_NF_MATCH_CONNTRACK) += ipt_conntrack.o |
| 20 | obj-$(CONFIG_IP_NF_MATCH_UNCLEAN) += ipt_unclean.o |
| 21 | obj-$(CONFIG_IP_NF_MATCH_STRING) += ipt_string.o |
| 22 | --- a/net/ipv4/netfilter/ip_conntrack_amanda.c |
| 23 | +++ b/net/ipv4/netfilter/ip_conntrack_amanda.c |
| 24 | @@ -75,7 +75,7 @@ static int help(const struct iphdr *iph, |
| 25 | |
| 26 | /* increase the UDP timeout of the master connection as replies from |
| 27 | * Amanda clients to the server can be quite delayed */ |
| 28 | - ip_ct_refresh(ct, master_timeout * HZ); |
| 29 | + ip_ct_refresh_acct(ct,ctinfo,NULL, master_timeout * HZ); |
| 30 | |
| 31 | /* Search for "CONNECT " string */ |
| 32 | do { |
| 33 | --- a/net/ipv4/netfilter/ip_conntrack_proto_tcp.c |
| 34 | +++ b/net/ipv4/netfilter/ip_conntrack_proto_tcp.c |
| 35 | @@ -211,7 +211,7 @@ static int tcp_packet(struct ip_conntrac |
| 36 | set_bit(IPS_ASSURED_BIT, &conntrack->status); |
| 37 | |
| 38 | WRITE_UNLOCK(&tcp_lock); |
| 39 | - ip_ct_refresh(conntrack, *tcp_timeouts[newconntrack]); |
| 40 | + ip_ct_refresh_acct(conntrack,ctinfo,iph, *tcp_timeouts[newconntrack]); |
| 41 | } |
| 42 | |
| 43 | return NF_ACCEPT; |
| 44 | --- a/net/ipv4/netfilter/ip_conntrack_proto_udp.c |
| 45 | +++ b/net/ipv4/netfilter/ip_conntrack_proto_udp.c |
| 46 | @@ -47,16 +47,16 @@ static unsigned int udp_print_conntrack( |
| 47 | /* Returns verdict for packet, and may modify conntracktype */ |
| 48 | static int udp_packet(struct ip_conntrack *conntrack, |
| 49 | struct iphdr *iph, size_t len, |
| 50 | - enum ip_conntrack_info conntrackinfo) |
| 51 | + enum ip_conntrack_info ctinfo) |
| 52 | { |
| 53 | /* If we've seen traffic both ways, this is some kind of UDP |
| 54 | stream. Extend timeout. */ |
| 55 | if (test_bit(IPS_SEEN_REPLY_BIT, &conntrack->status)) { |
| 56 | - ip_ct_refresh(conntrack, ip_ct_udp_timeout_stream); |
| 57 | + ip_ct_refresh_acct(conntrack,ctinfo,iph,ip_ct_udp_timeout_stream); |
| 58 | /* Also, more likely to be important, and not a probe */ |
| 59 | set_bit(IPS_ASSURED_BIT, &conntrack->status); |
| 60 | } else |
| 61 | - ip_ct_refresh(conntrack, ip_ct_udp_timeout); |
| 62 | + ip_ct_refresh_acct(conntrack,ctinfo,iph, ip_ct_udp_timeout); |
| 63 | |
| 64 | return NF_ACCEPT; |
| 65 | } |
| 66 | --- a/net/ipv4/netfilter/ip_conntrack_standalone.c |
| 67 | +++ b/net/ipv4/netfilter/ip_conntrack_standalone.c |
| 68 | @@ -79,6 +79,18 @@ print_expect(char *buffer, const struct |
| 69 | return len; |
| 70 | } |
| 71 | |
| 72 | +#if defined(CONFIG_IP_NF_CT_ACCT) || \ |
| 73 | + defined(CONFIG_IP_NF_CT_ACCT_MODULE) |
| 74 | +static unsigned int |
| 75 | +print_counters(char *buffer, struct ip_conntrack_counter *counter) |
| 76 | +{ |
| 77 | + return sprintf(buffer, "packets=%llu bytes=%llu ", |
| 78 | + counter->packets, counter->bytes); |
| 79 | +} |
| 80 | +#else |
| 81 | +#define print_counters(x, y) 0 |
| 82 | +#endif |
| 83 | + |
| 84 | static unsigned int |
| 85 | print_conntrack(char *buffer, struct ip_conntrack *conntrack) |
| 86 | { |
| 87 | @@ -98,11 +110,15 @@ print_conntrack(char *buffer, struct ip_ |
| 88 | len += print_tuple(buffer + len, |
| 89 | &conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple, |
| 90 | proto); |
| 91 | + len += print_counters(buffer + len, |
| 92 | + &conntrack->counters[IP_CT_DIR_ORIGINAL]); |
| 93 | if (!(test_bit(IPS_SEEN_REPLY_BIT, &conntrack->status))) |
| 94 | len += sprintf(buffer + len, "[UNREPLIED] "); |
| 95 | len += print_tuple(buffer + len, |
| 96 | &conntrack->tuplehash[IP_CT_DIR_REPLY].tuple, |
| 97 | proto); |
| 98 | + len += print_counters(buffer + len, |
| 99 | + &conntrack->counters[IP_CT_DIR_REPLY]); |
| 100 | if (test_bit(IPS_ASSURED_BIT, &conntrack->status)) |
| 101 | len += sprintf(buffer + len, "[ASSURED] "); |
| 102 | len += sprintf(buffer + len, "use=%u ", |
| 103 | @@ -481,7 +497,7 @@ EXPORT_SYMBOL(ip_conntrack_get); |
| 104 | EXPORT_SYMBOL(ip_conntrack_helper_register); |
| 105 | EXPORT_SYMBOL(ip_conntrack_helper_unregister); |
| 106 | EXPORT_SYMBOL(ip_ct_iterate_cleanup); |
| 107 | -EXPORT_SYMBOL(ip_ct_refresh); |
| 108 | +EXPORT_SYMBOL(ip_ct_refresh_acct); |
| 109 | EXPORT_SYMBOL(ip_ct_find_proto); |
| 110 | EXPORT_SYMBOL(__ip_ct_find_proto); |
| 111 | EXPORT_SYMBOL(ip_ct_find_helper); |
| 112 | --- a/net/ipv4/netfilter/ip_conntrack_proto_generic.c |
| 113 | +++ b/net/ipv4/netfilter/ip_conntrack_proto_generic.c |
| 114 | @@ -41,9 +41,9 @@ static unsigned int generic_print_conntr |
| 115 | /* Returns verdict for packet, or -1 for invalid. */ |
| 116 | static int established(struct ip_conntrack *conntrack, |
| 117 | struct iphdr *iph, size_t len, |
| 118 | - enum ip_conntrack_info conntrackinfo) |
| 119 | + enum ip_conntrack_info ctinfo) |
| 120 | { |
| 121 | - ip_ct_refresh(conntrack, ip_ct_generic_timeout); |
| 122 | + ip_ct_refresh_acct(conntrack, ctinfo,iph,ip_ct_generic_timeout); |
| 123 | return NF_ACCEPT; |
| 124 | } |
| 125 | |
| 126 | --- a/net/ipv4/netfilter/ip_conntrack_proto_icmp.c |
| 127 | +++ b/net/ipv4/netfilter/ip_conntrack_proto_icmp.c |
| 128 | @@ -82,7 +82,7 @@ static int icmp_packet(struct ip_conntra |
| 129 | ct->timeout.function((unsigned long)ct); |
| 130 | } else { |
| 131 | atomic_inc(&ct->proto.icmp.count); |
| 132 | - ip_ct_refresh(ct, ip_ct_icmp_timeout); |
| 133 | + ip_ct_refresh_acct(ct,ctinfo,iph, ip_ct_icmp_timeout); |
| 134 | } |
| 135 | |
| 136 | return NF_ACCEPT; |
| 137 | --- a/net/ipv4/netfilter/ip_conntrack_core.c |
| 138 | +++ b/net/ipv4/netfilter/ip_conntrack_core.c |
| 139 | @@ -1193,22 +1193,40 @@ void ip_conntrack_helper_unregister(stru |
| 140 | |
| 141 | MOD_DEC_USE_COUNT; |
| 142 | } |
| 143 | +static inline void ct_add_counters(struct ip_conntrack *ct, |
| 144 | + enum ip_conntrack_info ctinfo, |
| 145 | + const struct iphdr *iph) |
| 146 | +{ |
| 147 | +#if defined(CONFIG_IP_NF_CT_ACCT) || \ |
| 148 | + defined(CONFIG_IP_NF_CT_ACCT_MODULE) |
| 149 | + if (iph) { |
| 150 | + ct->counters[CTINFO2DIR(ctinfo)].packets++; |
| 151 | + ct->counters[CTINFO2DIR(ctinfo)].bytes += |
| 152 | + ntohs(iph->tot_len); |
| 153 | + } |
| 154 | +#endif |
| 155 | +} |
| 156 | |
| 157 | /* Refresh conntrack for this many jiffies. */ |
| 158 | -void ip_ct_refresh(struct ip_conntrack *ct, unsigned long extra_jiffies) |
| 159 | +void ip_ct_refresh_acct(struct ip_conntrack *ct, |
| 160 | + enum ip_conntrack_info ctinfo, |
| 161 | + const struct iphdr *iph, |
| 162 | + unsigned long extra_jiffies) |
| 163 | { |
| 164 | IP_NF_ASSERT(ct->timeout.data == (unsigned long)ct); |
| 165 | |
| 166 | WRITE_LOCK(&ip_conntrack_lock); |
| 167 | /* If not in hash table, timer will not be active yet */ |
| 168 | - if (!is_confirmed(ct)) |
| 169 | + if (!is_confirmed(ct)) { |
| 170 | ct->timeout.expires = extra_jiffies; |
| 171 | - else { |
| 172 | + ct_add_counters(ct, ctinfo,iph); |
| 173 | + } else { |
| 174 | /* Need del_timer for race avoidance (may already be dying). */ |
| 175 | if (del_timer(&ct->timeout)) { |
| 176 | ct->timeout.expires = jiffies + extra_jiffies; |
| 177 | add_timer(&ct->timeout); |
| 178 | } |
| 179 | + ct_add_counters(ct, ctinfo, iph); |
| 180 | } |
| 181 | WRITE_UNLOCK(&ip_conntrack_lock); |
| 182 | } |
| 183 | --- a/include/linux/netfilter_ipv4/ip_conntrack.h |
| 184 | +++ b/include/linux/netfilter_ipv4/ip_conntrack.h |
| 185 | @@ -156,6 +156,12 @@ struct ip_conntrack_expect |
| 186 | union ip_conntrack_expect_help help; |
| 187 | }; |
| 188 | |
| 189 | +struct ip_conntrack_counter |
| 190 | +{ |
| 191 | + u_int64_t packets; |
| 192 | + u_int64_t bytes; |
| 193 | +}; |
| 194 | + |
| 195 | struct ip_conntrack_helper; |
| 196 | |
| 197 | struct ip_conntrack |
| 198 | @@ -173,6 +179,12 @@ struct ip_conntrack |
| 199 | /* Timer function; drops refcnt when it goes off. */ |
| 200 | struct timer_list timeout; |
| 201 | |
| 202 | +#if defined(CONFIG_IP_NF_CT_ACCT) || \ |
| 203 | + defined(CONFIG_IP_NF_CT_ACCT_MODULE) |
| 204 | + /* Accounting Information (same cache line as other written members) */ |
| 205 | + struct ip_conntrack_counter counters[IP_CT_DIR_MAX]; |
| 206 | +#endif |
| 207 | + |
| 208 | /* If we're expecting another related connection, this will be |
| 209 | in expected linked list */ |
| 210 | struct list_head sibling_list; |
| 211 | @@ -256,8 +268,10 @@ extern int invert_tuplepr(struct ip_conn |
| 212 | const struct ip_conntrack_tuple *orig); |
| 213 | |
| 214 | /* Refresh conntrack for this many jiffies */ |
| 215 | -extern void ip_ct_refresh(struct ip_conntrack *ct, |
| 216 | - unsigned long extra_jiffies); |
| 217 | +extern void ip_ct_refresh_acct(struct ip_conntrack *ct, |
| 218 | + enum ip_conntrack_info ctinfo, |
| 219 | + const struct iphdr *iph, |
| 220 | + unsigned long extra_jiffies); |
| 221 | |
| 222 | /* These are for NAT. Icky. */ |
| 223 | /* Call me when a conntrack is destroyed. */ |
| 224 | --- /dev/null |
| 225 | +++ b/net/ipv4/netfilter/ipt_connbytes.c |
| 226 | @@ -0,0 +1,163 @@ |
| 227 | +/* Kernel module to match connection tracking byte counter. |
| 228 | + * GPL (C) 2002 Martin Devera (devik@cdi.cz). |
| 229 | + * |
| 230 | + * 2004-07-20 Harald Welte <laforge at netfilter.org> |
| 231 | + * - reimplemented to use per-connection accounting counters |
| 232 | + * - add functionality to match number of packets |
| 233 | + * - add functionality to match average packet size |
| 234 | + * - add support to match directions seperately |
| 235 | + * |
| 236 | + * 2004-10-24 Piotr Chytla <pch at fouk.org> |
| 237 | + * - Connbytes with per-connection accouting backported to 2.4 |
| 238 | + * |
| 239 | + */ |
| 240 | + |
| 241 | +#include <linux/module.h> |
| 242 | +#include <linux/skbuff.h> |
| 243 | +#include <linux/types.h> |
| 244 | +#include <linux/netfilter_ipv4/ip_conntrack.h> |
| 245 | +#include <linux/netfilter_ipv4/ip_tables.h> |
| 246 | +#include <linux/netfilter_ipv4/ipt_connbytes.h> |
| 247 | + |
| 248 | +#include <asm/div64.h> |
| 249 | + |
| 250 | +static u_int64_t mydiv(u_int64_t arg1,u_int32_t arg2) |
| 251 | +{ |
| 252 | + do_div(arg1,arg2); |
| 253 | + return arg1; |
| 254 | +} |
| 255 | + |
| 256 | +static int |
| 257 | +match(const struct sk_buff *skb, |
| 258 | + const struct net_device *in, |
| 259 | + const struct net_device *out, |
| 260 | + const void *matchinfo, |
| 261 | + int offset, |
| 262 | + const void *hdr, |
| 263 | + u_int16_t datalen, |
| 264 | + int *hotdrop) |
| 265 | +{ |
| 266 | + static u_int64_t what; |
| 267 | + const struct ipt_connbytes_info *sinfo = matchinfo; |
| 268 | + enum ip_conntrack_info ctinfo; |
| 269 | + struct ip_conntrack *ct; |
| 270 | + |
| 271 | + if (!(ct = ip_conntrack_get((struct sk_buff *)skb, &ctinfo))) |
| 272 | + return 0; /* no match */ |
| 273 | + switch (sinfo->what) { |
| 274 | + case IPT_CONNBYTES_PKTS: |
| 275 | + switch (sinfo->direction) { |
| 276 | + case IPT_CONNBYTES_DIR_ORIGINAL: |
| 277 | + what = ct->counters[IP_CT_DIR_ORIGINAL].packets; |
| 278 | + break; |
| 279 | + case IPT_CONNBYTES_DIR_REPLY: |
| 280 | + what = ct->counters[IP_CT_DIR_REPLY].packets; |
| 281 | + break; |
| 282 | + case IPT_CONNBYTES_DIR_BOTH: |
| 283 | + what = ct->counters[IP_CT_DIR_ORIGINAL].packets; |
| 284 | + what += ct->counters[IP_CT_DIR_REPLY].packets; |
| 285 | + break; |
| 286 | + } |
| 287 | + break; |
| 288 | + case IPT_CONNBYTES_BYTES: |
| 289 | + switch (sinfo->direction) { |
| 290 | + case IPT_CONNBYTES_DIR_ORIGINAL: |
| 291 | + what = ct->counters[IP_CT_DIR_ORIGINAL].bytes; |
| 292 | + break; |
| 293 | + case IPT_CONNBYTES_DIR_REPLY: |
| 294 | + what = ct->counters[IP_CT_DIR_REPLY].bytes; |
| 295 | + break; |
| 296 | + case IPT_CONNBYTES_DIR_BOTH: |
| 297 | + what = ct->counters[IP_CT_DIR_ORIGINAL].bytes; |
| 298 | + what += ct->counters[IP_CT_DIR_REPLY].bytes; |
| 299 | + break; |
| 300 | + } |
| 301 | + break; |
| 302 | + case IPT_CONNBYTES_AVGPKT: |
| 303 | + switch (sinfo->direction) { |
| 304 | + case IPT_CONNBYTES_DIR_ORIGINAL: |
| 305 | + { |
| 306 | + u_int32_t pkts32; |
| 307 | + |
| 308 | + if (ct->counters[IP_CT_DIR_ORIGINAL].packets > 0xfffffffff) |
| 309 | + pkts32 = 0xffffffff; |
| 310 | + else |
| 311 | + pkts32 = ct->counters[IP_CT_DIR_ORIGINAL].packets; |
| 312 | + what = mydiv(ct->counters[IP_CT_DIR_ORIGINAL].bytes,pkts32); |
| 313 | + } |
| 314 | + break; |
| 315 | + case IPT_CONNBYTES_DIR_REPLY: |
| 316 | + { |
| 317 | + u_int32_t pkts32; |
| 318 | + |
| 319 | + if (ct->counters[IP_CT_DIR_REPLY].packets > 0xffffffff) |
| 320 | + pkts32 = 0xffffffff; |
| 321 | + else |
| 322 | + pkts32 = ct->counters[IP_CT_DIR_REPLY].packets; |
| 323 | + what = mydiv(ct->counters[IP_CT_DIR_REPLY].bytes,pkts32); |
| 324 | + } |
| 325 | + break; |
| 326 | + case IPT_CONNBYTES_DIR_BOTH: |
| 327 | + { |
| 328 | + u_int64_t bytes; |
| 329 | + u_int64_t pkts; |
| 330 | + u_int32_t pkts32; |
| 331 | + bytes = ct->counters[IP_CT_DIR_ORIGINAL].bytes + |
| 332 | + ct->counters[IP_CT_DIR_REPLY].bytes; |
| 333 | + pkts = ct->counters[IP_CT_DIR_ORIGINAL].packets + |
| 334 | + ct->counters[IP_CT_DIR_REPLY].packets; |
| 335 | + if (pkts > 0xffffffff) |
| 336 | + pkts32 = 0xffffffff; |
| 337 | + else |
| 338 | + pkts32 = pkts; |
| 339 | + what = mydiv(bytes,pkts); |
| 340 | + } |
| 341 | + break; |
| 342 | + } |
| 343 | + break; |
| 344 | + } |
| 345 | + if (sinfo->count.to) |
| 346 | + return (what <= sinfo->count.to && what >= sinfo->count.from); |
| 347 | + else |
| 348 | + return (what >= sinfo->count.from); |
| 349 | +} |
| 350 | + |
| 351 | +static int check(const char *tablename, |
| 352 | + const struct ipt_ip *ip, |
| 353 | + void *matchinfo, |
| 354 | + unsigned int matchsize, |
| 355 | + unsigned int hook_mask) |
| 356 | +{ |
| 357 | + const struct ipt_connbytes_info *sinfo = matchinfo; |
| 358 | + |
| 359 | + if (matchsize != IPT_ALIGN(sizeof(struct ipt_connbytes_info))) |
| 360 | + return 0; |
| 361 | + if (sinfo->what != IPT_CONNBYTES_PKTS && |
| 362 | + sinfo->what != IPT_CONNBYTES_BYTES && |
| 363 | + sinfo->what != IPT_CONNBYTES_AVGPKT) |
| 364 | + return 0; |
| 365 | + |
| 366 | + if (sinfo->direction != IPT_CONNBYTES_DIR_ORIGINAL && |
| 367 | + sinfo->direction != IPT_CONNBYTES_DIR_REPLY && |
| 368 | + sinfo->direction != IPT_CONNBYTES_DIR_BOTH) |
| 369 | + return 0; |
| 370 | + |
| 371 | + return 1; |
| 372 | +} |
| 373 | + |
| 374 | +static struct ipt_match state_match |
| 375 | += { { NULL, NULL }, "connbytes", &match, &check, NULL, THIS_MODULE }; |
| 376 | + |
| 377 | +static int __init init(void) |
| 378 | +{ |
| 379 | + return ipt_register_match(&state_match); |
| 380 | +} |
| 381 | + |
| 382 | +static void __exit fini(void) |
| 383 | +{ |
| 384 | + ipt_unregister_match(&state_match); |
| 385 | +} |
| 386 | + |
| 387 | +module_init(init); |
| 388 | +module_exit(fini); |
| 389 | +MODULE_LICENSE("GPL"); |
| 390 | --- /dev/null |
| 391 | +++ b/include/linux/netfilter_ipv4/ipt_connbytes.h |
| 392 | @@ -0,0 +1,25 @@ |
| 393 | +#ifndef _IPT_CONNBYTES_H |
| 394 | +#define _IPT_CONNBYTES_H |
| 395 | +enum ipt_connbytes_what { |
| 396 | + IPT_CONNBYTES_PKTS, |
| 397 | + IPT_CONNBYTES_BYTES, |
| 398 | + IPT_CONNBYTES_AVGPKT, |
| 399 | +}; |
| 400 | + |
| 401 | +enum ipt_connbytes_direction { |
| 402 | + IPT_CONNBYTES_DIR_ORIGINAL, |
| 403 | + IPT_CONNBYTES_DIR_REPLY, |
| 404 | + IPT_CONNBYTES_DIR_BOTH, |
| 405 | +}; |
| 406 | + |
| 407 | +struct ipt_connbytes_info |
| 408 | +{ |
| 409 | + struct { |
| 410 | + u_int64_t from; /* count to be matched */ |
| 411 | + u_int64_t to; /* count to be matched */ |
| 412 | + } count; |
| 413 | + u_int8_t what; /* ipt_connbytes_what */ |
| 414 | + u_int8_t direction; /* ipt_connbytes_direction */ |
| 415 | +}; |
| 416 | + |
| 417 | +#endif |
| 418 | |