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> 155a0e3ad6STejun Heo #include <linux/slab.h> 16d62f9ed4SPatrick McHardy #include <linux/mutex.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/notifier.h> 228f03dea5SMartin Josefsson #include <linux/kernel.h> 238f03dea5SMartin Josefsson #include <linux/netdevice.h> 24efb9a8c2SAlexey Dobriyan #include <linux/rtnetlink.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, 422c352f44SGao feng struct ctl_table *table, 432c352f44SGao feng unsigned int *users) 44d62f9ed4SPatrick McHardy { 45d62f9ed4SPatrick McHardy if (*header == NULL) { 462c352f44SGao feng *header = register_net_sysctl(net, path, table); 47d62f9ed4SPatrick McHardy if (*header == NULL) 48d62f9ed4SPatrick McHardy return -ENOMEM; 49d62f9ed4SPatrick McHardy } 50d62f9ed4SPatrick McHardy if (users != NULL) 51d62f9ed4SPatrick McHardy (*users)++; 522c352f44SGao feng 53d62f9ed4SPatrick McHardy return 0; 54d62f9ed4SPatrick McHardy } 55d62f9ed4SPatrick McHardy 56d62f9ed4SPatrick McHardy static void 57d62f9ed4SPatrick McHardy nf_ct_unregister_sysctl(struct ctl_table_header **header, 582c352f44SGao feng struct ctl_table **table, 592c352f44SGao feng unsigned int *users) 60d62f9ed4SPatrick McHardy { 61d62f9ed4SPatrick McHardy if (users != NULL && --*users > 0) 62d62f9ed4SPatrick McHardy return; 63b3fd3ffeSPavel Emelyanov 645dd3df10SEric W. Biederman unregister_net_sysctl_table(*header); 652c352f44SGao feng kfree(*table); 66d62f9ed4SPatrick McHardy *header = NULL; 672c352f44SGao feng *table = NULL; 68d62f9ed4SPatrick McHardy } 69d62f9ed4SPatrick McHardy #endif 70d62f9ed4SPatrick McHardy 71605dcad6SMartin Josefsson struct nf_conntrack_l4proto * 72605dcad6SMartin Josefsson __nf_ct_l4proto_find(u_int16_t l3proto, u_int8_t l4proto) 738f03dea5SMartin Josefsson { 748f03dea5SMartin Josefsson if (unlikely(l3proto >= AF_MAX || nf_ct_protos[l3proto] == NULL)) 75605dcad6SMartin Josefsson return &nf_conntrack_l4proto_generic; 768f03dea5SMartin Josefsson 77923f4902SPatrick McHardy return rcu_dereference(nf_ct_protos[l3proto][l4proto]); 788f03dea5SMartin Josefsson } 7913b18339SPatrick McHardy EXPORT_SYMBOL_GPL(__nf_ct_l4proto_find); 808f03dea5SMartin Josefsson 818f03dea5SMartin Josefsson /* this is guaranteed to always return a valid protocol helper, since 828f03dea5SMartin Josefsson * it falls back to generic_protocol */ 838f03dea5SMartin Josefsson struct nf_conntrack_l3proto * 848f03dea5SMartin Josefsson nf_ct_l3proto_find_get(u_int16_t l3proto) 858f03dea5SMartin Josefsson { 868f03dea5SMartin Josefsson struct nf_conntrack_l3proto *p; 878f03dea5SMartin Josefsson 88923f4902SPatrick McHardy rcu_read_lock(); 898f03dea5SMartin Josefsson p = __nf_ct_l3proto_find(l3proto); 908f03dea5SMartin Josefsson if (!try_module_get(p->me)) 91605dcad6SMartin Josefsson p = &nf_conntrack_l3proto_generic; 92923f4902SPatrick McHardy rcu_read_unlock(); 938f03dea5SMartin Josefsson 948f03dea5SMartin Josefsson return p; 958f03dea5SMartin Josefsson } 9613b18339SPatrick McHardy EXPORT_SYMBOL_GPL(nf_ct_l3proto_find_get); 978f03dea5SMartin Josefsson 988f03dea5SMartin Josefsson void nf_ct_l3proto_put(struct nf_conntrack_l3proto *p) 998f03dea5SMartin Josefsson { 1008f03dea5SMartin Josefsson module_put(p->me); 1018f03dea5SMartin Josefsson } 10213b18339SPatrick McHardy EXPORT_SYMBOL_GPL(nf_ct_l3proto_put); 1038f03dea5SMartin Josefsson 1048f03dea5SMartin Josefsson int 1058f03dea5SMartin Josefsson nf_ct_l3proto_try_module_get(unsigned short l3proto) 1068f03dea5SMartin Josefsson { 1078f03dea5SMartin Josefsson int ret; 1088f03dea5SMartin Josefsson struct nf_conntrack_l3proto *p; 1098f03dea5SMartin Josefsson 1108f03dea5SMartin Josefsson retry: p = nf_ct_l3proto_find_get(l3proto); 111605dcad6SMartin Josefsson if (p == &nf_conntrack_l3proto_generic) { 1128f03dea5SMartin Josefsson ret = request_module("nf_conntrack-%d", l3proto); 1138f03dea5SMartin Josefsson if (!ret) 1148f03dea5SMartin Josefsson goto retry; 1158f03dea5SMartin Josefsson 1168f03dea5SMartin Josefsson return -EPROTOTYPE; 1178f03dea5SMartin Josefsson } 1188f03dea5SMartin Josefsson 1198f03dea5SMartin Josefsson return 0; 1208f03dea5SMartin Josefsson } 12113b18339SPatrick McHardy EXPORT_SYMBOL_GPL(nf_ct_l3proto_try_module_get); 1228f03dea5SMartin Josefsson 1238f03dea5SMartin Josefsson void nf_ct_l3proto_module_put(unsigned short l3proto) 1248f03dea5SMartin Josefsson { 1258f03dea5SMartin Josefsson struct nf_conntrack_l3proto *p; 1268f03dea5SMartin Josefsson 1273b254c54SPatrick McHardy /* rcu_read_lock not necessary since the caller holds a reference, but 1283b254c54SPatrick McHardy * taken anyways to avoid lockdep warnings in __nf_ct_l3proto_find() 1293b254c54SPatrick McHardy */ 1303b254c54SPatrick McHardy rcu_read_lock(); 1318f03dea5SMartin Josefsson p = __nf_ct_l3proto_find(l3proto); 1328f03dea5SMartin Josefsson module_put(p->me); 1333b254c54SPatrick McHardy rcu_read_unlock(); 1348f03dea5SMartin Josefsson } 13513b18339SPatrick McHardy EXPORT_SYMBOL_GPL(nf_ct_l3proto_module_put); 1368f03dea5SMartin Josefsson 137c1ebd7dfSPablo Neira Ayuso struct nf_conntrack_l4proto * 138c1ebd7dfSPablo Neira Ayuso nf_ct_l4proto_find_get(u_int16_t l3num, u_int8_t l4num) 139c1ebd7dfSPablo Neira Ayuso { 140c1ebd7dfSPablo Neira Ayuso struct nf_conntrack_l4proto *p; 141c1ebd7dfSPablo Neira Ayuso 142c1ebd7dfSPablo Neira Ayuso rcu_read_lock(); 143c1ebd7dfSPablo Neira Ayuso p = __nf_ct_l4proto_find(l3num, l4num); 144c1ebd7dfSPablo Neira Ayuso if (!try_module_get(p->me)) 145c1ebd7dfSPablo Neira Ayuso p = &nf_conntrack_l4proto_generic; 146c1ebd7dfSPablo Neira Ayuso rcu_read_unlock(); 147c1ebd7dfSPablo Neira Ayuso 148c1ebd7dfSPablo Neira Ayuso return p; 149c1ebd7dfSPablo Neira Ayuso } 150c1ebd7dfSPablo Neira Ayuso EXPORT_SYMBOL_GPL(nf_ct_l4proto_find_get); 151c1ebd7dfSPablo Neira Ayuso 152c1ebd7dfSPablo Neira Ayuso void nf_ct_l4proto_put(struct nf_conntrack_l4proto *p) 153c1ebd7dfSPablo Neira Ayuso { 154c1ebd7dfSPablo Neira Ayuso module_put(p->me); 155c1ebd7dfSPablo Neira Ayuso } 156c1ebd7dfSPablo Neira Ayuso EXPORT_SYMBOL_GPL(nf_ct_l4proto_put); 157c1ebd7dfSPablo Neira Ayuso 1588f03dea5SMartin Josefsson static int kill_l3proto(struct nf_conn *i, void *data) 1598f03dea5SMartin Josefsson { 1605e8fbe2aSPatrick McHardy return nf_ct_l3num(i) == ((struct nf_conntrack_l3proto *)data)->l3proto; 1618f03dea5SMartin Josefsson } 1628f03dea5SMartin Josefsson 163605dcad6SMartin Josefsson static int kill_l4proto(struct nf_conn *i, void *data) 1648f03dea5SMartin Josefsson { 165605dcad6SMartin Josefsson struct nf_conntrack_l4proto *l4proto; 166605dcad6SMartin Josefsson l4proto = (struct nf_conntrack_l4proto *)data; 1675e8fbe2aSPatrick McHardy return nf_ct_protonum(i) == l4proto->l4proto && 1685e8fbe2aSPatrick McHardy nf_ct_l3num(i) == l4proto->l3proto; 1698f03dea5SMartin Josefsson } 1708f03dea5SMartin Josefsson 171d62f9ed4SPatrick McHardy static int nf_ct_l3proto_register_sysctl(struct nf_conntrack_l3proto *l3proto) 172d62f9ed4SPatrick McHardy { 173d62f9ed4SPatrick McHardy int err = 0; 174d62f9ed4SPatrick McHardy 175d62f9ed4SPatrick McHardy #ifdef CONFIG_SYSCTL 176d62f9ed4SPatrick McHardy if (l3proto->ctl_table != NULL) { 1772c352f44SGao feng err = nf_ct_register_sysctl(&init_net, 1782c352f44SGao feng &l3proto->ctl_table_header, 179d62f9ed4SPatrick McHardy l3proto->ctl_table_path, 180d62f9ed4SPatrick McHardy l3proto->ctl_table, NULL); 181d62f9ed4SPatrick McHardy } 182d62f9ed4SPatrick McHardy #endif 183d62f9ed4SPatrick McHardy return err; 184d62f9ed4SPatrick McHardy } 185d62f9ed4SPatrick McHardy 186d62f9ed4SPatrick McHardy static void nf_ct_l3proto_unregister_sysctl(struct nf_conntrack_l3proto *l3proto) 187d62f9ed4SPatrick McHardy { 188d62f9ed4SPatrick McHardy #ifdef CONFIG_SYSCTL 189d62f9ed4SPatrick McHardy if (l3proto->ctl_table_header != NULL) 190d62f9ed4SPatrick McHardy nf_ct_unregister_sysctl(&l3proto->ctl_table_header, 1912c352f44SGao feng &l3proto->ctl_table, NULL); 192d62f9ed4SPatrick McHardy #endif 193d62f9ed4SPatrick McHardy } 194d62f9ed4SPatrick McHardy 1958f03dea5SMartin Josefsson int nf_conntrack_l3proto_register(struct nf_conntrack_l3proto *proto) 1968f03dea5SMartin Josefsson { 1978f03dea5SMartin Josefsson int ret = 0; 1980e60ebe0SEric Dumazet struct nf_conntrack_l3proto *old; 1998f03dea5SMartin Josefsson 2000661cca9SPatrick McHardy if (proto->l3proto >= AF_MAX) 2010661cca9SPatrick McHardy return -EBUSY; 2028f03dea5SMartin Josefsson 203d0dba725SHolger Eitzenberger if (proto->tuple_to_nlattr && !proto->nlattr_tuple_size) 204d0dba725SHolger Eitzenberger return -EINVAL; 205d0dba725SHolger Eitzenberger 206b19caa0cSPatrick McHardy mutex_lock(&nf_ct_proto_mutex); 2070e60ebe0SEric Dumazet old = rcu_dereference_protected(nf_ct_l3protos[proto->l3proto], 2080e60ebe0SEric Dumazet lockdep_is_held(&nf_ct_proto_mutex)); 2090e60ebe0SEric Dumazet if (old != &nf_conntrack_l3proto_generic) { 210ae5718fbSMartin Josefsson ret = -EBUSY; 211ae5718fbSMartin Josefsson goto out_unlock; 212ae5718fbSMartin Josefsson } 213d62f9ed4SPatrick McHardy 214d62f9ed4SPatrick McHardy ret = nf_ct_l3proto_register_sysctl(proto); 215d62f9ed4SPatrick McHardy if (ret < 0) 2160661cca9SPatrick McHardy goto out_unlock; 2170661cca9SPatrick McHardy 218d0dba725SHolger Eitzenberger if (proto->nlattr_tuple_size) 219d0dba725SHolger Eitzenberger proto->nla_size = 3 * proto->nlattr_tuple_size(); 220d0dba725SHolger Eitzenberger 2210661cca9SPatrick McHardy rcu_assign_pointer(nf_ct_l3protos[proto->l3proto], proto); 222ae5718fbSMartin Josefsson 223ae5718fbSMartin Josefsson out_unlock: 224b19caa0cSPatrick McHardy mutex_unlock(&nf_ct_proto_mutex); 2258f03dea5SMartin Josefsson return ret; 2268f03dea5SMartin Josefsson } 22713b18339SPatrick McHardy EXPORT_SYMBOL_GPL(nf_conntrack_l3proto_register); 2288f03dea5SMartin Josefsson 229fe3eb20cSPatrick McHardy void nf_conntrack_l3proto_unregister(struct nf_conntrack_l3proto *proto) 2308f03dea5SMartin Josefsson { 231678d6675SAlexey Dobriyan struct net *net; 232678d6675SAlexey Dobriyan 233fe3eb20cSPatrick McHardy BUG_ON(proto->l3proto >= AF_MAX); 234ae5718fbSMartin Josefsson 235b19caa0cSPatrick McHardy mutex_lock(&nf_ct_proto_mutex); 2360e60ebe0SEric Dumazet BUG_ON(rcu_dereference_protected(nf_ct_l3protos[proto->l3proto], 2370e60ebe0SEric Dumazet lockdep_is_held(&nf_ct_proto_mutex) 2380e60ebe0SEric Dumazet ) != proto); 239923f4902SPatrick McHardy rcu_assign_pointer(nf_ct_l3protos[proto->l3proto], 240923f4902SPatrick McHardy &nf_conntrack_l3proto_generic); 241d62f9ed4SPatrick McHardy nf_ct_l3proto_unregister_sysctl(proto); 2420661cca9SPatrick McHardy mutex_unlock(&nf_ct_proto_mutex); 2430661cca9SPatrick McHardy 2440661cca9SPatrick McHardy synchronize_rcu(); 245d62f9ed4SPatrick McHardy 2468f03dea5SMartin Josefsson /* Remove all contrack entries for this protocol */ 247efb9a8c2SAlexey Dobriyan rtnl_lock(); 248678d6675SAlexey Dobriyan for_each_net(net) 249678d6675SAlexey Dobriyan nf_ct_iterate_cleanup(net, kill_l3proto, proto); 250efb9a8c2SAlexey Dobriyan rtnl_unlock(); 2518f03dea5SMartin Josefsson } 25213b18339SPatrick McHardy EXPORT_SYMBOL_GPL(nf_conntrack_l3proto_unregister); 2538f03dea5SMartin Josefsson 2542c352f44SGao feng static struct nf_proto_net *nf_ct_l4proto_net(struct net *net, 2552c352f44SGao feng struct nf_conntrack_l4proto *l4proto) 2562c352f44SGao feng { 2572c352f44SGao feng if (l4proto->net_id) 2582c352f44SGao feng return net_generic(net, *l4proto->net_id); 2592c352f44SGao feng else 2602c352f44SGao feng return NULL; 2612c352f44SGao feng } 2622c352f44SGao feng 2632c352f44SGao feng static 2642c352f44SGao feng int nf_ct_l4proto_register_sysctl(struct net *net, 2652c352f44SGao feng struct nf_conntrack_l4proto *l4proto) 266d62f9ed4SPatrick McHardy { 267d62f9ed4SPatrick McHardy int err = 0; 2682c352f44SGao feng struct nf_proto_net *pn = nf_ct_l4proto_net(net, l4proto); 2692c352f44SGao feng if (pn == NULL) 2702c352f44SGao feng return 0; 271d62f9ed4SPatrick McHardy 272d62f9ed4SPatrick McHardy #ifdef CONFIG_SYSCTL 2732c352f44SGao feng if (pn->ctl_table != NULL) { 2742c352f44SGao feng err = nf_ct_register_sysctl(net, 2752c352f44SGao feng &pn->ctl_table_header, 276f99e8f71SEric W. Biederman "net/netfilter", 2772c352f44SGao feng pn->ctl_table, 2782c352f44SGao feng &pn->users); 2792c352f44SGao feng if (err < 0) { 2802c352f44SGao feng if (!pn->users) { 2812c352f44SGao feng kfree(pn->ctl_table); 2822c352f44SGao feng pn->ctl_table = NULL; 2832c352f44SGao feng } 284a999e683SPatrick McHardy goto out; 285d62f9ed4SPatrick McHardy } 2862c352f44SGao feng } 287a999e683SPatrick McHardy #ifdef CONFIG_NF_CONNTRACK_PROC_COMPAT 2882c352f44SGao feng if (l4proto->l3proto != AF_INET6 && pn->ctl_compat_table != NULL) { 2892c352f44SGao feng err = nf_ct_register_sysctl(net, 2902c352f44SGao feng &pn->ctl_compat_header, 291f99e8f71SEric W. Biederman "net/ipv4/netfilter", 2922c352f44SGao feng pn->ctl_compat_table, 2932c352f44SGao feng NULL); 294a999e683SPatrick McHardy if (err == 0) 295a999e683SPatrick McHardy goto out; 2962c352f44SGao feng 2972c352f44SGao feng kfree(pn->ctl_compat_table); 2982c352f44SGao feng pn->ctl_compat_table = NULL; 2992c352f44SGao feng nf_ct_unregister_sysctl(&pn->ctl_table_header, 3002c352f44SGao feng &pn->ctl_table, 3012c352f44SGao feng &pn->users); 302a999e683SPatrick McHardy } 303a999e683SPatrick McHardy #endif /* CONFIG_NF_CONNTRACK_PROC_COMPAT */ 304a999e683SPatrick McHardy out: 305933a41e7SPatrick McHardy #endif /* CONFIG_SYSCTL */ 306d62f9ed4SPatrick McHardy return err; 307d62f9ed4SPatrick McHardy } 308d62f9ed4SPatrick McHardy 3092c352f44SGao feng static 3102c352f44SGao feng void nf_ct_l4proto_unregister_sysctl(struct net *net, 3112c352f44SGao feng struct nf_conntrack_l4proto *l4proto) 312d62f9ed4SPatrick McHardy { 3132c352f44SGao feng struct nf_proto_net *pn = nf_ct_l4proto_net(net, l4proto); 3142c352f44SGao feng if (pn == NULL) 3152c352f44SGao feng return; 316d62f9ed4SPatrick McHardy #ifdef CONFIG_SYSCTL 3172c352f44SGao feng if (pn->ctl_table_header != NULL) 3182c352f44SGao feng nf_ct_unregister_sysctl(&pn->ctl_table_header, 3192c352f44SGao feng &pn->ctl_table, 3202c352f44SGao feng &pn->users); 3212c352f44SGao feng 322a999e683SPatrick McHardy #ifdef CONFIG_NF_CONNTRACK_PROC_COMPAT 3232c352f44SGao feng if (l4proto->l3proto != AF_INET6 && pn->ctl_compat_header != NULL) 3242c352f44SGao feng nf_ct_unregister_sysctl(&pn->ctl_compat_header, 3252c352f44SGao feng &pn->ctl_compat_table, 3262c352f44SGao feng NULL); 327a999e683SPatrick McHardy #endif /* CONFIG_NF_CONNTRACK_PROC_COMPAT */ 3282c352f44SGao feng #else 3292c352f44SGao feng pn->users--; 330933a41e7SPatrick McHardy #endif /* CONFIG_SYSCTL */ 331d62f9ed4SPatrick McHardy } 332d62f9ed4SPatrick McHardy 3338f03dea5SMartin Josefsson /* FIXME: Allow NULL functions and sub in pointers to generic for 3348f03dea5SMartin Josefsson them. --RR */ 3352c352f44SGao feng static int 3362c352f44SGao feng nf_conntrack_l4proto_register_net(struct nf_conntrack_l4proto *l4proto) 3378f03dea5SMartin Josefsson { 3388f03dea5SMartin Josefsson int ret = 0; 3398f03dea5SMartin Josefsson 3400661cca9SPatrick McHardy if (l4proto->l3proto >= PF_MAX) 3410661cca9SPatrick McHardy return -EBUSY; 342ae5718fbSMartin Josefsson 343d0dba725SHolger Eitzenberger if ((l4proto->to_nlattr && !l4proto->nlattr_size) 344d0dba725SHolger Eitzenberger || (l4proto->tuple_to_nlattr && !l4proto->nlattr_tuple_size)) 345d0dba725SHolger Eitzenberger return -EINVAL; 346d0dba725SHolger Eitzenberger 347b19caa0cSPatrick McHardy mutex_lock(&nf_ct_proto_mutex); 348c6a1e615SPatrick McHardy if (!nf_ct_protos[l4proto->l3proto]) { 3498f03dea5SMartin Josefsson /* l3proto may be loaded latter. */ 350c5d277d2SEric Dumazet struct nf_conntrack_l4proto __rcu **proto_array; 3518f03dea5SMartin Josefsson int i; 3528f03dea5SMartin Josefsson 353c6a1e615SPatrick McHardy proto_array = kmalloc(MAX_NF_CT_PROTO * 354605dcad6SMartin Josefsson sizeof(struct nf_conntrack_l4proto *), 3558f03dea5SMartin Josefsson GFP_KERNEL); 3568f03dea5SMartin Josefsson if (proto_array == NULL) { 3578f03dea5SMartin Josefsson ret = -ENOMEM; 358b19caa0cSPatrick McHardy goto out_unlock; 3598f03dea5SMartin Josefsson } 360c6a1e615SPatrick McHardy 3618f03dea5SMartin Josefsson for (i = 0; i < MAX_NF_CT_PROTO; i++) 362c5d277d2SEric Dumazet RCU_INIT_POINTER(proto_array[i], &nf_conntrack_l4proto_generic); 363d817d29dSEric Dumazet 364d817d29dSEric Dumazet /* Before making proto_array visible to lockless readers, 365d817d29dSEric Dumazet * we must make sure its content is committed to memory. 366d817d29dSEric Dumazet */ 367d817d29dSEric Dumazet smp_wmb(); 368d817d29dSEric Dumazet 369605dcad6SMartin Josefsson nf_ct_protos[l4proto->l3proto] = proto_array; 3700e60ebe0SEric Dumazet } else if (rcu_dereference_protected( 3710e60ebe0SEric Dumazet nf_ct_protos[l4proto->l3proto][l4proto->l4proto], 3720e60ebe0SEric Dumazet lockdep_is_held(&nf_ct_proto_mutex) 3730e60ebe0SEric Dumazet ) != &nf_conntrack_l4proto_generic) { 374c6a1e615SPatrick McHardy ret = -EBUSY; 375c6a1e615SPatrick McHardy goto out_unlock; 3768f03dea5SMartin Josefsson } 3778f03dea5SMartin Josefsson 378d0dba725SHolger Eitzenberger l4proto->nla_size = 0; 379d0dba725SHolger Eitzenberger if (l4proto->nlattr_size) 380d0dba725SHolger Eitzenberger l4proto->nla_size += l4proto->nlattr_size(); 381d0dba725SHolger Eitzenberger if (l4proto->nlattr_tuple_size) 382d0dba725SHolger Eitzenberger l4proto->nla_size += 3 * l4proto->nlattr_tuple_size(); 383d0dba725SHolger Eitzenberger 384c6a1e615SPatrick McHardy rcu_assign_pointer(nf_ct_protos[l4proto->l3proto][l4proto->l4proto], 385c6a1e615SPatrick McHardy l4proto); 3868f03dea5SMartin Josefsson out_unlock: 387b19caa0cSPatrick McHardy mutex_unlock(&nf_ct_proto_mutex); 3888f03dea5SMartin Josefsson return ret; 3898f03dea5SMartin Josefsson } 3902c352f44SGao feng 3912c352f44SGao feng int nf_conntrack_l4proto_register(struct net *net, 3922c352f44SGao feng struct nf_conntrack_l4proto *l4proto) 3932c352f44SGao feng { 3942c352f44SGao feng int ret = 0; 3952c352f44SGao feng if (net == &init_net) 3962c352f44SGao feng ret = nf_conntrack_l4proto_register_net(l4proto); 3972c352f44SGao feng 3982c352f44SGao feng if (ret < 0) 3992c352f44SGao feng return ret; 4002c352f44SGao feng 4012c352f44SGao feng if (l4proto->init_net) 4022c352f44SGao feng ret = l4proto->init_net(net); 4032c352f44SGao feng 4042c352f44SGao feng if (ret < 0) 4052c352f44SGao feng return ret; 4062c352f44SGao feng 4072c352f44SGao feng return nf_ct_l4proto_register_sysctl(net, l4proto); 4082c352f44SGao feng } 40913b18339SPatrick McHardy EXPORT_SYMBOL_GPL(nf_conntrack_l4proto_register); 4108f03dea5SMartin Josefsson 4112c352f44SGao feng static void 4122c352f44SGao feng nf_conntrack_l4proto_unregister_net(struct nf_conntrack_l4proto *l4proto) 4138f03dea5SMartin Josefsson { 414fe3eb20cSPatrick McHardy BUG_ON(l4proto->l3proto >= PF_MAX); 415ae5718fbSMartin Josefsson 416b19caa0cSPatrick McHardy mutex_lock(&nf_ct_proto_mutex); 4170e60ebe0SEric Dumazet BUG_ON(rcu_dereference_protected( 4180e60ebe0SEric Dumazet nf_ct_protos[l4proto->l3proto][l4proto->l4proto], 4190e60ebe0SEric Dumazet lockdep_is_held(&nf_ct_proto_mutex) 4200e60ebe0SEric Dumazet ) != l4proto); 421923f4902SPatrick McHardy rcu_assign_pointer(nf_ct_protos[l4proto->l3proto][l4proto->l4proto], 422923f4902SPatrick McHardy &nf_conntrack_l4proto_generic); 4230661cca9SPatrick McHardy mutex_unlock(&nf_ct_proto_mutex); 4240661cca9SPatrick McHardy 4250661cca9SPatrick McHardy synchronize_rcu(); 4262c352f44SGao feng } 427d62f9ed4SPatrick McHardy 4282c352f44SGao feng void nf_conntrack_l4proto_unregister(struct net *net, 4292c352f44SGao feng struct nf_conntrack_l4proto *l4proto) 4302c352f44SGao feng { 4312c352f44SGao feng if (net == &init_net) 4322c352f44SGao feng nf_conntrack_l4proto_unregister_net(l4proto); 4332c352f44SGao feng 4342c352f44SGao feng nf_ct_l4proto_unregister_sysctl(net, l4proto); 4358f03dea5SMartin Josefsson /* Remove all contrack entries for this protocol */ 436efb9a8c2SAlexey Dobriyan rtnl_lock(); 437678d6675SAlexey Dobriyan nf_ct_iterate_cleanup(net, kill_l4proto, l4proto); 438efb9a8c2SAlexey Dobriyan rtnl_unlock(); 4398f03dea5SMartin Josefsson } 44013b18339SPatrick McHardy EXPORT_SYMBOL_GPL(nf_conntrack_l4proto_unregister); 441ac5357ebSPatrick McHardy 442ac5357ebSPatrick McHardy int nf_conntrack_proto_init(void) 443ac5357ebSPatrick McHardy { 444ac5357ebSPatrick McHardy unsigned int i; 445ac5357ebSPatrick McHardy int err; 446ac5357ebSPatrick McHardy 4472c352f44SGao feng err = nf_ct_l4proto_register_sysctl(&init_net, &nf_conntrack_l4proto_generic); 448ac5357ebSPatrick McHardy if (err < 0) 449ac5357ebSPatrick McHardy return err; 450ac5357ebSPatrick McHardy 451ac5357ebSPatrick McHardy for (i = 0; i < AF_MAX; i++) 452ac5357ebSPatrick McHardy rcu_assign_pointer(nf_ct_l3protos[i], 453ac5357ebSPatrick McHardy &nf_conntrack_l3proto_generic); 454ac5357ebSPatrick McHardy return 0; 455ac5357ebSPatrick McHardy } 456ac5357ebSPatrick McHardy 457ac5357ebSPatrick McHardy void nf_conntrack_proto_fini(void) 458ac5357ebSPatrick McHardy { 459ac5357ebSPatrick McHardy unsigned int i; 460ac5357ebSPatrick McHardy 4612c352f44SGao feng nf_ct_l4proto_unregister_sysctl(&init_net, &nf_conntrack_l4proto_generic); 462ac5357ebSPatrick McHardy 463ac5357ebSPatrick McHardy /* free l3proto protocol tables */ 464ac5357ebSPatrick McHardy for (i = 0; i < PF_MAX; i++) 465ac5357ebSPatrick McHardy kfree(nf_ct_protos[i]); 466ac5357ebSPatrick McHardy } 467