--- a/net/ipv4/netfilter/Config.in
+++ b/net/ipv4/netfilter/Config.in
@@ -11,6 +11,8 @@ if [ "$CONFIG_IP_NF_CONNTRACK" != "n" ];
   dep_tristate '  Amanda protocol support' CONFIG_IP_NF_AMANDA $CONFIG_IP_NF_CONNTRACK
   dep_tristate '  TFTP protocol support' CONFIG_IP_NF_TFTP $CONFIG_IP_NF_CONNTRACK
   dep_tristate '  IRC protocol support' CONFIG_IP_NF_IRC $CONFIG_IP_NF_CONNTRACK
+  dep_tristate '  Connection tracking flow accounting' CONFIG_IP_NF_CT_ACCT $CONFIG_IP_NF_CONNTRACK
+  dep_tristate '  Connection byte counter support' CONFIG_IP_NF_MATCH_CONNBYTES $CONFIG_IP_NF_CT_ACCT $CONFIG_IP_NF_CONNTRACK $CONFIG_IP_NF_IPTABLES
 fi
 
 if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
--- a/net/ipv4/netfilter/Makefile
+++ b/net/ipv4/netfilter/Makefile
@@ -85,6 +85,7 @@ obj-$(CONFIG_IP_NF_MATCH_LENGTH) += ipt_
 obj-$(CONFIG_IP_NF_MATCH_TTL) += ipt_ttl.o
 obj-$(CONFIG_IP_NF_MATCH_STATE) += ipt_state.o
 obj-$(CONFIG_IP_NF_MATCH_CONNMARK) += ipt_connmark.o
+obj-$(CONFIG_IP_NF_MATCH_CONNBYTES) += ipt_connbytes.o
 obj-$(CONFIG_IP_NF_MATCH_CONNTRACK) += ipt_conntrack.o
 obj-$(CONFIG_IP_NF_MATCH_UNCLEAN) += ipt_unclean.o
 obj-$(CONFIG_IP_NF_MATCH_STRING) += ipt_string.o
--- a/net/ipv4/netfilter/ip_conntrack_amanda.c
+++ b/net/ipv4/netfilter/ip_conntrack_amanda.c
@@ -75,7 +75,7 @@ static int help(const struct iphdr *iph,
 
 	/* increase the UDP timeout of the master connection as replies from
 	 * Amanda clients to the server can be quite delayed */
-	ip_ct_refresh(ct, master_timeout * HZ);
+	ip_ct_refresh_acct(ct,ctinfo,NULL, master_timeout * HZ);
 	
 	/* Search for "CONNECT " string */
 	do {
--- a/net/ipv4/netfilter/ip_conntrack_proto_tcp.c
+++ b/net/ipv4/netfilter/ip_conntrack_proto_tcp.c
@@ -211,7 +211,7 @@ static int tcp_packet(struct ip_conntrac
 			set_bit(IPS_ASSURED_BIT, &conntrack->status);
 
 		WRITE_UNLOCK(&tcp_lock);
-		ip_ct_refresh(conntrack, *tcp_timeouts[newconntrack]);
+		ip_ct_refresh_acct(conntrack,ctinfo,iph, *tcp_timeouts[newconntrack]);
 	}
 
 	return NF_ACCEPT;
--- a/net/ipv4/netfilter/ip_conntrack_proto_udp.c
+++ b/net/ipv4/netfilter/ip_conntrack_proto_udp.c
@@ -47,16 +47,16 @@ static unsigned int udp_print_conntrack(
 /* Returns verdict for packet, and may modify conntracktype */
 static int udp_packet(struct ip_conntrack *conntrack,
 		      struct iphdr *iph, size_t len,
-		      enum ip_conntrack_info conntrackinfo)
+		      enum ip_conntrack_info ctinfo)
 {
 	/* If we've seen traffic both ways, this is some kind of UDP
 	   stream.  Extend timeout. */
 	if (test_bit(IPS_SEEN_REPLY_BIT, &conntrack->status)) {
-		ip_ct_refresh(conntrack, ip_ct_udp_timeout_stream);
+		ip_ct_refresh_acct(conntrack,ctinfo,iph,ip_ct_udp_timeout_stream);
 		/* Also, more likely to be important, and not a probe */
 		set_bit(IPS_ASSURED_BIT, &conntrack->status);
 	} else
-		ip_ct_refresh(conntrack, ip_ct_udp_timeout);
+		ip_ct_refresh_acct(conntrack,ctinfo,iph, ip_ct_udp_timeout);
 
 	return NF_ACCEPT;
 }
--- a/net/ipv4/netfilter/ip_conntrack_standalone.c
+++ b/net/ipv4/netfilter/ip_conntrack_standalone.c
@@ -79,6 +79,18 @@ print_expect(char *buffer, const struct 
 	return len;
 }
 
+#if defined(CONFIG_IP_NF_CT_ACCT) || \
+	defined(CONFIG_IP_NF_CT_ACCT_MODULE)
+static unsigned int
+print_counters(char *buffer, struct ip_conntrack_counter *counter)
+{
+       return sprintf(buffer, "packets=%llu bytes=%llu ", 
+                       counter->packets, counter->bytes);
+}
+#else
+#define print_counters(x, y)   0
+#endif
+
 static unsigned int
 print_conntrack(char *buffer, struct ip_conntrack *conntrack)
 {
@@ -98,11 +110,15 @@ print_conntrack(char *buffer, struct ip_
 	len += print_tuple(buffer + len,
 			   &conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple,
 			   proto);
+        len += print_counters(buffer + len, 
+                           &conntrack->counters[IP_CT_DIR_ORIGINAL]);
 	if (!(test_bit(IPS_SEEN_REPLY_BIT, &conntrack->status)))
 		len += sprintf(buffer + len, "[UNREPLIED] ");
 	len += print_tuple(buffer + len,
 			   &conntrack->tuplehash[IP_CT_DIR_REPLY].tuple,
 			   proto);
+        len += print_counters(buffer + len, 
+                           &conntrack->counters[IP_CT_DIR_REPLY]);
 	if (test_bit(IPS_ASSURED_BIT, &conntrack->status))
 		len += sprintf(buffer + len, "[ASSURED] ");
 	len += sprintf(buffer + len, "use=%u ",
@@ -481,7 +497,7 @@ EXPORT_SYMBOL(ip_conntrack_get);
 EXPORT_SYMBOL(ip_conntrack_helper_register);
 EXPORT_SYMBOL(ip_conntrack_helper_unregister);
 EXPORT_SYMBOL(ip_ct_iterate_cleanup);
-EXPORT_SYMBOL(ip_ct_refresh);
+EXPORT_SYMBOL(ip_ct_refresh_acct);
 EXPORT_SYMBOL(ip_ct_find_proto);
 EXPORT_SYMBOL(__ip_ct_find_proto);
 EXPORT_SYMBOL(ip_ct_find_helper);
--- a/net/ipv4/netfilter/ip_conntrack_proto_generic.c
+++ b/net/ipv4/netfilter/ip_conntrack_proto_generic.c
@@ -41,9 +41,9 @@ static unsigned int generic_print_conntr
 /* Returns verdict for packet, or -1 for invalid. */
 static int established(struct ip_conntrack *conntrack,
 		       struct iphdr *iph, size_t len,
-		       enum ip_conntrack_info conntrackinfo)
+		       enum ip_conntrack_info ctinfo)
 {
-	ip_ct_refresh(conntrack, ip_ct_generic_timeout);
+	ip_ct_refresh_acct(conntrack, ctinfo,iph,ip_ct_generic_timeout);
 	return NF_ACCEPT;
 }
 
--- a/net/ipv4/netfilter/ip_conntrack_proto_icmp.c
+++ b/net/ipv4/netfilter/ip_conntrack_proto_icmp.c
@@ -82,7 +82,7 @@ static int icmp_packet(struct ip_conntra
 			ct->timeout.function((unsigned long)ct);
 	} else {
 		atomic_inc(&ct->proto.icmp.count);
-		ip_ct_refresh(ct, ip_ct_icmp_timeout);
+		ip_ct_refresh_acct(ct,ctinfo,iph, ip_ct_icmp_timeout);
 	}
 
 	return NF_ACCEPT;
--- a/net/ipv4/netfilter/ip_conntrack_core.c
+++ b/net/ipv4/netfilter/ip_conntrack_core.c
@@ -1193,22 +1193,40 @@ void ip_conntrack_helper_unregister(stru
 
 	MOD_DEC_USE_COUNT;
 }
+static inline void ct_add_counters(struct ip_conntrack *ct,
+                                enum ip_conntrack_info ctinfo,
+                                 const struct iphdr *iph)
+{
+#if defined(CONFIG_IP_NF_CT_ACCT) || \
+	defined(CONFIG_IP_NF_CT_ACCT_MODULE)
+     if (iph) {
+            ct->counters[CTINFO2DIR(ctinfo)].packets++;
+            ct->counters[CTINFO2DIR(ctinfo)].bytes += 
+                                ntohs(iph->tot_len);
+   }
+#endif
+}
 
 /* Refresh conntrack for this many jiffies. */
-void ip_ct_refresh(struct ip_conntrack *ct, unsigned long extra_jiffies)
+void ip_ct_refresh_acct(struct ip_conntrack *ct, 
+                       enum ip_conntrack_info ctinfo,
+                       const struct iphdr *iph,
+                       unsigned long extra_jiffies)
 {
 	IP_NF_ASSERT(ct->timeout.data == (unsigned long)ct);
 
 	WRITE_LOCK(&ip_conntrack_lock);
 	/* If not in hash table, timer will not be active yet */
-	if (!is_confirmed(ct))
+	if (!is_confirmed(ct)) {
 		ct->timeout.expires = extra_jiffies;
-	else {
+		ct_add_counters(ct, ctinfo,iph);
+	} else {
 		/* Need del_timer for race avoidance (may already be dying). */
 		if (del_timer(&ct->timeout)) {
 			ct->timeout.expires = jiffies + extra_jiffies;
 			add_timer(&ct->timeout);
 		}
+		ct_add_counters(ct, ctinfo, iph);
 	}
 	WRITE_UNLOCK(&ip_conntrack_lock);
 }
--- a/include/linux/netfilter_ipv4/ip_conntrack.h
+++ b/include/linux/netfilter_ipv4/ip_conntrack.h
@@ -156,6 +156,12 @@ struct ip_conntrack_expect
 	union ip_conntrack_expect_help help;
 };
 
+struct ip_conntrack_counter
+{
+       u_int64_t packets;
+       u_int64_t bytes;
+};
+
 struct ip_conntrack_helper;
 
 struct ip_conntrack
@@ -173,6 +179,12 @@ struct ip_conntrack
 	/* Timer function; drops refcnt when it goes off. */
 	struct timer_list timeout;
 
+#if defined(CONFIG_IP_NF_CT_ACCT) || \
+	defined(CONFIG_IP_NF_CT_ACCT_MODULE)
+       /* Accounting Information (same cache line as other written members) */
+       struct ip_conntrack_counter counters[IP_CT_DIR_MAX];
+#endif
+
 	/* If we're expecting another related connection, this will be
            in expected linked list */
 	struct list_head sibling_list;
@@ -256,8 +268,10 @@ extern int invert_tuplepr(struct ip_conn
 			  const struct ip_conntrack_tuple *orig);
 
 /* Refresh conntrack for this many jiffies */
-extern void ip_ct_refresh(struct ip_conntrack *ct,
-			  unsigned long extra_jiffies);
+extern void ip_ct_refresh_acct(struct ip_conntrack *ct,
+                              enum ip_conntrack_info ctinfo,
+                              const struct iphdr *iph,
+                              unsigned long extra_jiffies);
 
 /* These are for NAT.  Icky. */
 /* Call me when a conntrack is destroyed. */
--- /dev/null
+++ b/net/ipv4/netfilter/ipt_connbytes.c
@@ -0,0 +1,163 @@
+/* Kernel module to match connection tracking byte counter.
+ * GPL (C) 2002 Martin Devera (devik@cdi.cz).
+ *
+ * 2004-07-20 Harald Welte <laforge at netfilter.org>
+ *      - 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
+ *
+ * 2004-10-24 Piotr Chytla <pch at fouk.org>
+ * 	- Connbytes with per-connection accouting backported to 2.4
+ * 	
+ */
+
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/types.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>
+
+static u_int64_t mydiv(u_int64_t arg1,u_int32_t arg2)
+{
+	do_div(arg1,arg2);
+	return arg1;
+}
+
+static int
+match(const struct sk_buff *skb,
+      const struct net_device *in,
+      const struct net_device *out,
+      const void *matchinfo,
+      int offset,
+      const void *hdr,
+      u_int16_t datalen,
+      int *hotdrop)
+{
+	static u_int64_t what;
+	const struct ipt_connbytes_info *sinfo = matchinfo;
+	enum ip_conntrack_info ctinfo;
+	struct ip_conntrack *ct;
+
+	if (!(ct = ip_conntrack_get((struct sk_buff *)skb, &ctinfo)))
+		return 0; /* no match */
+        switch (sinfo->what) {
+        case IPT_CONNBYTES_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_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_AVGPKT:
+                switch (sinfo->direction) {
+                case IPT_CONNBYTES_DIR_ORIGINAL:
+                        {
+                                u_int32_t pkts32;
+
+                                if (ct->counters[IP_CT_DIR_ORIGINAL].packets > 0xfffffffff)
+                                        pkts32 = 0xffffffff;
+                                else
+                                        pkts32 = ct->counters[IP_CT_DIR_ORIGINAL].packets;
+				what = mydiv(ct->counters[IP_CT_DIR_ORIGINAL].bytes,pkts32);
+                        }
+                        break;
+                case IPT_CONNBYTES_DIR_REPLY:
+                        {
+                                u_int32_t pkts32;
+
+                                if (ct->counters[IP_CT_DIR_REPLY].packets > 0xffffffff)
+                                        pkts32 = 0xffffffff;
+                                else
+                                        pkts32 = ct->counters[IP_CT_DIR_REPLY].packets;
+				what = mydiv(ct->counters[IP_CT_DIR_REPLY].bytes,pkts32);
+                        }
+                        break;
+                case IPT_CONNBYTES_DIR_BOTH:
+                        {
+                                u_int64_t bytes;
+                                u_int64_t pkts;
+                                u_int32_t pkts32;
+                                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;
+                                if (pkts > 0xffffffff)
+                                        pkts32 =  0xffffffff;
+                                else
+                                        pkts32 = pkts;
+				what = mydiv(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_PKTS &&
+			sinfo->what != IPT_CONNBYTES_BYTES &&
+		        sinfo->what != IPT_CONNBYTES_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
+= { { NULL, NULL }, "connbytes", &match, &check, NULL, 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);
+MODULE_LICENSE("GPL");
--- /dev/null
+++ b/include/linux/netfilter_ipv4/ipt_connbytes.h
@@ -0,0 +1,25 @@
+#ifndef _IPT_CONNBYTES_H
+#define _IPT_CONNBYTES_H
+enum ipt_connbytes_what {
+	        IPT_CONNBYTES_PKTS,
+	        IPT_CONNBYTES_BYTES,
+	        IPT_CONNBYTES_AVGPKT,
+};
+
+enum ipt_connbytes_direction {
+	        IPT_CONNBYTES_DIR_ORIGINAL,
+	        IPT_CONNBYTES_DIR_REPLY,
+	        IPT_CONNBYTES_DIR_BOTH,
+};
+
+struct ipt_connbytes_info
+{
+        struct {
+                u_int64_t from; /* count to be matched */
+                u_int64_t to;   /* count to be matched */
+        } count;
+        u_int8_t what;          /* ipt_connbytes_what */
+        u_int8_t direction;     /* ipt_connbytes_direction */
+};
+
+#endif
