nf_conntrack_helper.c revision b8a7fe6c10511fce10b20efa163123f4041f2550
1/* Helper handling for netfilter. */ 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/skbuff.h> 16#include <linux/vmalloc.h> 17#include <linux/stddef.h> 18#include <linux/slab.h> 19#include <linux/random.h> 20#include <linux/err.h> 21#include <linux/kernel.h> 22#include <linux/netdevice.h> 23 24#include <net/netfilter/nf_conntrack.h> 25#include <net/netfilter/nf_conntrack_l3proto.h> 26#include <net/netfilter/nf_conntrack_l4proto.h> 27#include <net/netfilter/nf_conntrack_helper.h> 28#include <net/netfilter/nf_conntrack_core.h> 29#include <net/netfilter/nf_conntrack_extend.h> 30 31static struct hlist_head *nf_ct_helper_hash __read_mostly; 32static unsigned int nf_ct_helper_hsize __read_mostly; 33static unsigned int nf_ct_helper_count __read_mostly; 34static int nf_ct_helper_vmalloc; 35 36 37/* Stupid hash, but collision free for the default registrations of the 38 * helpers currently in the kernel. */ 39static unsigned int helper_hash(const struct nf_conntrack_tuple *tuple) 40{ 41 return (((tuple->src.l3num << 8) | tuple->dst.protonum) ^ 42 tuple->src.u.all) % nf_ct_helper_hsize; 43} 44 45struct nf_conntrack_helper * 46__nf_ct_helper_find(const struct nf_conntrack_tuple *tuple) 47{ 48 struct nf_conntrack_helper *helper; 49 struct nf_conntrack_tuple_mask mask = { .src.u.all = htons(0xFFFF) }; 50 struct hlist_node *n; 51 unsigned int h; 52 53 if (!nf_ct_helper_count) 54 return NULL; 55 56 h = helper_hash(tuple); 57 hlist_for_each_entry(helper, n, &nf_ct_helper_hash[h], hnode) { 58 if (nf_ct_tuple_src_mask_cmp(tuple, &helper->tuple, &mask)) 59 return helper; 60 } 61 return NULL; 62} 63 64struct nf_conntrack_helper * 65nf_ct_helper_find_get(const struct nf_conntrack_tuple *tuple) 66{ 67 struct nf_conntrack_helper *helper; 68 69 /* need nf_conntrack_lock to assure that helper exists until 70 * try_module_get() is called */ 71 read_lock_bh(&nf_conntrack_lock); 72 73 helper = __nf_ct_helper_find(tuple); 74 if (helper) { 75 /* need to increase module usage count to assure helper will 76 * not go away while the caller is e.g. busy putting a 77 * conntrack in the hash that uses the helper */ 78 if (!try_module_get(helper->me)) 79 helper = NULL; 80 } 81 82 read_unlock_bh(&nf_conntrack_lock); 83 84 return helper; 85} 86EXPORT_SYMBOL_GPL(nf_ct_helper_find_get); 87 88void nf_ct_helper_put(struct nf_conntrack_helper *helper) 89{ 90 module_put(helper->me); 91} 92EXPORT_SYMBOL_GPL(nf_ct_helper_put); 93 94struct nf_conntrack_helper * 95__nf_conntrack_helper_find_byname(const char *name) 96{ 97 struct nf_conntrack_helper *h; 98 struct hlist_node *n; 99 unsigned int i; 100 101 for (i = 0; i < nf_ct_helper_hsize; i++) { 102 hlist_for_each_entry(h, n, &nf_ct_helper_hash[i], hnode) { 103 if (!strcmp(h->name, name)) 104 return h; 105 } 106 } 107 return NULL; 108} 109EXPORT_SYMBOL_GPL(__nf_conntrack_helper_find_byname); 110 111struct nf_conn_help *nf_ct_helper_ext_add(struct nf_conn *ct, gfp_t gfp) 112{ 113 struct nf_conn_help *help; 114 115 help = nf_ct_ext_add(ct, NF_CT_EXT_HELPER, gfp); 116 if (help) 117 INIT_HLIST_HEAD(&help->expectations); 118 else 119 pr_debug("failed to add helper extension area"); 120 return help; 121} 122EXPORT_SYMBOL_GPL(nf_ct_helper_ext_add); 123 124static inline int unhelp(struct nf_conntrack_tuple_hash *i, 125 const struct nf_conntrack_helper *me) 126{ 127 struct nf_conn *ct = nf_ct_tuplehash_to_ctrack(i); 128 struct nf_conn_help *help = nfct_help(ct); 129 130 if (help && help->helper == me) { 131 nf_conntrack_event(IPCT_HELPER, ct); 132 rcu_assign_pointer(help->helper, NULL); 133 } 134 return 0; 135} 136 137int nf_conntrack_helper_register(struct nf_conntrack_helper *me) 138{ 139 unsigned int h = helper_hash(&me->tuple); 140 141 BUG_ON(me->timeout == 0); 142 143 write_lock_bh(&nf_conntrack_lock); 144 hlist_add_head(&me->hnode, &nf_ct_helper_hash[h]); 145 nf_ct_helper_count++; 146 write_unlock_bh(&nf_conntrack_lock); 147 148 return 0; 149} 150EXPORT_SYMBOL_GPL(nf_conntrack_helper_register); 151 152void nf_conntrack_helper_unregister(struct nf_conntrack_helper *me) 153{ 154 struct nf_conntrack_tuple_hash *h; 155 struct nf_conntrack_expect *exp; 156 struct hlist_node *n, *next; 157 unsigned int i; 158 159 /* Need write lock here, to delete helper. */ 160 write_lock_bh(&nf_conntrack_lock); 161 hlist_del(&me->hnode); 162 nf_ct_helper_count--; 163 164 /* Get rid of expectations */ 165 for (i = 0; i < nf_ct_expect_hsize; i++) { 166 hlist_for_each_entry_safe(exp, n, next, 167 &nf_ct_expect_hash[i], hnode) { 168 struct nf_conn_help *help = nfct_help(exp->master); 169 if ((help->helper == me || exp->helper == me) && 170 del_timer(&exp->timeout)) { 171 nf_ct_unlink_expect(exp); 172 nf_ct_expect_put(exp); 173 } 174 } 175 } 176 177 /* Get rid of expecteds, set helpers to NULL. */ 178 hlist_for_each_entry(h, n, &unconfirmed, hnode) 179 unhelp(h, me); 180 for (i = 0; i < nf_conntrack_htable_size; i++) { 181 hlist_for_each_entry(h, n, &nf_conntrack_hash[i], hnode) 182 unhelp(h, me); 183 } 184 write_unlock_bh(&nf_conntrack_lock); 185 186 /* Someone could be still looking at the helper in a bh. */ 187 synchronize_net(); 188} 189EXPORT_SYMBOL_GPL(nf_conntrack_helper_unregister); 190 191static struct nf_ct_ext_type helper_extend __read_mostly = { 192 .len = sizeof(struct nf_conn_help), 193 .align = __alignof__(struct nf_conn_help), 194 .id = NF_CT_EXT_HELPER, 195}; 196 197int nf_conntrack_helper_init() 198{ 199 int err; 200 201 nf_ct_helper_hsize = 1; /* gets rounded up to use one page */ 202 nf_ct_helper_hash = nf_ct_alloc_hashtable(&nf_ct_helper_hsize, 203 &nf_ct_helper_vmalloc); 204 if (!nf_ct_helper_hash) 205 return -ENOMEM; 206 207 err = nf_ct_extend_register(&helper_extend); 208 if (err < 0) 209 goto err1; 210 211 return 0; 212 213err1: 214 nf_ct_free_hashtable(nf_ct_helper_hash, nf_ct_helper_vmalloc, 215 nf_ct_helper_hsize); 216 return err; 217} 218 219void nf_conntrack_helper_fini() 220{ 221 nf_ct_extend_unregister(&helper_extend); 222 nf_ct_free_hashtable(nf_ct_helper_hash, nf_ct_helper_vmalloc, 223 nf_ct_helper_hsize); 224} 225