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> 6f229f6ceSPatrick McHardy * (C) 2006-2012 Patrick McHardy <kaber@trash.net> 78f03dea5SMartin Josefsson * 88f03dea5SMartin Josefsson * This program is free software; you can redistribute it and/or modify 98f03dea5SMartin Josefsson * it under the terms of the GNU General Public License version 2 as 108f03dea5SMartin Josefsson * published by the Free Software Foundation. 118f03dea5SMartin Josefsson */ 128f03dea5SMartin Josefsson 138f03dea5SMartin Josefsson #include <linux/types.h> 148f03dea5SMartin Josefsson #include <linux/netfilter.h> 158f03dea5SMartin Josefsson #include <linux/module.h> 165a0e3ad6STejun Heo #include <linux/slab.h> 17d62f9ed4SPatrick McHardy #include <linux/mutex.h> 188f03dea5SMartin Josefsson #include <linux/vmalloc.h> 198f03dea5SMartin Josefsson #include <linux/stddef.h> 208f03dea5SMartin Josefsson #include <linux/err.h> 218f03dea5SMartin Josefsson #include <linux/percpu.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 310906a372SArnd Bergmann static struct nf_conntrack_l4proto __rcu **nf_ct_protos[PF_MAX] __read_mostly; 320906a372SArnd Bergmann struct nf_conntrack_l3proto __rcu *nf_ct_l3protos[AF_MAX] __read_mostly; 3313b18339SPatrick McHardy EXPORT_SYMBOL_GPL(nf_ct_l3protos); 348f03dea5SMartin Josefsson 35b19caa0cSPatrick McHardy static DEFINE_MUTEX(nf_ct_proto_mutex); 36d62f9ed4SPatrick McHardy 37b19caa0cSPatrick McHardy #ifdef CONFIG_SYSCTL 38d62f9ed4SPatrick McHardy static int 392c352f44SGao feng nf_ct_register_sysctl(struct net *net, 402c352f44SGao feng struct ctl_table_header **header, 412c352f44SGao feng const char *path, 42fa34fff5SGao feng struct ctl_table *table) 43d62f9ed4SPatrick McHardy { 44d62f9ed4SPatrick McHardy if (*header == NULL) { 452c352f44SGao feng *header = register_net_sysctl(net, path, table); 46d62f9ed4SPatrick McHardy if (*header == NULL) 47d62f9ed4SPatrick McHardy return -ENOMEM; 48d62f9ed4SPatrick McHardy } 492c352f44SGao feng 50d62f9ed4SPatrick McHardy return 0; 51d62f9ed4SPatrick McHardy } 52d62f9ed4SPatrick McHardy 53d62f9ed4SPatrick McHardy static void 54d62f9ed4SPatrick McHardy nf_ct_unregister_sysctl(struct ctl_table_header **header, 552c352f44SGao feng struct ctl_table **table, 56fa34fff5SGao feng unsigned int users) 57d62f9ed4SPatrick McHardy { 58fa34fff5SGao feng if (users > 0) 59d62f9ed4SPatrick McHardy return; 60b3fd3ffeSPavel Emelyanov 615dd3df10SEric W. Biederman unregister_net_sysctl_table(*header); 622c352f44SGao feng kfree(*table); 63d62f9ed4SPatrick McHardy *header = NULL; 642c352f44SGao feng *table = NULL; 65d62f9ed4SPatrick McHardy } 66d62f9ed4SPatrick McHardy #endif 67d62f9ed4SPatrick McHardy 68605dcad6SMartin Josefsson struct nf_conntrack_l4proto * 69605dcad6SMartin Josefsson __nf_ct_l4proto_find(u_int16_t l3proto, u_int8_t l4proto) 708f03dea5SMartin Josefsson { 718f03dea5SMartin Josefsson if (unlikely(l3proto >= AF_MAX || nf_ct_protos[l3proto] == NULL)) 72605dcad6SMartin Josefsson return &nf_conntrack_l4proto_generic; 738f03dea5SMartin Josefsson 74923f4902SPatrick McHardy return rcu_dereference(nf_ct_protos[l3proto][l4proto]); 758f03dea5SMartin Josefsson } 7613b18339SPatrick McHardy EXPORT_SYMBOL_GPL(__nf_ct_l4proto_find); 778f03dea5SMartin Josefsson 788f03dea5SMartin Josefsson /* this is guaranteed to always return a valid protocol helper, since 798f03dea5SMartin Josefsson * it falls back to generic_protocol */ 808f03dea5SMartin Josefsson struct nf_conntrack_l3proto * 818f03dea5SMartin Josefsson nf_ct_l3proto_find_get(u_int16_t l3proto) 828f03dea5SMartin Josefsson { 838f03dea5SMartin Josefsson struct nf_conntrack_l3proto *p; 848f03dea5SMartin Josefsson 85923f4902SPatrick McHardy rcu_read_lock(); 868f03dea5SMartin Josefsson p = __nf_ct_l3proto_find(l3proto); 878f03dea5SMartin Josefsson if (!try_module_get(p->me)) 88605dcad6SMartin Josefsson p = &nf_conntrack_l3proto_generic; 89923f4902SPatrick McHardy rcu_read_unlock(); 908f03dea5SMartin Josefsson 918f03dea5SMartin Josefsson return p; 928f03dea5SMartin Josefsson } 9313b18339SPatrick McHardy EXPORT_SYMBOL_GPL(nf_ct_l3proto_find_get); 948f03dea5SMartin Josefsson 958f03dea5SMartin Josefsson void nf_ct_l3proto_put(struct nf_conntrack_l3proto *p) 968f03dea5SMartin Josefsson { 978f03dea5SMartin Josefsson module_put(p->me); 988f03dea5SMartin Josefsson } 9913b18339SPatrick McHardy EXPORT_SYMBOL_GPL(nf_ct_l3proto_put); 1008f03dea5SMartin Josefsson 1018f03dea5SMartin Josefsson int 1028f03dea5SMartin Josefsson nf_ct_l3proto_try_module_get(unsigned short l3proto) 1038f03dea5SMartin Josefsson { 1048f03dea5SMartin Josefsson int ret; 1058f03dea5SMartin Josefsson struct nf_conntrack_l3proto *p; 1068f03dea5SMartin Josefsson 1078f03dea5SMartin Josefsson retry: p = nf_ct_l3proto_find_get(l3proto); 108605dcad6SMartin Josefsson if (p == &nf_conntrack_l3proto_generic) { 1098f03dea5SMartin Josefsson ret = request_module("nf_conntrack-%d", l3proto); 1108f03dea5SMartin Josefsson if (!ret) 1118f03dea5SMartin Josefsson goto retry; 1128f03dea5SMartin Josefsson 1138f03dea5SMartin Josefsson return -EPROTOTYPE; 1148f03dea5SMartin Josefsson } 1158f03dea5SMartin Josefsson 1168f03dea5SMartin Josefsson return 0; 1178f03dea5SMartin Josefsson } 11813b18339SPatrick McHardy EXPORT_SYMBOL_GPL(nf_ct_l3proto_try_module_get); 1198f03dea5SMartin Josefsson 1208f03dea5SMartin Josefsson void nf_ct_l3proto_module_put(unsigned short l3proto) 1218f03dea5SMartin Josefsson { 1228f03dea5SMartin Josefsson struct nf_conntrack_l3proto *p; 1238f03dea5SMartin Josefsson 1243b254c54SPatrick McHardy /* rcu_read_lock not necessary since the caller holds a reference, but 1253b254c54SPatrick McHardy * taken anyways to avoid lockdep warnings in __nf_ct_l3proto_find() 1263b254c54SPatrick McHardy */ 1273b254c54SPatrick McHardy rcu_read_lock(); 1288f03dea5SMartin Josefsson p = __nf_ct_l3proto_find(l3proto); 1298f03dea5SMartin Josefsson module_put(p->me); 1303b254c54SPatrick McHardy rcu_read_unlock(); 1318f03dea5SMartin Josefsson } 13213b18339SPatrick McHardy EXPORT_SYMBOL_GPL(nf_ct_l3proto_module_put); 1338f03dea5SMartin Josefsson 134c1ebd7dfSPablo Neira Ayuso struct nf_conntrack_l4proto * 135c1ebd7dfSPablo Neira Ayuso nf_ct_l4proto_find_get(u_int16_t l3num, u_int8_t l4num) 136c1ebd7dfSPablo Neira Ayuso { 137c1ebd7dfSPablo Neira Ayuso struct nf_conntrack_l4proto *p; 138c1ebd7dfSPablo Neira Ayuso 139c1ebd7dfSPablo Neira Ayuso rcu_read_lock(); 140c1ebd7dfSPablo Neira Ayuso p = __nf_ct_l4proto_find(l3num, l4num); 141c1ebd7dfSPablo Neira Ayuso if (!try_module_get(p->me)) 142c1ebd7dfSPablo Neira Ayuso p = &nf_conntrack_l4proto_generic; 143c1ebd7dfSPablo Neira Ayuso rcu_read_unlock(); 144c1ebd7dfSPablo Neira Ayuso 145c1ebd7dfSPablo Neira Ayuso return p; 146c1ebd7dfSPablo Neira Ayuso } 147c1ebd7dfSPablo Neira Ayuso EXPORT_SYMBOL_GPL(nf_ct_l4proto_find_get); 148c1ebd7dfSPablo Neira Ayuso 149c1ebd7dfSPablo Neira Ayuso void nf_ct_l4proto_put(struct nf_conntrack_l4proto *p) 150c1ebd7dfSPablo Neira Ayuso { 151c1ebd7dfSPablo Neira Ayuso module_put(p->me); 152c1ebd7dfSPablo Neira Ayuso } 153c1ebd7dfSPablo Neira Ayuso EXPORT_SYMBOL_GPL(nf_ct_l4proto_put); 154c1ebd7dfSPablo Neira Ayuso 1558f03dea5SMartin Josefsson static int kill_l3proto(struct nf_conn *i, void *data) 1568f03dea5SMartin Josefsson { 1575e8fbe2aSPatrick McHardy return nf_ct_l3num(i) == ((struct nf_conntrack_l3proto *)data)->l3proto; 1588f03dea5SMartin Josefsson } 1598f03dea5SMartin Josefsson 160605dcad6SMartin Josefsson static int kill_l4proto(struct nf_conn *i, void *data) 1618f03dea5SMartin Josefsson { 162605dcad6SMartin Josefsson struct nf_conntrack_l4proto *l4proto; 163605dcad6SMartin Josefsson l4proto = (struct nf_conntrack_l4proto *)data; 1645e8fbe2aSPatrick McHardy return nf_ct_protonum(i) == l4proto->l4proto && 1655e8fbe2aSPatrick McHardy nf_ct_l3num(i) == l4proto->l3proto; 1668f03dea5SMartin Josefsson } 1678f03dea5SMartin Josefsson 168524a53e5SGao feng static struct nf_ip_net *nf_ct_l3proto_net(struct net *net, 169524a53e5SGao feng struct nf_conntrack_l3proto *l3proto) 170524a53e5SGao feng { 171524a53e5SGao feng if (l3proto->l3proto == PF_INET) 172524a53e5SGao feng return &net->ct.nf_ct_proto; 173524a53e5SGao feng else 174524a53e5SGao feng return NULL; 175524a53e5SGao feng } 176524a53e5SGao feng 177524a53e5SGao feng static int nf_ct_l3proto_register_sysctl(struct net *net, 178524a53e5SGao feng struct nf_conntrack_l3proto *l3proto) 179d62f9ed4SPatrick McHardy { 180d62f9ed4SPatrick McHardy int err = 0; 181524a53e5SGao feng struct nf_ip_net *in = nf_ct_l3proto_net(net, l3proto); 182524a53e5SGao feng /* nf_conntrack_l3proto_ipv6 doesn't support sysctl */ 183524a53e5SGao feng if (in == NULL) 184524a53e5SGao feng return 0; 185d62f9ed4SPatrick McHardy 186524a53e5SGao feng #if defined(CONFIG_SYSCTL) && defined(CONFIG_NF_CONNTRACK_PROC_COMPAT) 187524a53e5SGao feng if (in->ctl_table != NULL) { 188524a53e5SGao feng err = nf_ct_register_sysctl(net, 189524a53e5SGao feng &in->ctl_table_header, 190d62f9ed4SPatrick McHardy l3proto->ctl_table_path, 191fa34fff5SGao feng in->ctl_table); 192524a53e5SGao feng if (err < 0) { 193524a53e5SGao feng kfree(in->ctl_table); 194524a53e5SGao feng in->ctl_table = NULL; 195524a53e5SGao feng } 196d62f9ed4SPatrick McHardy } 197d62f9ed4SPatrick McHardy #endif 198d62f9ed4SPatrick McHardy return err; 199d62f9ed4SPatrick McHardy } 200d62f9ed4SPatrick McHardy 201524a53e5SGao feng static void nf_ct_l3proto_unregister_sysctl(struct net *net, 202524a53e5SGao feng struct nf_conntrack_l3proto *l3proto) 203d62f9ed4SPatrick McHardy { 204524a53e5SGao feng struct nf_ip_net *in = nf_ct_l3proto_net(net, l3proto); 205524a53e5SGao feng 206524a53e5SGao feng if (in == NULL) 207524a53e5SGao feng return; 208524a53e5SGao feng #if defined(CONFIG_SYSCTL) && defined(CONFIG_NF_CONNTRACK_PROC_COMPAT) 209524a53e5SGao feng if (in->ctl_table_header != NULL) 210524a53e5SGao feng nf_ct_unregister_sysctl(&in->ctl_table_header, 211524a53e5SGao feng &in->ctl_table, 212fa34fff5SGao feng 0); 213d62f9ed4SPatrick McHardy #endif 214d62f9ed4SPatrick McHardy } 215d62f9ed4SPatrick McHardy 2166330750dSGao feng int nf_ct_l3proto_register(struct nf_conntrack_l3proto *proto) 2178f03dea5SMartin Josefsson { 2188f03dea5SMartin Josefsson int ret = 0; 2190e60ebe0SEric Dumazet struct nf_conntrack_l3proto *old; 2208f03dea5SMartin Josefsson 2210661cca9SPatrick McHardy if (proto->l3proto >= AF_MAX) 2220661cca9SPatrick McHardy return -EBUSY; 2238f03dea5SMartin Josefsson 224d0dba725SHolger Eitzenberger if (proto->tuple_to_nlattr && !proto->nlattr_tuple_size) 225d0dba725SHolger Eitzenberger return -EINVAL; 226d0dba725SHolger Eitzenberger 227b19caa0cSPatrick McHardy mutex_lock(&nf_ct_proto_mutex); 2280e60ebe0SEric Dumazet old = rcu_dereference_protected(nf_ct_l3protos[proto->l3proto], 2290e60ebe0SEric Dumazet lockdep_is_held(&nf_ct_proto_mutex)); 2300e60ebe0SEric Dumazet if (old != &nf_conntrack_l3proto_generic) { 231ae5718fbSMartin Josefsson ret = -EBUSY; 232ae5718fbSMartin Josefsson goto out_unlock; 233ae5718fbSMartin Josefsson } 234d62f9ed4SPatrick McHardy 235d0dba725SHolger Eitzenberger if (proto->nlattr_tuple_size) 236d0dba725SHolger Eitzenberger proto->nla_size = 3 * proto->nlattr_tuple_size(); 237d0dba725SHolger Eitzenberger 2380661cca9SPatrick McHardy rcu_assign_pointer(nf_ct_l3protos[proto->l3proto], proto); 239ae5718fbSMartin Josefsson 240ae5718fbSMartin Josefsson out_unlock: 241b19caa0cSPatrick McHardy mutex_unlock(&nf_ct_proto_mutex); 2428f03dea5SMartin Josefsson return ret; 243524a53e5SGao feng 244524a53e5SGao feng } 2456330750dSGao feng EXPORT_SYMBOL_GPL(nf_ct_l3proto_register); 246524a53e5SGao feng 2476330750dSGao feng int nf_ct_l3proto_pernet_register(struct net *net, 248524a53e5SGao feng struct nf_conntrack_l3proto *proto) 249524a53e5SGao feng { 250524a53e5SGao feng int ret = 0; 251524a53e5SGao feng 252524a53e5SGao feng if (proto->init_net) { 253524a53e5SGao feng ret = proto->init_net(net); 254524a53e5SGao feng if (ret < 0) 255524a53e5SGao feng return ret; 256524a53e5SGao feng } 257fa0f61f0SGao feng 2586330750dSGao feng return nf_ct_l3proto_register_sysctl(net, proto); 259fa0f61f0SGao feng } 2606330750dSGao feng EXPORT_SYMBOL_GPL(nf_ct_l3proto_pernet_register); 261fa0f61f0SGao feng 2626330750dSGao feng void nf_ct_l3proto_unregister(struct nf_conntrack_l3proto *proto) 2638f03dea5SMartin Josefsson { 264fe3eb20cSPatrick McHardy BUG_ON(proto->l3proto >= AF_MAX); 265ae5718fbSMartin Josefsson 266b19caa0cSPatrick McHardy mutex_lock(&nf_ct_proto_mutex); 2670e60ebe0SEric Dumazet BUG_ON(rcu_dereference_protected(nf_ct_l3protos[proto->l3proto], 2680e60ebe0SEric Dumazet lockdep_is_held(&nf_ct_proto_mutex) 2690e60ebe0SEric Dumazet ) != proto); 270923f4902SPatrick McHardy rcu_assign_pointer(nf_ct_l3protos[proto->l3proto], 271923f4902SPatrick McHardy &nf_conntrack_l3proto_generic); 2720661cca9SPatrick McHardy mutex_unlock(&nf_ct_proto_mutex); 2730661cca9SPatrick McHardy 2740661cca9SPatrick McHardy synchronize_rcu(); 275524a53e5SGao feng } 2766330750dSGao feng EXPORT_SYMBOL_GPL(nf_ct_l3proto_unregister); 277524a53e5SGao feng 2786330750dSGao feng void nf_ct_l3proto_pernet_unregister(struct net *net, 279524a53e5SGao feng struct nf_conntrack_l3proto *proto) 280524a53e5SGao feng { 281524a53e5SGao feng nf_ct_l3proto_unregister_sysctl(net, proto); 282d62f9ed4SPatrick McHardy 2838f03dea5SMartin Josefsson /* Remove all contrack entries for this protocol */ 284678d6675SAlexey Dobriyan nf_ct_iterate_cleanup(net, kill_l3proto, proto); 2858f03dea5SMartin Josefsson } 2866330750dSGao feng EXPORT_SYMBOL_GPL(nf_ct_l3proto_pernet_unregister); 2878f03dea5SMartin Josefsson 2882c352f44SGao feng static struct nf_proto_net *nf_ct_l4proto_net(struct net *net, 2892c352f44SGao feng struct nf_conntrack_l4proto *l4proto) 2902c352f44SGao feng { 29108911475SPablo Neira Ayuso if (l4proto->get_net_proto) { 29208911475SPablo Neira Ayuso /* statically built-in protocols use static per-net */ 29308911475SPablo Neira Ayuso return l4proto->get_net_proto(net); 29408911475SPablo Neira Ayuso } else if (l4proto->net_id) { 29508911475SPablo Neira Ayuso /* ... and loadable protocols use dynamic per-net */ 2962c352f44SGao feng return net_generic(net, *l4proto->net_id); 2972c352f44SGao feng } 29815f585bdSGao feng return NULL; 29915f585bdSGao feng } 3002c352f44SGao feng 3012c352f44SGao feng static 3022c352f44SGao feng int nf_ct_l4proto_register_sysctl(struct net *net, 303fa34fff5SGao feng struct nf_proto_net *pn, 3042c352f44SGao feng struct nf_conntrack_l4proto *l4proto) 305d62f9ed4SPatrick McHardy { 306d62f9ed4SPatrick McHardy int err = 0; 307d62f9ed4SPatrick McHardy 308d62f9ed4SPatrick McHardy #ifdef CONFIG_SYSCTL 3092c352f44SGao feng if (pn->ctl_table != NULL) { 3102c352f44SGao feng err = nf_ct_register_sysctl(net, 3112c352f44SGao feng &pn->ctl_table_header, 312f99e8f71SEric W. Biederman "net/netfilter", 313fa34fff5SGao feng pn->ctl_table); 3142c352f44SGao feng if (err < 0) { 3152c352f44SGao feng if (!pn->users) { 3162c352f44SGao feng kfree(pn->ctl_table); 3172c352f44SGao feng pn->ctl_table = NULL; 3182c352f44SGao feng } 319d62f9ed4SPatrick McHardy } 3202c352f44SGao feng } 321a999e683SPatrick McHardy #ifdef CONFIG_NF_CONNTRACK_PROC_COMPAT 3222c352f44SGao feng if (l4proto->l3proto != AF_INET6 && pn->ctl_compat_table != NULL) { 32312c26df3SGao feng if (err < 0) { 32412c26df3SGao feng nf_ct_kfree_compat_sysctl_table(pn); 32512c26df3SGao feng goto out; 32612c26df3SGao feng } 3272c352f44SGao feng err = nf_ct_register_sysctl(net, 3282c352f44SGao feng &pn->ctl_compat_header, 329f99e8f71SEric W. Biederman "net/ipv4/netfilter", 330fa34fff5SGao feng pn->ctl_compat_table); 331a999e683SPatrick McHardy if (err == 0) 332a999e683SPatrick McHardy goto out; 3332c352f44SGao feng 334f28997e2SGao feng nf_ct_kfree_compat_sysctl_table(pn); 3352c352f44SGao feng nf_ct_unregister_sysctl(&pn->ctl_table_header, 3362c352f44SGao feng &pn->ctl_table, 337fa34fff5SGao feng pn->users); 338a999e683SPatrick McHardy } 339a999e683SPatrick McHardy out: 34012c26df3SGao feng #endif /* CONFIG_NF_CONNTRACK_PROC_COMPAT */ 341933a41e7SPatrick McHardy #endif /* CONFIG_SYSCTL */ 342d62f9ed4SPatrick McHardy return err; 343d62f9ed4SPatrick McHardy } 344d62f9ed4SPatrick McHardy 3452c352f44SGao feng static 3462c352f44SGao feng void nf_ct_l4proto_unregister_sysctl(struct net *net, 347fa34fff5SGao feng struct nf_proto_net *pn, 3482c352f44SGao feng struct nf_conntrack_l4proto *l4proto) 349d62f9ed4SPatrick McHardy { 350d62f9ed4SPatrick McHardy #ifdef CONFIG_SYSCTL 3512c352f44SGao feng if (pn->ctl_table_header != NULL) 3522c352f44SGao feng nf_ct_unregister_sysctl(&pn->ctl_table_header, 3532c352f44SGao feng &pn->ctl_table, 354fa34fff5SGao feng pn->users); 3552c352f44SGao feng 356a999e683SPatrick McHardy #ifdef CONFIG_NF_CONNTRACK_PROC_COMPAT 3572c352f44SGao feng if (l4proto->l3proto != AF_INET6 && pn->ctl_compat_header != NULL) 3582c352f44SGao feng nf_ct_unregister_sysctl(&pn->ctl_compat_header, 3592c352f44SGao feng &pn->ctl_compat_table, 360fa34fff5SGao feng 0); 361a999e683SPatrick McHardy #endif /* CONFIG_NF_CONNTRACK_PROC_COMPAT */ 362933a41e7SPatrick McHardy #endif /* CONFIG_SYSCTL */ 363d62f9ed4SPatrick McHardy } 364d62f9ed4SPatrick McHardy 3658f03dea5SMartin Josefsson /* FIXME: Allow NULL functions and sub in pointers to generic for 3668f03dea5SMartin Josefsson them. --RR */ 367c296bb4dSGao feng int nf_ct_l4proto_register(struct nf_conntrack_l4proto *l4proto) 3688f03dea5SMartin Josefsson { 3698f03dea5SMartin Josefsson int ret = 0; 3708f03dea5SMartin Josefsson 3710661cca9SPatrick McHardy if (l4proto->l3proto >= PF_MAX) 3720661cca9SPatrick McHardy return -EBUSY; 373ae5718fbSMartin Josefsson 374d0dba725SHolger Eitzenberger if ((l4proto->to_nlattr && !l4proto->nlattr_size) 375d0dba725SHolger Eitzenberger || (l4proto->tuple_to_nlattr && !l4proto->nlattr_tuple_size)) 376d0dba725SHolger Eitzenberger return -EINVAL; 377d0dba725SHolger Eitzenberger 378b19caa0cSPatrick McHardy mutex_lock(&nf_ct_proto_mutex); 379c6a1e615SPatrick McHardy if (!nf_ct_protos[l4proto->l3proto]) { 3808f03dea5SMartin Josefsson /* l3proto may be loaded latter. */ 381c5d277d2SEric Dumazet struct nf_conntrack_l4proto __rcu **proto_array; 3828f03dea5SMartin Josefsson int i; 3838f03dea5SMartin Josefsson 384c6a1e615SPatrick McHardy proto_array = kmalloc(MAX_NF_CT_PROTO * 385605dcad6SMartin Josefsson sizeof(struct nf_conntrack_l4proto *), 3868f03dea5SMartin Josefsson GFP_KERNEL); 3878f03dea5SMartin Josefsson if (proto_array == NULL) { 3888f03dea5SMartin Josefsson ret = -ENOMEM; 389b19caa0cSPatrick McHardy goto out_unlock; 3908f03dea5SMartin Josefsson } 391c6a1e615SPatrick McHardy 3928f03dea5SMartin Josefsson for (i = 0; i < MAX_NF_CT_PROTO; i++) 393c5d277d2SEric Dumazet RCU_INIT_POINTER(proto_array[i], &nf_conntrack_l4proto_generic); 394d817d29dSEric Dumazet 395d817d29dSEric Dumazet /* Before making proto_array visible to lockless readers, 396d817d29dSEric Dumazet * we must make sure its content is committed to memory. 397d817d29dSEric Dumazet */ 398d817d29dSEric Dumazet smp_wmb(); 399d817d29dSEric Dumazet 400605dcad6SMartin Josefsson nf_ct_protos[l4proto->l3proto] = proto_array; 4010e60ebe0SEric Dumazet } else if (rcu_dereference_protected( 4020e60ebe0SEric Dumazet nf_ct_protos[l4proto->l3proto][l4proto->l4proto], 4030e60ebe0SEric Dumazet lockdep_is_held(&nf_ct_proto_mutex) 4040e60ebe0SEric Dumazet ) != &nf_conntrack_l4proto_generic) { 405c6a1e615SPatrick McHardy ret = -EBUSY; 406c6a1e615SPatrick McHardy goto out_unlock; 4078f03dea5SMartin Josefsson } 4088f03dea5SMartin Josefsson 409d0dba725SHolger Eitzenberger l4proto->nla_size = 0; 410d0dba725SHolger Eitzenberger if (l4proto->nlattr_size) 411d0dba725SHolger Eitzenberger l4proto->nla_size += l4proto->nlattr_size(); 412d0dba725SHolger Eitzenberger if (l4proto->nlattr_tuple_size) 413d0dba725SHolger Eitzenberger l4proto->nla_size += 3 * l4proto->nlattr_tuple_size(); 414d0dba725SHolger Eitzenberger 415c6a1e615SPatrick McHardy rcu_assign_pointer(nf_ct_protos[l4proto->l3proto][l4proto->l4proto], 416c6a1e615SPatrick McHardy l4proto); 4178f03dea5SMartin Josefsson out_unlock: 418b19caa0cSPatrick McHardy mutex_unlock(&nf_ct_proto_mutex); 4198f03dea5SMartin Josefsson return ret; 4208f03dea5SMartin Josefsson } 421c296bb4dSGao feng EXPORT_SYMBOL_GPL(nf_ct_l4proto_register); 4222c352f44SGao feng 423c296bb4dSGao feng int nf_ct_l4proto_pernet_register(struct net *net, 4242c352f44SGao feng struct nf_conntrack_l4proto *l4proto) 4252c352f44SGao feng { 4262c352f44SGao feng int ret = 0; 427fa34fff5SGao feng struct nf_proto_net *pn = NULL; 4282c352f44SGao feng 429fa0f61f0SGao feng if (l4proto->init_net) { 430f1caad27SGao feng ret = l4proto->init_net(net, l4proto->l3proto); 431fa0f61f0SGao feng if (ret < 0) 432fa34fff5SGao feng goto out; 433fa0f61f0SGao feng } 4342c352f44SGao feng 435fa34fff5SGao feng pn = nf_ct_l4proto_net(net, l4proto); 436fa34fff5SGao feng if (pn == NULL) 437fa34fff5SGao feng goto out; 438fa34fff5SGao feng 439fa34fff5SGao feng ret = nf_ct_l4proto_register_sysctl(net, pn, l4proto); 4402c352f44SGao feng if (ret < 0) 441fa34fff5SGao feng goto out; 4422c352f44SGao feng 443fa34fff5SGao feng pn->users++; 444fa34fff5SGao feng out: 445fa0f61f0SGao feng return ret; 4462c352f44SGao feng } 447c296bb4dSGao feng EXPORT_SYMBOL_GPL(nf_ct_l4proto_pernet_register); 4488f03dea5SMartin Josefsson 449c296bb4dSGao feng void nf_ct_l4proto_unregister(struct nf_conntrack_l4proto *l4proto) 4508f03dea5SMartin Josefsson { 451fe3eb20cSPatrick McHardy BUG_ON(l4proto->l3proto >= PF_MAX); 452ae5718fbSMartin Josefsson 453b19caa0cSPatrick McHardy mutex_lock(&nf_ct_proto_mutex); 4540e60ebe0SEric Dumazet BUG_ON(rcu_dereference_protected( 4550e60ebe0SEric Dumazet nf_ct_protos[l4proto->l3proto][l4proto->l4proto], 4560e60ebe0SEric Dumazet lockdep_is_held(&nf_ct_proto_mutex) 4570e60ebe0SEric Dumazet ) != l4proto); 458923f4902SPatrick McHardy rcu_assign_pointer(nf_ct_protos[l4proto->l3proto][l4proto->l4proto], 459923f4902SPatrick McHardy &nf_conntrack_l4proto_generic); 4600661cca9SPatrick McHardy mutex_unlock(&nf_ct_proto_mutex); 4610661cca9SPatrick McHardy 4620661cca9SPatrick McHardy synchronize_rcu(); 4632c352f44SGao feng } 464c296bb4dSGao feng EXPORT_SYMBOL_GPL(nf_ct_l4proto_unregister); 465d62f9ed4SPatrick McHardy 466c296bb4dSGao feng void nf_ct_l4proto_pernet_unregister(struct net *net, 4672c352f44SGao feng struct nf_conntrack_l4proto *l4proto) 4682c352f44SGao feng { 469fa34fff5SGao feng struct nf_proto_net *pn = NULL; 470fa34fff5SGao feng 471fa34fff5SGao feng pn = nf_ct_l4proto_net(net, l4proto); 472fa34fff5SGao feng if (pn == NULL) 473fa34fff5SGao feng return; 474fa34fff5SGao feng 475fa34fff5SGao feng pn->users--; 476fa34fff5SGao feng nf_ct_l4proto_unregister_sysctl(net, pn, l4proto); 477fa34fff5SGao feng 4788f03dea5SMartin Josefsson /* Remove all contrack entries for this protocol */ 479678d6675SAlexey Dobriyan nf_ct_iterate_cleanup(net, kill_l4proto, l4proto); 4808f03dea5SMartin Josefsson } 481c296bb4dSGao feng EXPORT_SYMBOL_GPL(nf_ct_l4proto_pernet_unregister); 482ac5357ebSPatrick McHardy 48304d87001SGao feng int nf_conntrack_proto_pernet_init(struct net *net) 484ac5357ebSPatrick McHardy { 485ac5357ebSPatrick McHardy int err; 486fa34fff5SGao feng struct nf_proto_net *pn = nf_ct_l4proto_net(net, 487fa34fff5SGao feng &nf_conntrack_l4proto_generic); 488fa34fff5SGao feng 489f1caad27SGao feng err = nf_conntrack_l4proto_generic.init_net(net, 490f1caad27SGao feng nf_conntrack_l4proto_generic.l3proto); 49115f585bdSGao feng if (err < 0) 49215f585bdSGao feng return err; 49315f585bdSGao feng err = nf_ct_l4proto_register_sysctl(net, 494fa34fff5SGao feng pn, 49515f585bdSGao feng &nf_conntrack_l4proto_generic); 496ac5357ebSPatrick McHardy if (err < 0) 497ac5357ebSPatrick McHardy return err; 498ac5357ebSPatrick McHardy 499fa34fff5SGao feng pn->users++; 500ac5357ebSPatrick McHardy return 0; 501ac5357ebSPatrick McHardy } 502ac5357ebSPatrick McHardy 50304d87001SGao feng void nf_conntrack_proto_pernet_fini(struct net *net) 504ac5357ebSPatrick McHardy { 505fa34fff5SGao feng struct nf_proto_net *pn = nf_ct_l4proto_net(net, 506fa34fff5SGao feng &nf_conntrack_l4proto_generic); 507fa34fff5SGao feng 508fa34fff5SGao feng pn->users--; 50915f585bdSGao feng nf_ct_l4proto_unregister_sysctl(net, 510fa34fff5SGao feng pn, 51115f585bdSGao feng &nf_conntrack_l4proto_generic); 51204d87001SGao feng } 51304d87001SGao feng 51404d87001SGao feng int nf_conntrack_proto_init(void) 51504d87001SGao feng { 51604d87001SGao feng unsigned int i; 51704d87001SGao feng for (i = 0; i < AF_MAX; i++) 51804d87001SGao feng rcu_assign_pointer(nf_ct_l3protos[i], 51904d87001SGao feng &nf_conntrack_l3proto_generic); 52004d87001SGao feng return 0; 52104d87001SGao feng } 52204d87001SGao feng 52304d87001SGao feng void nf_conntrack_proto_fini(void) 52404d87001SGao feng { 52504d87001SGao feng unsigned int i; 526ac5357ebSPatrick McHardy /* free l3proto protocol tables */ 527ac5357ebSPatrick McHardy for (i = 0; i < PF_MAX; i++) 528ac5357ebSPatrick McHardy kfree(nf_ct_protos[i]); 529ac5357ebSPatrick McHardy } 530