18f03dea5SMartin Josefsson /* L3/L4 protocol support for nf_conntrack. */ 28f03dea5SMartin Josefsson 38f03dea5SMartin Josefsson /* (C) 1999-2001 Paul `Rusty' Russell 48f03dea5SMartin Josefsson * (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org> 58f03dea5SMartin Josefsson * (C) 2003,2004 USAGI/WIDE Project <http://www.linux-ipv6.org> 68f03dea5SMartin Josefsson * 78f03dea5SMartin Josefsson * This program is free software; you can redistribute it and/or modify 88f03dea5SMartin Josefsson * it under the terms of the GNU General Public License version 2 as 98f03dea5SMartin Josefsson * published by the Free Software Foundation. 108f03dea5SMartin Josefsson */ 118f03dea5SMartin Josefsson 128f03dea5SMartin Josefsson #include <linux/types.h> 138f03dea5SMartin Josefsson #include <linux/netfilter.h> 148f03dea5SMartin Josefsson #include <linux/module.h> 15d62f9ed4SPatrick McHardy #include <linux/mutex.h> 168f03dea5SMartin Josefsson #include <linux/skbuff.h> 178f03dea5SMartin Josefsson #include <linux/vmalloc.h> 188f03dea5SMartin Josefsson #include <linux/stddef.h> 198f03dea5SMartin Josefsson #include <linux/err.h> 208f03dea5SMartin Josefsson #include <linux/percpu.h> 218f03dea5SMartin Josefsson #include <linux/moduleparam.h> 228f03dea5SMartin Josefsson #include <linux/notifier.h> 238f03dea5SMartin Josefsson #include <linux/kernel.h> 248f03dea5SMartin Josefsson #include <linux/netdevice.h> 258f03dea5SMartin Josefsson 268f03dea5SMartin Josefsson #include <net/netfilter/nf_conntrack.h> 278f03dea5SMartin Josefsson #include <net/netfilter/nf_conntrack_l3proto.h> 28605dcad6SMartin Josefsson #include <net/netfilter/nf_conntrack_l4proto.h> 298f03dea5SMartin Josefsson #include <net/netfilter/nf_conntrack_core.h> 308f03dea5SMartin Josefsson 31605dcad6SMartin Josefsson struct nf_conntrack_l4proto **nf_ct_protos[PF_MAX] __read_mostly; 32ae5718fbSMartin Josefsson struct nf_conntrack_l3proto *nf_ct_l3protos[AF_MAX] __read_mostly; 3313b18339SPatrick McHardy EXPORT_SYMBOL_GPL(nf_ct_l3protos); 348f03dea5SMartin Josefsson 35d62f9ed4SPatrick McHardy #ifdef CONFIG_SYSCTL 36d62f9ed4SPatrick McHardy static DEFINE_MUTEX(nf_ct_proto_sysctl_mutex); 37d62f9ed4SPatrick McHardy 38d62f9ed4SPatrick McHardy static int 39d62f9ed4SPatrick McHardy nf_ct_register_sysctl(struct ctl_table_header **header, struct ctl_table *path, 40d62f9ed4SPatrick McHardy struct ctl_table *table, unsigned int *users) 41d62f9ed4SPatrick McHardy { 42d62f9ed4SPatrick McHardy if (*header == NULL) { 43d62f9ed4SPatrick McHardy *header = nf_register_sysctl_table(path, table); 44d62f9ed4SPatrick McHardy if (*header == NULL) 45d62f9ed4SPatrick McHardy return -ENOMEM; 46d62f9ed4SPatrick McHardy } 47d62f9ed4SPatrick McHardy if (users != NULL) 48d62f9ed4SPatrick McHardy (*users)++; 49d62f9ed4SPatrick McHardy return 0; 50d62f9ed4SPatrick McHardy } 51d62f9ed4SPatrick McHardy 52d62f9ed4SPatrick McHardy static void 53d62f9ed4SPatrick McHardy nf_ct_unregister_sysctl(struct ctl_table_header **header, 54d62f9ed4SPatrick McHardy struct ctl_table *table, unsigned int *users) 55d62f9ed4SPatrick McHardy { 56d62f9ed4SPatrick McHardy if (users != NULL && --*users > 0) 57d62f9ed4SPatrick McHardy return; 58d62f9ed4SPatrick McHardy nf_unregister_sysctl_table(*header, table); 59d62f9ed4SPatrick McHardy *header = NULL; 60d62f9ed4SPatrick McHardy } 61d62f9ed4SPatrick McHardy #endif 62d62f9ed4SPatrick McHardy 63605dcad6SMartin Josefsson struct nf_conntrack_l4proto * 64605dcad6SMartin Josefsson __nf_ct_l4proto_find(u_int16_t l3proto, u_int8_t l4proto) 658f03dea5SMartin Josefsson { 668f03dea5SMartin Josefsson if (unlikely(l3proto >= AF_MAX || nf_ct_protos[l3proto] == NULL)) 67605dcad6SMartin Josefsson return &nf_conntrack_l4proto_generic; 688f03dea5SMartin Josefsson 69923f4902SPatrick McHardy return rcu_dereference(nf_ct_protos[l3proto][l4proto]); 708f03dea5SMartin Josefsson } 7113b18339SPatrick McHardy EXPORT_SYMBOL_GPL(__nf_ct_l4proto_find); 728f03dea5SMartin Josefsson 738f03dea5SMartin Josefsson /* this is guaranteed to always return a valid protocol helper, since 748f03dea5SMartin Josefsson * it falls back to generic_protocol */ 75605dcad6SMartin Josefsson struct nf_conntrack_l4proto * 76605dcad6SMartin Josefsson nf_ct_l4proto_find_get(u_int16_t l3proto, u_int8_t l4proto) 778f03dea5SMartin Josefsson { 78605dcad6SMartin Josefsson struct nf_conntrack_l4proto *p; 798f03dea5SMartin Josefsson 80923f4902SPatrick McHardy rcu_read_lock(); 81605dcad6SMartin Josefsson p = __nf_ct_l4proto_find(l3proto, l4proto); 828f03dea5SMartin Josefsson if (!try_module_get(p->me)) 83605dcad6SMartin Josefsson p = &nf_conntrack_l4proto_generic; 84923f4902SPatrick McHardy rcu_read_unlock(); 858f03dea5SMartin Josefsson 868f03dea5SMartin Josefsson return p; 878f03dea5SMartin Josefsson } 8813b18339SPatrick McHardy EXPORT_SYMBOL_GPL(nf_ct_l4proto_find_get); 898f03dea5SMartin Josefsson 90605dcad6SMartin Josefsson void nf_ct_l4proto_put(struct nf_conntrack_l4proto *p) 918f03dea5SMartin Josefsson { 928f03dea5SMartin Josefsson module_put(p->me); 938f03dea5SMartin Josefsson } 9413b18339SPatrick McHardy EXPORT_SYMBOL_GPL(nf_ct_l4proto_put); 958f03dea5SMartin Josefsson 968f03dea5SMartin Josefsson struct nf_conntrack_l3proto * 978f03dea5SMartin Josefsson nf_ct_l3proto_find_get(u_int16_t l3proto) 988f03dea5SMartin Josefsson { 998f03dea5SMartin Josefsson struct nf_conntrack_l3proto *p; 1008f03dea5SMartin Josefsson 101923f4902SPatrick McHardy rcu_read_lock(); 1028f03dea5SMartin Josefsson p = __nf_ct_l3proto_find(l3proto); 1038f03dea5SMartin Josefsson if (!try_module_get(p->me)) 104605dcad6SMartin Josefsson p = &nf_conntrack_l3proto_generic; 105923f4902SPatrick McHardy rcu_read_unlock(); 1068f03dea5SMartin Josefsson 1078f03dea5SMartin Josefsson return p; 1088f03dea5SMartin Josefsson } 10913b18339SPatrick McHardy EXPORT_SYMBOL_GPL(nf_ct_l3proto_find_get); 1108f03dea5SMartin Josefsson 1118f03dea5SMartin Josefsson void nf_ct_l3proto_put(struct nf_conntrack_l3proto *p) 1128f03dea5SMartin Josefsson { 1138f03dea5SMartin Josefsson module_put(p->me); 1148f03dea5SMartin Josefsson } 11513b18339SPatrick McHardy EXPORT_SYMBOL_GPL(nf_ct_l3proto_put); 1168f03dea5SMartin Josefsson 1178f03dea5SMartin Josefsson int 1188f03dea5SMartin Josefsson nf_ct_l3proto_try_module_get(unsigned short l3proto) 1198f03dea5SMartin Josefsson { 1208f03dea5SMartin Josefsson int ret; 1218f03dea5SMartin Josefsson struct nf_conntrack_l3proto *p; 1228f03dea5SMartin Josefsson 1238f03dea5SMartin Josefsson retry: p = nf_ct_l3proto_find_get(l3proto); 124605dcad6SMartin Josefsson if (p == &nf_conntrack_l3proto_generic) { 1258f03dea5SMartin Josefsson ret = request_module("nf_conntrack-%d", l3proto); 1268f03dea5SMartin Josefsson if (!ret) 1278f03dea5SMartin Josefsson goto retry; 1288f03dea5SMartin Josefsson 1298f03dea5SMartin Josefsson return -EPROTOTYPE; 1308f03dea5SMartin Josefsson } 1318f03dea5SMartin Josefsson 1328f03dea5SMartin Josefsson return 0; 1338f03dea5SMartin Josefsson } 13413b18339SPatrick McHardy EXPORT_SYMBOL_GPL(nf_ct_l3proto_try_module_get); 1358f03dea5SMartin Josefsson 1368f03dea5SMartin Josefsson void nf_ct_l3proto_module_put(unsigned short l3proto) 1378f03dea5SMartin Josefsson { 1388f03dea5SMartin Josefsson struct nf_conntrack_l3proto *p; 1398f03dea5SMartin Josefsson 140923f4902SPatrick McHardy /* rcu_read_lock not necessary since the caller holds a reference */ 1418f03dea5SMartin Josefsson p = __nf_ct_l3proto_find(l3proto); 1428f03dea5SMartin Josefsson module_put(p->me); 1438f03dea5SMartin Josefsson } 14413b18339SPatrick McHardy EXPORT_SYMBOL_GPL(nf_ct_l3proto_module_put); 1458f03dea5SMartin Josefsson 1468f03dea5SMartin Josefsson static int kill_l3proto(struct nf_conn *i, void *data) 1478f03dea5SMartin Josefsson { 1488f03dea5SMartin Josefsson return (i->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.l3num == 1498f03dea5SMartin Josefsson ((struct nf_conntrack_l3proto *)data)->l3proto); 1508f03dea5SMartin Josefsson } 1518f03dea5SMartin Josefsson 152605dcad6SMartin Josefsson static int kill_l4proto(struct nf_conn *i, void *data) 1538f03dea5SMartin Josefsson { 154605dcad6SMartin Josefsson struct nf_conntrack_l4proto *l4proto; 155605dcad6SMartin Josefsson l4proto = (struct nf_conntrack_l4proto *)data; 1568f03dea5SMartin Josefsson return (i->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum == 157605dcad6SMartin Josefsson l4proto->l4proto) && 1588f03dea5SMartin Josefsson (i->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.l3num == 159605dcad6SMartin Josefsson l4proto->l3proto); 1608f03dea5SMartin Josefsson } 1618f03dea5SMartin Josefsson 162d62f9ed4SPatrick McHardy static int nf_ct_l3proto_register_sysctl(struct nf_conntrack_l3proto *l3proto) 163d62f9ed4SPatrick McHardy { 164d62f9ed4SPatrick McHardy int err = 0; 165d62f9ed4SPatrick McHardy 166d62f9ed4SPatrick McHardy #ifdef CONFIG_SYSCTL 167d62f9ed4SPatrick McHardy mutex_lock(&nf_ct_proto_sysctl_mutex); 168d62f9ed4SPatrick McHardy if (l3proto->ctl_table != NULL) { 169d62f9ed4SPatrick McHardy err = nf_ct_register_sysctl(&l3proto->ctl_table_header, 170d62f9ed4SPatrick McHardy l3proto->ctl_table_path, 171d62f9ed4SPatrick McHardy l3proto->ctl_table, NULL); 172d62f9ed4SPatrick McHardy } 173d62f9ed4SPatrick McHardy mutex_unlock(&nf_ct_proto_sysctl_mutex); 174d62f9ed4SPatrick McHardy #endif 175d62f9ed4SPatrick McHardy return err; 176d62f9ed4SPatrick McHardy } 177d62f9ed4SPatrick McHardy 178d62f9ed4SPatrick McHardy static void nf_ct_l3proto_unregister_sysctl(struct nf_conntrack_l3proto *l3proto) 179d62f9ed4SPatrick McHardy { 180d62f9ed4SPatrick McHardy #ifdef CONFIG_SYSCTL 181d62f9ed4SPatrick McHardy mutex_lock(&nf_ct_proto_sysctl_mutex); 182d62f9ed4SPatrick McHardy if (l3proto->ctl_table_header != NULL) 183d62f9ed4SPatrick McHardy nf_ct_unregister_sysctl(&l3proto->ctl_table_header, 184d62f9ed4SPatrick McHardy l3proto->ctl_table, NULL); 185d62f9ed4SPatrick McHardy mutex_unlock(&nf_ct_proto_sysctl_mutex); 186d62f9ed4SPatrick McHardy #endif 187d62f9ed4SPatrick McHardy } 188d62f9ed4SPatrick McHardy 1898f03dea5SMartin Josefsson int nf_conntrack_l3proto_register(struct nf_conntrack_l3proto *proto) 1908f03dea5SMartin Josefsson { 1918f03dea5SMartin Josefsson int ret = 0; 1928f03dea5SMartin Josefsson 193ae5718fbSMartin Josefsson if (proto->l3proto >= AF_MAX) { 1948f03dea5SMartin Josefsson ret = -EBUSY; 1958f03dea5SMartin Josefsson goto out; 1968f03dea5SMartin Josefsson } 1978f03dea5SMartin Josefsson 198ae5718fbSMartin Josefsson write_lock_bh(&nf_conntrack_lock); 199ae5718fbSMartin Josefsson if (nf_ct_l3protos[proto->l3proto] != &nf_conntrack_l3proto_generic) { 200ae5718fbSMartin Josefsson ret = -EBUSY; 201ae5718fbSMartin Josefsson goto out_unlock; 202ae5718fbSMartin Josefsson } 203923f4902SPatrick McHardy rcu_assign_pointer(nf_ct_l3protos[proto->l3proto], proto); 204d62f9ed4SPatrick McHardy write_unlock_bh(&nf_conntrack_lock); 205d62f9ed4SPatrick McHardy 206d62f9ed4SPatrick McHardy ret = nf_ct_l3proto_register_sysctl(proto); 207d62f9ed4SPatrick McHardy if (ret < 0) 208d62f9ed4SPatrick McHardy nf_conntrack_l3proto_unregister(proto); 209d62f9ed4SPatrick McHardy return ret; 210ae5718fbSMartin Josefsson 211ae5718fbSMartin Josefsson out_unlock: 212ae5718fbSMartin Josefsson write_unlock_bh(&nf_conntrack_lock); 213ae5718fbSMartin Josefsson out: 2148f03dea5SMartin Josefsson return ret; 2158f03dea5SMartin Josefsson } 21613b18339SPatrick McHardy EXPORT_SYMBOL_GPL(nf_conntrack_l3proto_register); 2178f03dea5SMartin Josefsson 218fe3eb20cSPatrick McHardy void nf_conntrack_l3proto_unregister(struct nf_conntrack_l3proto *proto) 2198f03dea5SMartin Josefsson { 220fe3eb20cSPatrick McHardy BUG_ON(proto->l3proto >= AF_MAX); 221ae5718fbSMartin Josefsson 2228f03dea5SMartin Josefsson write_lock_bh(&nf_conntrack_lock); 223fe3eb20cSPatrick McHardy BUG_ON(nf_ct_l3protos[proto->l3proto] != proto); 224923f4902SPatrick McHardy rcu_assign_pointer(nf_ct_l3protos[proto->l3proto], 225923f4902SPatrick McHardy &nf_conntrack_l3proto_generic); 2268f03dea5SMartin Josefsson write_unlock_bh(&nf_conntrack_lock); 227923f4902SPatrick McHardy synchronize_rcu(); 2288f03dea5SMartin Josefsson 229d62f9ed4SPatrick McHardy nf_ct_l3proto_unregister_sysctl(proto); 230d62f9ed4SPatrick McHardy 2318f03dea5SMartin Josefsson /* Remove all contrack entries for this protocol */ 2328f03dea5SMartin Josefsson nf_ct_iterate_cleanup(kill_l3proto, proto); 2338f03dea5SMartin Josefsson } 23413b18339SPatrick McHardy EXPORT_SYMBOL_GPL(nf_conntrack_l3proto_unregister); 2358f03dea5SMartin Josefsson 236d62f9ed4SPatrick McHardy static int nf_ct_l4proto_register_sysctl(struct nf_conntrack_l4proto *l4proto) 237d62f9ed4SPatrick McHardy { 238d62f9ed4SPatrick McHardy int err = 0; 239d62f9ed4SPatrick McHardy 240d62f9ed4SPatrick McHardy #ifdef CONFIG_SYSCTL 241d62f9ed4SPatrick McHardy mutex_lock(&nf_ct_proto_sysctl_mutex); 242d62f9ed4SPatrick McHardy if (l4proto->ctl_table != NULL) { 243d62f9ed4SPatrick McHardy err = nf_ct_register_sysctl(l4proto->ctl_table_header, 244d62f9ed4SPatrick McHardy nf_net_netfilter_sysctl_path, 245d62f9ed4SPatrick McHardy l4proto->ctl_table, 246d62f9ed4SPatrick McHardy l4proto->ctl_table_users); 247a999e683SPatrick McHardy if (err < 0) 248a999e683SPatrick McHardy goto out; 249d62f9ed4SPatrick McHardy } 250a999e683SPatrick McHardy #ifdef CONFIG_NF_CONNTRACK_PROC_COMPAT 251a999e683SPatrick McHardy if (l4proto->ctl_compat_table != NULL) { 252a999e683SPatrick McHardy err = nf_ct_register_sysctl(&l4proto->ctl_compat_table_header, 253a999e683SPatrick McHardy nf_net_ipv4_netfilter_sysctl_path, 254a999e683SPatrick McHardy l4proto->ctl_compat_table, NULL); 255a999e683SPatrick McHardy if (err == 0) 256a999e683SPatrick McHardy goto out; 257a999e683SPatrick McHardy nf_ct_unregister_sysctl(l4proto->ctl_table_header, 258a999e683SPatrick McHardy l4proto->ctl_table, 259a999e683SPatrick McHardy l4proto->ctl_table_users); 260a999e683SPatrick McHardy } 261a999e683SPatrick McHardy #endif /* CONFIG_NF_CONNTRACK_PROC_COMPAT */ 262a999e683SPatrick McHardy out: 263d62f9ed4SPatrick McHardy mutex_unlock(&nf_ct_proto_sysctl_mutex); 264933a41e7SPatrick McHardy #endif /* CONFIG_SYSCTL */ 265d62f9ed4SPatrick McHardy return err; 266d62f9ed4SPatrick McHardy } 267d62f9ed4SPatrick McHardy 268d62f9ed4SPatrick McHardy static void nf_ct_l4proto_unregister_sysctl(struct nf_conntrack_l4proto *l4proto) 269d62f9ed4SPatrick McHardy { 270d62f9ed4SPatrick McHardy #ifdef CONFIG_SYSCTL 271d62f9ed4SPatrick McHardy mutex_lock(&nf_ct_proto_sysctl_mutex); 272d62f9ed4SPatrick McHardy if (l4proto->ctl_table_header != NULL && 273d62f9ed4SPatrick McHardy *l4proto->ctl_table_header != NULL) 274d62f9ed4SPatrick McHardy nf_ct_unregister_sysctl(l4proto->ctl_table_header, 275d62f9ed4SPatrick McHardy l4proto->ctl_table, 276d62f9ed4SPatrick McHardy l4proto->ctl_table_users); 277a999e683SPatrick McHardy #ifdef CONFIG_NF_CONNTRACK_PROC_COMPAT 278a999e683SPatrick McHardy if (l4proto->ctl_compat_table_header != NULL) 279a999e683SPatrick McHardy nf_ct_unregister_sysctl(&l4proto->ctl_compat_table_header, 280a999e683SPatrick McHardy l4proto->ctl_compat_table, NULL); 281a999e683SPatrick McHardy #endif /* CONFIG_NF_CONNTRACK_PROC_COMPAT */ 282d62f9ed4SPatrick McHardy mutex_unlock(&nf_ct_proto_sysctl_mutex); 283933a41e7SPatrick McHardy #endif /* CONFIG_SYSCTL */ 284d62f9ed4SPatrick McHardy } 285d62f9ed4SPatrick McHardy 2868f03dea5SMartin Josefsson /* FIXME: Allow NULL functions and sub in pointers to generic for 2878f03dea5SMartin Josefsson them. --RR */ 288605dcad6SMartin Josefsson int nf_conntrack_l4proto_register(struct nf_conntrack_l4proto *l4proto) 2898f03dea5SMartin Josefsson { 2908f03dea5SMartin Josefsson int ret = 0; 2918f03dea5SMartin Josefsson 292ae5718fbSMartin Josefsson if (l4proto->l3proto >= PF_MAX) { 293ae5718fbSMartin Josefsson ret = -EBUSY; 294ae5718fbSMartin Josefsson goto out; 295ae5718fbSMartin Josefsson } 296ae5718fbSMartin Josefsson 297933a41e7SPatrick McHardy if (l4proto == &nf_conntrack_l4proto_generic) 298933a41e7SPatrick McHardy return nf_ct_l4proto_register_sysctl(l4proto); 299933a41e7SPatrick McHardy 3008f03dea5SMartin Josefsson retry: 3018f03dea5SMartin Josefsson write_lock_bh(&nf_conntrack_lock); 302605dcad6SMartin Josefsson if (nf_ct_protos[l4proto->l3proto]) { 303605dcad6SMartin Josefsson if (nf_ct_protos[l4proto->l3proto][l4proto->l4proto] 304605dcad6SMartin Josefsson != &nf_conntrack_l4proto_generic) { 3058f03dea5SMartin Josefsson ret = -EBUSY; 3068f03dea5SMartin Josefsson goto out_unlock; 3078f03dea5SMartin Josefsson } 3088f03dea5SMartin Josefsson } else { 3098f03dea5SMartin Josefsson /* l3proto may be loaded latter. */ 310605dcad6SMartin Josefsson struct nf_conntrack_l4proto **proto_array; 3118f03dea5SMartin Josefsson int i; 3128f03dea5SMartin Josefsson 3138f03dea5SMartin Josefsson write_unlock_bh(&nf_conntrack_lock); 3148f03dea5SMartin Josefsson 315605dcad6SMartin Josefsson proto_array = (struct nf_conntrack_l4proto **) 3168f03dea5SMartin Josefsson kmalloc(MAX_NF_CT_PROTO * 317605dcad6SMartin Josefsson sizeof(struct nf_conntrack_l4proto *), 3188f03dea5SMartin Josefsson GFP_KERNEL); 3198f03dea5SMartin Josefsson if (proto_array == NULL) { 3208f03dea5SMartin Josefsson ret = -ENOMEM; 3218f03dea5SMartin Josefsson goto out; 3228f03dea5SMartin Josefsson } 3238f03dea5SMartin Josefsson for (i = 0; i < MAX_NF_CT_PROTO; i++) 324605dcad6SMartin Josefsson proto_array[i] = &nf_conntrack_l4proto_generic; 3258f03dea5SMartin Josefsson 3268f03dea5SMartin Josefsson write_lock_bh(&nf_conntrack_lock); 327605dcad6SMartin Josefsson if (nf_ct_protos[l4proto->l3proto]) { 3288f03dea5SMartin Josefsson /* bad timing, but no problem */ 3298f03dea5SMartin Josefsson write_unlock_bh(&nf_conntrack_lock); 3308f03dea5SMartin Josefsson kfree(proto_array); 3318f03dea5SMartin Josefsson } else { 332605dcad6SMartin Josefsson nf_ct_protos[l4proto->l3proto] = proto_array; 3338f03dea5SMartin Josefsson write_unlock_bh(&nf_conntrack_lock); 3348f03dea5SMartin Josefsson } 3358f03dea5SMartin Josefsson 3368f03dea5SMartin Josefsson /* 3378f03dea5SMartin Josefsson * Just once because array is never freed until unloading 3388f03dea5SMartin Josefsson * nf_conntrack.ko 3398f03dea5SMartin Josefsson */ 3408f03dea5SMartin Josefsson goto retry; 3418f03dea5SMartin Josefsson } 3428f03dea5SMartin Josefsson 343923f4902SPatrick McHardy rcu_assign_pointer(nf_ct_protos[l4proto->l3proto][l4proto->l4proto], l4proto); 344d62f9ed4SPatrick McHardy write_unlock_bh(&nf_conntrack_lock); 345d62f9ed4SPatrick McHardy 346d62f9ed4SPatrick McHardy ret = nf_ct_l4proto_register_sysctl(l4proto); 347d62f9ed4SPatrick McHardy if (ret < 0) 348d62f9ed4SPatrick McHardy nf_conntrack_l4proto_unregister(l4proto); 349d62f9ed4SPatrick McHardy return ret; 3508f03dea5SMartin Josefsson 3518f03dea5SMartin Josefsson out_unlock: 3528f03dea5SMartin Josefsson write_unlock_bh(&nf_conntrack_lock); 3538f03dea5SMartin Josefsson out: 3548f03dea5SMartin Josefsson return ret; 3558f03dea5SMartin Josefsson } 35613b18339SPatrick McHardy EXPORT_SYMBOL_GPL(nf_conntrack_l4proto_register); 3578f03dea5SMartin Josefsson 358fe3eb20cSPatrick McHardy void nf_conntrack_l4proto_unregister(struct nf_conntrack_l4proto *l4proto) 3598f03dea5SMartin Josefsson { 360fe3eb20cSPatrick McHardy BUG_ON(l4proto->l3proto >= PF_MAX); 361ae5718fbSMartin Josefsson 362933a41e7SPatrick McHardy if (l4proto == &nf_conntrack_l4proto_generic) { 363933a41e7SPatrick McHardy nf_ct_l4proto_unregister_sysctl(l4proto); 364fe3eb20cSPatrick McHardy return; 365933a41e7SPatrick McHardy } 366933a41e7SPatrick McHardy 3678f03dea5SMartin Josefsson write_lock_bh(&nf_conntrack_lock); 368fe3eb20cSPatrick McHardy BUG_ON(nf_ct_protos[l4proto->l3proto][l4proto->l4proto] != l4proto); 369923f4902SPatrick McHardy rcu_assign_pointer(nf_ct_protos[l4proto->l3proto][l4proto->l4proto], 370923f4902SPatrick McHardy &nf_conntrack_l4proto_generic); 3718f03dea5SMartin Josefsson write_unlock_bh(&nf_conntrack_lock); 372923f4902SPatrick McHardy synchronize_rcu(); 3738f03dea5SMartin Josefsson 374d62f9ed4SPatrick McHardy nf_ct_l4proto_unregister_sysctl(l4proto); 375d62f9ed4SPatrick McHardy 3768f03dea5SMartin Josefsson /* Remove all contrack entries for this protocol */ 377605dcad6SMartin Josefsson nf_ct_iterate_cleanup(kill_l4proto, l4proto); 3788f03dea5SMartin Josefsson } 37913b18339SPatrick McHardy EXPORT_SYMBOL_GPL(nf_conntrack_l4proto_unregister); 380