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

netfilter: netns nf_conntrack: GRE conntracking in netns

* make keymap list per-netns
* per-netns keymal lock (not strictly necessary)
* flush keymap at netns stop and module unload.

Signed-off-by: Alexey Dobriyan <adobriyan@gmail.com>
Signed-off-by: Patrick McHardy <kaber@trash.net>

authored by

Alexey Dobriyan and committed by
Patrick McHardy
3bb0d1c0 84541cc1

+76 -25
+1 -1
include/linux/netfilter/nf_conntrack_proto_gre.h
··· 87 87 /* delete keymap entries */ 88 88 void nf_ct_gre_keymap_destroy(struct nf_conn *ct); 89 89 90 - extern void nf_ct_gre_keymap_flush(void); 90 + extern void nf_ct_gre_keymap_flush(struct net *net); 91 91 extern void nf_nat_need_gre(void); 92 92 93 93 #endif /* __KERNEL__ */
+1 -1
net/netfilter/nf_conntrack_pptp.c
··· 602 602 static void __exit nf_conntrack_pptp_fini(void) 603 603 { 604 604 nf_conntrack_helper_unregister(&pptp); 605 - nf_ct_gre_keymap_flush(); 605 + nf_ct_gre_keymap_flush(&init_net); 606 606 } 607 607 608 608 module_init(nf_conntrack_pptp_init);
+74 -23
net/netfilter/nf_conntrack_proto_gre.c
··· 29 29 #include <linux/list.h> 30 30 #include <linux/seq_file.h> 31 31 #include <linux/in.h> 32 + #include <linux/netdevice.h> 32 33 #include <linux/skbuff.h> 33 - 34 + #include <net/dst.h> 35 + #include <net/net_namespace.h> 36 + #include <net/netns/generic.h> 34 37 #include <net/netfilter/nf_conntrack_l4proto.h> 35 38 #include <net/netfilter/nf_conntrack_helper.h> 36 39 #include <net/netfilter/nf_conntrack_core.h> ··· 43 40 #define GRE_TIMEOUT (30 * HZ) 44 41 #define GRE_STREAM_TIMEOUT (180 * HZ) 45 42 46 - static DEFINE_RWLOCK(nf_ct_gre_lock); 47 - static LIST_HEAD(gre_keymap_list); 43 + static int proto_gre_net_id; 44 + struct netns_proto_gre { 45 + rwlock_t keymap_lock; 46 + struct list_head keymap_list; 47 + }; 48 48 49 - void nf_ct_gre_keymap_flush(void) 49 + void nf_ct_gre_keymap_flush(struct net *net) 50 50 { 51 + struct netns_proto_gre *net_gre = net_generic(net, proto_gre_net_id); 51 52 struct nf_ct_gre_keymap *km, *tmp; 52 53 53 - write_lock_bh(&nf_ct_gre_lock); 54 - list_for_each_entry_safe(km, tmp, &gre_keymap_list, list) { 54 + write_lock_bh(&net_gre->keymap_lock); 55 + list_for_each_entry_safe(km, tmp, &net_gre->keymap_list, list) { 55 56 list_del(&km->list); 56 57 kfree(km); 57 58 } 58 - write_unlock_bh(&nf_ct_gre_lock); 59 + write_unlock_bh(&net_gre->keymap_lock); 59 60 } 60 61 EXPORT_SYMBOL(nf_ct_gre_keymap_flush); 61 62 ··· 74 67 } 75 68 76 69 /* look up the source key for a given tuple */ 77 - static __be16 gre_keymap_lookup(struct nf_conntrack_tuple *t) 70 + static __be16 gre_keymap_lookup(struct net *net, struct nf_conntrack_tuple *t) 78 71 { 72 + struct netns_proto_gre *net_gre = net_generic(net, proto_gre_net_id); 79 73 struct nf_ct_gre_keymap *km; 80 74 __be16 key = 0; 81 75 82 - read_lock_bh(&nf_ct_gre_lock); 83 - list_for_each_entry(km, &gre_keymap_list, list) { 76 + read_lock_bh(&net_gre->keymap_lock); 77 + list_for_each_entry(km, &net_gre->keymap_list, list) { 84 78 if (gre_key_cmpfn(km, t)) { 85 79 key = km->tuple.src.u.gre.key; 86 80 break; 87 81 } 88 82 } 89 - read_unlock_bh(&nf_ct_gre_lock); 83 + read_unlock_bh(&net_gre->keymap_lock); 90 84 91 85 pr_debug("lookup src key 0x%x for ", key); 92 86 nf_ct_dump_tuple(t); ··· 99 91 int nf_ct_gre_keymap_add(struct nf_conn *ct, enum ip_conntrack_dir dir, 100 92 struct nf_conntrack_tuple *t) 101 93 { 94 + struct net *net = nf_ct_net(ct); 95 + struct netns_proto_gre *net_gre = net_generic(net, proto_gre_net_id); 102 96 struct nf_conn_help *help = nfct_help(ct); 103 97 struct nf_ct_gre_keymap **kmp, *km; 104 98 105 99 kmp = &help->help.ct_pptp_info.keymap[dir]; 106 100 if (*kmp) { 107 101 /* check whether it's a retransmission */ 108 - read_lock_bh(&nf_ct_gre_lock); 109 - list_for_each_entry(km, &gre_keymap_list, list) { 102 + read_lock_bh(&net_gre->keymap_lock); 103 + list_for_each_entry(km, &net_gre->keymap_list, list) { 110 104 if (gre_key_cmpfn(km, t) && km == *kmp) { 111 - read_unlock_bh(&nf_ct_gre_lock); 105 + read_unlock_bh(&net_gre->keymap_lock); 112 106 return 0; 113 107 } 114 108 } 115 - read_unlock_bh(&nf_ct_gre_lock); 109 + read_unlock_bh(&net_gre->keymap_lock); 116 110 pr_debug("trying to override keymap_%s for ct %p\n", 117 111 dir == IP_CT_DIR_REPLY ? "reply" : "orig", ct); 118 112 return -EEXIST; ··· 129 119 pr_debug("adding new entry %p: ", km); 130 120 nf_ct_dump_tuple(&km->tuple); 131 121 132 - write_lock_bh(&nf_ct_gre_lock); 133 - list_add_tail(&km->list, &gre_keymap_list); 134 - write_unlock_bh(&nf_ct_gre_lock); 122 + write_lock_bh(&net_gre->keymap_lock); 123 + list_add_tail(&km->list, &net_gre->keymap_list); 124 + write_unlock_bh(&net_gre->keymap_lock); 135 125 136 126 return 0; 137 127 } ··· 140 130 /* destroy the keymap entries associated with specified master ct */ 141 131 void nf_ct_gre_keymap_destroy(struct nf_conn *ct) 142 132 { 133 + struct net *net = nf_ct_net(ct); 134 + struct netns_proto_gre *net_gre = net_generic(net, proto_gre_net_id); 143 135 struct nf_conn_help *help = nfct_help(ct); 144 136 enum ip_conntrack_dir dir; 145 137 146 138 pr_debug("entering for ct %p\n", ct); 147 139 148 - write_lock_bh(&nf_ct_gre_lock); 140 + write_lock_bh(&net_gre->keymap_lock); 149 141 for (dir = IP_CT_DIR_ORIGINAL; dir < IP_CT_DIR_MAX; dir++) { 150 142 if (help->help.ct_pptp_info.keymap[dir]) { 151 143 pr_debug("removing %p from list\n", ··· 157 145 help->help.ct_pptp_info.keymap[dir] = NULL; 158 146 } 159 147 } 160 - write_unlock_bh(&nf_ct_gre_lock); 148 + write_unlock_bh(&net_gre->keymap_lock); 161 149 } 162 150 EXPORT_SYMBOL_GPL(nf_ct_gre_keymap_destroy); 163 151 ··· 176 164 static bool gre_pkt_to_tuple(const struct sk_buff *skb, unsigned int dataoff, 177 165 struct nf_conntrack_tuple *tuple) 178 166 { 167 + struct net *net = dev_net(skb->dev ? skb->dev : skb->dst->dev); 179 168 const struct gre_hdr_pptp *pgrehdr; 180 169 struct gre_hdr_pptp _pgrehdr; 181 170 __be16 srckey; ··· 203 190 } 204 191 205 192 tuple->dst.u.gre.key = pgrehdr->call_id; 206 - srckey = gre_keymap_lookup(tuple); 193 + srckey = gre_keymap_lookup(net, tuple); 207 194 tuple->src.u.gre.key = srckey; 208 195 209 196 return true; ··· 298 285 #endif 299 286 }; 300 287 288 + static int proto_gre_net_init(struct net *net) 289 + { 290 + struct netns_proto_gre *net_gre; 291 + int rv; 292 + 293 + net_gre = kmalloc(sizeof(struct netns_proto_gre), GFP_KERNEL); 294 + if (!net_gre) 295 + return -ENOMEM; 296 + rwlock_init(&net_gre->keymap_lock); 297 + INIT_LIST_HEAD(&net_gre->keymap_list); 298 + 299 + rv = net_assign_generic(net, proto_gre_net_id, net_gre); 300 + if (rv < 0) 301 + kfree(net_gre); 302 + return rv; 303 + } 304 + 305 + static void proto_gre_net_exit(struct net *net) 306 + { 307 + struct netns_proto_gre *net_gre = net_generic(net, proto_gre_net_id); 308 + 309 + nf_ct_gre_keymap_flush(net); 310 + kfree(net_gre); 311 + } 312 + 313 + static struct pernet_operations proto_gre_net_ops = { 314 + .init = proto_gre_net_init, 315 + .exit = proto_gre_net_exit, 316 + }; 317 + 301 318 static int __init nf_ct_proto_gre_init(void) 302 319 { 303 - return nf_conntrack_l4proto_register(&nf_conntrack_l4proto_gre4); 320 + int rv; 321 + 322 + rv = nf_conntrack_l4proto_register(&nf_conntrack_l4proto_gre4); 323 + if (rv < 0) 324 + return rv; 325 + rv = register_pernet_gen_device(&proto_gre_net_id, &proto_gre_net_ops); 326 + if (rv < 0) 327 + nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_gre4); 328 + return rv; 304 329 } 305 330 306 331 static void nf_ct_proto_gre_fini(void) 307 332 { 308 333 nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_gre4); 309 - nf_ct_gre_keymap_flush(); 334 + unregister_pernet_gen_device(proto_gre_net_id, &proto_gre_net_ops); 310 335 } 311 336 312 337 module_init(nf_ct_proto_gre_init);