1304046Sae/*- 2346211Sae * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3346211Sae * 4346211Sae * Copyright (c) 2015-2019 Yandex LLC 5304046Sae * Copyright (c) 2015 Alexander V. Chernikov <melifaro@FreeBSD.org> 6346211Sae * Copyright (c) 2015-2019 Andrey V. Elsukov <ae@FreeBSD.org> 7304046Sae * 8304046Sae * Redistribution and use in source and binary forms, with or without 9304046Sae * modification, are permitted provided that the following conditions 10304046Sae * are met: 11304046Sae * 12304046Sae * 1. Redistributions of source code must retain the above copyright 13304046Sae * notice, this list of conditions and the following disclaimer. 14304046Sae * 2. Redistributions in binary form must reproduce the above copyright 15304046Sae * notice, this list of conditions and the following disclaimer in the 16304046Sae * documentation and/or other materials provided with the distribution. 17304046Sae * 18304046Sae * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19304046Sae * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20304046Sae * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21304046Sae * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22304046Sae * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 23304046Sae * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24304046Sae * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25304046Sae * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26304046Sae * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27304046Sae * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28304046Sae */ 29304046Sae 30304046Sae#include <sys/cdefs.h> 31304046Sae__FBSDID("$FreeBSD: stable/11/sys/netpfil/ipfw/nat64/nat64stl_control.c 346211 2019-04-14 12:35:58Z ae $"); 32304046Sae 33304046Sae#include <sys/param.h> 34304046Sae#include <sys/systm.h> 35304046Sae#include <sys/counter.h> 36304046Sae#include <sys/errno.h> 37304046Sae#include <sys/kernel.h> 38304046Sae#include <sys/lock.h> 39304046Sae#include <sys/malloc.h> 40304046Sae#include <sys/mbuf.h> 41304046Sae#include <sys/module.h> 42304046Sae#include <sys/rmlock.h> 43304046Sae#include <sys/rwlock.h> 44304046Sae#include <sys/socket.h> 45304046Sae#include <sys/sockopt.h> 46304046Sae#include <sys/queue.h> 47304046Sae#include <sys/syslog.h> 48304046Sae#include <sys/sysctl.h> 49304046Sae 50304046Sae#include <net/if.h> 51304046Sae#include <net/if_var.h> 52304046Sae#include <net/pfil.h> 53304046Sae#include <net/route.h> 54304046Sae#include <net/vnet.h> 55304046Sae 56304046Sae#include <netinet/in.h> 57304046Sae#include <netinet/ip_var.h> 58304046Sae#include <netinet/ip_fw.h> 59304046Sae#include <netinet6/in6_var.h> 60304046Sae#include <netinet6/ip6_var.h> 61334836Sae#include <netinet6/ip_fw_nat64.h> 62304046Sae 63304046Sae#include <netpfil/ipfw/ip_fw_private.h> 64304046Sae 65334836Sae#include "nat64stl.h" 66334836Sae 67304046SaeVNET_DEFINE(uint16_t, nat64stl_eid) = 0; 68304046Sae 69334836Saestatic struct nat64stl_cfg *nat64stl_alloc_config(const char *name, 70334836Sae uint8_t set); 71304046Saestatic void nat64stl_free_config(struct nat64stl_cfg *cfg); 72304046Saestatic struct nat64stl_cfg *nat64stl_find(struct namedobj_instance *ni, 73304046Sae const char *name, uint8_t set); 74304046Sae 75304046Saestatic struct nat64stl_cfg * 76304046Saenat64stl_alloc_config(const char *name, uint8_t set) 77304046Sae{ 78304046Sae struct nat64stl_cfg *cfg; 79304046Sae 80304046Sae cfg = malloc(sizeof(struct nat64stl_cfg), M_IPFW, M_WAITOK | M_ZERO); 81334836Sae COUNTER_ARRAY_ALLOC(cfg->base.stats.cnt, NAT64STATS, M_WAITOK); 82304046Sae cfg->no.name = cfg->name; 83304046Sae cfg->no.etlv = IPFW_TLV_NAT64STL_NAME; 84304046Sae cfg->no.set = set; 85304046Sae strlcpy(cfg->name, name, sizeof(cfg->name)); 86304046Sae return (cfg); 87304046Sae} 88304046Sae 89304046Saestatic void 90304046Saenat64stl_free_config(struct nat64stl_cfg *cfg) 91304046Sae{ 92304046Sae 93334836Sae COUNTER_ARRAY_FREE(cfg->base.stats.cnt, NAT64STATS); 94304046Sae free(cfg, M_IPFW); 95304046Sae} 96304046Sae 97304046Saestatic void 98304046Saenat64stl_export_config(struct ip_fw_chain *ch, struct nat64stl_cfg *cfg, 99304046Sae ipfw_nat64stl_cfg *uc) 100304046Sae{ 101304046Sae struct named_object *no; 102304046Sae 103346210Sae uc->prefix6 = cfg->base.plat_prefix; 104346210Sae uc->plen6 = cfg->base.plat_plen; 105334836Sae uc->flags = cfg->base.flags & NAT64STL_FLAGSMASK; 106304046Sae uc->set = cfg->no.set; 107304046Sae strlcpy(uc->name, cfg->no.name, sizeof(uc->name)); 108304046Sae 109304046Sae no = ipfw_objhash_lookup_table_kidx(ch, cfg->map64); 110304046Sae ipfw_export_obj_ntlv(no, &uc->ntlv6); 111304046Sae no = ipfw_objhash_lookup_table_kidx(ch, cfg->map46); 112304046Sae ipfw_export_obj_ntlv(no, &uc->ntlv4); 113304046Sae} 114304046Sae 115304046Saestruct nat64stl_dump_arg { 116304046Sae struct ip_fw_chain *ch; 117304046Sae struct sockopt_data *sd; 118304046Sae}; 119304046Sae 120304046Saestatic int 121304046Saeexport_config_cb(struct namedobj_instance *ni, struct named_object *no, 122304046Sae void *arg) 123304046Sae{ 124304046Sae struct nat64stl_dump_arg *da = (struct nat64stl_dump_arg *)arg; 125304046Sae ipfw_nat64stl_cfg *uc; 126304046Sae 127304046Sae uc = (ipfw_nat64stl_cfg *)ipfw_get_sopt_space(da->sd, sizeof(*uc)); 128304046Sae nat64stl_export_config(da->ch, (struct nat64stl_cfg *)no, uc); 129304046Sae return (0); 130304046Sae} 131304046Sae 132304046Saestatic struct nat64stl_cfg * 133304046Saenat64stl_find(struct namedobj_instance *ni, const char *name, uint8_t set) 134304046Sae{ 135304046Sae struct nat64stl_cfg *cfg; 136304046Sae 137304046Sae cfg = (struct nat64stl_cfg *)ipfw_objhash_lookup_name_type(ni, set, 138304046Sae IPFW_TLV_NAT64STL_NAME, name); 139304046Sae 140304046Sae return (cfg); 141304046Sae} 142304046Sae 143304046Sae 144304046Saestatic int 145304046Saenat64stl_create_internal(struct ip_fw_chain *ch, struct nat64stl_cfg *cfg, 146304046Sae ipfw_nat64stl_cfg *i) 147304046Sae{ 148304046Sae 149304046Sae IPFW_UH_WLOCK_ASSERT(ch); 150304046Sae 151304046Sae if (ipfw_objhash_alloc_idx(CHAIN_TO_SRV(ch), &cfg->no.kidx) != 0) 152304046Sae return (ENOSPC); 153334836Sae cfg->base.flags |= NAT64STL_KIDX; 154304046Sae 155304046Sae if (ipfw_ref_table(ch, &i->ntlv4, &cfg->map46) != 0) 156304046Sae return (EINVAL); 157334836Sae cfg->base.flags |= NAT64STL_46T; 158304046Sae 159304046Sae if (ipfw_ref_table(ch, &i->ntlv6, &cfg->map64) != 0) 160304046Sae return (EINVAL); 161334836Sae cfg->base.flags |= NAT64STL_64T; 162304046Sae 163304046Sae ipfw_objhash_add(CHAIN_TO_SRV(ch), &cfg->no); 164304046Sae 165304046Sae return (0); 166304046Sae} 167304046Sae 168304046Sae/* 169304046Sae * Creates new nat64 instance. 170304046Sae * Data layout (v0)(current): 171304046Sae * Request: [ ipfw_obj_lheader ipfw_nat64stl_cfg ] 172304046Sae * 173304046Sae * Returns 0 on success 174304046Sae */ 175304046Saestatic int 176304046Saenat64stl_create(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 177304046Sae struct sockopt_data *sd) 178304046Sae{ 179304046Sae ipfw_obj_lheader *olh; 180304046Sae ipfw_nat64stl_cfg *uc; 181304046Sae struct namedobj_instance *ni; 182304046Sae struct nat64stl_cfg *cfg; 183304046Sae int error; 184304046Sae 185304046Sae if (sd->valsize != sizeof(*olh) + sizeof(*uc)) 186304046Sae return (EINVAL); 187304046Sae 188304046Sae olh = (ipfw_obj_lheader *)sd->kbuf; 189304046Sae uc = (ipfw_nat64stl_cfg *)(olh + 1); 190304046Sae 191304046Sae if (ipfw_check_object_name_generic(uc->name) != 0) 192304046Sae return (EINVAL); 193334836Sae if (uc->set >= IPFW_MAX_SETS || 194334836Sae nat64_check_prefix6(&uc->prefix6, uc->plen6) != 0) 195304046Sae return (EINVAL); 196304046Sae 197304046Sae /* XXX: check types of tables */ 198304046Sae 199304046Sae ni = CHAIN_TO_SRV(ch); 200304046Sae error = 0; 201304046Sae 202304046Sae IPFW_UH_RLOCK(ch); 203304046Sae if (nat64stl_find(ni, uc->name, uc->set) != NULL) { 204304046Sae IPFW_UH_RUNLOCK(ch); 205304046Sae return (EEXIST); 206304046Sae } 207304046Sae IPFW_UH_RUNLOCK(ch); 208304046Sae 209304046Sae cfg = nat64stl_alloc_config(uc->name, uc->set); 210346210Sae cfg->base.plat_prefix = uc->prefix6; 211346210Sae cfg->base.plat_plen = uc->plen6; 212346210Sae cfg->base.flags = (uc->flags & NAT64STL_FLAGSMASK) | NAT64_PLATPFX; 213346210Sae if (IN6_IS_ADDR_WKPFX(&cfg->base.plat_prefix)) 214334836Sae cfg->base.flags |= NAT64_WKPFX; 215304046Sae 216304046Sae IPFW_UH_WLOCK(ch); 217304046Sae 218304046Sae if (nat64stl_find(ni, uc->name, uc->set) != NULL) { 219304046Sae IPFW_UH_WUNLOCK(ch); 220304046Sae nat64stl_free_config(cfg); 221304046Sae return (EEXIST); 222304046Sae } 223304046Sae error = nat64stl_create_internal(ch, cfg, uc); 224304046Sae if (error == 0) { 225304046Sae /* Okay, let's link data */ 226304046Sae SRV_OBJECT(ch, cfg->no.kidx) = cfg; 227304046Sae IPFW_UH_WUNLOCK(ch); 228304046Sae return (0); 229304046Sae } 230304046Sae 231334836Sae if (cfg->base.flags & NAT64STL_KIDX) 232304046Sae ipfw_objhash_free_idx(ni, cfg->no.kidx); 233334836Sae if (cfg->base.flags & NAT64STL_46T) 234304046Sae ipfw_unref_table(ch, cfg->map46); 235334836Sae if (cfg->base.flags & NAT64STL_64T) 236304046Sae ipfw_unref_table(ch, cfg->map64); 237304046Sae 238304046Sae IPFW_UH_WUNLOCK(ch); 239304046Sae nat64stl_free_config(cfg); 240304046Sae return (error); 241304046Sae} 242304046Sae 243304046Sae/* 244304046Sae * Change existing nat64stl instance configuration. 245304046Sae * Data layout (v0)(current): 246304046Sae * Request: [ ipfw_obj_header ipfw_nat64stl_cfg ] 247304046Sae * Reply: [ ipfw_obj_header ipfw_nat64stl_cfg ] 248304046Sae * 249304046Sae * Returns 0 on success 250304046Sae */ 251304046Saestatic int 252304046Saenat64stl_config(struct ip_fw_chain *ch, ip_fw3_opheader *op, 253304046Sae struct sockopt_data *sd) 254304046Sae{ 255304046Sae ipfw_obj_header *oh; 256304046Sae ipfw_nat64stl_cfg *uc; 257304046Sae struct nat64stl_cfg *cfg; 258304046Sae struct namedobj_instance *ni; 259304046Sae 260304046Sae if (sd->valsize != sizeof(*oh) + sizeof(*uc)) 261304046Sae return (EINVAL); 262304046Sae 263304046Sae oh = (ipfw_obj_header *)ipfw_get_sopt_space(sd, 264304046Sae sizeof(*oh) + sizeof(*uc)); 265304046Sae uc = (ipfw_nat64stl_cfg *)(oh + 1); 266304046Sae 267304046Sae if (ipfw_check_object_name_generic(oh->ntlv.name) != 0 || 268304046Sae oh->ntlv.set >= IPFW_MAX_SETS) 269304046Sae return (EINVAL); 270304046Sae 271304046Sae ni = CHAIN_TO_SRV(ch); 272304046Sae if (sd->sopt->sopt_dir == SOPT_GET) { 273304046Sae IPFW_UH_RLOCK(ch); 274304046Sae cfg = nat64stl_find(ni, oh->ntlv.name, oh->ntlv.set); 275304046Sae if (cfg == NULL) { 276304046Sae IPFW_UH_RUNLOCK(ch); 277304046Sae return (EEXIST); 278304046Sae } 279304046Sae nat64stl_export_config(ch, cfg, uc); 280304046Sae IPFW_UH_RUNLOCK(ch); 281304046Sae return (0); 282304046Sae } 283304046Sae 284304046Sae IPFW_UH_WLOCK(ch); 285304046Sae cfg = nat64stl_find(ni, oh->ntlv.name, oh->ntlv.set); 286304046Sae if (cfg == NULL) { 287304046Sae IPFW_UH_WUNLOCK(ch); 288304046Sae return (EEXIST); 289304046Sae } 290304046Sae 291304046Sae /* 292304046Sae * For now allow to change only following values: 293304046Sae * flags. 294304046Sae */ 295334836Sae cfg->base.flags &= ~NAT64STL_FLAGSMASK; 296334836Sae cfg->base.flags |= uc->flags & NAT64STL_FLAGSMASK; 297334836Sae 298304046Sae IPFW_UH_WUNLOCK(ch); 299304046Sae return (0); 300304046Sae} 301304046Sae 302304046Saestatic void 303304046Saenat64stl_detach_config(struct ip_fw_chain *ch, struct nat64stl_cfg *cfg) 304304046Sae{ 305304046Sae 306304046Sae IPFW_UH_WLOCK_ASSERT(ch); 307304046Sae 308304046Sae ipfw_objhash_del(CHAIN_TO_SRV(ch), &cfg->no); 309304046Sae ipfw_objhash_free_idx(CHAIN_TO_SRV(ch), cfg->no.kidx); 310304046Sae ipfw_unref_table(ch, cfg->map46); 311304046Sae ipfw_unref_table(ch, cfg->map64); 312304046Sae} 313304046Sae 314304046Sae/* 315304046Sae * Destroys nat64 instance. 316304046Sae * Data layout (v0)(current): 317304046Sae * Request: [ ipfw_obj_header ] 318304046Sae * 319304046Sae * Returns 0 on success 320304046Sae */ 321304046Saestatic int 322304046Saenat64stl_destroy(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 323304046Sae struct sockopt_data *sd) 324304046Sae{ 325304046Sae ipfw_obj_header *oh; 326304046Sae struct nat64stl_cfg *cfg; 327304046Sae 328304046Sae if (sd->valsize != sizeof(*oh)) 329304046Sae return (EINVAL); 330304046Sae 331304046Sae oh = (ipfw_obj_header *)sd->kbuf; 332304046Sae if (ipfw_check_object_name_generic(oh->ntlv.name) != 0) 333304046Sae return (EINVAL); 334304046Sae 335304046Sae IPFW_UH_WLOCK(ch); 336304046Sae cfg = nat64stl_find(CHAIN_TO_SRV(ch), oh->ntlv.name, oh->ntlv.set); 337304046Sae if (cfg == NULL) { 338304046Sae IPFW_UH_WUNLOCK(ch); 339304046Sae return (ESRCH); 340304046Sae } 341304046Sae if (cfg->no.refcnt > 0) { 342304046Sae IPFW_UH_WUNLOCK(ch); 343304046Sae return (EBUSY); 344304046Sae } 345304046Sae 346346205Sae ipfw_reset_eaction_instance(ch, V_nat64stl_eid, cfg->no.kidx); 347304046Sae SRV_OBJECT(ch, cfg->no.kidx) = NULL; 348304046Sae nat64stl_detach_config(ch, cfg); 349304046Sae IPFW_UH_WUNLOCK(ch); 350304046Sae 351304046Sae nat64stl_free_config(cfg); 352304046Sae return (0); 353304046Sae} 354304046Sae 355304046Sae/* 356304046Sae * Lists all nat64stl instances currently available in kernel. 357304046Sae * Data layout (v0)(current): 358304046Sae * Request: [ ipfw_obj_lheader ] 359304046Sae * Reply: [ ipfw_obj_lheader ipfw_nat64stl_cfg x N ] 360304046Sae * 361304046Sae * Returns 0 on success 362304046Sae */ 363304046Saestatic int 364304046Saenat64stl_list(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 365304046Sae struct sockopt_data *sd) 366304046Sae{ 367304046Sae ipfw_obj_lheader *olh; 368304046Sae struct nat64stl_dump_arg da; 369304046Sae 370304046Sae /* Check minimum header size */ 371304046Sae if (sd->valsize < sizeof(ipfw_obj_lheader)) 372304046Sae return (EINVAL); 373304046Sae 374304046Sae olh = (ipfw_obj_lheader *)ipfw_get_sopt_header(sd, sizeof(*olh)); 375304046Sae 376304046Sae IPFW_UH_RLOCK(ch); 377304046Sae olh->count = ipfw_objhash_count_type(CHAIN_TO_SRV(ch), 378304046Sae IPFW_TLV_NAT64STL_NAME); 379304046Sae olh->objsize = sizeof(ipfw_nat64stl_cfg); 380304046Sae olh->size = sizeof(*olh) + olh->count * olh->objsize; 381304046Sae 382304046Sae if (sd->valsize < olh->size) { 383304046Sae IPFW_UH_RUNLOCK(ch); 384304046Sae return (ENOMEM); 385304046Sae } 386304046Sae memset(&da, 0, sizeof(da)); 387304046Sae da.ch = ch; 388304046Sae da.sd = sd; 389304046Sae ipfw_objhash_foreach_type(CHAIN_TO_SRV(ch), export_config_cb, 390304046Sae &da, IPFW_TLV_NAT64STL_NAME); 391304046Sae IPFW_UH_RUNLOCK(ch); 392304046Sae 393304046Sae return (0); 394304046Sae} 395304046Sae 396304046Sae#define __COPY_STAT_FIELD(_cfg, _stats, _field) \ 397334836Sae (_stats)->_field = NAT64STAT_FETCH(&(_cfg)->base.stats, _field) 398304046Saestatic void 399304046Saeexport_stats(struct ip_fw_chain *ch, struct nat64stl_cfg *cfg, 400304046Sae struct ipfw_nat64stl_stats *stats) 401304046Sae{ 402304046Sae 403304046Sae __COPY_STAT_FIELD(cfg, stats, opcnt64); 404304046Sae __COPY_STAT_FIELD(cfg, stats, opcnt46); 405304046Sae __COPY_STAT_FIELD(cfg, stats, ofrags); 406304046Sae __COPY_STAT_FIELD(cfg, stats, ifrags); 407304046Sae __COPY_STAT_FIELD(cfg, stats, oerrors); 408304046Sae __COPY_STAT_FIELD(cfg, stats, noroute4); 409304046Sae __COPY_STAT_FIELD(cfg, stats, noroute6); 410304046Sae __COPY_STAT_FIELD(cfg, stats, noproto); 411304046Sae __COPY_STAT_FIELD(cfg, stats, nomem); 412304046Sae __COPY_STAT_FIELD(cfg, stats, dropped); 413304046Sae} 414304046Sae 415304046Sae/* 416304046Sae * Get nat64stl statistics. 417304046Sae * Data layout (v0)(current): 418304046Sae * Request: [ ipfw_obj_header ] 419304046Sae * Reply: [ ipfw_obj_header ipfw_obj_ctlv [ uint64_t x N ]] 420304046Sae * 421304046Sae * Returns 0 on success 422304046Sae */ 423304046Saestatic int 424304046Saenat64stl_stats(struct ip_fw_chain *ch, ip_fw3_opheader *op, 425304046Sae struct sockopt_data *sd) 426304046Sae{ 427304046Sae struct ipfw_nat64stl_stats stats; 428304046Sae struct nat64stl_cfg *cfg; 429304046Sae ipfw_obj_header *oh; 430304046Sae ipfw_obj_ctlv *ctlv; 431304046Sae size_t sz; 432304046Sae 433304046Sae sz = sizeof(ipfw_obj_header) + sizeof(ipfw_obj_ctlv) + sizeof(stats); 434304046Sae if (sd->valsize % sizeof(uint64_t)) 435304046Sae return (EINVAL); 436304046Sae if (sd->valsize < sz) 437304046Sae return (ENOMEM); 438304046Sae oh = (ipfw_obj_header *)ipfw_get_sopt_header(sd, sz); 439304046Sae if (oh == NULL) 440304046Sae return (EINVAL); 441304046Sae memset(&stats, 0, sizeof(stats)); 442304046Sae 443304046Sae IPFW_UH_RLOCK(ch); 444304046Sae cfg = nat64stl_find(CHAIN_TO_SRV(ch), oh->ntlv.name, oh->ntlv.set); 445304046Sae if (cfg == NULL) { 446304046Sae IPFW_UH_RUNLOCK(ch); 447304046Sae return (ESRCH); 448304046Sae } 449304046Sae export_stats(ch, cfg, &stats); 450304046Sae IPFW_UH_RUNLOCK(ch); 451304046Sae 452304046Sae ctlv = (ipfw_obj_ctlv *)(oh + 1); 453304046Sae memset(ctlv, 0, sizeof(*ctlv)); 454304046Sae ctlv->head.type = IPFW_TLV_COUNTERS; 455304046Sae ctlv->head.length = sz - sizeof(ipfw_obj_header); 456304046Sae ctlv->count = sizeof(stats) / sizeof(uint64_t); 457304046Sae ctlv->objsize = sizeof(uint64_t); 458304046Sae ctlv->version = IPFW_NAT64_VERSION; 459304046Sae memcpy(ctlv + 1, &stats, sizeof(stats)); 460304046Sae return (0); 461304046Sae} 462304046Sae 463304046Sae/* 464304046Sae * Reset nat64stl statistics. 465304046Sae * Data layout (v0)(current): 466304046Sae * Request: [ ipfw_obj_header ] 467304046Sae * 468304046Sae * Returns 0 on success 469304046Sae */ 470304046Saestatic int 471304046Saenat64stl_reset_stats(struct ip_fw_chain *ch, ip_fw3_opheader *op, 472304046Sae struct sockopt_data *sd) 473304046Sae{ 474304046Sae struct nat64stl_cfg *cfg; 475304046Sae ipfw_obj_header *oh; 476304046Sae 477304046Sae if (sd->valsize != sizeof(*oh)) 478304046Sae return (EINVAL); 479304046Sae oh = (ipfw_obj_header *)sd->kbuf; 480304046Sae if (ipfw_check_object_name_generic(oh->ntlv.name) != 0 || 481304046Sae oh->ntlv.set >= IPFW_MAX_SETS) 482304046Sae return (EINVAL); 483304046Sae 484304046Sae IPFW_UH_WLOCK(ch); 485304046Sae cfg = nat64stl_find(CHAIN_TO_SRV(ch), oh->ntlv.name, oh->ntlv.set); 486304046Sae if (cfg == NULL) { 487304046Sae IPFW_UH_WUNLOCK(ch); 488304046Sae return (ESRCH); 489304046Sae } 490334836Sae COUNTER_ARRAY_ZERO(cfg->base.stats.cnt, NAT64STATS); 491304046Sae IPFW_UH_WUNLOCK(ch); 492304046Sae return (0); 493304046Sae} 494304046Sae 495304046Saestatic struct ipfw_sopt_handler scodes[] = { 496304046Sae 497304046Sae { IP_FW_NAT64STL_CREATE, 0, HDIR_SET, nat64stl_create }, 498304046Sae { IP_FW_NAT64STL_DESTROY,0, HDIR_SET, nat64stl_destroy }, 499304046Sae { IP_FW_NAT64STL_CONFIG, 0, HDIR_BOTH, nat64stl_config }, 500304046Sae { IP_FW_NAT64STL_LIST, 0, HDIR_GET, nat64stl_list }, 501304046Sae { IP_FW_NAT64STL_STATS, 0, HDIR_GET, nat64stl_stats }, 502304046Sae { IP_FW_NAT64STL_RESET_STATS,0, HDIR_SET, nat64stl_reset_stats }, 503304046Sae}; 504304046Sae 505304046Saestatic int 506304046Saenat64stl_classify(ipfw_insn *cmd, uint16_t *puidx, uint8_t *ptype) 507304046Sae{ 508304046Sae ipfw_insn *icmd; 509304046Sae 510304046Sae icmd = cmd - 1; 511304046Sae if (icmd->opcode != O_EXTERNAL_ACTION || 512304046Sae icmd->arg1 != V_nat64stl_eid) 513304046Sae return (1); 514304046Sae 515304046Sae *puidx = cmd->arg1; 516304046Sae *ptype = 0; 517304046Sae return (0); 518304046Sae} 519304046Sae 520304046Saestatic void 521304046Saenat64stl_update_arg1(ipfw_insn *cmd, uint16_t idx) 522304046Sae{ 523304046Sae 524304046Sae cmd->arg1 = idx; 525304046Sae} 526304046Sae 527304046Saestatic int 528304046Saenat64stl_findbyname(struct ip_fw_chain *ch, struct tid_info *ti, 529304046Sae struct named_object **pno) 530304046Sae{ 531304046Sae int err; 532304046Sae 533304046Sae err = ipfw_objhash_find_type(CHAIN_TO_SRV(ch), ti, 534304046Sae IPFW_TLV_NAT64STL_NAME, pno); 535304046Sae return (err); 536304046Sae} 537304046Sae 538304046Saestatic struct named_object * 539304046Saenat64stl_findbykidx(struct ip_fw_chain *ch, uint16_t idx) 540304046Sae{ 541304046Sae struct namedobj_instance *ni; 542304046Sae struct named_object *no; 543304046Sae 544304046Sae IPFW_UH_WLOCK_ASSERT(ch); 545304046Sae ni = CHAIN_TO_SRV(ch); 546304046Sae no = ipfw_objhash_lookup_kidx(ni, idx); 547304046Sae KASSERT(no != NULL, ("NAT with index %d not found", idx)); 548304046Sae 549304046Sae return (no); 550304046Sae} 551304046Sae 552304046Saestatic int 553304046Saenat64stl_manage_sets(struct ip_fw_chain *ch, uint16_t set, uint8_t new_set, 554304046Sae enum ipfw_sets_cmd cmd) 555304046Sae{ 556304046Sae 557304046Sae return (ipfw_obj_manage_sets(CHAIN_TO_SRV(ch), IPFW_TLV_NAT64STL_NAME, 558304046Sae set, new_set, cmd)); 559304046Sae} 560304046Sae 561304046Saestatic struct opcode_obj_rewrite opcodes[] = { 562304046Sae { 563304046Sae .opcode = O_EXTERNAL_INSTANCE, 564304046Sae .etlv = IPFW_TLV_EACTION /* just show it isn't table */, 565304046Sae .classifier = nat64stl_classify, 566304046Sae .update = nat64stl_update_arg1, 567304046Sae .find_byname = nat64stl_findbyname, 568304046Sae .find_bykidx = nat64stl_findbykidx, 569304046Sae .manage_sets = nat64stl_manage_sets, 570304046Sae }, 571304046Sae}; 572304046Sae 573304046Saestatic int 574304046Saedestroy_config_cb(struct namedobj_instance *ni, struct named_object *no, 575304046Sae void *arg) 576304046Sae{ 577304046Sae struct nat64stl_cfg *cfg; 578304046Sae struct ip_fw_chain *ch; 579304046Sae 580304046Sae ch = (struct ip_fw_chain *)arg; 581304046Sae cfg = (struct nat64stl_cfg *)SRV_OBJECT(ch, no->kidx); 582304046Sae SRV_OBJECT(ch, no->kidx) = NULL; 583304046Sae nat64stl_detach_config(ch, cfg); 584304046Sae nat64stl_free_config(cfg); 585304046Sae return (0); 586304046Sae} 587304046Sae 588304046Saeint 589304046Saenat64stl_init(struct ip_fw_chain *ch, int first) 590304046Sae{ 591304046Sae 592304046Sae V_nat64stl_eid = ipfw_add_eaction(ch, ipfw_nat64stl, "nat64stl"); 593304046Sae if (V_nat64stl_eid == 0) 594304046Sae return (ENXIO); 595304046Sae IPFW_ADD_SOPT_HANDLER(first, scodes); 596304046Sae IPFW_ADD_OBJ_REWRITER(first, opcodes); 597304046Sae return (0); 598304046Sae} 599304046Sae 600304046Saevoid 601304046Saenat64stl_uninit(struct ip_fw_chain *ch, int last) 602304046Sae{ 603304046Sae 604304046Sae IPFW_DEL_OBJ_REWRITER(last, opcodes); 605304046Sae IPFW_DEL_SOPT_HANDLER(last, scodes); 606304046Sae ipfw_del_eaction(ch, V_nat64stl_eid); 607304046Sae /* 608304046Sae * Since we already have deregistered external action, 609304046Sae * our named objects become unaccessible via rules, because 610304046Sae * all rules were truncated by ipfw_del_eaction(). 611304046Sae * So, we can unlink and destroy our named objects without holding 612304046Sae * IPFW_WLOCK(). 613304046Sae */ 614304046Sae IPFW_UH_WLOCK(ch); 615304046Sae ipfw_objhash_foreach_type(CHAIN_TO_SRV(ch), destroy_config_cb, ch, 616304046Sae IPFW_TLV_NAT64STL_NAME); 617304046Sae V_nat64stl_eid = 0; 618304046Sae IPFW_UH_WUNLOCK(ch); 619304046Sae} 620304046Sae 621