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> 30c4f3db15SFlorian Westphal #include <net/netfilter/nf_log.h> 318f03dea5SMartin Josefsson 32b7b5fda4SFlorian Westphal static struct nf_conntrack_l4proto __rcu **nf_ct_protos[NFPROTO_NUMPROTO] __read_mostly; 33b7b5fda4SFlorian Westphal struct nf_conntrack_l3proto __rcu *nf_ct_l3protos[NFPROTO_NUMPROTO] __read_mostly; 3413b18339SPatrick McHardy EXPORT_SYMBOL_GPL(nf_ct_l3protos); 358f03dea5SMartin Josefsson 36b19caa0cSPatrick McHardy static DEFINE_MUTEX(nf_ct_proto_mutex); 37d62f9ed4SPatrick McHardy 38b19caa0cSPatrick McHardy #ifdef CONFIG_SYSCTL 39d62f9ed4SPatrick McHardy static int 402c352f44SGao feng nf_ct_register_sysctl(struct net *net, 412c352f44SGao feng struct ctl_table_header **header, 422c352f44SGao feng const char *path, 43fa34fff5SGao feng struct ctl_table *table) 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 } 502c352f44SGao feng 51d62f9ed4SPatrick McHardy return 0; 52d62f9ed4SPatrick McHardy } 53d62f9ed4SPatrick McHardy 54d62f9ed4SPatrick McHardy static void 55d62f9ed4SPatrick McHardy nf_ct_unregister_sysctl(struct ctl_table_header **header, 562c352f44SGao feng struct ctl_table **table, 57fa34fff5SGao feng unsigned int users) 58d62f9ed4SPatrick McHardy { 59fa34fff5SGao feng if (users > 0) 60d62f9ed4SPatrick McHardy return; 61b3fd3ffeSPavel Emelyanov 625dd3df10SEric W. Biederman unregister_net_sysctl_table(*header); 632c352f44SGao feng kfree(*table); 64d62f9ed4SPatrick McHardy *header = NULL; 652c352f44SGao feng *table = NULL; 66d62f9ed4SPatrick McHardy } 67c4f3db15SFlorian Westphal 68c4f3db15SFlorian Westphal __printf(5, 6) 69c4f3db15SFlorian Westphal void nf_l4proto_log_invalid(const struct sk_buff *skb, 70c4f3db15SFlorian Westphal struct net *net, 71c4f3db15SFlorian Westphal u16 pf, u8 protonum, 72c4f3db15SFlorian Westphal const char *fmt, ...) 73c4f3db15SFlorian Westphal { 74c4f3db15SFlorian Westphal struct va_format vaf; 75c4f3db15SFlorian Westphal va_list args; 76c4f3db15SFlorian Westphal 77c4f3db15SFlorian Westphal if (net->ct.sysctl_log_invalid != protonum || 78c4f3db15SFlorian Westphal net->ct.sysctl_log_invalid != IPPROTO_RAW) 79c4f3db15SFlorian Westphal return; 80c4f3db15SFlorian Westphal 81c4f3db15SFlorian Westphal va_start(args, fmt); 82c4f3db15SFlorian Westphal vaf.fmt = fmt; 83c4f3db15SFlorian Westphal vaf.va = &args; 84c4f3db15SFlorian Westphal 85c4f3db15SFlorian Westphal nf_log_packet(net, pf, 0, skb, NULL, NULL, NULL, 86c4f3db15SFlorian Westphal "nf_ct_proto_%d: %pV ", protonum, &vaf); 87c4f3db15SFlorian Westphal va_end(args); 88c4f3db15SFlorian Westphal } 89c4f3db15SFlorian Westphal EXPORT_SYMBOL_GPL(nf_l4proto_log_invalid); 903d0b527bSFlorian Westphal 913d0b527bSFlorian Westphal __printf(3, 4) 923d0b527bSFlorian Westphal void nf_ct_l4proto_log_invalid(const struct sk_buff *skb, 933d0b527bSFlorian Westphal const struct nf_conn *ct, 943d0b527bSFlorian Westphal const char *fmt, ...) 953d0b527bSFlorian Westphal { 963d0b527bSFlorian Westphal struct va_format vaf; 973d0b527bSFlorian Westphal struct net *net; 983d0b527bSFlorian Westphal va_list args; 993d0b527bSFlorian Westphal 1003d0b527bSFlorian Westphal net = nf_ct_net(ct); 1013d0b527bSFlorian Westphal if (likely(net->ct.sysctl_log_invalid == 0)) 1023d0b527bSFlorian Westphal return; 1033d0b527bSFlorian Westphal 1043d0b527bSFlorian Westphal va_start(args, fmt); 1053d0b527bSFlorian Westphal vaf.fmt = fmt; 1063d0b527bSFlorian Westphal vaf.va = &args; 1073d0b527bSFlorian Westphal 1083d0b527bSFlorian Westphal nf_l4proto_log_invalid(skb, net, nf_ct_l3num(ct), 1093d0b527bSFlorian Westphal nf_ct_protonum(ct), "%pV", &vaf); 1103d0b527bSFlorian Westphal va_end(args); 1113d0b527bSFlorian Westphal } 1123d0b527bSFlorian Westphal EXPORT_SYMBOL_GPL(nf_ct_l4proto_log_invalid); 113d62f9ed4SPatrick McHardy #endif 114d62f9ed4SPatrick McHardy 115b3480fe0SFlorian Westphal const struct nf_conntrack_l4proto * 116605dcad6SMartin Josefsson __nf_ct_l4proto_find(u_int16_t l3proto, u_int8_t l4proto) 1178f03dea5SMartin Josefsson { 118b7b5fda4SFlorian Westphal if (unlikely(l3proto >= NFPROTO_NUMPROTO || nf_ct_protos[l3proto] == NULL)) 119605dcad6SMartin Josefsson return &nf_conntrack_l4proto_generic; 1208f03dea5SMartin Josefsson 121923f4902SPatrick McHardy return rcu_dereference(nf_ct_protos[l3proto][l4proto]); 1228f03dea5SMartin Josefsson } 12313b18339SPatrick McHardy EXPORT_SYMBOL_GPL(__nf_ct_l4proto_find); 1248f03dea5SMartin Josefsson 1258f03dea5SMartin Josefsson /* this is guaranteed to always return a valid protocol helper, since 1268f03dea5SMartin Josefsson * it falls back to generic_protocol */ 127b3480fe0SFlorian Westphal const struct nf_conntrack_l3proto * 1288f03dea5SMartin Josefsson nf_ct_l3proto_find_get(u_int16_t l3proto) 1298f03dea5SMartin Josefsson { 1308f03dea5SMartin Josefsson struct nf_conntrack_l3proto *p; 1318f03dea5SMartin Josefsson 132923f4902SPatrick McHardy rcu_read_lock(); 1338f03dea5SMartin Josefsson p = __nf_ct_l3proto_find(l3proto); 1348f03dea5SMartin Josefsson if (!try_module_get(p->me)) 135605dcad6SMartin Josefsson p = &nf_conntrack_l3proto_generic; 136923f4902SPatrick McHardy rcu_read_unlock(); 1378f03dea5SMartin Josefsson 1388f03dea5SMartin Josefsson return p; 1398f03dea5SMartin Josefsson } 14013b18339SPatrick McHardy EXPORT_SYMBOL_GPL(nf_ct_l3proto_find_get); 1418f03dea5SMartin Josefsson 1428f03dea5SMartin Josefsson int 1438f03dea5SMartin Josefsson nf_ct_l3proto_try_module_get(unsigned short l3proto) 1448f03dea5SMartin Josefsson { 145b3480fe0SFlorian Westphal const struct nf_conntrack_l3proto *p; 1468f03dea5SMartin Josefsson int ret; 1478f03dea5SMartin Josefsson 1488f03dea5SMartin Josefsson retry: p = nf_ct_l3proto_find_get(l3proto); 149605dcad6SMartin Josefsson if (p == &nf_conntrack_l3proto_generic) { 1508f03dea5SMartin Josefsson ret = request_module("nf_conntrack-%d", l3proto); 1518f03dea5SMartin Josefsson if (!ret) 1528f03dea5SMartin Josefsson goto retry; 1538f03dea5SMartin Josefsson 1548f03dea5SMartin Josefsson return -EPROTOTYPE; 1558f03dea5SMartin Josefsson } 1568f03dea5SMartin Josefsson 1578f03dea5SMartin Josefsson return 0; 1588f03dea5SMartin Josefsson } 15913b18339SPatrick McHardy EXPORT_SYMBOL_GPL(nf_ct_l3proto_try_module_get); 1608f03dea5SMartin Josefsson 1618f03dea5SMartin Josefsson void nf_ct_l3proto_module_put(unsigned short l3proto) 1628f03dea5SMartin Josefsson { 1638f03dea5SMartin Josefsson struct nf_conntrack_l3proto *p; 1648f03dea5SMartin Josefsson 1653b254c54SPatrick McHardy /* rcu_read_lock not necessary since the caller holds a reference, but 1663b254c54SPatrick McHardy * taken anyways to avoid lockdep warnings in __nf_ct_l3proto_find() 1673b254c54SPatrick McHardy */ 1683b254c54SPatrick McHardy rcu_read_lock(); 1698f03dea5SMartin Josefsson p = __nf_ct_l3proto_find(l3proto); 1708f03dea5SMartin Josefsson module_put(p->me); 1713b254c54SPatrick McHardy rcu_read_unlock(); 1728f03dea5SMartin Josefsson } 17313b18339SPatrick McHardy EXPORT_SYMBOL_GPL(nf_ct_l3proto_module_put); 1748f03dea5SMartin Josefsson 1757e35ec0eSPablo Neira Ayuso static int nf_ct_netns_do_get(struct net *net, u8 nfproto) 176ecb2421bSFlorian Westphal { 1770c66dc1eSFlorian Westphal const struct nf_conntrack_l3proto *l3proto; 1780c66dc1eSFlorian Westphal int ret; 1790c66dc1eSFlorian Westphal 1800c66dc1eSFlorian Westphal might_sleep(); 1810c66dc1eSFlorian Westphal 1820c66dc1eSFlorian Westphal ret = nf_ct_l3proto_try_module_get(nfproto); 1830c66dc1eSFlorian Westphal if (ret < 0) 1840c66dc1eSFlorian Westphal return ret; 1850c66dc1eSFlorian Westphal 1860c66dc1eSFlorian Westphal /* we already have a reference, can't fail */ 1870c66dc1eSFlorian Westphal rcu_read_lock(); 1880c66dc1eSFlorian Westphal l3proto = __nf_ct_l3proto_find(nfproto); 1890c66dc1eSFlorian Westphal rcu_read_unlock(); 1900c66dc1eSFlorian Westphal 1910c66dc1eSFlorian Westphal if (!l3proto->net_ns_get) 1920c66dc1eSFlorian Westphal return 0; 1930c66dc1eSFlorian Westphal 1940c66dc1eSFlorian Westphal ret = l3proto->net_ns_get(net); 1950c66dc1eSFlorian Westphal if (ret < 0) 1960c66dc1eSFlorian Westphal nf_ct_l3proto_module_put(nfproto); 1970c66dc1eSFlorian Westphal 1980c66dc1eSFlorian Westphal return ret; 199ecb2421bSFlorian Westphal } 2007e35ec0eSPablo Neira Ayuso 2017e35ec0eSPablo Neira Ayuso int nf_ct_netns_get(struct net *net, u8 nfproto) 2027e35ec0eSPablo Neira Ayuso { 2037e35ec0eSPablo Neira Ayuso int err; 2047e35ec0eSPablo Neira Ayuso 2057e35ec0eSPablo Neira Ayuso if (nfproto == NFPROTO_INET) { 2067e35ec0eSPablo Neira Ayuso err = nf_ct_netns_do_get(net, NFPROTO_IPV4); 2077e35ec0eSPablo Neira Ayuso if (err < 0) 2087e35ec0eSPablo Neira Ayuso goto err1; 2097e35ec0eSPablo Neira Ayuso err = nf_ct_netns_do_get(net, NFPROTO_IPV6); 2107e35ec0eSPablo Neira Ayuso if (err < 0) 2117e35ec0eSPablo Neira Ayuso goto err2; 2127e35ec0eSPablo Neira Ayuso } else { 2137e35ec0eSPablo Neira Ayuso err = nf_ct_netns_do_get(net, nfproto); 2147e35ec0eSPablo Neira Ayuso if (err < 0) 2157e35ec0eSPablo Neira Ayuso goto err1; 2167e35ec0eSPablo Neira Ayuso } 2177e35ec0eSPablo Neira Ayuso return 0; 2187e35ec0eSPablo Neira Ayuso 2197e35ec0eSPablo Neira Ayuso err2: 2207e35ec0eSPablo Neira Ayuso nf_ct_netns_put(net, NFPROTO_IPV4); 2217e35ec0eSPablo Neira Ayuso err1: 2227e35ec0eSPablo Neira Ayuso return err; 2237e35ec0eSPablo Neira Ayuso } 224ecb2421bSFlorian Westphal EXPORT_SYMBOL_GPL(nf_ct_netns_get); 225ecb2421bSFlorian Westphal 2267e35ec0eSPablo Neira Ayuso static void nf_ct_netns_do_put(struct net *net, u8 nfproto) 227ecb2421bSFlorian Westphal { 2280c66dc1eSFlorian Westphal const struct nf_conntrack_l3proto *l3proto; 2290c66dc1eSFlorian Westphal 2300c66dc1eSFlorian Westphal might_sleep(); 2310c66dc1eSFlorian Westphal 2320c66dc1eSFlorian Westphal /* same as nf_conntrack_netns_get(), reference assumed */ 2330c66dc1eSFlorian Westphal rcu_read_lock(); 2340c66dc1eSFlorian Westphal l3proto = __nf_ct_l3proto_find(nfproto); 2350c66dc1eSFlorian Westphal rcu_read_unlock(); 2360c66dc1eSFlorian Westphal 2370c66dc1eSFlorian Westphal if (WARN_ON(!l3proto)) 2380c66dc1eSFlorian Westphal return; 2390c66dc1eSFlorian Westphal 2400c66dc1eSFlorian Westphal if (l3proto->net_ns_put) 2410c66dc1eSFlorian Westphal l3proto->net_ns_put(net); 2420c66dc1eSFlorian Westphal 243ecb2421bSFlorian Westphal nf_ct_l3proto_module_put(nfproto); 244ecb2421bSFlorian Westphal } 2457e35ec0eSPablo Neira Ayuso 2467e35ec0eSPablo Neira Ayuso void nf_ct_netns_put(struct net *net, uint8_t nfproto) 2477e35ec0eSPablo Neira Ayuso { 2487e35ec0eSPablo Neira Ayuso if (nfproto == NFPROTO_INET) { 2497e35ec0eSPablo Neira Ayuso nf_ct_netns_do_put(net, NFPROTO_IPV4); 2507e35ec0eSPablo Neira Ayuso nf_ct_netns_do_put(net, NFPROTO_IPV6); 2517e35ec0eSPablo Neira Ayuso } else 2527e35ec0eSPablo Neira Ayuso nf_ct_netns_do_put(net, nfproto); 2537e35ec0eSPablo Neira Ayuso } 254ecb2421bSFlorian Westphal EXPORT_SYMBOL_GPL(nf_ct_netns_put); 255ecb2421bSFlorian Westphal 256b3480fe0SFlorian Westphal const struct nf_conntrack_l4proto * 257c1ebd7dfSPablo Neira Ayuso nf_ct_l4proto_find_get(u_int16_t l3num, u_int8_t l4num) 258c1ebd7dfSPablo Neira Ayuso { 259b3480fe0SFlorian Westphal const struct nf_conntrack_l4proto *p; 260c1ebd7dfSPablo Neira Ayuso 261c1ebd7dfSPablo Neira Ayuso rcu_read_lock(); 262c1ebd7dfSPablo Neira Ayuso p = __nf_ct_l4proto_find(l3num, l4num); 263c1ebd7dfSPablo Neira Ayuso if (!try_module_get(p->me)) 264c1ebd7dfSPablo Neira Ayuso p = &nf_conntrack_l4proto_generic; 265c1ebd7dfSPablo Neira Ayuso rcu_read_unlock(); 266c1ebd7dfSPablo Neira Ayuso 267c1ebd7dfSPablo Neira Ayuso return p; 268c1ebd7dfSPablo Neira Ayuso } 269c1ebd7dfSPablo Neira Ayuso EXPORT_SYMBOL_GPL(nf_ct_l4proto_find_get); 270c1ebd7dfSPablo Neira Ayuso 2712a04aabfSJulia Lawall void nf_ct_l4proto_put(const struct nf_conntrack_l4proto *p) 272c1ebd7dfSPablo Neira Ayuso { 273c1ebd7dfSPablo Neira Ayuso module_put(p->me); 274c1ebd7dfSPablo Neira Ayuso } 275c1ebd7dfSPablo Neira Ayuso EXPORT_SYMBOL_GPL(nf_ct_l4proto_put); 276c1ebd7dfSPablo Neira Ayuso 2778f03dea5SMartin Josefsson static int kill_l3proto(struct nf_conn *i, void *data) 2788f03dea5SMartin Josefsson { 279b3480fe0SFlorian Westphal return nf_ct_l3num(i) == ((const struct nf_conntrack_l3proto *)data)->l3proto; 2808f03dea5SMartin Josefsson } 2818f03dea5SMartin Josefsson 282605dcad6SMartin Josefsson static int kill_l4proto(struct nf_conn *i, void *data) 2838f03dea5SMartin Josefsson { 284b3480fe0SFlorian Westphal const struct nf_conntrack_l4proto *l4proto; 28568ad546aSsimran singhal l4proto = data; 2865e8fbe2aSPatrick McHardy return nf_ct_protonum(i) == l4proto->l4proto && 2875e8fbe2aSPatrick McHardy nf_ct_l3num(i) == l4proto->l3proto; 2888f03dea5SMartin Josefsson } 2898f03dea5SMartin Josefsson 290b3480fe0SFlorian Westphal int nf_ct_l3proto_register(const struct nf_conntrack_l3proto *proto) 2918f03dea5SMartin Josefsson { 2928f03dea5SMartin Josefsson int ret = 0; 2930e60ebe0SEric Dumazet struct nf_conntrack_l3proto *old; 2948f03dea5SMartin Josefsson 295b7b5fda4SFlorian Westphal if (proto->l3proto >= NFPROTO_NUMPROTO) 2960661cca9SPatrick McHardy return -EBUSY; 2970d035100SFlorian Westphal #if IS_ENABLED(CONFIG_NF_CT_NETLINK) 2980d035100SFlorian Westphal if (proto->tuple_to_nlattr && proto->nla_size == 0) 299d0dba725SHolger Eitzenberger return -EINVAL; 3000d035100SFlorian Westphal #endif 301b19caa0cSPatrick McHardy mutex_lock(&nf_ct_proto_mutex); 3020e60ebe0SEric Dumazet old = rcu_dereference_protected(nf_ct_l3protos[proto->l3proto], 3030e60ebe0SEric Dumazet lockdep_is_held(&nf_ct_proto_mutex)); 3040e60ebe0SEric Dumazet if (old != &nf_conntrack_l3proto_generic) { 305ae5718fbSMartin Josefsson ret = -EBUSY; 306ae5718fbSMartin Josefsson goto out_unlock; 307ae5718fbSMartin Josefsson } 308d62f9ed4SPatrick McHardy 3090661cca9SPatrick McHardy rcu_assign_pointer(nf_ct_l3protos[proto->l3proto], proto); 310ae5718fbSMartin Josefsson 311ae5718fbSMartin Josefsson out_unlock: 312b19caa0cSPatrick McHardy mutex_unlock(&nf_ct_proto_mutex); 3138f03dea5SMartin Josefsson return ret; 314524a53e5SGao feng 315524a53e5SGao feng } 3166330750dSGao feng EXPORT_SYMBOL_GPL(nf_ct_l3proto_register); 317524a53e5SGao feng 318b3480fe0SFlorian Westphal void nf_ct_l3proto_unregister(const struct nf_conntrack_l3proto *proto) 3198f03dea5SMartin Josefsson { 320b7b5fda4SFlorian Westphal BUG_ON(proto->l3proto >= NFPROTO_NUMPROTO); 321ae5718fbSMartin Josefsson 322b19caa0cSPatrick McHardy mutex_lock(&nf_ct_proto_mutex); 3230e60ebe0SEric Dumazet BUG_ON(rcu_dereference_protected(nf_ct_l3protos[proto->l3proto], 3240e60ebe0SEric Dumazet lockdep_is_held(&nf_ct_proto_mutex) 3250e60ebe0SEric Dumazet ) != proto); 326923f4902SPatrick McHardy rcu_assign_pointer(nf_ct_l3protos[proto->l3proto], 327923f4902SPatrick McHardy &nf_conntrack_l3proto_generic); 3280661cca9SPatrick McHardy mutex_unlock(&nf_ct_proto_mutex); 3290661cca9SPatrick McHardy 3300661cca9SPatrick McHardy synchronize_rcu(); 3312c41f33cSFlorian Westphal /* Remove all contrack entries for this protocol */ 332b3480fe0SFlorian Westphal nf_ct_iterate_destroy(kill_l3proto, (void*)proto); 333524a53e5SGao feng } 3346330750dSGao feng EXPORT_SYMBOL_GPL(nf_ct_l3proto_unregister); 335524a53e5SGao feng 3362c352f44SGao feng static struct nf_proto_net *nf_ct_l4proto_net(struct net *net, 3372a04aabfSJulia Lawall const struct nf_conntrack_l4proto *l4proto) 3382c352f44SGao feng { 33908911475SPablo Neira Ayuso if (l4proto->get_net_proto) { 34008911475SPablo Neira Ayuso /* statically built-in protocols use static per-net */ 34108911475SPablo Neira Ayuso return l4proto->get_net_proto(net); 34208911475SPablo Neira Ayuso } else if (l4proto->net_id) { 34308911475SPablo Neira Ayuso /* ... and loadable protocols use dynamic per-net */ 3442c352f44SGao feng return net_generic(net, *l4proto->net_id); 3452c352f44SGao feng } 34615f585bdSGao feng return NULL; 34715f585bdSGao feng } 3482c352f44SGao feng 3492c352f44SGao feng static 3502c352f44SGao feng int nf_ct_l4proto_register_sysctl(struct net *net, 351fa34fff5SGao feng struct nf_proto_net *pn, 3522a04aabfSJulia Lawall const struct nf_conntrack_l4proto *l4proto) 353d62f9ed4SPatrick McHardy { 354d62f9ed4SPatrick McHardy int err = 0; 355d62f9ed4SPatrick McHardy 356d62f9ed4SPatrick McHardy #ifdef CONFIG_SYSCTL 3572c352f44SGao feng if (pn->ctl_table != NULL) { 3582c352f44SGao feng err = nf_ct_register_sysctl(net, 3592c352f44SGao feng &pn->ctl_table_header, 360f99e8f71SEric W. Biederman "net/netfilter", 361fa34fff5SGao feng pn->ctl_table); 3622c352f44SGao feng if (err < 0) { 3632c352f44SGao feng if (!pn->users) { 3642c352f44SGao feng kfree(pn->ctl_table); 3652c352f44SGao feng pn->ctl_table = NULL; 3662c352f44SGao feng } 367d62f9ed4SPatrick McHardy } 3682c352f44SGao feng } 369933a41e7SPatrick McHardy #endif /* CONFIG_SYSCTL */ 370d62f9ed4SPatrick McHardy return err; 371d62f9ed4SPatrick McHardy } 372d62f9ed4SPatrick McHardy 3732c352f44SGao feng static 3742c352f44SGao feng void nf_ct_l4proto_unregister_sysctl(struct net *net, 375fa34fff5SGao feng struct nf_proto_net *pn, 3762a04aabfSJulia Lawall const struct nf_conntrack_l4proto *l4proto) 377d62f9ed4SPatrick McHardy { 378d62f9ed4SPatrick McHardy #ifdef CONFIG_SYSCTL 3792c352f44SGao feng if (pn->ctl_table_header != NULL) 3802c352f44SGao feng nf_ct_unregister_sysctl(&pn->ctl_table_header, 3812c352f44SGao feng &pn->ctl_table, 382fa34fff5SGao feng pn->users); 383933a41e7SPatrick McHardy #endif /* CONFIG_SYSCTL */ 384d62f9ed4SPatrick McHardy } 385d62f9ed4SPatrick McHardy 3868f03dea5SMartin Josefsson /* FIXME: Allow NULL functions and sub in pointers to generic for 3878f03dea5SMartin Josefsson them. --RR */ 388cd9ceafcSFlorian Westphal int nf_ct_l4proto_register_one(const struct nf_conntrack_l4proto *l4proto) 3898f03dea5SMartin Josefsson { 3908f03dea5SMartin Josefsson int ret = 0; 3918f03dea5SMartin Josefsson 392b7b5fda4SFlorian Westphal if (l4proto->l3proto >= ARRAY_SIZE(nf_ct_protos)) 3930661cca9SPatrick McHardy return -EBUSY; 394ae5718fbSMartin Josefsson 39539215846SFlorian Westphal if ((l4proto->to_nlattr && l4proto->nlattr_size == 0) || 3960e54d217SDavide Caratti (l4proto->tuple_to_nlattr && !l4proto->nlattr_tuple_size)) 397d0dba725SHolger Eitzenberger return -EINVAL; 398d0dba725SHolger Eitzenberger 399b19caa0cSPatrick McHardy mutex_lock(&nf_ct_proto_mutex); 400c6a1e615SPatrick McHardy if (!nf_ct_protos[l4proto->l3proto]) { 4018f03dea5SMartin Josefsson /* l3proto may be loaded latter. */ 402c5d277d2SEric Dumazet struct nf_conntrack_l4proto __rcu **proto_array; 4038f03dea5SMartin Josefsson int i; 4048f03dea5SMartin Josefsson 405c6a1e615SPatrick McHardy proto_array = kmalloc(MAX_NF_CT_PROTO * 406605dcad6SMartin Josefsson sizeof(struct nf_conntrack_l4proto *), 4078f03dea5SMartin Josefsson GFP_KERNEL); 4088f03dea5SMartin Josefsson if (proto_array == NULL) { 4098f03dea5SMartin Josefsson ret = -ENOMEM; 410b19caa0cSPatrick McHardy goto out_unlock; 4118f03dea5SMartin Josefsson } 412c6a1e615SPatrick McHardy 4138f03dea5SMartin Josefsson for (i = 0; i < MAX_NF_CT_PROTO; i++) 4140e54d217SDavide Caratti RCU_INIT_POINTER(proto_array[i], 4150e54d217SDavide Caratti &nf_conntrack_l4proto_generic); 416d817d29dSEric Dumazet 417d817d29dSEric Dumazet /* Before making proto_array visible to lockless readers, 418d817d29dSEric Dumazet * we must make sure its content is committed to memory. 419d817d29dSEric Dumazet */ 420d817d29dSEric Dumazet smp_wmb(); 421d817d29dSEric Dumazet 422605dcad6SMartin Josefsson nf_ct_protos[l4proto->l3proto] = proto_array; 4230e60ebe0SEric Dumazet } else if (rcu_dereference_protected( 4240e60ebe0SEric Dumazet nf_ct_protos[l4proto->l3proto][l4proto->l4proto], 4250e60ebe0SEric Dumazet lockdep_is_held(&nf_ct_proto_mutex) 4260e60ebe0SEric Dumazet ) != &nf_conntrack_l4proto_generic) { 427c6a1e615SPatrick McHardy ret = -EBUSY; 428c6a1e615SPatrick McHardy goto out_unlock; 4298f03dea5SMartin Josefsson } 4308f03dea5SMartin Josefsson 431c6a1e615SPatrick McHardy rcu_assign_pointer(nf_ct_protos[l4proto->l3proto][l4proto->l4proto], 432c6a1e615SPatrick McHardy l4proto); 4338f03dea5SMartin Josefsson out_unlock: 434b19caa0cSPatrick McHardy mutex_unlock(&nf_ct_proto_mutex); 4358f03dea5SMartin Josefsson return ret; 4368f03dea5SMartin Josefsson } 4370e54d217SDavide Caratti EXPORT_SYMBOL_GPL(nf_ct_l4proto_register_one); 4382c352f44SGao feng 4390e54d217SDavide Caratti int nf_ct_l4proto_pernet_register_one(struct net *net, 4402a04aabfSJulia Lawall const struct nf_conntrack_l4proto *l4proto) 4412c352f44SGao feng { 4422c352f44SGao feng int ret = 0; 443fa34fff5SGao feng struct nf_proto_net *pn = NULL; 4442c352f44SGao feng 445fa0f61f0SGao feng if (l4proto->init_net) { 446f1caad27SGao feng ret = l4proto->init_net(net, l4proto->l3proto); 447fa0f61f0SGao feng if (ret < 0) 448fa34fff5SGao feng goto out; 449fa0f61f0SGao feng } 4502c352f44SGao feng 451fa34fff5SGao feng pn = nf_ct_l4proto_net(net, l4proto); 452fa34fff5SGao feng if (pn == NULL) 453fa34fff5SGao feng goto out; 454fa34fff5SGao feng 455fa34fff5SGao feng ret = nf_ct_l4proto_register_sysctl(net, pn, l4proto); 4562c352f44SGao feng if (ret < 0) 457fa34fff5SGao feng goto out; 4582c352f44SGao feng 459fa34fff5SGao feng pn->users++; 460fa34fff5SGao feng out: 461fa0f61f0SGao feng return ret; 4622c352f44SGao feng } 4630e54d217SDavide Caratti EXPORT_SYMBOL_GPL(nf_ct_l4proto_pernet_register_one); 4648f03dea5SMartin Josefsson 4652a04aabfSJulia Lawall static void __nf_ct_l4proto_unregister_one(const struct nf_conntrack_l4proto *l4proto) 4662c41f33cSFlorian Westphal 4678f03dea5SMartin Josefsson { 468b7b5fda4SFlorian Westphal BUG_ON(l4proto->l3proto >= ARRAY_SIZE(nf_ct_protos)); 469ae5718fbSMartin Josefsson 4700e60ebe0SEric Dumazet BUG_ON(rcu_dereference_protected( 4710e60ebe0SEric Dumazet nf_ct_protos[l4proto->l3proto][l4proto->l4proto], 4720e60ebe0SEric Dumazet lockdep_is_held(&nf_ct_proto_mutex) 4730e60ebe0SEric Dumazet ) != l4proto); 474923f4902SPatrick McHardy rcu_assign_pointer(nf_ct_protos[l4proto->l3proto][l4proto->l4proto], 475923f4902SPatrick McHardy &nf_conntrack_l4proto_generic); 4762c41f33cSFlorian Westphal } 4772c41f33cSFlorian Westphal 4782a04aabfSJulia Lawall void nf_ct_l4proto_unregister_one(const struct nf_conntrack_l4proto *l4proto) 4792c41f33cSFlorian Westphal { 4802c41f33cSFlorian Westphal mutex_lock(&nf_ct_proto_mutex); 4812c41f33cSFlorian Westphal __nf_ct_l4proto_unregister_one(l4proto); 4820661cca9SPatrick McHardy mutex_unlock(&nf_ct_proto_mutex); 4830661cca9SPatrick McHardy 4840661cca9SPatrick McHardy synchronize_rcu(); 4852c352f44SGao feng } 4860e54d217SDavide Caratti EXPORT_SYMBOL_GPL(nf_ct_l4proto_unregister_one); 487d62f9ed4SPatrick McHardy 4880e54d217SDavide Caratti void nf_ct_l4proto_pernet_unregister_one(struct net *net, 4892a04aabfSJulia Lawall const struct nf_conntrack_l4proto *l4proto) 4902c352f44SGao feng { 491809c2d9aSAaron Conole struct nf_proto_net *pn = nf_ct_l4proto_net(net, l4proto); 492fa34fff5SGao feng 493fa34fff5SGao feng if (pn == NULL) 494fa34fff5SGao feng return; 495fa34fff5SGao feng 496fa34fff5SGao feng pn->users--; 497fa34fff5SGao feng nf_ct_l4proto_unregister_sysctl(net, pn, l4proto); 4988f03dea5SMartin Josefsson } 4990e54d217SDavide Caratti EXPORT_SYMBOL_GPL(nf_ct_l4proto_pernet_unregister_one); 5000e54d217SDavide Caratti 501cd9ceafcSFlorian Westphal int nf_ct_l4proto_register(const struct nf_conntrack_l4proto * const l4proto[], 5020e54d217SDavide Caratti unsigned int num_proto) 5030e54d217SDavide Caratti { 5040e54d217SDavide Caratti int ret = -EINVAL, ver; 5050e54d217SDavide Caratti unsigned int i; 5060e54d217SDavide Caratti 5070e54d217SDavide Caratti for (i = 0; i < num_proto; i++) { 5080e54d217SDavide Caratti ret = nf_ct_l4proto_register_one(l4proto[i]); 5090e54d217SDavide Caratti if (ret < 0) 5100e54d217SDavide Caratti break; 5110e54d217SDavide Caratti } 5120e54d217SDavide Caratti if (i != num_proto) { 5130e54d217SDavide Caratti ver = l4proto[i]->l3proto == PF_INET6 ? 6 : 4; 51409ec82f5SFlorian Westphal pr_err("nf_conntrack_ipv%d: can't register l4 %d proto.\n", 51509ec82f5SFlorian Westphal ver, l4proto[i]->l4proto); 5160e54d217SDavide Caratti nf_ct_l4proto_unregister(l4proto, i); 5170e54d217SDavide Caratti } 5180e54d217SDavide Caratti return ret; 5190e54d217SDavide Caratti } 5200e54d217SDavide Caratti EXPORT_SYMBOL_GPL(nf_ct_l4proto_register); 5210e54d217SDavide Caratti 5220e54d217SDavide Caratti int nf_ct_l4proto_pernet_register(struct net *net, 523cd9ceafcSFlorian Westphal const struct nf_conntrack_l4proto *const l4proto[], 5240e54d217SDavide Caratti unsigned int num_proto) 5250e54d217SDavide Caratti { 5260e54d217SDavide Caratti int ret = -EINVAL; 5270e54d217SDavide Caratti unsigned int i; 5280e54d217SDavide Caratti 5290e54d217SDavide Caratti for (i = 0; i < num_proto; i++) { 5300e54d217SDavide Caratti ret = nf_ct_l4proto_pernet_register_one(net, l4proto[i]); 5310e54d217SDavide Caratti if (ret < 0) 5320e54d217SDavide Caratti break; 5330e54d217SDavide Caratti } 5340e54d217SDavide Caratti if (i != num_proto) { 53509ec82f5SFlorian Westphal pr_err("nf_conntrack_proto_%d %d: pernet registration failed\n", 53609ec82f5SFlorian Westphal l4proto[i]->l4proto, 5370e54d217SDavide Caratti l4proto[i]->l3proto == PF_INET6 ? 6 : 4); 5380e54d217SDavide Caratti nf_ct_l4proto_pernet_unregister(net, l4proto, i); 5390e54d217SDavide Caratti } 5400e54d217SDavide Caratti return ret; 5410e54d217SDavide Caratti } 5420e54d217SDavide Caratti EXPORT_SYMBOL_GPL(nf_ct_l4proto_pernet_register); 5430e54d217SDavide Caratti 544cd9ceafcSFlorian Westphal void nf_ct_l4proto_unregister(const struct nf_conntrack_l4proto * const l4proto[], 5450e54d217SDavide Caratti unsigned int num_proto) 5460e54d217SDavide Caratti { 5472c41f33cSFlorian Westphal mutex_lock(&nf_ct_proto_mutex); 5480e54d217SDavide Caratti while (num_proto-- != 0) 5492c41f33cSFlorian Westphal __nf_ct_l4proto_unregister_one(l4proto[num_proto]); 5502c41f33cSFlorian Westphal mutex_unlock(&nf_ct_proto_mutex); 5512c41f33cSFlorian Westphal 5522c41f33cSFlorian Westphal synchronize_net(); 5532c41f33cSFlorian Westphal /* Remove all contrack entries for this protocol */ 554cd9ceafcSFlorian Westphal nf_ct_iterate_destroy(kill_l4proto, (void *)l4proto); 5550e54d217SDavide Caratti } 5560e54d217SDavide Caratti EXPORT_SYMBOL_GPL(nf_ct_l4proto_unregister); 5570e54d217SDavide Caratti 5580e54d217SDavide Caratti void nf_ct_l4proto_pernet_unregister(struct net *net, 559cd9ceafcSFlorian Westphal const struct nf_conntrack_l4proto *const l4proto[], 5600e54d217SDavide Caratti unsigned int num_proto) 5610e54d217SDavide Caratti { 5620e54d217SDavide Caratti while (num_proto-- != 0) 5630e54d217SDavide Caratti nf_ct_l4proto_pernet_unregister_one(net, l4proto[num_proto]); 5640e54d217SDavide Caratti } 565c296bb4dSGao feng EXPORT_SYMBOL_GPL(nf_ct_l4proto_pernet_unregister); 566ac5357ebSPatrick McHardy 56704d87001SGao feng int nf_conntrack_proto_pernet_init(struct net *net) 568ac5357ebSPatrick McHardy { 569ac5357ebSPatrick McHardy int err; 570fa34fff5SGao feng struct nf_proto_net *pn = nf_ct_l4proto_net(net, 571fa34fff5SGao feng &nf_conntrack_l4proto_generic); 572fa34fff5SGao feng 573f1caad27SGao feng err = nf_conntrack_l4proto_generic.init_net(net, 574f1caad27SGao feng nf_conntrack_l4proto_generic.l3proto); 57515f585bdSGao feng if (err < 0) 57615f585bdSGao feng return err; 57715f585bdSGao feng err = nf_ct_l4proto_register_sysctl(net, 578fa34fff5SGao feng pn, 57915f585bdSGao feng &nf_conntrack_l4proto_generic); 580ac5357ebSPatrick McHardy if (err < 0) 581ac5357ebSPatrick McHardy return err; 582ac5357ebSPatrick McHardy 583fa34fff5SGao feng pn->users++; 584ac5357ebSPatrick McHardy return 0; 585ac5357ebSPatrick McHardy } 586ac5357ebSPatrick McHardy 58704d87001SGao feng void nf_conntrack_proto_pernet_fini(struct net *net) 588ac5357ebSPatrick McHardy { 589fa34fff5SGao feng struct nf_proto_net *pn = nf_ct_l4proto_net(net, 590fa34fff5SGao feng &nf_conntrack_l4proto_generic); 591fa34fff5SGao feng 592fa34fff5SGao feng pn->users--; 59315f585bdSGao feng nf_ct_l4proto_unregister_sysctl(net, 594fa34fff5SGao feng pn, 59515f585bdSGao feng &nf_conntrack_l4proto_generic); 59604d87001SGao feng } 59704d87001SGao feng 59804d87001SGao feng int nf_conntrack_proto_init(void) 59904d87001SGao feng { 60004d87001SGao feng unsigned int i; 601b7b5fda4SFlorian Westphal for (i = 0; i < NFPROTO_NUMPROTO; i++) 60204d87001SGao feng rcu_assign_pointer(nf_ct_l3protos[i], 60304d87001SGao feng &nf_conntrack_l3proto_generic); 60404d87001SGao feng return 0; 60504d87001SGao feng } 60604d87001SGao feng 60704d87001SGao feng void nf_conntrack_proto_fini(void) 60804d87001SGao feng { 60904d87001SGao feng unsigned int i; 610ac5357ebSPatrick McHardy /* free l3proto protocol tables */ 611b7b5fda4SFlorian Westphal for (i = 0; i < ARRAY_SIZE(nf_ct_protos); i++) 612ac5357ebSPatrick McHardy kfree(nf_ct_protos[i]); 613ac5357ebSPatrick McHardy } 614