| 1 | // SPDX-License-Identifier: GPL-2.0-only | 
|---|
| 2 | /* Helper handling for netfilter. */ | 
|---|
| 3 |  | 
|---|
| 4 | /* (C) 1999-2001 Paul `Rusty' Russell | 
|---|
| 5 | * (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org> | 
|---|
| 6 | * (C) 2003,2004 USAGI/WIDE Project <http://www.linux-ipv6.org> | 
|---|
| 7 | * (C) 2006-2012 Patrick McHardy <kaber@trash.net> | 
|---|
| 8 | */ | 
|---|
| 9 |  | 
|---|
| 10 | #include <linux/types.h> | 
|---|
| 11 | #include <linux/netfilter.h> | 
|---|
| 12 | #include <linux/module.h> | 
|---|
| 13 | #include <linux/skbuff.h> | 
|---|
| 14 | #include <linux/vmalloc.h> | 
|---|
| 15 | #include <linux/stddef.h> | 
|---|
| 16 | #include <linux/random.h> | 
|---|
| 17 | #include <linux/err.h> | 
|---|
| 18 | #include <linux/kernel.h> | 
|---|
| 19 | #include <linux/netdevice.h> | 
|---|
| 20 | #include <linux/rculist.h> | 
|---|
| 21 | #include <linux/rtnetlink.h> | 
|---|
| 22 |  | 
|---|
| 23 | #include <net/netfilter/nf_conntrack.h> | 
|---|
| 24 | #include <net/netfilter/nf_conntrack_core.h> | 
|---|
| 25 | #include <net/netfilter/nf_conntrack_ecache.h> | 
|---|
| 26 | #include <net/netfilter/nf_conntrack_extend.h> | 
|---|
| 27 | #include <net/netfilter/nf_conntrack_helper.h> | 
|---|
| 28 | #include <net/netfilter/nf_conntrack_l4proto.h> | 
|---|
| 29 | #include <net/netfilter/nf_conntrack_seqadj.h> | 
|---|
| 30 | #include <net/netfilter/nf_log.h> | 
|---|
| 31 | #include <net/ip.h> | 
|---|
| 32 |  | 
|---|
| 33 | static DEFINE_MUTEX(nf_ct_helper_mutex); | 
|---|
| 34 | struct hlist_head *nf_ct_helper_hash __read_mostly; | 
|---|
| 35 | EXPORT_SYMBOL_GPL(nf_ct_helper_hash); | 
|---|
| 36 | unsigned int nf_ct_helper_hsize __read_mostly; | 
|---|
| 37 | EXPORT_SYMBOL_GPL(nf_ct_helper_hsize); | 
|---|
| 38 | static unsigned int nf_ct_helper_count __read_mostly; | 
|---|
| 39 |  | 
|---|
| 40 | static DEFINE_MUTEX(nf_ct_nat_helpers_mutex); | 
|---|
| 41 | static struct list_head nf_ct_nat_helpers __read_mostly; | 
|---|
| 42 |  | 
|---|
| 43 | /* Stupid hash, but collision free for the default registrations of the | 
|---|
| 44 | * helpers currently in the kernel. */ | 
|---|
| 45 | static unsigned int helper_hash(const struct nf_conntrack_tuple *tuple) | 
|---|
| 46 | { | 
|---|
| 47 | return (((tuple->src.l3num << 8) | tuple->dst.protonum) ^ | 
|---|
| 48 | (__force __u16)tuple->src.u.all) % nf_ct_helper_hsize; | 
|---|
| 49 | } | 
|---|
| 50 |  | 
|---|
| 51 | struct nf_conntrack_helper * | 
|---|
| 52 | __nf_conntrack_helper_find(const char *name, u16 l3num, u8 protonum) | 
|---|
| 53 | { | 
|---|
| 54 | struct nf_conntrack_helper *h; | 
|---|
| 55 | unsigned int i; | 
|---|
| 56 |  | 
|---|
| 57 | for (i = 0; i < nf_ct_helper_hsize; i++) { | 
|---|
| 58 | hlist_for_each_entry_rcu(h, &nf_ct_helper_hash[i], hnode) { | 
|---|
| 59 | if (strcmp(h->name, name)) | 
|---|
| 60 | continue; | 
|---|
| 61 |  | 
|---|
| 62 | if (h->tuple.src.l3num != NFPROTO_UNSPEC && | 
|---|
| 63 | h->tuple.src.l3num != l3num) | 
|---|
| 64 | continue; | 
|---|
| 65 |  | 
|---|
| 66 | if (h->tuple.dst.protonum == protonum) | 
|---|
| 67 | return h; | 
|---|
| 68 | } | 
|---|
| 69 | } | 
|---|
| 70 | return NULL; | 
|---|
| 71 | } | 
|---|
| 72 | EXPORT_SYMBOL_GPL(__nf_conntrack_helper_find); | 
|---|
| 73 |  | 
|---|
| 74 | struct nf_conntrack_helper * | 
|---|
| 75 | nf_conntrack_helper_try_module_get(const char *name, u16 l3num, u8 protonum) | 
|---|
| 76 | { | 
|---|
| 77 | struct nf_conntrack_helper *h; | 
|---|
| 78 |  | 
|---|
| 79 | rcu_read_lock(); | 
|---|
| 80 |  | 
|---|
| 81 | h = __nf_conntrack_helper_find(name, l3num, protonum); | 
|---|
| 82 | #ifdef CONFIG_MODULES | 
|---|
| 83 | if (h == NULL) { | 
|---|
| 84 | rcu_read_unlock(); | 
|---|
| 85 | if (request_module( "nfct-helper-%s", name) == 0) { | 
|---|
| 86 | rcu_read_lock(); | 
|---|
| 87 | h = __nf_conntrack_helper_find(name, l3num, protonum); | 
|---|
| 88 | } else { | 
|---|
| 89 | return h; | 
|---|
| 90 | } | 
|---|
| 91 | } | 
|---|
| 92 | #endif | 
|---|
| 93 | if (h != NULL && !try_module_get(module: h->me)) | 
|---|
| 94 | h = NULL; | 
|---|
| 95 | if (h != NULL && !refcount_inc_not_zero(r: &h->refcnt)) { | 
|---|
| 96 | module_put(module: h->me); | 
|---|
| 97 | h = NULL; | 
|---|
| 98 | } | 
|---|
| 99 |  | 
|---|
| 100 | rcu_read_unlock(); | 
|---|
| 101 |  | 
|---|
| 102 | return h; | 
|---|
| 103 | } | 
|---|
| 104 | EXPORT_SYMBOL_GPL(nf_conntrack_helper_try_module_get); | 
|---|
| 105 |  | 
|---|
| 106 | void nf_conntrack_helper_put(struct nf_conntrack_helper *helper) | 
|---|
| 107 | { | 
|---|
| 108 | refcount_dec(r: &helper->refcnt); | 
|---|
| 109 | module_put(module: helper->me); | 
|---|
| 110 | } | 
|---|
| 111 | EXPORT_SYMBOL_GPL(nf_conntrack_helper_put); | 
|---|
| 112 |  | 
|---|
| 113 | static struct nf_conntrack_nat_helper * | 
|---|
| 114 | nf_conntrack_nat_helper_find(const char *mod_name) | 
|---|
| 115 | { | 
|---|
| 116 | struct nf_conntrack_nat_helper *cur; | 
|---|
| 117 | bool found = false; | 
|---|
| 118 |  | 
|---|
| 119 | list_for_each_entry_rcu(cur, &nf_ct_nat_helpers, list) { | 
|---|
| 120 | if (!strcmp(cur->mod_name, mod_name)) { | 
|---|
| 121 | found = true; | 
|---|
| 122 | break; | 
|---|
| 123 | } | 
|---|
| 124 | } | 
|---|
| 125 | return found ? cur : NULL; | 
|---|
| 126 | } | 
|---|
| 127 |  | 
|---|
| 128 | int | 
|---|
| 129 | nf_nat_helper_try_module_get(const char *name, u16 l3num, u8 protonum) | 
|---|
| 130 | { | 
|---|
| 131 | struct nf_conntrack_helper *h; | 
|---|
| 132 | struct nf_conntrack_nat_helper *nat; | 
|---|
| 133 | char mod_name[NF_CT_HELPER_NAME_LEN]; | 
|---|
| 134 | int ret = 0; | 
|---|
| 135 |  | 
|---|
| 136 | rcu_read_lock(); | 
|---|
| 137 | h = __nf_conntrack_helper_find(name, l3num, protonum); | 
|---|
| 138 | if (!h) { | 
|---|
| 139 | rcu_read_unlock(); | 
|---|
| 140 | return -ENOENT; | 
|---|
| 141 | } | 
|---|
| 142 |  | 
|---|
| 143 | nat = nf_conntrack_nat_helper_find(mod_name: h->nat_mod_name); | 
|---|
| 144 | if (!nat) { | 
|---|
| 145 | snprintf(buf: mod_name, size: sizeof(mod_name), fmt: "%s", h->nat_mod_name); | 
|---|
| 146 | rcu_read_unlock(); | 
|---|
| 147 | request_module( "%s", mod_name); | 
|---|
| 148 |  | 
|---|
| 149 | rcu_read_lock(); | 
|---|
| 150 | nat = nf_conntrack_nat_helper_find(mod_name); | 
|---|
| 151 | if (!nat) { | 
|---|
| 152 | rcu_read_unlock(); | 
|---|
| 153 | return -ENOENT; | 
|---|
| 154 | } | 
|---|
| 155 | } | 
|---|
| 156 |  | 
|---|
| 157 | if (!try_module_get(module: nat->module)) | 
|---|
| 158 | ret = -ENOENT; | 
|---|
| 159 |  | 
|---|
| 160 | rcu_read_unlock(); | 
|---|
| 161 | return ret; | 
|---|
| 162 | } | 
|---|
| 163 | EXPORT_SYMBOL_GPL(nf_nat_helper_try_module_get); | 
|---|
| 164 |  | 
|---|
| 165 | void nf_nat_helper_put(struct nf_conntrack_helper *helper) | 
|---|
| 166 | { | 
|---|
| 167 | struct nf_conntrack_nat_helper *nat; | 
|---|
| 168 |  | 
|---|
| 169 | nat = nf_conntrack_nat_helper_find(mod_name: helper->nat_mod_name); | 
|---|
| 170 | if (WARN_ON_ONCE(!nat)) | 
|---|
| 171 | return; | 
|---|
| 172 |  | 
|---|
| 173 | module_put(module: nat->module); | 
|---|
| 174 | } | 
|---|
| 175 | EXPORT_SYMBOL_GPL(nf_nat_helper_put); | 
|---|
| 176 |  | 
|---|
| 177 | struct nf_conn_help * | 
|---|
| 178 | nf_ct_helper_ext_add(struct nf_conn *ct, gfp_t gfp) | 
|---|
| 179 | { | 
|---|
| 180 | struct nf_conn_help *help; | 
|---|
| 181 |  | 
|---|
| 182 | help = nf_ct_ext_add(ct, id: NF_CT_EXT_HELPER, gfp); | 
|---|
| 183 | if (help) | 
|---|
| 184 | INIT_HLIST_HEAD(&help->expectations); | 
|---|
| 185 | else | 
|---|
| 186 | pr_debug( "failed to add helper extension area"); | 
|---|
| 187 | return help; | 
|---|
| 188 | } | 
|---|
| 189 | EXPORT_SYMBOL_GPL(nf_ct_helper_ext_add); | 
|---|
| 190 |  | 
|---|
| 191 | int __nf_ct_try_assign_helper(struct nf_conn *ct, struct nf_conn *tmpl, | 
|---|
| 192 | gfp_t flags) | 
|---|
| 193 | { | 
|---|
| 194 | struct nf_conntrack_helper *helper = NULL; | 
|---|
| 195 | struct nf_conn_help *help; | 
|---|
| 196 |  | 
|---|
| 197 | /* We already got a helper explicitly attached (e.g. nft_ct) */ | 
|---|
| 198 | if (test_bit(IPS_HELPER_BIT, &ct->status)) | 
|---|
| 199 | return 0; | 
|---|
| 200 |  | 
|---|
| 201 | if (WARN_ON_ONCE(!tmpl)) | 
|---|
| 202 | return 0; | 
|---|
| 203 |  | 
|---|
| 204 | help = nfct_help(ct: tmpl); | 
|---|
| 205 | if (help != NULL) { | 
|---|
| 206 | helper = rcu_dereference(help->helper); | 
|---|
| 207 | set_bit(nr: IPS_HELPER_BIT, addr: &ct->status); | 
|---|
| 208 | } | 
|---|
| 209 |  | 
|---|
| 210 | help = nfct_help(ct); | 
|---|
| 211 |  | 
|---|
| 212 | if (helper == NULL) { | 
|---|
| 213 | if (help) | 
|---|
| 214 | RCU_INIT_POINTER(help->helper, NULL); | 
|---|
| 215 | return 0; | 
|---|
| 216 | } | 
|---|
| 217 |  | 
|---|
| 218 | if (help == NULL) { | 
|---|
| 219 | help = nf_ct_helper_ext_add(ct, flags); | 
|---|
| 220 | if (help == NULL) | 
|---|
| 221 | return -ENOMEM; | 
|---|
| 222 | } else { | 
|---|
| 223 | /* We only allow helper re-assignment of the same sort since | 
|---|
| 224 | * we cannot reallocate the helper extension area. | 
|---|
| 225 | */ | 
|---|
| 226 | struct nf_conntrack_helper *tmp = rcu_dereference(help->helper); | 
|---|
| 227 |  | 
|---|
| 228 | if (tmp && tmp->help != helper->help) { | 
|---|
| 229 | RCU_INIT_POINTER(help->helper, NULL); | 
|---|
| 230 | return 0; | 
|---|
| 231 | } | 
|---|
| 232 | } | 
|---|
| 233 |  | 
|---|
| 234 | rcu_assign_pointer(help->helper, helper); | 
|---|
| 235 |  | 
|---|
| 236 | return 0; | 
|---|
| 237 | } | 
|---|
| 238 | EXPORT_SYMBOL_GPL(__nf_ct_try_assign_helper); | 
|---|
| 239 |  | 
|---|
| 240 | /* appropriate ct lock protecting must be taken by caller */ | 
|---|
| 241 | static int unhelp(struct nf_conn *ct, void *me) | 
|---|
| 242 | { | 
|---|
| 243 | struct nf_conn_help *help = nfct_help(ct); | 
|---|
| 244 |  | 
|---|
| 245 | if (help && rcu_dereference_raw(help->helper) == me) { | 
|---|
| 246 | nf_conntrack_event(event: IPCT_HELPER, ct); | 
|---|
| 247 | RCU_INIT_POINTER(help->helper, NULL); | 
|---|
| 248 | } | 
|---|
| 249 |  | 
|---|
| 250 | /* We are not intended to delete this conntrack. */ | 
|---|
| 251 | return 0; | 
|---|
| 252 | } | 
|---|
| 253 |  | 
|---|
| 254 | void nf_ct_helper_destroy(struct nf_conn *ct) | 
|---|
| 255 | { | 
|---|
| 256 | struct nf_conn_help *help = nfct_help(ct); | 
|---|
| 257 | struct nf_conntrack_helper *helper; | 
|---|
| 258 |  | 
|---|
| 259 | if (help) { | 
|---|
| 260 | rcu_read_lock(); | 
|---|
| 261 | helper = rcu_dereference(help->helper); | 
|---|
| 262 | if (helper && helper->destroy) | 
|---|
| 263 | helper->destroy(ct); | 
|---|
| 264 | rcu_read_unlock(); | 
|---|
| 265 | } | 
|---|
| 266 | } | 
|---|
| 267 |  | 
|---|
| 268 | static LIST_HEAD(nf_ct_helper_expectfn_list); | 
|---|
| 269 |  | 
|---|
| 270 | void nf_ct_helper_expectfn_register(struct nf_ct_helper_expectfn *n) | 
|---|
| 271 | { | 
|---|
| 272 | spin_lock_bh(lock: &nf_conntrack_expect_lock); | 
|---|
| 273 | list_add_rcu(new: &n->head, head: &nf_ct_helper_expectfn_list); | 
|---|
| 274 | spin_unlock_bh(lock: &nf_conntrack_expect_lock); | 
|---|
| 275 | } | 
|---|
| 276 | EXPORT_SYMBOL_GPL(nf_ct_helper_expectfn_register); | 
|---|
| 277 |  | 
|---|
| 278 | void nf_ct_helper_expectfn_unregister(struct nf_ct_helper_expectfn *n) | 
|---|
| 279 | { | 
|---|
| 280 | spin_lock_bh(lock: &nf_conntrack_expect_lock); | 
|---|
| 281 | list_del_rcu(entry: &n->head); | 
|---|
| 282 | spin_unlock_bh(lock: &nf_conntrack_expect_lock); | 
|---|
| 283 | } | 
|---|
| 284 | EXPORT_SYMBOL_GPL(nf_ct_helper_expectfn_unregister); | 
|---|
| 285 |  | 
|---|
| 286 | /* Caller should hold the rcu lock */ | 
|---|
| 287 | struct nf_ct_helper_expectfn * | 
|---|
| 288 | nf_ct_helper_expectfn_find_by_name(const char *name) | 
|---|
| 289 | { | 
|---|
| 290 | struct nf_ct_helper_expectfn *cur; | 
|---|
| 291 | bool found = false; | 
|---|
| 292 |  | 
|---|
| 293 | list_for_each_entry_rcu(cur, &nf_ct_helper_expectfn_list, head) { | 
|---|
| 294 | if (!strcmp(cur->name, name)) { | 
|---|
| 295 | found = true; | 
|---|
| 296 | break; | 
|---|
| 297 | } | 
|---|
| 298 | } | 
|---|
| 299 | return found ? cur : NULL; | 
|---|
| 300 | } | 
|---|
| 301 | EXPORT_SYMBOL_GPL(nf_ct_helper_expectfn_find_by_name); | 
|---|
| 302 |  | 
|---|
| 303 | /* Caller should hold the rcu lock */ | 
|---|
| 304 | struct nf_ct_helper_expectfn * | 
|---|
| 305 | nf_ct_helper_expectfn_find_by_symbol(const void *symbol) | 
|---|
| 306 | { | 
|---|
| 307 | struct nf_ct_helper_expectfn *cur; | 
|---|
| 308 | bool found = false; | 
|---|
| 309 |  | 
|---|
| 310 | list_for_each_entry_rcu(cur, &nf_ct_helper_expectfn_list, head) { | 
|---|
| 311 | if (cur->expectfn == symbol) { | 
|---|
| 312 | found = true; | 
|---|
| 313 | break; | 
|---|
| 314 | } | 
|---|
| 315 | } | 
|---|
| 316 | return found ? cur : NULL; | 
|---|
| 317 | } | 
|---|
| 318 | EXPORT_SYMBOL_GPL(nf_ct_helper_expectfn_find_by_symbol); | 
|---|
| 319 |  | 
|---|
| 320 | __printf(3, 4) | 
|---|
| 321 | void nf_ct_helper_log(struct sk_buff *skb, const struct nf_conn *ct, | 
|---|
| 322 | const char *fmt, ...) | 
|---|
| 323 | { | 
|---|
| 324 | const struct nf_conn_help *help; | 
|---|
| 325 | const struct nf_conntrack_helper *helper; | 
|---|
| 326 | struct va_format vaf; | 
|---|
| 327 | va_list args; | 
|---|
| 328 |  | 
|---|
| 329 | va_start(args, fmt); | 
|---|
| 330 |  | 
|---|
| 331 | vaf.fmt = fmt; | 
|---|
| 332 | vaf.va = &args; | 
|---|
| 333 |  | 
|---|
| 334 | /* Called from the helper function, this call never fails */ | 
|---|
| 335 | help = nfct_help(ct); | 
|---|
| 336 |  | 
|---|
| 337 | /* rcu_read_lock()ed by nf_hook_thresh */ | 
|---|
| 338 | helper = rcu_dereference(help->helper); | 
|---|
| 339 |  | 
|---|
| 340 | nf_log_packet(net: nf_ct_net(ct), pf: nf_ct_l3num(ct), hooknum: 0, skb, NULL, NULL, NULL, | 
|---|
| 341 | fmt: "nf_ct_%s: dropping packet: %pV ", helper->name, &vaf); | 
|---|
| 342 |  | 
|---|
| 343 | va_end(args); | 
|---|
| 344 | } | 
|---|
| 345 | EXPORT_SYMBOL_GPL(nf_ct_helper_log); | 
|---|
| 346 |  | 
|---|
| 347 | int nf_conntrack_helper_register(struct nf_conntrack_helper *me) | 
|---|
| 348 | { | 
|---|
| 349 | struct nf_conntrack_tuple_mask mask = { .src.u.all = htons(0xFFFF) }; | 
|---|
| 350 | unsigned int h = helper_hash(tuple: &me->tuple); | 
|---|
| 351 | struct nf_conntrack_helper *cur; | 
|---|
| 352 | int ret = 0, i; | 
|---|
| 353 |  | 
|---|
| 354 | BUG_ON(me->expect_policy == NULL); | 
|---|
| 355 | BUG_ON(me->expect_class_max >= NF_CT_MAX_EXPECT_CLASSES); | 
|---|
| 356 | BUG_ON(strlen(me->name) > NF_CT_HELPER_NAME_LEN - 1); | 
|---|
| 357 |  | 
|---|
| 358 | if (!nf_ct_helper_hash) | 
|---|
| 359 | return -ENOENT; | 
|---|
| 360 |  | 
|---|
| 361 | if (me->expect_policy->max_expected > NF_CT_EXPECT_MAX_CNT) | 
|---|
| 362 | return -EINVAL; | 
|---|
| 363 |  | 
|---|
| 364 | mutex_lock(lock: &nf_ct_helper_mutex); | 
|---|
| 365 | for (i = 0; i < nf_ct_helper_hsize; i++) { | 
|---|
| 366 | hlist_for_each_entry(cur, &nf_ct_helper_hash[i], hnode) { | 
|---|
| 367 | if (!strcmp(cur->name, me->name) && | 
|---|
| 368 | (cur->tuple.src.l3num == NFPROTO_UNSPEC || | 
|---|
| 369 | cur->tuple.src.l3num == me->tuple.src.l3num) && | 
|---|
| 370 | cur->tuple.dst.protonum == me->tuple.dst.protonum) { | 
|---|
| 371 | ret = -EBUSY; | 
|---|
| 372 | goto out; | 
|---|
| 373 | } | 
|---|
| 374 | } | 
|---|
| 375 | } | 
|---|
| 376 |  | 
|---|
| 377 | /* avoid unpredictable behaviour for auto_assign_helper */ | 
|---|
| 378 | if (!(me->flags & NF_CT_HELPER_F_USERSPACE)) { | 
|---|
| 379 | hlist_for_each_entry(cur, &nf_ct_helper_hash[h], hnode) { | 
|---|
| 380 | if (nf_ct_tuple_src_mask_cmp(t1: &cur->tuple, t2: &me->tuple, | 
|---|
| 381 | mask: &mask)) { | 
|---|
| 382 | ret = -EBUSY; | 
|---|
| 383 | goto out; | 
|---|
| 384 | } | 
|---|
| 385 | } | 
|---|
| 386 | } | 
|---|
| 387 | refcount_set(r: &me->refcnt, n: 1); | 
|---|
| 388 | hlist_add_head_rcu(n: &me->hnode, h: &nf_ct_helper_hash[h]); | 
|---|
| 389 | nf_ct_helper_count++; | 
|---|
| 390 | out: | 
|---|
| 391 | mutex_unlock(lock: &nf_ct_helper_mutex); | 
|---|
| 392 | return ret; | 
|---|
| 393 | } | 
|---|
| 394 | EXPORT_SYMBOL_GPL(nf_conntrack_helper_register); | 
|---|
| 395 |  | 
|---|
| 396 | static bool expect_iter_me(struct nf_conntrack_expect *exp, void *data) | 
|---|
| 397 | { | 
|---|
| 398 | struct nf_conn_help *help = nfct_help(ct: exp->master); | 
|---|
| 399 | const struct nf_conntrack_helper *me = data; | 
|---|
| 400 | const struct nf_conntrack_helper *this; | 
|---|
| 401 |  | 
|---|
| 402 | if (exp->helper == me) | 
|---|
| 403 | return true; | 
|---|
| 404 |  | 
|---|
| 405 | this = rcu_dereference_protected(help->helper, | 
|---|
| 406 | lockdep_is_held(&nf_conntrack_expect_lock)); | 
|---|
| 407 | return this == me; | 
|---|
| 408 | } | 
|---|
| 409 |  | 
|---|
| 410 | void nf_conntrack_helper_unregister(struct nf_conntrack_helper *me) | 
|---|
| 411 | { | 
|---|
| 412 | mutex_lock(lock: &nf_ct_helper_mutex); | 
|---|
| 413 | hlist_del_rcu(n: &me->hnode); | 
|---|
| 414 | nf_ct_helper_count--; | 
|---|
| 415 | mutex_unlock(lock: &nf_ct_helper_mutex); | 
|---|
| 416 |  | 
|---|
| 417 | /* Make sure every nothing is still using the helper unless its a | 
|---|
| 418 | * connection in the hash. | 
|---|
| 419 | */ | 
|---|
| 420 | synchronize_rcu(); | 
|---|
| 421 |  | 
|---|
| 422 | nf_ct_expect_iterate_destroy(iter: expect_iter_me, NULL); | 
|---|
| 423 | nf_ct_iterate_destroy(iter: unhelp, data: me); | 
|---|
| 424 | } | 
|---|
| 425 | EXPORT_SYMBOL_GPL(nf_conntrack_helper_unregister); | 
|---|
| 426 |  | 
|---|
| 427 | void nf_ct_helper_init(struct nf_conntrack_helper *helper, | 
|---|
| 428 | u16 l3num, u16 protonum, const char *name, | 
|---|
| 429 | u16 default_port, u16 spec_port, u32 id, | 
|---|
| 430 | const struct nf_conntrack_expect_policy *exp_pol, | 
|---|
| 431 | u32 expect_class_max, | 
|---|
| 432 | int (*help)(struct sk_buff *skb, unsigned int protoff, | 
|---|
| 433 | struct nf_conn *ct, | 
|---|
| 434 | enum ip_conntrack_info ctinfo), | 
|---|
| 435 | int (*from_nlattr)(struct nlattr *attr, | 
|---|
| 436 | struct nf_conn *ct), | 
|---|
| 437 | struct module *module) | 
|---|
| 438 | { | 
|---|
| 439 | helper->tuple.src.l3num = l3num; | 
|---|
| 440 | helper->tuple.dst.protonum = protonum; | 
|---|
| 441 | helper->tuple.src.u.all = htons(spec_port); | 
|---|
| 442 | helper->expect_policy = exp_pol; | 
|---|
| 443 | helper->expect_class_max = expect_class_max; | 
|---|
| 444 | helper->help = help; | 
|---|
| 445 | helper->from_nlattr = from_nlattr; | 
|---|
| 446 | helper->me = module; | 
|---|
| 447 | snprintf(buf: helper->nat_mod_name, size: sizeof(helper->nat_mod_name), | 
|---|
| 448 | NF_NAT_HELPER_PREFIX "%s", name); | 
|---|
| 449 |  | 
|---|
| 450 | if (spec_port == default_port) | 
|---|
| 451 | snprintf(buf: helper->name, size: sizeof(helper->name), fmt: "%s", name); | 
|---|
| 452 | else | 
|---|
| 453 | snprintf(buf: helper->name, size: sizeof(helper->name), fmt: "%s-%u", name, id); | 
|---|
| 454 | } | 
|---|
| 455 | EXPORT_SYMBOL_GPL(nf_ct_helper_init); | 
|---|
| 456 |  | 
|---|
| 457 | int nf_conntrack_helpers_register(struct nf_conntrack_helper *helper, | 
|---|
| 458 | unsigned int n) | 
|---|
| 459 | { | 
|---|
| 460 | unsigned int i; | 
|---|
| 461 | int err = 0; | 
|---|
| 462 |  | 
|---|
| 463 | for (i = 0; i < n; i++) { | 
|---|
| 464 | err = nf_conntrack_helper_register(&helper[i]); | 
|---|
| 465 | if (err < 0) | 
|---|
| 466 | goto err; | 
|---|
| 467 | } | 
|---|
| 468 |  | 
|---|
| 469 | return err; | 
|---|
| 470 | err: | 
|---|
| 471 | if (i > 0) | 
|---|
| 472 | nf_conntrack_helpers_unregister(helper, i); | 
|---|
| 473 | return err; | 
|---|
| 474 | } | 
|---|
| 475 | EXPORT_SYMBOL_GPL(nf_conntrack_helpers_register); | 
|---|
| 476 |  | 
|---|
| 477 | void nf_conntrack_helpers_unregister(struct nf_conntrack_helper *helper, | 
|---|
| 478 | unsigned int n) | 
|---|
| 479 | { | 
|---|
| 480 | while (n-- > 0) | 
|---|
| 481 | nf_conntrack_helper_unregister(&helper[n]); | 
|---|
| 482 | } | 
|---|
| 483 | EXPORT_SYMBOL_GPL(nf_conntrack_helpers_unregister); | 
|---|
| 484 |  | 
|---|
| 485 | void nf_nat_helper_register(struct nf_conntrack_nat_helper *nat) | 
|---|
| 486 | { | 
|---|
| 487 | mutex_lock(lock: &nf_ct_nat_helpers_mutex); | 
|---|
| 488 | list_add_rcu(new: &nat->list, head: &nf_ct_nat_helpers); | 
|---|
| 489 | mutex_unlock(lock: &nf_ct_nat_helpers_mutex); | 
|---|
| 490 | } | 
|---|
| 491 | EXPORT_SYMBOL_GPL(nf_nat_helper_register); | 
|---|
| 492 |  | 
|---|
| 493 | void nf_nat_helper_unregister(struct nf_conntrack_nat_helper *nat) | 
|---|
| 494 | { | 
|---|
| 495 | mutex_lock(lock: &nf_ct_nat_helpers_mutex); | 
|---|
| 496 | list_del_rcu(entry: &nat->list); | 
|---|
| 497 | mutex_unlock(lock: &nf_ct_nat_helpers_mutex); | 
|---|
| 498 | } | 
|---|
| 499 | EXPORT_SYMBOL_GPL(nf_nat_helper_unregister); | 
|---|
| 500 |  | 
|---|
| 501 | int nf_conntrack_helper_init(void) | 
|---|
| 502 | { | 
|---|
| 503 | nf_ct_helper_hsize = 1; /* gets rounded up to use one page */ | 
|---|
| 504 | nf_ct_helper_hash = | 
|---|
| 505 | nf_ct_alloc_hashtable(sizep: &nf_ct_helper_hsize, nulls: 0); | 
|---|
| 506 | if (!nf_ct_helper_hash) | 
|---|
| 507 | return -ENOMEM; | 
|---|
| 508 |  | 
|---|
| 509 | INIT_LIST_HEAD(list: &nf_ct_nat_helpers); | 
|---|
| 510 | return 0; | 
|---|
| 511 | } | 
|---|
| 512 |  | 
|---|
| 513 | void nf_conntrack_helper_fini(void) | 
|---|
| 514 | { | 
|---|
| 515 | kvfree(addr: nf_ct_helper_hash); | 
|---|
| 516 | nf_ct_helper_hash = NULL; | 
|---|
| 517 | } | 
|---|
| 518 |  | 
|---|