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, 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 216524a53e5SGao feng static int 217524a53e5SGao feng nf_conntrack_l3proto_register_net(struct nf_conntrack_l3proto *proto) 2188f03dea5SMartin Josefsson { 2198f03dea5SMartin Josefsson int ret = 0; 2200e60ebe0SEric Dumazet struct nf_conntrack_l3proto *old; 2218f03dea5SMartin Josefsson 2220661cca9SPatrick McHardy if (proto->l3proto >= AF_MAX) 2230661cca9SPatrick McHardy return -EBUSY; 2248f03dea5SMartin Josefsson 225d0dba725SHolger Eitzenberger if (proto->tuple_to_nlattr && !proto->nlattr_tuple_size) 226d0dba725SHolger Eitzenberger return -EINVAL; 227d0dba725SHolger Eitzenberger 228b19caa0cSPatrick McHardy mutex_lock(&nf_ct_proto_mutex); 2290e60ebe0SEric Dumazet old = rcu_dereference_protected(nf_ct_l3protos[proto->l3proto], 2300e60ebe0SEric Dumazet lockdep_is_held(&nf_ct_proto_mutex)); 2310e60ebe0SEric Dumazet if (old != &nf_conntrack_l3proto_generic) { 232ae5718fbSMartin Josefsson ret = -EBUSY; 233ae5718fbSMartin Josefsson goto out_unlock; 234ae5718fbSMartin Josefsson } 235d62f9ed4SPatrick McHardy 236d0dba725SHolger Eitzenberger if (proto->nlattr_tuple_size) 237d0dba725SHolger Eitzenberger proto->nla_size = 3 * proto->nlattr_tuple_size(); 238d0dba725SHolger Eitzenberger 2390661cca9SPatrick McHardy rcu_assign_pointer(nf_ct_l3protos[proto->l3proto], proto); 240ae5718fbSMartin Josefsson 241ae5718fbSMartin Josefsson out_unlock: 242b19caa0cSPatrick McHardy mutex_unlock(&nf_ct_proto_mutex); 2438f03dea5SMartin Josefsson return ret; 244524a53e5SGao feng 245524a53e5SGao feng } 246524a53e5SGao feng 247524a53e5SGao feng int nf_conntrack_l3proto_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 258fa0f61f0SGao feng ret = nf_ct_l3proto_register_sysctl(net, proto); 259fa0f61f0SGao feng if (ret < 0) 260fa0f61f0SGao feng return ret; 261fa0f61f0SGao feng 262fa0f61f0SGao feng if (net == &init_net) { 263fa0f61f0SGao feng ret = nf_conntrack_l3proto_register_net(proto); 264fa0f61f0SGao feng if (ret < 0) 265fa0f61f0SGao feng nf_ct_l3proto_unregister_sysctl(net, proto); 266fa0f61f0SGao feng } 267fa0f61f0SGao feng 268fa0f61f0SGao feng return ret; 2698f03dea5SMartin Josefsson } 27013b18339SPatrick McHardy EXPORT_SYMBOL_GPL(nf_conntrack_l3proto_register); 2718f03dea5SMartin Josefsson 272524a53e5SGao feng static void 273524a53e5SGao feng nf_conntrack_l3proto_unregister_net(struct nf_conntrack_l3proto *proto) 2748f03dea5SMartin Josefsson { 275fe3eb20cSPatrick McHardy BUG_ON(proto->l3proto >= AF_MAX); 276ae5718fbSMartin Josefsson 277b19caa0cSPatrick McHardy mutex_lock(&nf_ct_proto_mutex); 2780e60ebe0SEric Dumazet BUG_ON(rcu_dereference_protected(nf_ct_l3protos[proto->l3proto], 2790e60ebe0SEric Dumazet lockdep_is_held(&nf_ct_proto_mutex) 2800e60ebe0SEric Dumazet ) != proto); 281923f4902SPatrick McHardy rcu_assign_pointer(nf_ct_l3protos[proto->l3proto], 282923f4902SPatrick McHardy &nf_conntrack_l3proto_generic); 2830661cca9SPatrick McHardy mutex_unlock(&nf_ct_proto_mutex); 2840661cca9SPatrick McHardy 2850661cca9SPatrick McHardy synchronize_rcu(); 286524a53e5SGao feng } 287524a53e5SGao feng 288524a53e5SGao feng void nf_conntrack_l3proto_unregister(struct net *net, 289524a53e5SGao feng struct nf_conntrack_l3proto *proto) 290524a53e5SGao feng { 291524a53e5SGao feng if (net == &init_net) 292524a53e5SGao feng nf_conntrack_l3proto_unregister_net(proto); 293524a53e5SGao feng 294524a53e5SGao feng nf_ct_l3proto_unregister_sysctl(net, proto); 295d62f9ed4SPatrick McHardy 2968f03dea5SMartin Josefsson /* Remove all contrack entries for this protocol */ 297efb9a8c2SAlexey Dobriyan rtnl_lock(); 298678d6675SAlexey Dobriyan nf_ct_iterate_cleanup(net, kill_l3proto, proto); 299efb9a8c2SAlexey Dobriyan rtnl_unlock(); 3008f03dea5SMartin Josefsson } 30113b18339SPatrick McHardy EXPORT_SYMBOL_GPL(nf_conntrack_l3proto_unregister); 3028f03dea5SMartin Josefsson 3032c352f44SGao feng static struct nf_proto_net *nf_ct_l4proto_net(struct net *net, 3042c352f44SGao feng struct nf_conntrack_l4proto *l4proto) 3052c352f44SGao feng { 30615f585bdSGao feng switch (l4proto->l4proto) { 307d2ba1fdeSGao feng case IPPROTO_TCP: 308d2ba1fdeSGao feng return (struct nf_proto_net *)&net->ct.nf_ct_proto.tcp; 3090ce490adSGao feng case IPPROTO_UDP: 3100ce490adSGao feng return (struct nf_proto_net *)&net->ct.nf_ct_proto.udp; 3114b626b9cSGao feng case IPPROTO_ICMP: 3124b626b9cSGao feng return (struct nf_proto_net *)&net->ct.nf_ct_proto.icmp; 3137080ba09SGao feng case IPPROTO_ICMPV6: 3147080ba09SGao feng return (struct nf_proto_net *)&net->ct.nf_ct_proto.icmpv6; 31515f585bdSGao feng case 255: /* l4proto_generic */ 31615f585bdSGao feng return (struct nf_proto_net *)&net->ct.nf_ct_proto.generic; 31715f585bdSGao feng default: 3182c352f44SGao feng if (l4proto->net_id) 3192c352f44SGao feng return net_generic(net, *l4proto->net_id); 3202c352f44SGao feng else 3212c352f44SGao feng return NULL; 3222c352f44SGao feng } 32315f585bdSGao feng return NULL; 32415f585bdSGao feng } 3252c352f44SGao feng 3262c352f44SGao feng static 3272c352f44SGao feng int nf_ct_l4proto_register_sysctl(struct net *net, 328fa34fff5SGao feng struct nf_proto_net *pn, 3292c352f44SGao feng struct nf_conntrack_l4proto *l4proto) 330d62f9ed4SPatrick McHardy { 331d62f9ed4SPatrick McHardy int err = 0; 332d62f9ed4SPatrick McHardy 333d62f9ed4SPatrick McHardy #ifdef CONFIG_SYSCTL 3342c352f44SGao feng if (pn->ctl_table != NULL) { 3352c352f44SGao feng err = nf_ct_register_sysctl(net, 3362c352f44SGao feng &pn->ctl_table_header, 337f99e8f71SEric W. Biederman "net/netfilter", 338fa34fff5SGao feng pn->ctl_table); 3392c352f44SGao feng if (err < 0) { 3402c352f44SGao feng if (!pn->users) { 3412c352f44SGao feng kfree(pn->ctl_table); 3422c352f44SGao feng pn->ctl_table = NULL; 3432c352f44SGao feng } 344d62f9ed4SPatrick McHardy } 3452c352f44SGao feng } 346a999e683SPatrick McHardy #ifdef CONFIG_NF_CONNTRACK_PROC_COMPAT 3472c352f44SGao feng if (l4proto->l3proto != AF_INET6 && pn->ctl_compat_table != NULL) { 34812c26df3SGao feng if (err < 0) { 34912c26df3SGao feng nf_ct_kfree_compat_sysctl_table(pn); 35012c26df3SGao feng goto out; 35112c26df3SGao feng } 3522c352f44SGao feng err = nf_ct_register_sysctl(net, 3532c352f44SGao feng &pn->ctl_compat_header, 354f99e8f71SEric W. Biederman "net/ipv4/netfilter", 355fa34fff5SGao feng pn->ctl_compat_table); 356a999e683SPatrick McHardy if (err == 0) 357a999e683SPatrick McHardy goto out; 3582c352f44SGao feng 359f28997e2SGao feng nf_ct_kfree_compat_sysctl_table(pn); 3602c352f44SGao feng nf_ct_unregister_sysctl(&pn->ctl_table_header, 3612c352f44SGao feng &pn->ctl_table, 362fa34fff5SGao feng pn->users); 363a999e683SPatrick McHardy } 364a999e683SPatrick McHardy out: 36512c26df3SGao feng #endif /* CONFIG_NF_CONNTRACK_PROC_COMPAT */ 366933a41e7SPatrick McHardy #endif /* CONFIG_SYSCTL */ 367d62f9ed4SPatrick McHardy return err; 368d62f9ed4SPatrick McHardy } 369d62f9ed4SPatrick McHardy 3702c352f44SGao feng static 3712c352f44SGao feng void nf_ct_l4proto_unregister_sysctl(struct net *net, 372fa34fff5SGao feng struct nf_proto_net *pn, 3732c352f44SGao feng struct nf_conntrack_l4proto *l4proto) 374d62f9ed4SPatrick McHardy { 375d62f9ed4SPatrick McHardy #ifdef CONFIG_SYSCTL 3762c352f44SGao feng if (pn->ctl_table_header != NULL) 3772c352f44SGao feng nf_ct_unregister_sysctl(&pn->ctl_table_header, 3782c352f44SGao feng &pn->ctl_table, 379fa34fff5SGao feng pn->users); 3802c352f44SGao feng 381a999e683SPatrick McHardy #ifdef CONFIG_NF_CONNTRACK_PROC_COMPAT 3822c352f44SGao feng if (l4proto->l3proto != AF_INET6 && pn->ctl_compat_header != NULL) 3832c352f44SGao feng nf_ct_unregister_sysctl(&pn->ctl_compat_header, 3842c352f44SGao feng &pn->ctl_compat_table, 385fa34fff5SGao feng 0); 386a999e683SPatrick McHardy #endif /* CONFIG_NF_CONNTRACK_PROC_COMPAT */ 387933a41e7SPatrick McHardy #endif /* CONFIG_SYSCTL */ 388d62f9ed4SPatrick McHardy } 389d62f9ed4SPatrick McHardy 3908f03dea5SMartin Josefsson /* FIXME: Allow NULL functions and sub in pointers to generic for 3918f03dea5SMartin Josefsson them. --RR */ 3922c352f44SGao feng static int 3932c352f44SGao feng nf_conntrack_l4proto_register_net(struct nf_conntrack_l4proto *l4proto) 3948f03dea5SMartin Josefsson { 3958f03dea5SMartin Josefsson int ret = 0; 3968f03dea5SMartin Josefsson 3970661cca9SPatrick McHardy if (l4proto->l3proto >= PF_MAX) 3980661cca9SPatrick McHardy return -EBUSY; 399ae5718fbSMartin Josefsson 400d0dba725SHolger Eitzenberger if ((l4proto->to_nlattr && !l4proto->nlattr_size) 401d0dba725SHolger Eitzenberger || (l4proto->tuple_to_nlattr && !l4proto->nlattr_tuple_size)) 402d0dba725SHolger Eitzenberger return -EINVAL; 403d0dba725SHolger Eitzenberger 404b19caa0cSPatrick McHardy mutex_lock(&nf_ct_proto_mutex); 405c6a1e615SPatrick McHardy if (!nf_ct_protos[l4proto->l3proto]) { 4068f03dea5SMartin Josefsson /* l3proto may be loaded latter. */ 407c5d277d2SEric Dumazet struct nf_conntrack_l4proto __rcu **proto_array; 4088f03dea5SMartin Josefsson int i; 4098f03dea5SMartin Josefsson 410c6a1e615SPatrick McHardy proto_array = kmalloc(MAX_NF_CT_PROTO * 411605dcad6SMartin Josefsson sizeof(struct nf_conntrack_l4proto *), 4128f03dea5SMartin Josefsson GFP_KERNEL); 4138f03dea5SMartin Josefsson if (proto_array == NULL) { 4148f03dea5SMartin Josefsson ret = -ENOMEM; 415b19caa0cSPatrick McHardy goto out_unlock; 4168f03dea5SMartin Josefsson } 417c6a1e615SPatrick McHardy 4188f03dea5SMartin Josefsson for (i = 0; i < MAX_NF_CT_PROTO; i++) 419c5d277d2SEric Dumazet RCU_INIT_POINTER(proto_array[i], &nf_conntrack_l4proto_generic); 420d817d29dSEric Dumazet 421d817d29dSEric Dumazet /* Before making proto_array visible to lockless readers, 422d817d29dSEric Dumazet * we must make sure its content is committed to memory. 423d817d29dSEric Dumazet */ 424d817d29dSEric Dumazet smp_wmb(); 425d817d29dSEric Dumazet 426605dcad6SMartin Josefsson nf_ct_protos[l4proto->l3proto] = proto_array; 4270e60ebe0SEric Dumazet } else if (rcu_dereference_protected( 4280e60ebe0SEric Dumazet nf_ct_protos[l4proto->l3proto][l4proto->l4proto], 4290e60ebe0SEric Dumazet lockdep_is_held(&nf_ct_proto_mutex) 4300e60ebe0SEric Dumazet ) != &nf_conntrack_l4proto_generic) { 431c6a1e615SPatrick McHardy ret = -EBUSY; 432c6a1e615SPatrick McHardy goto out_unlock; 4338f03dea5SMartin Josefsson } 4348f03dea5SMartin Josefsson 435d0dba725SHolger Eitzenberger l4proto->nla_size = 0; 436d0dba725SHolger Eitzenberger if (l4proto->nlattr_size) 437d0dba725SHolger Eitzenberger l4proto->nla_size += l4proto->nlattr_size(); 438d0dba725SHolger Eitzenberger if (l4proto->nlattr_tuple_size) 439d0dba725SHolger Eitzenberger l4proto->nla_size += 3 * l4proto->nlattr_tuple_size(); 440d0dba725SHolger Eitzenberger 441c6a1e615SPatrick McHardy rcu_assign_pointer(nf_ct_protos[l4proto->l3proto][l4proto->l4proto], 442c6a1e615SPatrick McHardy l4proto); 4438f03dea5SMartin Josefsson out_unlock: 444b19caa0cSPatrick McHardy mutex_unlock(&nf_ct_proto_mutex); 4458f03dea5SMartin Josefsson return ret; 4468f03dea5SMartin Josefsson } 4472c352f44SGao feng 4482c352f44SGao feng int nf_conntrack_l4proto_register(struct net *net, 4492c352f44SGao feng struct nf_conntrack_l4proto *l4proto) 4502c352f44SGao feng { 4512c352f44SGao feng int ret = 0; 452fa34fff5SGao feng struct nf_proto_net *pn = NULL; 4532c352f44SGao feng 454fa0f61f0SGao feng if (l4proto->init_net) { 455f1caad27SGao feng ret = l4proto->init_net(net, l4proto->l3proto); 456fa0f61f0SGao feng if (ret < 0) 457fa34fff5SGao feng goto out; 458fa0f61f0SGao feng } 4592c352f44SGao feng 460fa34fff5SGao feng pn = nf_ct_l4proto_net(net, l4proto); 461fa34fff5SGao feng if (pn == NULL) 462fa34fff5SGao feng goto out; 463fa34fff5SGao feng 464fa34fff5SGao feng ret = nf_ct_l4proto_register_sysctl(net, pn, l4proto); 4652c352f44SGao feng if (ret < 0) 466fa34fff5SGao feng goto out; 4672c352f44SGao feng 468fa0f61f0SGao feng if (net == &init_net) { 469fa0f61f0SGao feng ret = nf_conntrack_l4proto_register_net(l4proto); 470fa34fff5SGao feng if (ret < 0) { 471fa34fff5SGao feng nf_ct_l4proto_unregister_sysctl(net, pn, l4proto); 472fa34fff5SGao feng goto out; 473fa34fff5SGao feng } 474fa0f61f0SGao feng } 475fa0f61f0SGao feng 476fa34fff5SGao feng pn->users++; 477fa34fff5SGao feng out: 478fa0f61f0SGao feng return ret; 4792c352f44SGao feng } 48013b18339SPatrick McHardy EXPORT_SYMBOL_GPL(nf_conntrack_l4proto_register); 4818f03dea5SMartin Josefsson 4822c352f44SGao feng static void 4832c352f44SGao feng nf_conntrack_l4proto_unregister_net(struct nf_conntrack_l4proto *l4proto) 4848f03dea5SMartin Josefsson { 485fe3eb20cSPatrick McHardy BUG_ON(l4proto->l3proto >= PF_MAX); 486ae5718fbSMartin Josefsson 487b19caa0cSPatrick McHardy mutex_lock(&nf_ct_proto_mutex); 4880e60ebe0SEric Dumazet BUG_ON(rcu_dereference_protected( 4890e60ebe0SEric Dumazet nf_ct_protos[l4proto->l3proto][l4proto->l4proto], 4900e60ebe0SEric Dumazet lockdep_is_held(&nf_ct_proto_mutex) 4910e60ebe0SEric Dumazet ) != l4proto); 492923f4902SPatrick McHardy rcu_assign_pointer(nf_ct_protos[l4proto->l3proto][l4proto->l4proto], 493923f4902SPatrick McHardy &nf_conntrack_l4proto_generic); 4940661cca9SPatrick McHardy mutex_unlock(&nf_ct_proto_mutex); 4950661cca9SPatrick McHardy 4960661cca9SPatrick McHardy synchronize_rcu(); 4972c352f44SGao feng } 498d62f9ed4SPatrick McHardy 4992c352f44SGao feng void nf_conntrack_l4proto_unregister(struct net *net, 5002c352f44SGao feng struct nf_conntrack_l4proto *l4proto) 5012c352f44SGao feng { 502fa34fff5SGao feng struct nf_proto_net *pn = NULL; 503fa34fff5SGao feng 5042c352f44SGao feng if (net == &init_net) 5052c352f44SGao feng nf_conntrack_l4proto_unregister_net(l4proto); 5062c352f44SGao feng 507fa34fff5SGao feng pn = nf_ct_l4proto_net(net, l4proto); 508fa34fff5SGao feng if (pn == NULL) 509fa34fff5SGao feng return; 510fa34fff5SGao feng 511fa34fff5SGao feng pn->users--; 512fa34fff5SGao feng nf_ct_l4proto_unregister_sysctl(net, pn, l4proto); 513fa34fff5SGao feng 5148f03dea5SMartin Josefsson /* Remove all contrack entries for this protocol */ 515efb9a8c2SAlexey Dobriyan rtnl_lock(); 516678d6675SAlexey Dobriyan nf_ct_iterate_cleanup(net, kill_l4proto, l4proto); 517efb9a8c2SAlexey Dobriyan rtnl_unlock(); 5188f03dea5SMartin Josefsson } 51913b18339SPatrick McHardy EXPORT_SYMBOL_GPL(nf_conntrack_l4proto_unregister); 520ac5357ebSPatrick McHardy 52115f585bdSGao feng int nf_conntrack_proto_init(struct net *net) 522ac5357ebSPatrick McHardy { 523ac5357ebSPatrick McHardy unsigned int i; 524ac5357ebSPatrick McHardy int err; 525fa34fff5SGao feng struct nf_proto_net *pn = nf_ct_l4proto_net(net, 526fa34fff5SGao feng &nf_conntrack_l4proto_generic); 527fa34fff5SGao feng 528f1caad27SGao feng err = nf_conntrack_l4proto_generic.init_net(net, 529f1caad27SGao feng nf_conntrack_l4proto_generic.l3proto); 53015f585bdSGao feng if (err < 0) 53115f585bdSGao feng return err; 53215f585bdSGao feng err = nf_ct_l4proto_register_sysctl(net, 533fa34fff5SGao feng pn, 53415f585bdSGao feng &nf_conntrack_l4proto_generic); 535ac5357ebSPatrick McHardy if (err < 0) 536ac5357ebSPatrick McHardy return err; 537ac5357ebSPatrick McHardy 53815f585bdSGao feng if (net == &init_net) { 539ac5357ebSPatrick McHardy for (i = 0; i < AF_MAX; i++) 540ac5357ebSPatrick McHardy rcu_assign_pointer(nf_ct_l3protos[i], 541ac5357ebSPatrick McHardy &nf_conntrack_l3proto_generic); 54215f585bdSGao feng } 543fa34fff5SGao feng 544fa34fff5SGao feng pn->users++; 545ac5357ebSPatrick McHardy return 0; 546ac5357ebSPatrick McHardy } 547ac5357ebSPatrick McHardy 54815f585bdSGao feng void nf_conntrack_proto_fini(struct net *net) 549ac5357ebSPatrick McHardy { 550ac5357ebSPatrick McHardy unsigned int i; 551fa34fff5SGao feng struct nf_proto_net *pn = nf_ct_l4proto_net(net, 552fa34fff5SGao feng &nf_conntrack_l4proto_generic); 553fa34fff5SGao feng 554fa34fff5SGao feng pn->users--; 55515f585bdSGao feng nf_ct_l4proto_unregister_sysctl(net, 556fa34fff5SGao feng pn, 55715f585bdSGao feng &nf_conntrack_l4proto_generic); 55815f585bdSGao feng if (net == &init_net) { 559ac5357ebSPatrick McHardy /* free l3proto protocol tables */ 560ac5357ebSPatrick McHardy for (i = 0; i < PF_MAX; i++) 561ac5357ebSPatrick McHardy kfree(nf_ct_protos[i]); 562ac5357ebSPatrick McHardy } 56315f585bdSGao feng } 564