diff -ruN linux-orig/include/linux/netfilter/nf_conntrack_common.h linux-TIMEOUT/include/linux/netfilter/nf_conntrack_common.h --- linux-orig/include/linux/netfilter/nf_conntrack_common.h 2007-11-17 00:16:36.000000000 -0500 +++ linux-TIMEOUT/include/linux/netfilter/nf_conntrack_common.h 2007-11-24 19:44:21.000000000 -0500 @@ -73,6 +73,10 @@ /* Connection has fixed timeout. */ IPS_FIXED_TIMEOUT_BIT = 10, IPS_FIXED_TIMEOUT = (1 << IPS_FIXED_TIMEOUT_BIT), + + /* Connection has a manually configured timeout. */ + IPS_MANUAL_TIMEOUT_BIT = 11, + IPS_MANUAL_TIMEOUT = (1 << IPS_MANUAL_TIMEOUT_BIT), }; /* Connection tracking event bits */ diff -ruN linux-orig/include/linux/netfilter/xt_TIMEOUT.h linux-TIMEOUT/include/linux/netfilter/xt_TIMEOUT.h --- linux-orig/include/linux/netfilter/xt_TIMEOUT.h 1969-12-31 19:00:00.000000000 -0500 +++ linux-TIMEOUT/include/linux/netfilter/xt_TIMEOUT.h 2007-11-24 19:59:26.000000000 -0500 @@ -0,0 +1,17 @@ +#ifndef _XT_TIMEOUT_H +#define _XT_TIMEOUT_H + +struct nf_conn_timeout { + u_int32_t timeout; +}; + +#ifdef __KERNEL__ +#include + +static inline struct nf_conn_timeout *nfct_timeout(const struct nf_conn *ct) +{ + return nf_ct_ext_find(ct, NF_CT_EXT_TIMEOUT); +} +#endif + +#endif /*_XT_TIMEOUT_H*/ diff -ruN linux-orig/include/net/netfilter/nf_conntrack_extend.h linux-TIMEOUT/include/net/netfilter/nf_conntrack_extend.h --- linux-orig/include/net/netfilter/nf_conntrack_extend.h 2007-11-17 00:16:36.000000000 -0500 +++ linux-TIMEOUT/include/net/netfilter/nf_conntrack_extend.h 2007-11-24 19:44:21.000000000 -0500 @@ -7,11 +7,13 @@ { NF_CT_EXT_HELPER, NF_CT_EXT_NAT, + NF_CT_EXT_TIMEOUT, NF_CT_EXT_NUM, }; #define NF_CT_EXT_HELPER_TYPE struct nf_conn_help #define NF_CT_EXT_NAT_TYPE struct nf_conn_nat +#define NF_CT_EXT_TIMEOUT_TYPE struct nf_conn_timeout /* Extensions: optional stuff which isn't permanently in struct. */ struct nf_ct_ext { diff -ruN linux-orig/net/netfilter/Kconfig linux-TIMEOUT/net/netfilter/Kconfig --- linux-orig/net/netfilter/Kconfig 2007-11-17 00:16:36.000000000 -0500 +++ linux-TIMEOUT/net/netfilter/Kconfig 2007-11-24 19:44:21.000000000 -0500 @@ -365,6 +365,17 @@ If you want to compile it as a module, say M here and read . If unsure, say `N'. +config NETFILTER_XT_TARGET_TIMEOUT + tristate '"TIMEOUT" target support' + depends on NETFILTER_XTABLES + depends on NF_CONNTRACK + help + The TIMEOUT target allows you to alter the timeout of specific + sessions in the conntrack/NAT subsystem. Specific conntracks can + be given longer or shorter timeouts than the global defaults. + + If unsure, say `N'. + config NETFILTER_XT_TARGET_SECMARK tristate '"SECMARK" target support' depends on NETFILTER_XTABLES && NETWORK_SECMARK diff -ruN linux-orig/net/netfilter/Makefile linux-TIMEOUT/net/netfilter/Makefile --- linux-orig/net/netfilter/Makefile 2007-11-17 00:16:36.000000000 -0500 +++ linux-TIMEOUT/net/netfilter/Makefile 2007-11-24 19:48:24.000000000 -0500 @@ -48,6 +48,7 @@ obj-$(CONFIG_NETFILTER_XT_TARGET_NOTRACK) += xt_NOTRACK.o obj-$(CONFIG_NETFILTER_XT_TARGET_SECMARK) += xt_SECMARK.o obj-$(CONFIG_NETFILTER_XT_TARGET_TCPMSS) += xt_TCPMSS.o +obj-$(CONFIG_NETFILTER_XT_TARGET_TIMEOUT) += xt_TIMEOUT.o obj-$(CONFIG_NETFILTER_XT_TARGET_TRACE) += xt_TRACE.o # matches diff -ruN linux-orig/net/netfilter/nf_conntrack_proto_generic.c linux-TIMEOUT/net/netfilter/nf_conntrack_proto_generic.c --- linux-orig/net/netfilter/nf_conntrack_proto_generic.c 2007-11-17 00:16:36.000000000 -0500 +++ linux-TIMEOUT/net/netfilter/nf_conntrack_proto_generic.c 2007-11-27 13:00:36.000000000 -0500 @@ -11,6 +11,7 @@ #include #include #include +#include static unsigned int nf_ct_generic_timeout __read_mostly = 600*HZ; @@ -55,7 +56,14 @@ int pf, unsigned int hooknum) { - nf_ct_refresh_acct(conntrack, ctinfo, skb, nf_ct_generic_timeout); + unsigned int timeout; + + if (test_bit(IPS_MANUAL_TIMEOUT_BIT, &conntrack->status)) + timeout = nfct_timeout(conntrack)->timeout; + else + timeout = nf_ct_generic_timeout; + + nf_ct_refresh_acct(conntrack, ctinfo, skb, timeout); return NF_ACCEPT; } diff -ruN linux-orig/net/netfilter/nf_conntrack_proto_gre.c linux-TIMEOUT/net/netfilter/nf_conntrack_proto_gre.c --- linux-orig/net/netfilter/nf_conntrack_proto_gre.c 2007-11-17 00:16:36.000000000 -0500 +++ linux-TIMEOUT/net/netfilter/nf_conntrack_proto_gre.c 2007-11-27 12:59:57.000000000 -0500 @@ -36,6 +36,7 @@ #include #include #include +#include #define GRE_TIMEOUT (30 * HZ) #define GRE_STREAM_TIMEOUT (180 * HZ) @@ -217,11 +218,16 @@ int pf, unsigned int hooknum) { + unsigned int timeout; + /* If we've seen traffic both ways, this is a GRE connection. * Extend timeout. */ if (ct->status & IPS_SEEN_REPLY) { - nf_ct_refresh_acct(ct, ctinfo, skb, - ct->proto.gre.stream_timeout); + if (test_bit(IPS_MANUAL_TIMEOUT_BIT, &ct->status)) + timeout = nfct_timeout(ct)->timeout; + else + timeout = ct->proto.gre.stream_timeout; + nf_ct_refresh_acct(ct, ctinfo, skb, timeout); /* Also, more likely to be important, and not a probe. */ set_bit(IPS_ASSURED_BIT, &ct->status); nf_conntrack_event_cache(IPCT_STATUS, skb); diff -ruN linux-orig/net/netfilter/nf_conntrack_proto_sctp.c linux-TIMEOUT/net/netfilter/nf_conntrack_proto_sctp.c --- linux-orig/net/netfilter/nf_conntrack_proto_sctp.c 2007-11-17 00:16:36.000000000 -0500 +++ linux-TIMEOUT/net/netfilter/nf_conntrack_proto_sctp.c 2007-11-27 12:58:39.000000000 -0500 @@ -24,6 +24,7 @@ #include #include #include +#include /* Protects conntrack->proto.sctp */ static DEFINE_RWLOCK(sctp_lock); @@ -298,6 +299,7 @@ sctp_chunkhdr_t _sch, *sch; u_int32_t offset, count; char map[256 / sizeof (char)] = {0}; + unsigned int timeout; sh = skb_header_pointer(skb, dataoff, sizeof(_sctph), &_sctph); if (sh == NULL) @@ -387,7 +389,13 @@ write_unlock_bh(&sctp_lock); } - nf_ct_refresh_acct(conntrack, ctinfo, skb, *sctp_timeouts[newconntrack]); + if (test_bit(IPS_MANUAL_TIMEOUT_BIT, &conntrack->status) + && newconntrack == SCTP_CONNTRACK_ESTABLISHED) + timeout = nfct_timeout(conntrack)->timeout; + else + timeout = *sctp_timeouts[newconntrack]; + + nf_ct_refresh_acct(conntrack, ctinfo, skb, timeout); if (oldsctpstate == SCTP_CONNTRACK_COOKIE_ECHOED && CTINFO2DIR(ctinfo) == IP_CT_DIR_REPLY diff -ruN linux-orig/net/netfilter/nf_conntrack_proto_tcp.c linux-TIMEOUT/net/netfilter/nf_conntrack_proto_tcp.c --- linux-orig/net/netfilter/nf_conntrack_proto_tcp.c 2007-11-17 00:16:36.000000000 -0500 +++ linux-TIMEOUT/net/netfilter/nf_conntrack_proto_tcp.c 2007-11-27 13:02:03.000000000 -0500 @@ -24,6 +24,7 @@ #include #include #include +#include /* Protects conntrack->proto.tcp */ static DEFINE_RWLOCK(tcp_lock); @@ -941,9 +942,13 @@ && (new_state == TCP_CONNTRACK_FIN_WAIT || new_state == TCP_CONNTRACK_CLOSE)) conntrack->proto.tcp.seen[dir].flags |= IP_CT_TCP_FLAG_CLOSE_INIT; - timeout = conntrack->proto.tcp.retrans >= nf_ct_tcp_max_retrans - && *tcp_timeouts[new_state] > nf_ct_tcp_timeout_max_retrans - ? nf_ct_tcp_timeout_max_retrans : *tcp_timeouts[new_state]; + if (test_bit(IPS_MANUAL_TIMEOUT_BIT, &conntrack->status) + && new_state == TCP_CONNTRACK_ESTABLISHED) + timeout = nfct_timeout(conntrack)->timeout; + else + timeout = conntrack->proto.tcp.retrans >= nf_ct_tcp_max_retrans + && *tcp_timeouts[new_state] > nf_ct_tcp_timeout_max_retrans + ? nf_ct_tcp_timeout_max_retrans : *tcp_timeouts[new_state]; write_unlock_bh(&tcp_lock); nf_conntrack_event_cache(IPCT_PROTOINFO_VOLATILE, skb); diff -ruN linux-orig/net/netfilter/nf_conntrack_proto_udp.c linux-TIMEOUT/net/netfilter/nf_conntrack_proto_udp.c --- linux-orig/net/netfilter/nf_conntrack_proto_udp.c 2007-11-17 00:16:36.000000000 -0500 +++ linux-TIMEOUT/net/netfilter/nf_conntrack_proto_udp.c 2007-11-27 13:04:30.000000000 -0500 @@ -21,6 +21,7 @@ #include #include #include +#include static unsigned int nf_ct_udp_timeout __read_mostly = 30*HZ; static unsigned int nf_ct_udp_timeout_stream __read_mostly = 180*HZ; @@ -74,11 +75,16 @@ int pf, unsigned int hooknum) { + unsigned long timeout; + /* 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)) { - nf_ct_refresh_acct(conntrack, ctinfo, skb, - nf_ct_udp_timeout_stream); + if (test_bit(IPS_MANUAL_TIMEOUT_BIT, &conntrack->status)) + timeout = nfct_timeout(conntrack)->timeout; + else + timeout = nf_ct_udp_timeout_stream; + nf_ct_refresh_acct(conntrack, ctinfo, skb, timeout); /* Also, more likely to be important, and not a probe */ if (!test_and_set_bit(IPS_ASSURED_BIT, &conntrack->status)) nf_conntrack_event_cache(IPCT_STATUS, skb); diff -ruN linux-orig/net/netfilter/nf_conntrack_proto_udplite.c linux-TIMEOUT/net/netfilter/nf_conntrack_proto_udplite.c --- linux-orig/net/netfilter/nf_conntrack_proto_udplite.c 2007-11-17 00:16:36.000000000 -0500 +++ linux-TIMEOUT/net/netfilter/nf_conntrack_proto_udplite.c 2007-11-27 13:06:03.000000000 -0500 @@ -22,6 +22,7 @@ #include #include #include +#include static unsigned int nf_ct_udplite_timeout __read_mostly = 30*HZ; static unsigned int nf_ct_udplite_timeout_stream __read_mostly = 180*HZ; @@ -73,11 +74,16 @@ int pf, unsigned int hooknum) { + unsigned long timeout; + /* 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)) { - nf_ct_refresh_acct(conntrack, ctinfo, skb, - nf_ct_udplite_timeout_stream); + if (test_bit(IPS_MANUAL_TIMEOUT_BIT, &conntrack->status)) + timeout = nfct_timeout(conntrack)->timeout; + else + timeout = nf_ct_udplite_timeout_stream; + nf_ct_refresh_acct(conntrack, ctinfo, skb, timeout); /* Also, more likely to be important, and not a probe */ if (!test_and_set_bit(IPS_ASSURED_BIT, &conntrack->status)) nf_conntrack_event_cache(IPCT_STATUS, skb); diff -ruN linux-orig/net/netfilter/xt_TIMEOUT.c linux-TIMEOUT/net/netfilter/xt_TIMEOUT.c --- linux-orig/net/netfilter/xt_TIMEOUT.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-TIMEOUT/net/netfilter/xt_TIMEOUT.c 2007-11-24 19:44:21.000000000 -0500 @@ -0,0 +1,121 @@ +#include +#include +#include + +MODULE_AUTHOR("Phil Oester "); +MODULE_DESCRIPTION("x_tables per-conntrack TIMEOUT target"); +MODULE_LICENSE("GPL"); + +#include +#include +#include + +static unsigned int +target(struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + unsigned int hooknum, + const struct xt_target *target, + const void *targinfo) +{ + const struct nf_conn_timeout *timeoutinfo = targinfo; + struct nf_conn *ct; + struct nf_conn_timeout *timeout_ext; + enum ip_conntrack_info ctinfo; + + ct = nf_ct_get(skb, &ctinfo); + if (ct) { + timeout_ext = nfct_timeout(ct); + if (!timeout_ext) { + timeout_ext = nf_ct_ext_add(ct, NF_CT_EXT_TIMEOUT, + GFP_ATOMIC); + if (timeout_ext == NULL) { + pr_debug("failed to add TIMEOUT extension\n"); + return XT_CONTINUE; + } + } + timeout_ext->timeout = timeoutinfo->timeout * HZ; + set_bit(IPS_MANUAL_TIMEOUT_BIT, &ct->status); + } + + return XT_CONTINUE; +} + +static bool +checkentry(const char *tablename, + const void *entry, + const struct xt_target *target, + void *targinfo, + unsigned int hook_mask) +{ + const struct nf_conn_timeout *timeoutinfo = targinfo; + + if (nf_ct_l3proto_try_module_get(target->family) < 0) { + printk(KERN_WARNING "can't load conntrack support for " + "proto=%d\n", target->family); + return false; + } + if (timeoutinfo->timeout > LONG_MAX / HZ) + return false; + + return true; +} + +static void +destroy(const struct xt_target *target, void *targinfo) +{ + nf_ct_l3proto_module_put(target->family); +} + +static struct xt_target xt_timeout[] __read_mostly = { + { + .name = "TIMEOUT", + .family = AF_INET, + .checkentry = checkentry, + .destroy = destroy, + .target = target, + .targetsize = sizeof(struct nf_conn_timeout), + .me = THIS_MODULE + }, + { + .name = "TIMEOUT", + .family = AF_INET6, + .checkentry = checkentry, + .destroy = destroy, + .target = target, + .targetsize = sizeof(struct nf_conn_timeout), + .me = THIS_MODULE + }, +}; + +static struct nf_ct_ext_type timeout_extend __read_mostly = { + .len = sizeof(struct nf_conn_timeout), + .align = __alignof__(struct nf_conn_timeout), + .id = NF_CT_EXT_TIMEOUT, +}; + +static int __init xt_timeout_init(void) +{ + int ret; + + ret = nf_ct_extend_register(&timeout_extend); + if (ret < 0) { + printk(KERN_ERR "xt_TIMEOUT: Unable to register extension\n"); + return ret; + } + + ret = xt_register_targets(xt_timeout, ARRAY_SIZE(xt_timeout)); + if (ret < 0) + nf_ct_extend_unregister(&timeout_extend); + + return ret; +} + +static void __exit xt_timeout_fini(void) +{ + nf_ct_extend_unregister(&timeout_extend); + xt_unregister_targets(xt_timeout, ARRAY_SIZE(xt_timeout)); +} + +module_init(xt_timeout_init); +module_exit(xt_timeout_fini);