Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

netfilter: xt_CT: allow to attach timeout policy + glue code

This patch allows you to attach the timeout policy via the
CT target, it adds a new revision of the target to ensure
backward compatibility. Moreover, it also contains the glue
code to stick the timeout object defined via nfnetlink_cttimeout
to the given flow.

Example usage (it requires installing the nfct tool and
libnetfilter_cttimeout):

1) create the timeout policy:

nfct timeout add tcp-policy0 inet tcp \
established 1000 close 10 time_wait 10 last_ack 10

2) attach the timeout policy to the packet:

iptables -I PREROUTING -t raw -p tcp -j CT --timeout tcp-policy0

You have to install the following user-space software:

a) libnetfilter_cttimeout:
git://git.netfilter.org/libnetfilter_cttimeout

b) nfct:
git://git.netfilter.org/nfct

You also have to get iptables with -j CT --timeout support.

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>

+268 -16
+12
include/linux/netfilter/xt_CT.h
··· 16 16 struct nf_conn *ct __attribute__((aligned(8))); 17 17 }; 18 18 19 + struct xt_ct_target_info_v1 { 20 + __u16 flags; 21 + __u16 zone; 22 + __u32 ct_events; 23 + __u32 exp_events; 24 + char helper[16]; 25 + char timeout[32]; 26 + 27 + /* Used internally by the kernel */ 28 + struct nf_conn *ct __attribute__((aligned(8))); 29 + }; 30 + 19 31 #endif /* _XT_CT_H */
+10 -1
net/netfilter/nf_conntrack_core.c
··· 912 912 enum ip_conntrack_info ctinfo; 913 913 struct nf_conntrack_l3proto *l3proto; 914 914 struct nf_conntrack_l4proto *l4proto; 915 + struct nf_conn_timeout *timeout_ext; 915 916 unsigned int *timeouts; 916 917 unsigned int dataoff; 917 918 u_int8_t protonum; ··· 960 959 goto out; 961 960 } 962 961 963 - timeouts = l4proto->get_timeouts(net); 962 + /* Decide what timeout policy we want to apply to this flow. */ 963 + if (tmpl) { 964 + timeout_ext = nf_ct_timeout_find(tmpl); 965 + if (timeout_ext) 966 + timeouts = NF_CT_TIMEOUT_EXT_DATA(timeout_ext); 967 + else 968 + timeouts = l4proto->get_timeouts(net); 969 + } else 970 + timeouts = l4proto->get_timeouts(net); 964 971 965 972 ct = resolve_normal_ct(net, tmpl, skb, dataoff, pf, protonum, 966 973 l3proto, l4proto, &set_reply, &ctinfo,
+205 -15
net/netfilter/xt_CT.c
··· 16 16 #include <net/netfilter/nf_conntrack.h> 17 17 #include <net/netfilter/nf_conntrack_helper.h> 18 18 #include <net/netfilter/nf_conntrack_ecache.h> 19 + #include <net/netfilter/nf_conntrack_timeout.h> 19 20 #include <net/netfilter/nf_conntrack_zones.h> 20 21 21 - static unsigned int xt_ct_target(struct sk_buff *skb, 22 - const struct xt_action_param *par) 22 + static unsigned int xt_ct_target_v0(struct sk_buff *skb, 23 + const struct xt_action_param *par) 23 24 { 24 25 const struct xt_ct_target_info *info = par->targinfo; 26 + struct nf_conn *ct = info->ct; 27 + 28 + /* Previously seen (loopback)? Ignore. */ 29 + if (skb->nfct != NULL) 30 + return XT_CONTINUE; 31 + 32 + atomic_inc(&ct->ct_general.use); 33 + skb->nfct = &ct->ct_general; 34 + skb->nfctinfo = IP_CT_NEW; 35 + 36 + return XT_CONTINUE; 37 + } 38 + 39 + static unsigned int xt_ct_target_v1(struct sk_buff *skb, 40 + const struct xt_action_param *par) 41 + { 42 + const struct xt_ct_target_info_v1 *info = par->targinfo; 25 43 struct nf_conn *ct = info->ct; 26 44 27 45 /* Previously seen (loopback)? Ignore. */ ··· 71 53 return 0; 72 54 } 73 55 74 - static int xt_ct_tg_check(const struct xt_tgchk_param *par) 56 + static int xt_ct_tg_check_v0(const struct xt_tgchk_param *par) 75 57 { 76 58 struct xt_ct_target_info *info = par->targinfo; 77 59 struct nf_conntrack_tuple t; ··· 148 130 return ret; 149 131 } 150 132 151 - static void xt_ct_tg_destroy(const struct xt_tgdtor_param *par) 133 + static int xt_ct_tg_check_v1(const struct xt_tgchk_param *par) 134 + { 135 + struct xt_ct_target_info_v1 *info = par->targinfo; 136 + struct nf_conntrack_tuple t; 137 + struct nf_conn_help *help; 138 + struct nf_conn *ct; 139 + int ret = 0; 140 + u8 proto; 141 + 142 + if (info->flags & ~XT_CT_NOTRACK) 143 + return -EINVAL; 144 + 145 + if (info->flags & XT_CT_NOTRACK) { 146 + ct = nf_ct_untracked_get(); 147 + atomic_inc(&ct->ct_general.use); 148 + goto out; 149 + } 150 + 151 + #ifndef CONFIG_NF_CONNTRACK_ZONES 152 + if (info->zone) 153 + goto err1; 154 + #endif 155 + 156 + ret = nf_ct_l3proto_try_module_get(par->family); 157 + if (ret < 0) 158 + goto err1; 159 + 160 + memset(&t, 0, sizeof(t)); 161 + ct = nf_conntrack_alloc(par->net, info->zone, &t, &t, GFP_KERNEL); 162 + ret = PTR_ERR(ct); 163 + if (IS_ERR(ct)) 164 + goto err2; 165 + 166 + ret = 0; 167 + if ((info->ct_events || info->exp_events) && 168 + !nf_ct_ecache_ext_add(ct, info->ct_events, info->exp_events, 169 + GFP_KERNEL)) 170 + goto err3; 171 + 172 + if (info->helper[0]) { 173 + ret = -ENOENT; 174 + proto = xt_ct_find_proto(par); 175 + if (!proto) { 176 + pr_info("You must specify a L4 protocol, " 177 + "and not use inversions on it.\n"); 178 + goto err3; 179 + } 180 + 181 + ret = -ENOMEM; 182 + help = nf_ct_helper_ext_add(ct, GFP_KERNEL); 183 + if (help == NULL) 184 + goto err3; 185 + 186 + ret = -ENOENT; 187 + help->helper = nf_conntrack_helper_try_module_get(info->helper, 188 + par->family, 189 + proto); 190 + if (help->helper == NULL) { 191 + pr_info("No such helper \"%s\"\n", info->helper); 192 + goto err3; 193 + } 194 + } 195 + 196 + #ifdef CONFIG_NF_CONNTRACK_TIMEOUT 197 + if (info->timeout) { 198 + typeof(nf_ct_timeout_find_get_hook) timeout_find_get; 199 + struct ctnl_timeout *timeout; 200 + struct nf_conn_timeout *timeout_ext; 201 + 202 + timeout_find_get = 203 + rcu_dereference(nf_ct_timeout_find_get_hook); 204 + 205 + if (timeout_find_get) { 206 + const struct ipt_entry *e = par->entryinfo; 207 + 208 + if (e->ip.invflags & IPT_INV_PROTO) { 209 + ret = -EINVAL; 210 + pr_info("You cannot use inversion on " 211 + "L4 protocol\n"); 212 + goto err3; 213 + } 214 + timeout = timeout_find_get(info->timeout); 215 + if (timeout == NULL) { 216 + ret = -ENOENT; 217 + pr_info("No such timeout policy \"%s\"\n", 218 + info->timeout); 219 + goto err3; 220 + } 221 + if (timeout->l3num != par->family) { 222 + ret = -EINVAL; 223 + pr_info("Timeout policy `%s' can only be " 224 + "used by L3 protocol number %d\n", 225 + info->timeout, timeout->l3num); 226 + goto err3; 227 + } 228 + if (timeout->l4num != e->ip.proto) { 229 + ret = -EINVAL; 230 + pr_info("Timeout policy `%s' can only be " 231 + "used by L4 protocol number %d\n", 232 + info->timeout, timeout->l4num); 233 + goto err3; 234 + } 235 + timeout_ext = nf_ct_timeout_ext_add(ct, timeout, 236 + GFP_KERNEL); 237 + if (timeout_ext == NULL) { 238 + ret = -ENOMEM; 239 + goto err3; 240 + } 241 + } else { 242 + ret = -ENOENT; 243 + pr_info("Timeout policy base is empty\n"); 244 + goto err3; 245 + } 246 + } 247 + #endif 248 + 249 + __set_bit(IPS_TEMPLATE_BIT, &ct->status); 250 + __set_bit(IPS_CONFIRMED_BIT, &ct->status); 251 + out: 252 + info->ct = ct; 253 + return 0; 254 + 255 + err3: 256 + nf_conntrack_free(ct); 257 + err2: 258 + nf_ct_l3proto_module_put(par->family); 259 + err1: 260 + return ret; 261 + } 262 + 263 + static void xt_ct_tg_destroy_v0(const struct xt_tgdtor_param *par) 152 264 { 153 265 struct xt_ct_target_info *info = par->targinfo; 154 266 struct nf_conn *ct = info->ct; ··· 294 146 nf_ct_put(info->ct); 295 147 } 296 148 297 - static struct xt_target xt_ct_tg __read_mostly = { 298 - .name = "CT", 299 - .family = NFPROTO_UNSPEC, 300 - .targetsize = sizeof(struct xt_ct_target_info), 301 - .checkentry = xt_ct_tg_check, 302 - .destroy = xt_ct_tg_destroy, 303 - .target = xt_ct_target, 304 - .table = "raw", 305 - .me = THIS_MODULE, 149 + static void xt_ct_tg_destroy_v1(const struct xt_tgdtor_param *par) 150 + { 151 + struct xt_ct_target_info_v1 *info = par->targinfo; 152 + struct nf_conn *ct = info->ct; 153 + struct nf_conn_help *help; 154 + #ifdef CONFIG_NF_CONNTRACK_TIMEOUT 155 + struct nf_conn_timeout *timeout_ext; 156 + typeof(nf_ct_timeout_put_hook) timeout_put; 157 + #endif 158 + if (!nf_ct_is_untracked(ct)) { 159 + help = nfct_help(ct); 160 + if (help) 161 + module_put(help->helper->me); 162 + 163 + nf_ct_l3proto_module_put(par->family); 164 + 165 + #ifdef CONFIG_NF_CONNTRACK_TIMEOUT 166 + timeout_put = rcu_dereference(nf_ct_timeout_put_hook); 167 + 168 + if (timeout_put) { 169 + timeout_ext = nf_ct_timeout_find(ct); 170 + if (timeout_ext) 171 + timeout_put(timeout_ext->timeout); 172 + } 173 + #endif 174 + } 175 + nf_ct_put(info->ct); 176 + } 177 + 178 + static struct xt_target xt_ct_tg_reg[] __read_mostly = { 179 + { 180 + .name = "CT", 181 + .family = NFPROTO_UNSPEC, 182 + .targetsize = sizeof(struct xt_ct_target_info), 183 + .checkentry = xt_ct_tg_check_v0, 184 + .destroy = xt_ct_tg_destroy_v0, 185 + .target = xt_ct_target_v0, 186 + .table = "raw", 187 + .me = THIS_MODULE, 188 + }, 189 + { 190 + .name = "CT", 191 + .family = NFPROTO_UNSPEC, 192 + .revision = 1, 193 + .targetsize = sizeof(struct xt_ct_target_info_v1), 194 + .checkentry = xt_ct_tg_check_v1, 195 + .destroy = xt_ct_tg_destroy_v1, 196 + .target = xt_ct_target_v1, 197 + .table = "raw", 198 + .me = THIS_MODULE, 199 + }, 306 200 }; 307 201 308 202 static int __init xt_ct_tg_init(void) 309 203 { 310 - return xt_register_target(&xt_ct_tg); 204 + return xt_register_targets(xt_ct_tg_reg, ARRAY_SIZE(xt_ct_tg_reg)); 311 205 } 312 206 313 207 static void __exit xt_ct_tg_exit(void) 314 208 { 315 - xt_unregister_target(&xt_ct_tg); 209 + xt_unregister_targets(xt_ct_tg_reg, ARRAY_SIZE(xt_ct_tg_reg)); 316 210 } 317 211 318 212 module_init(xt_ct_tg_init);