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

[NETFILTER]: nf_nat: kill helper and seq_adjust hooks

Connection tracking helpers (specifically FTP) need to be called
before NAT sequence numbers adjustments are performed to be able
to compare them against previously seen ones. We've introduced
two new hooks around 2.6.11 to maintain this ordering when NAT
modules were changed to get called from conntrack helpers directly.

The cost of netfilter hooks is quite high and sequence number
adjustments are only rarely needed however. Add a RCU-protected
sequence number adjustment function pointer and call it from
IPv4 conntrack after calling the helper.

Signed-off-by: Patrick McHardy <kaber@trash.net>

+35 -67
-2
include/linux/netfilter_ipv4.h
··· 62 62 NF_IP_PRI_FILTER = 0, 63 63 NF_IP_PRI_NAT_SRC = 100, 64 64 NF_IP_PRI_SELINUX_LAST = 225, 65 - NF_IP_PRI_CONNTRACK_HELPER = INT_MAX - 2, 66 - NF_IP_PRI_NAT_SEQ_ADJUST = INT_MAX - 1, 67 65 NF_IP_PRI_CONNTRACK_CONFIRM = INT_MAX, 68 66 NF_IP_PRI_LAST = INT_MAX, 69 67 };
+3
include/net/netfilter/nf_nat_helper.h
··· 24 24 extern int nf_nat_seq_adjust(struct sk_buff *skb, 25 25 struct nf_conn *ct, 26 26 enum ip_conntrack_info ctinfo); 27 + extern int (*nf_nat_seq_adjust_hook)(struct sk_buff *skb, 28 + struct nf_conn *ct, 29 + enum ip_conntrack_info ctinfo); 27 30 28 31 /* Setup NAT on this expected conntrack so it follows master, but goes 29 32 * to port ct->master->saved_proto. */
+27 -29
net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c
··· 23 23 #include <net/netfilter/nf_conntrack_l3proto.h> 24 24 #include <net/netfilter/nf_conntrack_core.h> 25 25 #include <net/netfilter/ipv4/nf_conntrack_ipv4.h> 26 + #include <net/netfilter/nf_nat_helper.h> 27 + 28 + int (*nf_nat_seq_adjust_hook)(struct sk_buff *skb, 29 + struct nf_conn *ct, 30 + enum ip_conntrack_info ctinfo); 31 + EXPORT_SYMBOL_GPL(nf_nat_seq_adjust_hook); 26 32 27 33 static int ipv4_pkt_to_tuple(const struct sk_buff *skb, unsigned int nhoff, 28 34 struct nf_conntrack_tuple *tuple) ··· 107 101 const struct net_device *out, 108 102 int (*okfn)(struct sk_buff *)) 109 103 { 110 - /* We've seen it coming out the other side: confirm it */ 111 - return nf_conntrack_confirm(skb); 112 - } 113 - 114 - static unsigned int ipv4_conntrack_help(unsigned int hooknum, 115 - struct sk_buff *skb, 116 - const struct net_device *in, 117 - const struct net_device *out, 118 - int (*okfn)(struct sk_buff *)) 119 - { 120 104 struct nf_conn *ct; 121 105 enum ip_conntrack_info ctinfo; 122 106 const struct nf_conn_help *help; 123 107 const struct nf_conntrack_helper *helper; 108 + unsigned int ret; 124 109 125 110 /* This is where we call the helper: as the packet goes out. */ 126 111 ct = nf_ct_get(skb, &ctinfo); 127 112 if (!ct || ctinfo == IP_CT_RELATED + IP_CT_IS_REPLY) 128 - return NF_ACCEPT; 113 + goto out; 129 114 130 115 help = nfct_help(ct); 131 116 if (!help) 132 - return NF_ACCEPT; 117 + goto out; 118 + 133 119 /* rcu_read_lock()ed by nf_hook_slow */ 134 120 helper = rcu_dereference(help->helper); 135 121 if (!helper) 136 - return NF_ACCEPT; 137 - return helper->help(skb, skb_network_offset(skb) + ip_hdrlen(skb), 138 - ct, ctinfo); 122 + goto out; 123 + 124 + ret = helper->help(skb, skb_network_offset(skb) + ip_hdrlen(skb), 125 + ct, ctinfo); 126 + if (ret != NF_ACCEPT) 127 + return ret; 128 + 129 + if (test_bit(IPS_SEQ_ADJUST_BIT, &ct->status)) { 130 + typeof(nf_nat_seq_adjust_hook) seq_adjust; 131 + 132 + seq_adjust = rcu_dereference(nf_nat_seq_adjust_hook); 133 + if (!seq_adjust || !seq_adjust(skb, ct, ctinfo)) 134 + return NF_DROP; 135 + } 136 + out: 137 + /* We've seen it coming out the other side: confirm it */ 138 + return nf_conntrack_confirm(skb); 139 139 } 140 140 141 141 static unsigned int ipv4_conntrack_defrag(unsigned int hooknum, ··· 221 209 .pf = PF_INET, 222 210 .hooknum = NF_INET_LOCAL_OUT, 223 211 .priority = NF_IP_PRI_CONNTRACK, 224 - }, 225 - { 226 - .hook = ipv4_conntrack_help, 227 - .owner = THIS_MODULE, 228 - .pf = PF_INET, 229 - .hooknum = NF_INET_POST_ROUTING, 230 - .priority = NF_IP_PRI_CONNTRACK_HELPER, 231 - }, 232 - { 233 - .hook = ipv4_conntrack_help, 234 - .owner = THIS_MODULE, 235 - .pf = PF_INET, 236 - .hooknum = NF_INET_LOCAL_IN, 237 - .priority = NF_IP_PRI_CONNTRACK_HELPER, 238 212 }, 239 213 { 240 214 .hook = ipv4_confirm,
+5
net/ipv4/netfilter/nf_nat_core.c
··· 618 618 nf_conntrack_untracked.status |= IPS_NAT_DONE_MASK; 619 619 620 620 l3proto = nf_ct_l3proto_find_get((u_int16_t)AF_INET); 621 + 622 + BUG_ON(nf_nat_seq_adjust_hook != NULL); 623 + rcu_assign_pointer(nf_nat_seq_adjust_hook, nf_nat_seq_adjust); 621 624 return 0; 622 625 623 626 cleanup_extend: ··· 647 644 nf_ct_free_hashtable(bysource, nf_nat_vmalloced, nf_nat_htable_size); 648 645 nf_ct_l3proto_put(l3proto); 649 646 nf_ct_extend_unregister(&nat_extend); 647 + rcu_assign_pointer(nf_nat_seq_adjust_hook, NULL); 648 + synchronize_net(); 650 649 } 651 650 652 651 MODULE_LICENSE("GPL");
-1
net/ipv4/netfilter/nf_nat_helper.c
··· 416 416 417 417 return 1; 418 418 } 419 - EXPORT_SYMBOL(nf_nat_seq_adjust); 420 419 421 420 /* Setup NAT on this expected conntrack so it follows master. */ 422 421 /* If we fail to get a free NAT slot, we'll get dropped on confirm */
-35
net/ipv4/netfilter/nf_nat_standalone.c
··· 245 245 return ret; 246 246 } 247 247 248 - static unsigned int 249 - nf_nat_adjust(unsigned int hooknum, 250 - struct sk_buff *skb, 251 - const struct net_device *in, 252 - const struct net_device *out, 253 - int (*okfn)(struct sk_buff *)) 254 - { 255 - struct nf_conn *ct; 256 - enum ip_conntrack_info ctinfo; 257 - 258 - ct = nf_ct_get(skb, &ctinfo); 259 - if (ct && test_bit(IPS_SEQ_ADJUST_BIT, &ct->status)) { 260 - pr_debug("nf_nat_standalone: adjusting sequence number\n"); 261 - if (!nf_nat_seq_adjust(skb, ct, ctinfo)) 262 - return NF_DROP; 263 - } 264 - return NF_ACCEPT; 265 - } 266 - 267 248 /* We must be after connection tracking and before packet filtering. */ 268 249 269 250 static struct nf_hook_ops nf_nat_ops[] __read_mostly = { ··· 264 283 .hooknum = NF_INET_POST_ROUTING, 265 284 .priority = NF_IP_PRI_NAT_SRC, 266 285 }, 267 - /* After conntrack, adjust sequence number */ 268 - { 269 - .hook = nf_nat_adjust, 270 - .owner = THIS_MODULE, 271 - .pf = PF_INET, 272 - .hooknum = NF_INET_POST_ROUTING, 273 - .priority = NF_IP_PRI_NAT_SEQ_ADJUST, 274 - }, 275 286 /* Before packet filtering, change destination */ 276 287 { 277 288 .hook = nf_nat_local_fn, ··· 279 306 .pf = PF_INET, 280 307 .hooknum = NF_INET_LOCAL_IN, 281 308 .priority = NF_IP_PRI_NAT_SRC, 282 - }, 283 - /* After conntrack, adjust sequence number */ 284 - { 285 - .hook = nf_nat_adjust, 286 - .owner = THIS_MODULE, 287 - .pf = PF_INET, 288 - .hooknum = NF_INET_LOCAL_IN, 289 - .priority = NF_IP_PRI_NAT_SEQ_ADJUST, 290 309 }, 291 310 }; 292 311