1/* L3/L4 protocol support for nf_conntrack. */ 2 3/* (C) 1999-2001 Paul `Rusty' Russell 4 * (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org> 5 * (C) 2003,2004 USAGI/WIDE Project <http://www.linux-ipv6.org> 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License version 2 as 9 * published by the Free Software Foundation. 10 */ 11 12#include <linux/types.h> 13#include <linux/netfilter.h> 14#include <linux/module.h> 15#include <linux/slab.h> 16#include <linux/mutex.h> 17#include <linux/vmalloc.h> 18#include <linux/stddef.h> 19#include <linux/err.h> 20#include <linux/percpu.h> 21#include <linux/notifier.h> 22#include <linux/kernel.h> 23#include <linux/netdevice.h> 24#include <linux/rtnetlink.h> 25 26#include <net/netfilter/nf_conntrack.h> 27#include <net/netfilter/nf_conntrack_l3proto.h> 28#include <net/netfilter/nf_conntrack_l4proto.h> 29#include <net/netfilter/nf_conntrack_core.h> 30 31static struct nf_conntrack_l4proto **nf_ct_protos[PF_MAX] __read_mostly; 32struct nf_conntrack_l3proto *nf_ct_l3protos[AF_MAX] __read_mostly; 33EXPORT_SYMBOL_GPL(nf_ct_l3protos); 34 35static DEFINE_MUTEX(nf_ct_proto_mutex); 36 37#ifdef CONFIG_SYSCTL 38static int 39nf_ct_register_sysctl(struct ctl_table_header **header, struct ctl_path *path, 40 struct ctl_table *table, unsigned int *users) 41{ 42 if (*header == NULL) { 43 *header = register_sysctl_paths(path, table); 44 if (*header == NULL) 45 return -ENOMEM; 46 } 47 if (users != NULL) 48 (*users)++; 49 return 0; 50} 51 52static void 53nf_ct_unregister_sysctl(struct ctl_table_header **header, 54 struct ctl_table *table, unsigned int *users) 55{ 56 if (users != NULL && --*users > 0) 57 return; 58 59 unregister_sysctl_table(*header); 60 *header = NULL; 61} 62#endif 63 64struct nf_conntrack_l4proto * 65__nf_ct_l4proto_find(u_int16_t l3proto, u_int8_t l4proto) 66{ 67 if (unlikely(l3proto >= AF_MAX || nf_ct_protos[l3proto] == NULL)) 68 return &nf_conntrack_l4proto_generic; 69 70 return rcu_dereference(nf_ct_protos[l3proto][l4proto]); 71} 72EXPORT_SYMBOL_GPL(__nf_ct_l4proto_find); 73 74/* this is guaranteed to always return a valid protocol helper, since 75 * it falls back to generic_protocol */ 76struct nf_conntrack_l3proto * 77nf_ct_l3proto_find_get(u_int16_t l3proto) 78{ 79 struct nf_conntrack_l3proto *p; 80 81 rcu_read_lock(); 82 p = __nf_ct_l3proto_find(l3proto); 83 if (!try_module_get(p->me)) 84 p = &nf_conntrack_l3proto_generic; 85 rcu_read_unlock(); 86 87 return p; 88} 89EXPORT_SYMBOL_GPL(nf_ct_l3proto_find_get); 90 91void nf_ct_l3proto_put(struct nf_conntrack_l3proto *p) 92{ 93 module_put(p->me); 94} 95EXPORT_SYMBOL_GPL(nf_ct_l3proto_put); 96 97int 98nf_ct_l3proto_try_module_get(unsigned short l3proto) 99{ 100 int ret; 101 struct nf_conntrack_l3proto *p; 102 103retry: p = nf_ct_l3proto_find_get(l3proto); 104 if (p == &nf_conntrack_l3proto_generic) { 105 ret = request_module("nf_conntrack-%d", l3proto); 106 if (!ret) 107 goto retry; 108 109 return -EPROTOTYPE; 110 } 111 112 return 0; 113} 114EXPORT_SYMBOL_GPL(nf_ct_l3proto_try_module_get); 115 116void nf_ct_l3proto_module_put(unsigned short l3proto) 117{ 118 struct nf_conntrack_l3proto *p; 119 120 /* rcu_read_lock not necessary since the caller holds a reference, but 121 * taken anyways to avoid lockdep warnings in __nf_ct_l3proto_find() 122 */ 123 rcu_read_lock(); 124 p = __nf_ct_l3proto_find(l3proto); 125 module_put(p->me); 126 rcu_read_unlock(); 127} 128EXPORT_SYMBOL_GPL(nf_ct_l3proto_module_put); 129 130static int kill_l3proto(struct nf_conn *i, void *data) 131{ 132 return nf_ct_l3num(i) == ((struct nf_conntrack_l3proto *)data)->l3proto; 133} 134 135static int kill_l4proto(struct nf_conn *i, void *data) 136{ 137 struct nf_conntrack_l4proto *l4proto; 138 l4proto = (struct nf_conntrack_l4proto *)data; 139 return nf_ct_protonum(i) == l4proto->l4proto && 140 nf_ct_l3num(i) == l4proto->l3proto; 141} 142 143static int nf_ct_l3proto_register_sysctl(struct nf_conntrack_l3proto *l3proto) 144{ 145 int err = 0; 146 147#ifdef CONFIG_SYSCTL 148 if (l3proto->ctl_table != NULL) { 149 err = nf_ct_register_sysctl(&l3proto->ctl_table_header, 150 l3proto->ctl_table_path, 151 l3proto->ctl_table, NULL); 152 } 153#endif 154 return err; 155} 156 157static void nf_ct_l3proto_unregister_sysctl(struct nf_conntrack_l3proto *l3proto) 158{ 159#ifdef CONFIG_SYSCTL 160 if (l3proto->ctl_table_header != NULL) 161 nf_ct_unregister_sysctl(&l3proto->ctl_table_header, 162 l3proto->ctl_table, NULL); 163#endif 164} 165 166int nf_conntrack_l3proto_register(struct nf_conntrack_l3proto *proto) 167{ 168 int ret = 0; 169 170 if (proto->l3proto >= AF_MAX) 171 return -EBUSY; 172 173 if (proto->tuple_to_nlattr && !proto->nlattr_tuple_size) 174 return -EINVAL; 175 176 mutex_lock(&nf_ct_proto_mutex); 177 if (nf_ct_l3protos[proto->l3proto] != &nf_conntrack_l3proto_generic) { 178 ret = -EBUSY; 179 goto out_unlock; 180 } 181 182 ret = nf_ct_l3proto_register_sysctl(proto); 183 if (ret < 0) 184 goto out_unlock; 185 186 if (proto->nlattr_tuple_size) 187 proto->nla_size = 3 * proto->nlattr_tuple_size(); 188 189 rcu_assign_pointer(nf_ct_l3protos[proto->l3proto], proto); 190 191out_unlock: 192 mutex_unlock(&nf_ct_proto_mutex); 193 return ret; 194} 195EXPORT_SYMBOL_GPL(nf_conntrack_l3proto_register); 196 197void nf_conntrack_l3proto_unregister(struct nf_conntrack_l3proto *proto) 198{ 199 struct net *net; 200 201 BUG_ON(proto->l3proto >= AF_MAX); 202 203 mutex_lock(&nf_ct_proto_mutex); 204 BUG_ON(nf_ct_l3protos[proto->l3proto] != proto); 205 rcu_assign_pointer(nf_ct_l3protos[proto->l3proto], 206 &nf_conntrack_l3proto_generic); 207 nf_ct_l3proto_unregister_sysctl(proto); 208 mutex_unlock(&nf_ct_proto_mutex); 209 210 synchronize_rcu(); 211 212 /* Remove all contrack entries for this protocol */ 213 rtnl_lock(); 214 for_each_net(net) 215 nf_ct_iterate_cleanup(net, kill_l3proto, proto); 216 rtnl_unlock(); 217} 218EXPORT_SYMBOL_GPL(nf_conntrack_l3proto_unregister); 219 220static int nf_ct_l4proto_register_sysctl(struct nf_conntrack_l4proto *l4proto) 221{ 222 int err = 0; 223 224#ifdef CONFIG_SYSCTL 225 if (l4proto->ctl_table != NULL) { 226 err = nf_ct_register_sysctl(l4proto->ctl_table_header, 227 nf_net_netfilter_sysctl_path, 228 l4proto->ctl_table, 229 l4proto->ctl_table_users); 230 if (err < 0) 231 goto out; 232 } 233#ifdef CONFIG_NF_CONNTRACK_PROC_COMPAT 234 if (l4proto->ctl_compat_table != NULL) { 235 err = nf_ct_register_sysctl(&l4proto->ctl_compat_table_header, 236 nf_net_ipv4_netfilter_sysctl_path, 237 l4proto->ctl_compat_table, NULL); 238 if (err == 0) 239 goto out; 240 nf_ct_unregister_sysctl(l4proto->ctl_table_header, 241 l4proto->ctl_table, 242 l4proto->ctl_table_users); 243 } 244#endif /* CONFIG_NF_CONNTRACK_PROC_COMPAT */ 245out: 246#endif /* CONFIG_SYSCTL */ 247 return err; 248} 249 250static void nf_ct_l4proto_unregister_sysctl(struct nf_conntrack_l4proto *l4proto) 251{ 252#ifdef CONFIG_SYSCTL 253 if (l4proto->ctl_table_header != NULL && 254 *l4proto->ctl_table_header != NULL) 255 nf_ct_unregister_sysctl(l4proto->ctl_table_header, 256 l4proto->ctl_table, 257 l4proto->ctl_table_users); 258#ifdef CONFIG_NF_CONNTRACK_PROC_COMPAT 259 if (l4proto->ctl_compat_table_header != NULL) 260 nf_ct_unregister_sysctl(&l4proto->ctl_compat_table_header, 261 l4proto->ctl_compat_table, NULL); 262#endif /* CONFIG_NF_CONNTRACK_PROC_COMPAT */ 263#endif /* CONFIG_SYSCTL */ 264} 265 266int nf_conntrack_l4proto_register(struct nf_conntrack_l4proto *l4proto) 267{ 268 int ret = 0; 269 270 if (l4proto->l3proto >= PF_MAX) 271 return -EBUSY; 272 273 if ((l4proto->to_nlattr && !l4proto->nlattr_size) 274 || (l4proto->tuple_to_nlattr && !l4proto->nlattr_tuple_size)) 275 return -EINVAL; 276 277 mutex_lock(&nf_ct_proto_mutex); 278 if (!nf_ct_protos[l4proto->l3proto]) { 279 /* l3proto may be loaded latter. */ 280 struct nf_conntrack_l4proto **proto_array; 281 int i; 282 283 proto_array = kmalloc(MAX_NF_CT_PROTO * 284 sizeof(struct nf_conntrack_l4proto *), 285 GFP_KERNEL); 286 if (proto_array == NULL) { 287 ret = -ENOMEM; 288 goto out_unlock; 289 } 290 291 for (i = 0; i < MAX_NF_CT_PROTO; i++) 292 proto_array[i] = &nf_conntrack_l4proto_generic; 293 nf_ct_protos[l4proto->l3proto] = proto_array; 294 } else if (nf_ct_protos[l4proto->l3proto][l4proto->l4proto] != 295 &nf_conntrack_l4proto_generic) { 296 ret = -EBUSY; 297 goto out_unlock; 298 } 299 300 ret = nf_ct_l4proto_register_sysctl(l4proto); 301 if (ret < 0) 302 goto out_unlock; 303 304 l4proto->nla_size = 0; 305 if (l4proto->nlattr_size) 306 l4proto->nla_size += l4proto->nlattr_size(); 307 if (l4proto->nlattr_tuple_size) 308 l4proto->nla_size += 3 * l4proto->nlattr_tuple_size(); 309 310 rcu_assign_pointer(nf_ct_protos[l4proto->l3proto][l4proto->l4proto], 311 l4proto); 312 313out_unlock: 314 mutex_unlock(&nf_ct_proto_mutex); 315 return ret; 316} 317EXPORT_SYMBOL_GPL(nf_conntrack_l4proto_register); 318 319void nf_conntrack_l4proto_unregister(struct nf_conntrack_l4proto *l4proto) 320{ 321 struct net *net; 322 323 BUG_ON(l4proto->l3proto >= PF_MAX); 324 325 mutex_lock(&nf_ct_proto_mutex); 326 BUG_ON(nf_ct_protos[l4proto->l3proto][l4proto->l4proto] != l4proto); 327 rcu_assign_pointer(nf_ct_protos[l4proto->l3proto][l4proto->l4proto], 328 &nf_conntrack_l4proto_generic); 329 nf_ct_l4proto_unregister_sysctl(l4proto); 330 mutex_unlock(&nf_ct_proto_mutex); 331 332 synchronize_rcu(); 333 334 /* Remove all contrack entries for this protocol */ 335 rtnl_lock(); 336 for_each_net(net) 337 nf_ct_iterate_cleanup(net, kill_l4proto, l4proto); 338 rtnl_unlock(); 339} 340EXPORT_SYMBOL_GPL(nf_conntrack_l4proto_unregister); 341 342int nf_conntrack_proto_init(void) 343{ 344 unsigned int i; 345 int err; 346 347 err = nf_ct_l4proto_register_sysctl(&nf_conntrack_l4proto_generic); 348 if (err < 0) 349 return err; 350 351 for (i = 0; i < AF_MAX; i++) 352 rcu_assign_pointer(nf_ct_l3protos[i], 353 &nf_conntrack_l3proto_generic); 354 return 0; 355} 356 357void nf_conntrack_proto_fini(void) 358{ 359 unsigned int i; 360 361 nf_ct_l4proto_unregister_sysctl(&nf_conntrack_l4proto_generic); 362 363 /* free l3proto protocol tables */ 364 for (i = 0; i < PF_MAX; i++) 365 kfree(nf_ct_protos[i]); 366} 367