nat64clat_control.c revision 346212
1/*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2019 Yandex LLC 5 * Copyright (c) 2019 Andrey V. Elsukov <ae@FreeBSD.org> 6 * Copyright (c) 2019 Boris N. Lytochkin <lytboris@gmail.com> 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30#include <sys/cdefs.h> 31__FBSDID("$FreeBSD: stable/11/sys/netpfil/ipfw/nat64/nat64clat_control.c 346212 2019-04-14 12:39:09Z ae $"); 32 33#include <sys/param.h> 34#include <sys/systm.h> 35#include <sys/counter.h> 36#include <sys/errno.h> 37#include <sys/kernel.h> 38#include <sys/lock.h> 39#include <sys/malloc.h> 40#include <sys/mbuf.h> 41#include <sys/module.h> 42#include <sys/rmlock.h> 43#include <sys/rwlock.h> 44#include <sys/socket.h> 45#include <sys/sockopt.h> 46#include <sys/syslog.h> 47#include <sys/sysctl.h> 48 49#include <net/if.h> 50#include <net/if_var.h> 51#include <net/route.h> 52#include <net/vnet.h> 53#include <net/pfil.h> 54 55#include <netinet/in.h> 56#include <netinet/ip_var.h> 57#include <netinet/ip_fw.h> 58#include <netinet6/in6_var.h> 59#include <netinet6/ip6_var.h> 60#include <netinet6/ip_fw_nat64.h> 61 62#include <netpfil/ipfw/ip_fw_private.h> 63 64#include "nat64clat.h" 65 66VNET_DEFINE(uint16_t, nat64clat_eid) = 0; 67 68static struct nat64clat_cfg *nat64clat_alloc_config(const char *name, 69 uint8_t set); 70static void nat64clat_free_config(struct nat64clat_cfg *cfg); 71static struct nat64clat_cfg *nat64clat_find(struct namedobj_instance *ni, 72 const char *name, uint8_t set); 73 74static struct nat64clat_cfg * 75nat64clat_alloc_config(const char *name, uint8_t set) 76{ 77 struct nat64clat_cfg *cfg; 78 79 cfg = malloc(sizeof(struct nat64clat_cfg), M_IPFW, M_WAITOK | M_ZERO); 80 COUNTER_ARRAY_ALLOC(cfg->base.stats.cnt, NAT64STATS, M_WAITOK); 81 cfg->no.name = cfg->name; 82 cfg->no.etlv = IPFW_TLV_NAT64CLAT_NAME; 83 cfg->no.set = set; 84 strlcpy(cfg->name, name, sizeof(cfg->name)); 85 return (cfg); 86} 87 88static void 89nat64clat_free_config(struct nat64clat_cfg *cfg) 90{ 91 92 COUNTER_ARRAY_FREE(cfg->base.stats.cnt, NAT64STATS); 93 free(cfg, M_IPFW); 94} 95 96static void 97nat64clat_export_config(struct ip_fw_chain *ch, struct nat64clat_cfg *cfg, 98 ipfw_nat64clat_cfg *uc) 99{ 100 uc->plat_prefix = cfg->base.plat_prefix; 101 uc->plat_plen = cfg->base.plat_plen; 102 uc->clat_prefix = cfg->base.clat_prefix; 103 uc->clat_plen = cfg->base.clat_plen; 104 uc->flags = cfg->base.flags & NAT64CLAT_FLAGSMASK; 105 uc->set = cfg->no.set; 106 strlcpy(uc->name, cfg->no.name, sizeof(uc->name)); 107} 108 109struct nat64clat_dump_arg { 110 struct ip_fw_chain *ch; 111 struct sockopt_data *sd; 112}; 113 114static int 115export_config_cb(struct namedobj_instance *ni, struct named_object *no, 116 void *arg) 117{ 118 struct nat64clat_dump_arg *da = (struct nat64clat_dump_arg *)arg; 119 ipfw_nat64clat_cfg *uc; 120 121 uc = (ipfw_nat64clat_cfg *)ipfw_get_sopt_space(da->sd, sizeof(*uc)); 122 nat64clat_export_config(da->ch, (struct nat64clat_cfg *)no, uc); 123 return (0); 124} 125 126static struct nat64clat_cfg * 127nat64clat_find(struct namedobj_instance *ni, const char *name, uint8_t set) 128{ 129 struct nat64clat_cfg *cfg; 130 131 cfg = (struct nat64clat_cfg *)ipfw_objhash_lookup_name_type(ni, set, 132 IPFW_TLV_NAT64CLAT_NAME, name); 133 134 return (cfg); 135} 136 137/* 138 * Creates new consumer-side nat64 translator instance. 139 * Data layout (v0)(current): 140 * Request: [ ipfw_obj_lheader ipfw_nat64clat_cfg ] 141 * 142 * Returns 0 on success 143 */ 144static int 145nat64clat_create(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 146 struct sockopt_data *sd) 147{ 148 ipfw_obj_lheader *olh; 149 ipfw_nat64clat_cfg *uc; 150 struct namedobj_instance *ni; 151 struct nat64clat_cfg *cfg; 152 153 if (sd->valsize != sizeof(*olh) + sizeof(*uc)) 154 return (EINVAL); 155 156 olh = (ipfw_obj_lheader *)sd->kbuf; 157 uc = (ipfw_nat64clat_cfg *)(olh + 1); 158 159 if (ipfw_check_object_name_generic(uc->name) != 0) 160 return (EINVAL); 161 162 if (uc->set >= IPFW_MAX_SETS || 163 nat64_check_prefix6(&uc->plat_prefix, uc->plat_plen) != 0 || 164 nat64_check_prefix6(&uc->clat_prefix, uc->clat_plen) != 0) 165 return (EINVAL); 166 167 ni = CHAIN_TO_SRV(ch); 168 169 IPFW_UH_RLOCK(ch); 170 if (nat64clat_find(ni, uc->name, uc->set) != NULL) { 171 IPFW_UH_RUNLOCK(ch); 172 return (EEXIST); 173 } 174 IPFW_UH_RUNLOCK(ch); 175 176 cfg = nat64clat_alloc_config(uc->name, uc->set); 177 cfg->base.plat_prefix = uc->plat_prefix; 178 cfg->base.plat_plen = uc->plat_plen; 179 cfg->base.clat_prefix = uc->clat_prefix; 180 cfg->base.clat_plen = uc->clat_plen; 181 cfg->base.flags = (uc->flags & NAT64CLAT_FLAGSMASK) | 182 NAT64_CLATPFX | NAT64_PLATPFX; 183 if (IN6_IS_ADDR_WKPFX(&cfg->base.plat_prefix)) 184 cfg->base.flags |= NAT64_WKPFX; 185 186 IPFW_UH_WLOCK(ch); 187 188 if (nat64clat_find(ni, uc->name, uc->set) != NULL) { 189 IPFW_UH_WUNLOCK(ch); 190 nat64clat_free_config(cfg); 191 return (EEXIST); 192 } 193 194 if (ipfw_objhash_alloc_idx(ni, &cfg->no.kidx) != 0) { 195 IPFW_UH_WUNLOCK(ch); 196 nat64clat_free_config(cfg); 197 return (ENOSPC); 198 } 199 ipfw_objhash_add(CHAIN_TO_SRV(ch), &cfg->no); 200 /* Okay, let's link data */ 201 SRV_OBJECT(ch, cfg->no.kidx) = cfg; 202 IPFW_UH_WUNLOCK(ch); 203 204 return (0); 205} 206 207/* 208 * Change existing nat64clat instance configuration. 209 * Data layout (v0)(current): 210 * Request: [ ipfw_obj_header ipfw_nat64clat_cfg ] 211 * Reply: [ ipfw_obj_header ipfw_nat64clat_cfg ] 212 * 213 * Returns 0 on success 214 */ 215static int 216nat64clat_config(struct ip_fw_chain *ch, ip_fw3_opheader *op, 217 struct sockopt_data *sd) 218{ 219 ipfw_obj_header *oh; 220 ipfw_nat64clat_cfg *uc; 221 struct nat64clat_cfg *cfg; 222 struct namedobj_instance *ni; 223 uint32_t flags; 224 225 if (sd->valsize != sizeof(*oh) + sizeof(*uc)) 226 return (EINVAL); 227 228 oh = (ipfw_obj_header *)ipfw_get_sopt_space(sd, 229 sizeof(*oh) + sizeof(*uc)); 230 uc = (ipfw_nat64clat_cfg *)(oh + 1); 231 232 if (ipfw_check_object_name_generic(oh->ntlv.name) != 0 || 233 oh->ntlv.set >= IPFW_MAX_SETS) 234 return (EINVAL); 235 236 ni = CHAIN_TO_SRV(ch); 237 if (sd->sopt->sopt_dir == SOPT_GET) { 238 IPFW_UH_RLOCK(ch); 239 cfg = nat64clat_find(ni, oh->ntlv.name, oh->ntlv.set); 240 if (cfg == NULL) { 241 IPFW_UH_RUNLOCK(ch); 242 return (ENOENT); 243 } 244 nat64clat_export_config(ch, cfg, uc); 245 IPFW_UH_RUNLOCK(ch); 246 return (0); 247 } 248 249 IPFW_UH_WLOCK(ch); 250 cfg = nat64clat_find(ni, oh->ntlv.name, oh->ntlv.set); 251 if (cfg == NULL) { 252 IPFW_UH_WUNLOCK(ch); 253 return (ENOENT); 254 } 255 256 /* 257 * For now allow to change only following values: 258 * plat_prefix, plat_plen, clat_prefix, clat_plen, flags. 259 */ 260 flags = 0; 261 if (uc->plat_plen != cfg->base.plat_plen || 262 !IN6_ARE_ADDR_EQUAL(&uc->plat_prefix, &cfg->base.plat_prefix)) { 263 if (nat64_check_prefix6(&uc->plat_prefix, uc->plat_plen) != 0) { 264 IPFW_UH_WUNLOCK(ch); 265 return (EINVAL); 266 } 267 flags |= NAT64_PLATPFX; 268 } 269 270 if (uc->clat_plen != cfg->base.clat_plen || 271 !IN6_ARE_ADDR_EQUAL(&uc->clat_prefix, &cfg->base.clat_prefix)) { 272 if (nat64_check_prefix6(&uc->clat_prefix, uc->clat_plen) != 0) { 273 IPFW_UH_WUNLOCK(ch); 274 return (EINVAL); 275 } 276 flags |= NAT64_CLATPFX; 277 } 278 279 if (flags != 0) { 280 IPFW_WLOCK(ch); 281 if (flags & NAT64_PLATPFX) { 282 cfg->base.plat_prefix = uc->plat_prefix; 283 cfg->base.plat_plen = uc->plat_plen; 284 } 285 if (flags & NAT64_CLATPFX) { 286 cfg->base.clat_prefix = uc->clat_prefix; 287 cfg->base.clat_plen = uc->clat_plen; 288 } 289 IPFW_WUNLOCK(ch); 290 } 291 292 cfg->base.flags &= ~NAT64CLAT_FLAGSMASK; 293 cfg->base.flags |= uc->flags & NAT64CLAT_FLAGSMASK; 294 295 IPFW_UH_WUNLOCK(ch); 296 return (0); 297} 298 299static void 300nat64clat_detach_config(struct ip_fw_chain *ch, struct nat64clat_cfg *cfg) 301{ 302 303 IPFW_UH_WLOCK_ASSERT(ch); 304 305 ipfw_objhash_del(CHAIN_TO_SRV(ch), &cfg->no); 306 ipfw_objhash_free_idx(CHAIN_TO_SRV(ch), cfg->no.kidx); 307} 308 309/* 310 * Destroys nat64 instance. 311 * Data layout (v0)(current): 312 * Request: [ ipfw_obj_header ] 313 * 314 * Returns 0 on success 315 */ 316static int 317nat64clat_destroy(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 318 struct sockopt_data *sd) 319{ 320 ipfw_obj_header *oh; 321 struct nat64clat_cfg *cfg; 322 323 if (sd->valsize != sizeof(*oh)) 324 return (EINVAL); 325 326 oh = (ipfw_obj_header *)sd->kbuf; 327 if (ipfw_check_object_name_generic(oh->ntlv.name) != 0) 328 return (EINVAL); 329 330 IPFW_UH_WLOCK(ch); 331 cfg = nat64clat_find(CHAIN_TO_SRV(ch), oh->ntlv.name, oh->ntlv.set); 332 if (cfg == NULL) { 333 IPFW_UH_WUNLOCK(ch); 334 return (ENOENT); 335 } 336 if (cfg->no.refcnt > 0) { 337 IPFW_UH_WUNLOCK(ch); 338 return (EBUSY); 339 } 340 341 ipfw_reset_eaction_instance(ch, V_nat64clat_eid, cfg->no.kidx); 342 SRV_OBJECT(ch, cfg->no.kidx) = NULL; 343 nat64clat_detach_config(ch, cfg); 344 IPFW_UH_WUNLOCK(ch); 345 346 nat64clat_free_config(cfg); 347 return (0); 348} 349 350/* 351 * Lists all nat64clat instances currently available in kernel. 352 * Data layout (v0)(current): 353 * Request: [ ipfw_obj_lheader ] 354 * Reply: [ ipfw_obj_lheader ipfw_nat64clat_cfg x N ] 355 * 356 * Returns 0 on success 357 */ 358static int 359nat64clat_list(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 360 struct sockopt_data *sd) 361{ 362 ipfw_obj_lheader *olh; 363 struct nat64clat_dump_arg da; 364 365 /* Check minimum header size */ 366 if (sd->valsize < sizeof(ipfw_obj_lheader)) 367 return (EINVAL); 368 369 olh = (ipfw_obj_lheader *)ipfw_get_sopt_header(sd, sizeof(*olh)); 370 371 IPFW_UH_RLOCK(ch); 372 olh->count = ipfw_objhash_count_type(CHAIN_TO_SRV(ch), 373 IPFW_TLV_NAT64CLAT_NAME); 374 olh->objsize = sizeof(ipfw_nat64clat_cfg); 375 olh->size = sizeof(*olh) + olh->count * olh->objsize; 376 377 if (sd->valsize < olh->size) { 378 IPFW_UH_RUNLOCK(ch); 379 return (ENOMEM); 380 } 381 memset(&da, 0, sizeof(da)); 382 da.ch = ch; 383 da.sd = sd; 384 ipfw_objhash_foreach_type(CHAIN_TO_SRV(ch), export_config_cb, 385 &da, IPFW_TLV_NAT64CLAT_NAME); 386 IPFW_UH_RUNLOCK(ch); 387 388 return (0); 389} 390 391#define __COPY_STAT_FIELD(_cfg, _stats, _field) \ 392 (_stats)->_field = NAT64STAT_FETCH(&(_cfg)->base.stats, _field) 393static void 394export_stats(struct ip_fw_chain *ch, struct nat64clat_cfg *cfg, 395 struct ipfw_nat64clat_stats *stats) 396{ 397 398 __COPY_STAT_FIELD(cfg, stats, opcnt64); 399 __COPY_STAT_FIELD(cfg, stats, opcnt46); 400 __COPY_STAT_FIELD(cfg, stats, ofrags); 401 __COPY_STAT_FIELD(cfg, stats, ifrags); 402 __COPY_STAT_FIELD(cfg, stats, oerrors); 403 __COPY_STAT_FIELD(cfg, stats, noroute4); 404 __COPY_STAT_FIELD(cfg, stats, noroute6); 405 __COPY_STAT_FIELD(cfg, stats, noproto); 406 __COPY_STAT_FIELD(cfg, stats, nomem); 407 __COPY_STAT_FIELD(cfg, stats, dropped); 408} 409 410/* 411 * Get nat64clat statistics. 412 * Data layout (v0)(current): 413 * Request: [ ipfw_obj_header ] 414 * Reply: [ ipfw_obj_header ipfw_obj_ctlv [ uint64_t x N ]] 415 * 416 * Returns 0 on success 417 */ 418static int 419nat64clat_stats(struct ip_fw_chain *ch, ip_fw3_opheader *op, 420 struct sockopt_data *sd) 421{ 422 struct ipfw_nat64clat_stats stats; 423 struct nat64clat_cfg *cfg; 424 ipfw_obj_header *oh; 425 ipfw_obj_ctlv *ctlv; 426 size_t sz; 427 428 sz = sizeof(ipfw_obj_header) + sizeof(ipfw_obj_ctlv) + sizeof(stats); 429 if (sd->valsize % sizeof(uint64_t)) 430 return (EINVAL); 431 if (sd->valsize < sz) 432 return (ENOMEM); 433 oh = (ipfw_obj_header *)ipfw_get_sopt_header(sd, sz); 434 if (oh == NULL) 435 return (EINVAL); 436 memset(&stats, 0, sizeof(stats)); 437 438 IPFW_UH_RLOCK(ch); 439 cfg = nat64clat_find(CHAIN_TO_SRV(ch), oh->ntlv.name, oh->ntlv.set); 440 if (cfg == NULL) { 441 IPFW_UH_RUNLOCK(ch); 442 return (ENOENT); 443 } 444 export_stats(ch, cfg, &stats); 445 IPFW_UH_RUNLOCK(ch); 446 447 ctlv = (ipfw_obj_ctlv *)(oh + 1); 448 memset(ctlv, 0, sizeof(*ctlv)); 449 ctlv->head.type = IPFW_TLV_COUNTERS; 450 ctlv->head.length = sz - sizeof(ipfw_obj_header); 451 ctlv->count = sizeof(stats) / sizeof(uint64_t); 452 ctlv->objsize = sizeof(uint64_t); 453 ctlv->version = IPFW_NAT64_VERSION; 454 memcpy(ctlv + 1, &stats, sizeof(stats)); 455 return (0); 456} 457 458/* 459 * Reset nat64clat statistics. 460 * Data layout (v0)(current): 461 * Request: [ ipfw_obj_header ] 462 * 463 * Returns 0 on success 464 */ 465static int 466nat64clat_reset_stats(struct ip_fw_chain *ch, ip_fw3_opheader *op, 467 struct sockopt_data *sd) 468{ 469 struct nat64clat_cfg *cfg; 470 ipfw_obj_header *oh; 471 472 if (sd->valsize != sizeof(*oh)) 473 return (EINVAL); 474 oh = (ipfw_obj_header *)sd->kbuf; 475 if (ipfw_check_object_name_generic(oh->ntlv.name) != 0 || 476 oh->ntlv.set >= IPFW_MAX_SETS) 477 return (EINVAL); 478 479 IPFW_UH_WLOCK(ch); 480 cfg = nat64clat_find(CHAIN_TO_SRV(ch), oh->ntlv.name, oh->ntlv.set); 481 if (cfg == NULL) { 482 IPFW_UH_WUNLOCK(ch); 483 return (ENOENT); 484 } 485 COUNTER_ARRAY_ZERO(cfg->base.stats.cnt, NAT64STATS); 486 IPFW_UH_WUNLOCK(ch); 487 return (0); 488} 489 490static struct ipfw_sopt_handler scodes[] = { 491 492 { IP_FW_NAT64CLAT_CREATE, 0, HDIR_SET, nat64clat_create }, 493 { IP_FW_NAT64CLAT_DESTROY,0, HDIR_SET, nat64clat_destroy }, 494 { IP_FW_NAT64CLAT_CONFIG, 0, HDIR_BOTH, nat64clat_config }, 495 { IP_FW_NAT64CLAT_LIST, 0, HDIR_GET, nat64clat_list }, 496 { IP_FW_NAT64CLAT_STATS, 0, HDIR_GET, nat64clat_stats }, 497 { IP_FW_NAT64CLAT_RESET_STATS,0, HDIR_SET, nat64clat_reset_stats }, 498}; 499 500static int 501nat64clat_classify(ipfw_insn *cmd, uint16_t *puidx, uint8_t *ptype) 502{ 503 ipfw_insn *icmd; 504 505 icmd = cmd - 1; 506 if (icmd->opcode != O_EXTERNAL_ACTION || 507 icmd->arg1 != V_nat64clat_eid) 508 return (1); 509 510 *puidx = cmd->arg1; 511 *ptype = 0; 512 return (0); 513} 514 515static void 516nat64clat_update_arg1(ipfw_insn *cmd, uint16_t idx) 517{ 518 519 cmd->arg1 = idx; 520} 521 522static int 523nat64clat_findbyname(struct ip_fw_chain *ch, struct tid_info *ti, 524 struct named_object **pno) 525{ 526 int err; 527 528 err = ipfw_objhash_find_type(CHAIN_TO_SRV(ch), ti, 529 IPFW_TLV_NAT64CLAT_NAME, pno); 530 return (err); 531} 532 533static struct named_object * 534nat64clat_findbykidx(struct ip_fw_chain *ch, uint16_t idx) 535{ 536 struct namedobj_instance *ni; 537 struct named_object *no; 538 539 IPFW_UH_WLOCK_ASSERT(ch); 540 ni = CHAIN_TO_SRV(ch); 541 no = ipfw_objhash_lookup_kidx(ni, idx); 542 KASSERT(no != NULL, ("NAT with index %d not found", idx)); 543 544 return (no); 545} 546 547static int 548nat64clat_manage_sets(struct ip_fw_chain *ch, uint16_t set, uint8_t new_set, 549 enum ipfw_sets_cmd cmd) 550{ 551 552 return (ipfw_obj_manage_sets(CHAIN_TO_SRV(ch), IPFW_TLV_NAT64CLAT_NAME, 553 set, new_set, cmd)); 554} 555 556static struct opcode_obj_rewrite opcodes[] = { 557 { 558 .opcode = O_EXTERNAL_INSTANCE, 559 .etlv = IPFW_TLV_EACTION /* just show it isn't table */, 560 .classifier = nat64clat_classify, 561 .update = nat64clat_update_arg1, 562 .find_byname = nat64clat_findbyname, 563 .find_bykidx = nat64clat_findbykidx, 564 .manage_sets = nat64clat_manage_sets, 565 }, 566}; 567 568static int 569destroy_config_cb(struct namedobj_instance *ni, struct named_object *no, 570 void *arg) 571{ 572 struct nat64clat_cfg *cfg; 573 struct ip_fw_chain *ch; 574 575 ch = (struct ip_fw_chain *)arg; 576 cfg = (struct nat64clat_cfg *)SRV_OBJECT(ch, no->kidx); 577 SRV_OBJECT(ch, no->kidx) = NULL; 578 nat64clat_detach_config(ch, cfg); 579 nat64clat_free_config(cfg); 580 return (0); 581} 582 583int 584nat64clat_init(struct ip_fw_chain *ch, int first) 585{ 586 587 V_nat64clat_eid = ipfw_add_eaction(ch, ipfw_nat64clat, "nat64clat"); 588 if (V_nat64clat_eid == 0) 589 return (ENXIO); 590 IPFW_ADD_SOPT_HANDLER(first, scodes); 591 IPFW_ADD_OBJ_REWRITER(first, opcodes); 592 return (0); 593} 594 595void 596nat64clat_uninit(struct ip_fw_chain *ch, int last) 597{ 598 599 IPFW_DEL_OBJ_REWRITER(last, opcodes); 600 IPFW_DEL_SOPT_HANDLER(last, scodes); 601 ipfw_del_eaction(ch, V_nat64clat_eid); 602 /* 603 * Since we already have deregistered external action, 604 * our named objects become unaccessible via rules, because 605 * all rules were truncated by ipfw_del_eaction(). 606 * So, we can unlink and destroy our named objects without holding 607 * IPFW_WLOCK(). 608 */ 609 IPFW_UH_WLOCK(ch); 610 ipfw_objhash_foreach_type(CHAIN_TO_SRV(ch), destroy_config_cb, ch, 611 IPFW_TLV_NAT64CLAT_NAME); 612 V_nat64clat_eid = 0; 613 IPFW_UH_WUNLOCK(ch); 614} 615 616