]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/netpfil/ipfw/nat64/nat64stl_control.c
Update tcpdump to 4.9.2
[FreeBSD/FreeBSD.git] / sys / netpfil / ipfw / nat64 / nat64stl_control.c
1 /*-
2  * Copyright (c) 2015-2016 Yandex LLC
3  * Copyright (c) 2015-2016 Andrey V. Elsukov <ae@FreeBSD.org>
4  * Copyright (c) 2015 Alexander V. Chernikov <melifaro@FreeBSD.org>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/counter.h>
35 #include <sys/errno.h>
36 #include <sys/kernel.h>
37 #include <sys/lock.h>
38 #include <sys/malloc.h>
39 #include <sys/mbuf.h>
40 #include <sys/module.h>
41 #include <sys/rmlock.h>
42 #include <sys/rwlock.h>
43 #include <sys/socket.h>
44 #include <sys/sockopt.h>
45 #include <sys/queue.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/pfil.h>
52 #include <net/route.h>
53 #include <net/vnet.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
61 #include <netpfil/ipfw/ip_fw_private.h>
62 #include <netpfil/ipfw/nat64/ip_fw_nat64.h>
63 #include <netpfil/ipfw/nat64/nat64stl.h>
64 #include <netinet6/ip_fw_nat64.h>
65
66 VNET_DEFINE(uint16_t, nat64stl_eid) = 0;
67
68 static struct nat64stl_cfg *nat64stl_alloc_config(const char *name, uint8_t set);
69 static void nat64stl_free_config(struct nat64stl_cfg *cfg);
70 static struct nat64stl_cfg *nat64stl_find(struct namedobj_instance *ni,
71     const char *name, uint8_t set);
72
73 static struct nat64stl_cfg *
74 nat64stl_alloc_config(const char *name, uint8_t set)
75 {
76         struct nat64stl_cfg *cfg;
77
78         cfg = malloc(sizeof(struct nat64stl_cfg), M_IPFW, M_WAITOK | M_ZERO);
79         COUNTER_ARRAY_ALLOC(cfg->stats.stats, NAT64STATS, M_WAITOK);
80         cfg->no.name = cfg->name;
81         cfg->no.etlv = IPFW_TLV_NAT64STL_NAME;
82         cfg->no.set = set;
83         strlcpy(cfg->name, name, sizeof(cfg->name));
84         return (cfg);
85 }
86
87 static void
88 nat64stl_free_config(struct nat64stl_cfg *cfg)
89 {
90
91         COUNTER_ARRAY_FREE(cfg->stats.stats, NAT64STATS);
92         free(cfg, M_IPFW);
93 }
94
95 static void
96 nat64stl_export_config(struct ip_fw_chain *ch, struct nat64stl_cfg *cfg,
97     ipfw_nat64stl_cfg *uc)
98 {
99         struct named_object *no;
100
101         uc->prefix6 = cfg->prefix6;
102         uc->plen6 = cfg->plen6;
103         uc->flags = cfg->flags & NAT64STL_FLAGSMASK;
104         uc->set = cfg->no.set;
105         strlcpy(uc->name, cfg->no.name, sizeof(uc->name));
106
107         no = ipfw_objhash_lookup_table_kidx(ch, cfg->map64);
108         ipfw_export_obj_ntlv(no, &uc->ntlv6);
109         no = ipfw_objhash_lookup_table_kidx(ch, cfg->map46);
110         ipfw_export_obj_ntlv(no, &uc->ntlv4);
111 }
112
113 struct nat64stl_dump_arg {
114         struct ip_fw_chain *ch;
115         struct sockopt_data *sd;
116 };
117
118 static int
119 export_config_cb(struct namedobj_instance *ni, struct named_object *no,
120     void *arg)
121 {
122         struct nat64stl_dump_arg *da = (struct nat64stl_dump_arg *)arg;
123         ipfw_nat64stl_cfg *uc;
124
125         uc = (ipfw_nat64stl_cfg *)ipfw_get_sopt_space(da->sd, sizeof(*uc));
126         nat64stl_export_config(da->ch, (struct nat64stl_cfg *)no, uc);
127         return (0);
128 }
129
130 static struct nat64stl_cfg *
131 nat64stl_find(struct namedobj_instance *ni, const char *name, uint8_t set)
132 {
133         struct nat64stl_cfg *cfg;
134
135         cfg = (struct nat64stl_cfg *)ipfw_objhash_lookup_name_type(ni, set,
136             IPFW_TLV_NAT64STL_NAME, name);
137
138         return (cfg);
139 }
140
141
142 static int
143 nat64stl_create_internal(struct ip_fw_chain *ch, struct nat64stl_cfg *cfg,
144     ipfw_nat64stl_cfg *i)
145 {
146
147         IPFW_UH_WLOCK_ASSERT(ch);
148
149         if (ipfw_objhash_alloc_idx(CHAIN_TO_SRV(ch), &cfg->no.kidx) != 0)
150                 return (ENOSPC);
151         cfg->flags |= NAT64STL_KIDX;
152
153         if (ipfw_ref_table(ch, &i->ntlv4, &cfg->map46) != 0)
154                 return (EINVAL);
155         cfg->flags |= NAT64STL_46T;
156
157         if (ipfw_ref_table(ch, &i->ntlv6, &cfg->map64) != 0)
158                 return (EINVAL);
159         cfg->flags |= NAT64STL_64T;
160
161         ipfw_objhash_add(CHAIN_TO_SRV(ch), &cfg->no);
162
163         return (0);
164 }
165
166 /*
167  * Creates new nat64 instance.
168  * Data layout (v0)(current):
169  * Request: [ ipfw_obj_lheader ipfw_nat64stl_cfg ]
170  *
171  * Returns 0 on success
172  */
173 static int
174 nat64stl_create(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
175     struct sockopt_data *sd)
176 {
177         ipfw_obj_lheader *olh;
178         ipfw_nat64stl_cfg *uc;
179         struct namedobj_instance *ni;
180         struct nat64stl_cfg *cfg;
181         int error;
182
183         if (sd->valsize != sizeof(*olh) + sizeof(*uc))
184                 return (EINVAL);
185
186         olh = (ipfw_obj_lheader *)sd->kbuf;
187         uc = (ipfw_nat64stl_cfg *)(olh + 1);
188
189         if (ipfw_check_object_name_generic(uc->name) != 0)
190                 return (EINVAL);
191         if (!IN6_IS_ADDR_WKPFX(&uc->prefix6))
192                 return (EINVAL);
193         if (uc->plen6 != 96 || uc->set >= IPFW_MAX_SETS)
194                 return (EINVAL);
195
196         /* XXX: check types of tables */
197
198         ni = CHAIN_TO_SRV(ch);
199         error = 0;
200
201         IPFW_UH_RLOCK(ch);
202         if (nat64stl_find(ni, uc->name, uc->set) != NULL) {
203                 IPFW_UH_RUNLOCK(ch);
204                 return (EEXIST);
205         }
206         IPFW_UH_RUNLOCK(ch);
207
208         cfg = nat64stl_alloc_config(uc->name, uc->set);
209         cfg->prefix6 = uc->prefix6;
210         cfg->plen6 = uc->plen6;
211         cfg->flags = uc->flags & NAT64STL_FLAGSMASK;
212
213         IPFW_UH_WLOCK(ch);
214
215         if (nat64stl_find(ni, uc->name, uc->set) != NULL) {
216                 IPFW_UH_WUNLOCK(ch);
217                 nat64stl_free_config(cfg);
218                 return (EEXIST);
219         }
220         error = nat64stl_create_internal(ch, cfg, uc);
221         if (error == 0) {
222                 /* Okay, let's link data */
223                 SRV_OBJECT(ch, cfg->no.kidx) = cfg;
224                 IPFW_UH_WUNLOCK(ch);
225                 return (0);
226         }
227
228         if (cfg->flags & NAT64STL_KIDX)
229                 ipfw_objhash_free_idx(ni, cfg->no.kidx);
230         if (cfg->flags & NAT64STL_46T)
231                 ipfw_unref_table(ch, cfg->map46);
232         if (cfg->flags & NAT64STL_64T)
233                 ipfw_unref_table(ch, cfg->map64);
234
235         IPFW_UH_WUNLOCK(ch);
236         nat64stl_free_config(cfg);
237         return (error);
238 }
239
240 /*
241  * Change existing nat64stl instance configuration.
242  * Data layout (v0)(current):
243  * Request: [ ipfw_obj_header ipfw_nat64stl_cfg ]
244  * Reply: [ ipfw_obj_header ipfw_nat64stl_cfg ]
245  *
246  * Returns 0 on success
247  */
248 static int
249 nat64stl_config(struct ip_fw_chain *ch, ip_fw3_opheader *op,
250     struct sockopt_data *sd)
251 {
252         ipfw_obj_header *oh;
253         ipfw_nat64stl_cfg *uc;
254         struct nat64stl_cfg *cfg;
255         struct namedobj_instance *ni;
256
257         if (sd->valsize != sizeof(*oh) + sizeof(*uc))
258                 return (EINVAL);
259
260         oh = (ipfw_obj_header *)ipfw_get_sopt_space(sd,
261             sizeof(*oh) + sizeof(*uc));
262         uc = (ipfw_nat64stl_cfg *)(oh + 1);
263
264         if (ipfw_check_object_name_generic(oh->ntlv.name) != 0 ||
265             oh->ntlv.set >= IPFW_MAX_SETS)
266                 return (EINVAL);
267
268         ni = CHAIN_TO_SRV(ch);
269         if (sd->sopt->sopt_dir == SOPT_GET) {
270                 IPFW_UH_RLOCK(ch);
271                 cfg = nat64stl_find(ni, oh->ntlv.name, oh->ntlv.set);
272                 if (cfg == NULL) {
273                         IPFW_UH_RUNLOCK(ch);
274                         return (EEXIST);
275                 }
276                 nat64stl_export_config(ch, cfg, uc);
277                 IPFW_UH_RUNLOCK(ch);
278                 return (0);
279         }
280
281         IPFW_UH_WLOCK(ch);
282         cfg = nat64stl_find(ni, oh->ntlv.name, oh->ntlv.set);
283         if (cfg == NULL) {
284                 IPFW_UH_WUNLOCK(ch);
285                 return (EEXIST);
286         }
287
288         /*
289          * For now allow to change only following values:
290          *  flags.
291          */
292
293         cfg->flags = uc->flags & NAT64STL_FLAGSMASK;
294         IPFW_UH_WUNLOCK(ch);
295         return (0);
296 }
297
298 static void
299 nat64stl_detach_config(struct ip_fw_chain *ch, struct nat64stl_cfg *cfg)
300 {
301
302         IPFW_UH_WLOCK_ASSERT(ch);
303
304         ipfw_objhash_del(CHAIN_TO_SRV(ch), &cfg->no);
305         ipfw_objhash_free_idx(CHAIN_TO_SRV(ch), cfg->no.kidx);
306         ipfw_unref_table(ch, cfg->map46);
307         ipfw_unref_table(ch, cfg->map64);
308 }
309
310 /*
311  * Destroys nat64 instance.
312  * Data layout (v0)(current):
313  * Request: [ ipfw_obj_header ]
314  *
315  * Returns 0 on success
316  */
317 static int
318 nat64stl_destroy(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
319     struct sockopt_data *sd)
320 {
321         ipfw_obj_header *oh;
322         struct nat64stl_cfg *cfg;
323
324         if (sd->valsize != sizeof(*oh))
325                 return (EINVAL);
326
327         oh = (ipfw_obj_header *)sd->kbuf;
328         if (ipfw_check_object_name_generic(oh->ntlv.name) != 0)
329                 return (EINVAL);
330
331         IPFW_UH_WLOCK(ch);
332         cfg = nat64stl_find(CHAIN_TO_SRV(ch), oh->ntlv.name, oh->ntlv.set);
333         if (cfg == NULL) {
334                 IPFW_UH_WUNLOCK(ch);
335                 return (ESRCH);
336         }
337         if (cfg->no.refcnt > 0) {
338                 IPFW_UH_WUNLOCK(ch);
339                 return (EBUSY);
340         }
341
342         SRV_OBJECT(ch, cfg->no.kidx) = NULL;
343         nat64stl_detach_config(ch, cfg);
344         IPFW_UH_WUNLOCK(ch);
345
346         nat64stl_free_config(cfg);
347         return (0);
348 }
349
350 /*
351  * Lists all nat64stl instances currently available in kernel.
352  * Data layout (v0)(current):
353  * Request: [ ipfw_obj_lheader ]
354  * Reply: [ ipfw_obj_lheader ipfw_nat64stl_cfg x N ]
355  *
356  * Returns 0 on success
357  */
358 static int
359 nat64stl_list(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
360     struct sockopt_data *sd)
361 {
362         ipfw_obj_lheader *olh;
363         struct nat64stl_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_NAT64STL_NAME);
374         olh->objsize = sizeof(ipfw_nat64stl_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_NAT64STL_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)->stats, _field)
393 static void
394 export_stats(struct ip_fw_chain *ch, struct nat64stl_cfg *cfg,
395     struct ipfw_nat64stl_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 nat64stl 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  */
418 static int
419 nat64stl_stats(struct ip_fw_chain *ch, ip_fw3_opheader *op,
420     struct sockopt_data *sd)
421 {
422         struct ipfw_nat64stl_stats stats;
423         struct nat64stl_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 = nat64stl_find(CHAIN_TO_SRV(ch), oh->ntlv.name, oh->ntlv.set);
440         if (cfg == NULL) {
441                 IPFW_UH_RUNLOCK(ch);
442                 return (ESRCH);
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 nat64stl statistics.
460  * Data layout (v0)(current):
461  * Request: [ ipfw_obj_header ]
462  *
463  * Returns 0 on success
464  */
465 static int
466 nat64stl_reset_stats(struct ip_fw_chain *ch, ip_fw3_opheader *op,
467     struct sockopt_data *sd)
468 {
469         struct nat64stl_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 = nat64stl_find(CHAIN_TO_SRV(ch), oh->ntlv.name, oh->ntlv.set);
481         if (cfg == NULL) {
482                 IPFW_UH_WUNLOCK(ch);
483                 return (ESRCH);
484         }
485         COUNTER_ARRAY_ZERO(cfg->stats.stats, NAT64STATS);
486         IPFW_UH_WUNLOCK(ch);
487         return (0);
488 }
489
490 static struct ipfw_sopt_handler scodes[] = {
491
492         { IP_FW_NAT64STL_CREATE, 0,     HDIR_SET,       nat64stl_create },
493         { IP_FW_NAT64STL_DESTROY,0,     HDIR_SET,       nat64stl_destroy },
494         { IP_FW_NAT64STL_CONFIG, 0,     HDIR_BOTH,      nat64stl_config },
495         { IP_FW_NAT64STL_LIST,   0,     HDIR_GET,       nat64stl_list },
496         { IP_FW_NAT64STL_STATS,  0,     HDIR_GET,       nat64stl_stats },
497         { IP_FW_NAT64STL_RESET_STATS,0, HDIR_SET,       nat64stl_reset_stats },
498 };
499
500 static int
501 nat64stl_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_nat64stl_eid)
508                 return (1);
509
510         *puidx = cmd->arg1;
511         *ptype = 0;
512         return (0);
513 }
514
515 static void
516 nat64stl_update_arg1(ipfw_insn *cmd, uint16_t idx)
517 {
518
519         cmd->arg1 = idx;
520 }
521
522 static int
523 nat64stl_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_NAT64STL_NAME, pno);
530         return (err);
531 }
532
533 static struct named_object *
534 nat64stl_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
547 static int
548 nat64stl_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_NAT64STL_NAME,
553             set, new_set, cmd));
554 }
555
556 static struct opcode_obj_rewrite opcodes[] = {
557         {
558                 .opcode = O_EXTERNAL_INSTANCE,
559                 .etlv = IPFW_TLV_EACTION /* just show it isn't table */,
560                 .classifier = nat64stl_classify,
561                 .update = nat64stl_update_arg1,
562                 .find_byname = nat64stl_findbyname,
563                 .find_bykidx = nat64stl_findbykidx,
564                 .manage_sets = nat64stl_manage_sets,
565         },
566 };
567
568 static int
569 destroy_config_cb(struct namedobj_instance *ni, struct named_object *no,
570     void *arg)
571 {
572         struct nat64stl_cfg *cfg;
573         struct ip_fw_chain *ch;
574
575         ch = (struct ip_fw_chain *)arg;
576         cfg = (struct nat64stl_cfg *)SRV_OBJECT(ch, no->kidx);
577         SRV_OBJECT(ch, no->kidx) = NULL;
578         nat64stl_detach_config(ch, cfg);
579         nat64stl_free_config(cfg);
580         return (0);
581 }
582
583 int
584 nat64stl_init(struct ip_fw_chain *ch, int first)
585 {
586
587         V_nat64stl_eid = ipfw_add_eaction(ch, ipfw_nat64stl, "nat64stl");
588         if (V_nat64stl_eid == 0)
589                 return (ENXIO);
590         IPFW_ADD_SOPT_HANDLER(first, scodes);
591         IPFW_ADD_OBJ_REWRITER(first, opcodes);
592         return (0);
593 }
594
595 void
596 nat64stl_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_nat64stl_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_NAT64STL_NAME);
612         V_nat64stl_eid = 0;
613         IPFW_UH_WUNLOCK(ch);
614 }
615