1345264Sae/*- 2345264Sae * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3345264Sae * 4345264Sae * Copyright (c) 2019 Yandex LLC 5345264Sae * Copyright (c) 2019 Andrey V. Elsukov <ae@FreeBSD.org> 6345264Sae * Copyright (c) 2019 Boris N. Lytochkin <lytboris@gmail.com> 7345264Sae * 8345264Sae * Redistribution and use in source and binary forms, with or without 9345264Sae * modification, are permitted provided that the following conditions 10345264Sae * are met: 11345264Sae * 12345264Sae * 1. Redistributions of source code must retain the above copyright 13345264Sae * notice, this list of conditions and the following disclaimer. 14345264Sae * 2. Redistributions in binary form must reproduce the above copyright 15345264Sae * notice, this list of conditions and the following disclaimer in the 16345264Sae * documentation and/or other materials provided with the distribution. 17345264Sae * 18345264Sae * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19345264Sae * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20345264Sae * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21345264Sae * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22345264Sae * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 23345264Sae * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24345264Sae * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25345264Sae * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26345264Sae * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27345264Sae * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28345264Sae */ 29345264Sae 30345264Sae#include <sys/cdefs.h> 31345264Sae__FBSDID("$FreeBSD: stable/11/sys/netpfil/ipfw/nat64/nat64clat_control.c 346212 2019-04-14 12:39:09Z ae $"); 32345264Sae 33345264Sae#include <sys/param.h> 34345264Sae#include <sys/systm.h> 35345264Sae#include <sys/counter.h> 36345264Sae#include <sys/errno.h> 37345264Sae#include <sys/kernel.h> 38345264Sae#include <sys/lock.h> 39345264Sae#include <sys/malloc.h> 40345264Sae#include <sys/mbuf.h> 41345264Sae#include <sys/module.h> 42345264Sae#include <sys/rmlock.h> 43345264Sae#include <sys/rwlock.h> 44345264Sae#include <sys/socket.h> 45345264Sae#include <sys/sockopt.h> 46345264Sae#include <sys/syslog.h> 47345264Sae#include <sys/sysctl.h> 48345264Sae 49345264Sae#include <net/if.h> 50345264Sae#include <net/if_var.h> 51345264Sae#include <net/route.h> 52345264Sae#include <net/vnet.h> 53346212Sae#include <net/pfil.h> 54345264Sae 55345264Sae#include <netinet/in.h> 56345264Sae#include <netinet/ip_var.h> 57345264Sae#include <netinet/ip_fw.h> 58345264Sae#include <netinet6/in6_var.h> 59345264Sae#include <netinet6/ip6_var.h> 60345264Sae#include <netinet6/ip_fw_nat64.h> 61345264Sae 62345264Sae#include <netpfil/ipfw/ip_fw_private.h> 63345264Sae 64345264Sae#include "nat64clat.h" 65345264Sae 66345264SaeVNET_DEFINE(uint16_t, nat64clat_eid) = 0; 67345264Sae 68345264Saestatic struct nat64clat_cfg *nat64clat_alloc_config(const char *name, 69345264Sae uint8_t set); 70345264Saestatic void nat64clat_free_config(struct nat64clat_cfg *cfg); 71345264Saestatic struct nat64clat_cfg *nat64clat_find(struct namedobj_instance *ni, 72345264Sae const char *name, uint8_t set); 73345264Sae 74345264Saestatic struct nat64clat_cfg * 75345264Saenat64clat_alloc_config(const char *name, uint8_t set) 76345264Sae{ 77345264Sae struct nat64clat_cfg *cfg; 78345264Sae 79345264Sae cfg = malloc(sizeof(struct nat64clat_cfg), M_IPFW, M_WAITOK | M_ZERO); 80345264Sae COUNTER_ARRAY_ALLOC(cfg->base.stats.cnt, NAT64STATS, M_WAITOK); 81345264Sae cfg->no.name = cfg->name; 82345264Sae cfg->no.etlv = IPFW_TLV_NAT64CLAT_NAME; 83345264Sae cfg->no.set = set; 84345264Sae strlcpy(cfg->name, name, sizeof(cfg->name)); 85345264Sae return (cfg); 86345264Sae} 87345264Sae 88345264Saestatic void 89345264Saenat64clat_free_config(struct nat64clat_cfg *cfg) 90345264Sae{ 91345264Sae 92345264Sae COUNTER_ARRAY_FREE(cfg->base.stats.cnt, NAT64STATS); 93345264Sae free(cfg, M_IPFW); 94345264Sae} 95345264Sae 96345264Saestatic void 97345264Saenat64clat_export_config(struct ip_fw_chain *ch, struct nat64clat_cfg *cfg, 98345264Sae ipfw_nat64clat_cfg *uc) 99345264Sae{ 100345264Sae uc->plat_prefix = cfg->base.plat_prefix; 101345264Sae uc->plat_plen = cfg->base.plat_plen; 102345264Sae uc->clat_prefix = cfg->base.clat_prefix; 103345264Sae uc->clat_plen = cfg->base.clat_plen; 104345264Sae uc->flags = cfg->base.flags & NAT64CLAT_FLAGSMASK; 105345264Sae uc->set = cfg->no.set; 106345264Sae strlcpy(uc->name, cfg->no.name, sizeof(uc->name)); 107345264Sae} 108345264Sae 109345264Saestruct nat64clat_dump_arg { 110345264Sae struct ip_fw_chain *ch; 111345264Sae struct sockopt_data *sd; 112345264Sae}; 113345264Sae 114345264Saestatic int 115345264Saeexport_config_cb(struct namedobj_instance *ni, struct named_object *no, 116345264Sae void *arg) 117345264Sae{ 118345264Sae struct nat64clat_dump_arg *da = (struct nat64clat_dump_arg *)arg; 119345264Sae ipfw_nat64clat_cfg *uc; 120345264Sae 121345264Sae uc = (ipfw_nat64clat_cfg *)ipfw_get_sopt_space(da->sd, sizeof(*uc)); 122345264Sae nat64clat_export_config(da->ch, (struct nat64clat_cfg *)no, uc); 123345264Sae return (0); 124345264Sae} 125345264Sae 126345264Saestatic struct nat64clat_cfg * 127345264Saenat64clat_find(struct namedobj_instance *ni, const char *name, uint8_t set) 128345264Sae{ 129345264Sae struct nat64clat_cfg *cfg; 130345264Sae 131345264Sae cfg = (struct nat64clat_cfg *)ipfw_objhash_lookup_name_type(ni, set, 132345264Sae IPFW_TLV_NAT64CLAT_NAME, name); 133345264Sae 134345264Sae return (cfg); 135345264Sae} 136345264Sae 137345264Sae/* 138345264Sae * Creates new consumer-side nat64 translator instance. 139345264Sae * Data layout (v0)(current): 140345264Sae * Request: [ ipfw_obj_lheader ipfw_nat64clat_cfg ] 141345264Sae * 142345264Sae * Returns 0 on success 143345264Sae */ 144345264Saestatic int 145345264Saenat64clat_create(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 146345264Sae struct sockopt_data *sd) 147345264Sae{ 148345264Sae ipfw_obj_lheader *olh; 149345264Sae ipfw_nat64clat_cfg *uc; 150345264Sae struct namedobj_instance *ni; 151345264Sae struct nat64clat_cfg *cfg; 152345264Sae 153345264Sae if (sd->valsize != sizeof(*olh) + sizeof(*uc)) 154345264Sae return (EINVAL); 155345264Sae 156345264Sae olh = (ipfw_obj_lheader *)sd->kbuf; 157345264Sae uc = (ipfw_nat64clat_cfg *)(olh + 1); 158345264Sae 159345264Sae if (ipfw_check_object_name_generic(uc->name) != 0) 160345264Sae return (EINVAL); 161345264Sae 162345264Sae if (uc->set >= IPFW_MAX_SETS || 163345264Sae nat64_check_prefix6(&uc->plat_prefix, uc->plat_plen) != 0 || 164345264Sae nat64_check_prefix6(&uc->clat_prefix, uc->clat_plen) != 0) 165345264Sae return (EINVAL); 166345264Sae 167345264Sae ni = CHAIN_TO_SRV(ch); 168345264Sae 169345264Sae IPFW_UH_RLOCK(ch); 170345264Sae if (nat64clat_find(ni, uc->name, uc->set) != NULL) { 171345264Sae IPFW_UH_RUNLOCK(ch); 172345264Sae return (EEXIST); 173345264Sae } 174345264Sae IPFW_UH_RUNLOCK(ch); 175345264Sae 176345264Sae cfg = nat64clat_alloc_config(uc->name, uc->set); 177345264Sae cfg->base.plat_prefix = uc->plat_prefix; 178345264Sae cfg->base.plat_plen = uc->plat_plen; 179345264Sae cfg->base.clat_prefix = uc->clat_prefix; 180345264Sae cfg->base.clat_plen = uc->clat_plen; 181345264Sae cfg->base.flags = (uc->flags & NAT64CLAT_FLAGSMASK) | 182345264Sae NAT64_CLATPFX | NAT64_PLATPFX; 183345264Sae if (IN6_IS_ADDR_WKPFX(&cfg->base.plat_prefix)) 184345264Sae cfg->base.flags |= NAT64_WKPFX; 185345264Sae 186345264Sae IPFW_UH_WLOCK(ch); 187345264Sae 188345264Sae if (nat64clat_find(ni, uc->name, uc->set) != NULL) { 189345264Sae IPFW_UH_WUNLOCK(ch); 190345264Sae nat64clat_free_config(cfg); 191345264Sae return (EEXIST); 192345264Sae } 193345264Sae 194345264Sae if (ipfw_objhash_alloc_idx(ni, &cfg->no.kidx) != 0) { 195345264Sae IPFW_UH_WUNLOCK(ch); 196345264Sae nat64clat_free_config(cfg); 197345264Sae return (ENOSPC); 198345264Sae } 199345264Sae ipfw_objhash_add(CHAIN_TO_SRV(ch), &cfg->no); 200345264Sae /* Okay, let's link data */ 201345264Sae SRV_OBJECT(ch, cfg->no.kidx) = cfg; 202345264Sae IPFW_UH_WUNLOCK(ch); 203345264Sae 204345264Sae return (0); 205345264Sae} 206345264Sae 207345264Sae/* 208345264Sae * Change existing nat64clat instance configuration. 209345264Sae * Data layout (v0)(current): 210345264Sae * Request: [ ipfw_obj_header ipfw_nat64clat_cfg ] 211345264Sae * Reply: [ ipfw_obj_header ipfw_nat64clat_cfg ] 212345264Sae * 213345264Sae * Returns 0 on success 214345264Sae */ 215345264Saestatic int 216345264Saenat64clat_config(struct ip_fw_chain *ch, ip_fw3_opheader *op, 217345264Sae struct sockopt_data *sd) 218345264Sae{ 219345264Sae ipfw_obj_header *oh; 220345264Sae ipfw_nat64clat_cfg *uc; 221345264Sae struct nat64clat_cfg *cfg; 222345264Sae struct namedobj_instance *ni; 223345264Sae uint32_t flags; 224345264Sae 225345264Sae if (sd->valsize != sizeof(*oh) + sizeof(*uc)) 226345264Sae return (EINVAL); 227345264Sae 228345264Sae oh = (ipfw_obj_header *)ipfw_get_sopt_space(sd, 229345264Sae sizeof(*oh) + sizeof(*uc)); 230345264Sae uc = (ipfw_nat64clat_cfg *)(oh + 1); 231345264Sae 232345264Sae if (ipfw_check_object_name_generic(oh->ntlv.name) != 0 || 233345264Sae oh->ntlv.set >= IPFW_MAX_SETS) 234345264Sae return (EINVAL); 235345264Sae 236345264Sae ni = CHAIN_TO_SRV(ch); 237345264Sae if (sd->sopt->sopt_dir == SOPT_GET) { 238345264Sae IPFW_UH_RLOCK(ch); 239345264Sae cfg = nat64clat_find(ni, oh->ntlv.name, oh->ntlv.set); 240345264Sae if (cfg == NULL) { 241345264Sae IPFW_UH_RUNLOCK(ch); 242345264Sae return (ENOENT); 243345264Sae } 244345264Sae nat64clat_export_config(ch, cfg, uc); 245345264Sae IPFW_UH_RUNLOCK(ch); 246345264Sae return (0); 247345264Sae } 248345264Sae 249345264Sae IPFW_UH_WLOCK(ch); 250345264Sae cfg = nat64clat_find(ni, oh->ntlv.name, oh->ntlv.set); 251345264Sae if (cfg == NULL) { 252345264Sae IPFW_UH_WUNLOCK(ch); 253345264Sae return (ENOENT); 254345264Sae } 255345264Sae 256345264Sae /* 257345264Sae * For now allow to change only following values: 258345264Sae * plat_prefix, plat_plen, clat_prefix, clat_plen, flags. 259345264Sae */ 260345264Sae flags = 0; 261345264Sae if (uc->plat_plen != cfg->base.plat_plen || 262345264Sae !IN6_ARE_ADDR_EQUAL(&uc->plat_prefix, &cfg->base.plat_prefix)) { 263345264Sae if (nat64_check_prefix6(&uc->plat_prefix, uc->plat_plen) != 0) { 264345264Sae IPFW_UH_WUNLOCK(ch); 265345264Sae return (EINVAL); 266345264Sae } 267345264Sae flags |= NAT64_PLATPFX; 268345264Sae } 269345264Sae 270345264Sae if (uc->clat_plen != cfg->base.clat_plen || 271345264Sae !IN6_ARE_ADDR_EQUAL(&uc->clat_prefix, &cfg->base.clat_prefix)) { 272345264Sae if (nat64_check_prefix6(&uc->clat_prefix, uc->clat_plen) != 0) { 273345264Sae IPFW_UH_WUNLOCK(ch); 274345264Sae return (EINVAL); 275345264Sae } 276345264Sae flags |= NAT64_CLATPFX; 277345264Sae } 278345264Sae 279345264Sae if (flags != 0) { 280345264Sae IPFW_WLOCK(ch); 281345264Sae if (flags & NAT64_PLATPFX) { 282345264Sae cfg->base.plat_prefix = uc->plat_prefix; 283345264Sae cfg->base.plat_plen = uc->plat_plen; 284345264Sae } 285345264Sae if (flags & NAT64_CLATPFX) { 286345264Sae cfg->base.clat_prefix = uc->clat_prefix; 287345264Sae cfg->base.clat_plen = uc->clat_plen; 288345264Sae } 289345264Sae IPFW_WUNLOCK(ch); 290345264Sae } 291345264Sae 292345264Sae cfg->base.flags &= ~NAT64CLAT_FLAGSMASK; 293345264Sae cfg->base.flags |= uc->flags & NAT64CLAT_FLAGSMASK; 294345264Sae 295345264Sae IPFW_UH_WUNLOCK(ch); 296345264Sae return (0); 297345264Sae} 298345264Sae 299345264Saestatic void 300345264Saenat64clat_detach_config(struct ip_fw_chain *ch, struct nat64clat_cfg *cfg) 301345264Sae{ 302345264Sae 303345264Sae IPFW_UH_WLOCK_ASSERT(ch); 304345264Sae 305345264Sae ipfw_objhash_del(CHAIN_TO_SRV(ch), &cfg->no); 306345264Sae ipfw_objhash_free_idx(CHAIN_TO_SRV(ch), cfg->no.kidx); 307345264Sae} 308345264Sae 309345264Sae/* 310345264Sae * Destroys nat64 instance. 311345264Sae * Data layout (v0)(current): 312345264Sae * Request: [ ipfw_obj_header ] 313345264Sae * 314345264Sae * Returns 0 on success 315345264Sae */ 316345264Saestatic int 317345264Saenat64clat_destroy(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 318345264Sae struct sockopt_data *sd) 319345264Sae{ 320345264Sae ipfw_obj_header *oh; 321345264Sae struct nat64clat_cfg *cfg; 322345264Sae 323345264Sae if (sd->valsize != sizeof(*oh)) 324345264Sae return (EINVAL); 325345264Sae 326345264Sae oh = (ipfw_obj_header *)sd->kbuf; 327345264Sae if (ipfw_check_object_name_generic(oh->ntlv.name) != 0) 328345264Sae return (EINVAL); 329345264Sae 330345264Sae IPFW_UH_WLOCK(ch); 331345264Sae cfg = nat64clat_find(CHAIN_TO_SRV(ch), oh->ntlv.name, oh->ntlv.set); 332345264Sae if (cfg == NULL) { 333345264Sae IPFW_UH_WUNLOCK(ch); 334345264Sae return (ENOENT); 335345264Sae } 336345264Sae if (cfg->no.refcnt > 0) { 337345264Sae IPFW_UH_WUNLOCK(ch); 338345264Sae return (EBUSY); 339345264Sae } 340345264Sae 341345264Sae ipfw_reset_eaction_instance(ch, V_nat64clat_eid, cfg->no.kidx); 342345264Sae SRV_OBJECT(ch, cfg->no.kidx) = NULL; 343345264Sae nat64clat_detach_config(ch, cfg); 344345264Sae IPFW_UH_WUNLOCK(ch); 345345264Sae 346345264Sae nat64clat_free_config(cfg); 347345264Sae return (0); 348345264Sae} 349345264Sae 350345264Sae/* 351345264Sae * Lists all nat64clat instances currently available in kernel. 352345264Sae * Data layout (v0)(current): 353345264Sae * Request: [ ipfw_obj_lheader ] 354345264Sae * Reply: [ ipfw_obj_lheader ipfw_nat64clat_cfg x N ] 355345264Sae * 356345264Sae * Returns 0 on success 357345264Sae */ 358345264Saestatic int 359345264Saenat64clat_list(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 360345264Sae struct sockopt_data *sd) 361345264Sae{ 362345264Sae ipfw_obj_lheader *olh; 363345264Sae struct nat64clat_dump_arg da; 364345264Sae 365345264Sae /* Check minimum header size */ 366345264Sae if (sd->valsize < sizeof(ipfw_obj_lheader)) 367345264Sae return (EINVAL); 368345264Sae 369345264Sae olh = (ipfw_obj_lheader *)ipfw_get_sopt_header(sd, sizeof(*olh)); 370345264Sae 371345264Sae IPFW_UH_RLOCK(ch); 372345264Sae olh->count = ipfw_objhash_count_type(CHAIN_TO_SRV(ch), 373345264Sae IPFW_TLV_NAT64CLAT_NAME); 374345264Sae olh->objsize = sizeof(ipfw_nat64clat_cfg); 375345264Sae olh->size = sizeof(*olh) + olh->count * olh->objsize; 376345264Sae 377345264Sae if (sd->valsize < olh->size) { 378345264Sae IPFW_UH_RUNLOCK(ch); 379345264Sae return (ENOMEM); 380345264Sae } 381345264Sae memset(&da, 0, sizeof(da)); 382345264Sae da.ch = ch; 383345264Sae da.sd = sd; 384345264Sae ipfw_objhash_foreach_type(CHAIN_TO_SRV(ch), export_config_cb, 385345264Sae &da, IPFW_TLV_NAT64CLAT_NAME); 386345264Sae IPFW_UH_RUNLOCK(ch); 387345264Sae 388345264Sae return (0); 389345264Sae} 390345264Sae 391345264Sae#define __COPY_STAT_FIELD(_cfg, _stats, _field) \ 392345264Sae (_stats)->_field = NAT64STAT_FETCH(&(_cfg)->base.stats, _field) 393345264Saestatic void 394345264Saeexport_stats(struct ip_fw_chain *ch, struct nat64clat_cfg *cfg, 395345264Sae struct ipfw_nat64clat_stats *stats) 396345264Sae{ 397345264Sae 398345264Sae __COPY_STAT_FIELD(cfg, stats, opcnt64); 399345264Sae __COPY_STAT_FIELD(cfg, stats, opcnt46); 400345264Sae __COPY_STAT_FIELD(cfg, stats, ofrags); 401345264Sae __COPY_STAT_FIELD(cfg, stats, ifrags); 402345264Sae __COPY_STAT_FIELD(cfg, stats, oerrors); 403345264Sae __COPY_STAT_FIELD(cfg, stats, noroute4); 404345264Sae __COPY_STAT_FIELD(cfg, stats, noroute6); 405345264Sae __COPY_STAT_FIELD(cfg, stats, noproto); 406345264Sae __COPY_STAT_FIELD(cfg, stats, nomem); 407345264Sae __COPY_STAT_FIELD(cfg, stats, dropped); 408345264Sae} 409345264Sae 410345264Sae/* 411345264Sae * Get nat64clat statistics. 412345264Sae * Data layout (v0)(current): 413345264Sae * Request: [ ipfw_obj_header ] 414345264Sae * Reply: [ ipfw_obj_header ipfw_obj_ctlv [ uint64_t x N ]] 415345264Sae * 416345264Sae * Returns 0 on success 417345264Sae */ 418345264Saestatic int 419345264Saenat64clat_stats(struct ip_fw_chain *ch, ip_fw3_opheader *op, 420345264Sae struct sockopt_data *sd) 421345264Sae{ 422345264Sae struct ipfw_nat64clat_stats stats; 423345264Sae struct nat64clat_cfg *cfg; 424345264Sae ipfw_obj_header *oh; 425345264Sae ipfw_obj_ctlv *ctlv; 426345264Sae size_t sz; 427345264Sae 428345264Sae sz = sizeof(ipfw_obj_header) + sizeof(ipfw_obj_ctlv) + sizeof(stats); 429345264Sae if (sd->valsize % sizeof(uint64_t)) 430345264Sae return (EINVAL); 431345264Sae if (sd->valsize < sz) 432345264Sae return (ENOMEM); 433345264Sae oh = (ipfw_obj_header *)ipfw_get_sopt_header(sd, sz); 434345264Sae if (oh == NULL) 435345264Sae return (EINVAL); 436345264Sae memset(&stats, 0, sizeof(stats)); 437345264Sae 438345264Sae IPFW_UH_RLOCK(ch); 439345264Sae cfg = nat64clat_find(CHAIN_TO_SRV(ch), oh->ntlv.name, oh->ntlv.set); 440345264Sae if (cfg == NULL) { 441345264Sae IPFW_UH_RUNLOCK(ch); 442345264Sae return (ENOENT); 443345264Sae } 444345264Sae export_stats(ch, cfg, &stats); 445345264Sae IPFW_UH_RUNLOCK(ch); 446345264Sae 447345264Sae ctlv = (ipfw_obj_ctlv *)(oh + 1); 448345264Sae memset(ctlv, 0, sizeof(*ctlv)); 449345264Sae ctlv->head.type = IPFW_TLV_COUNTERS; 450345264Sae ctlv->head.length = sz - sizeof(ipfw_obj_header); 451345264Sae ctlv->count = sizeof(stats) / sizeof(uint64_t); 452345264Sae ctlv->objsize = sizeof(uint64_t); 453345264Sae ctlv->version = IPFW_NAT64_VERSION; 454345264Sae memcpy(ctlv + 1, &stats, sizeof(stats)); 455345264Sae return (0); 456345264Sae} 457345264Sae 458345264Sae/* 459345264Sae * Reset nat64clat statistics. 460345264Sae * Data layout (v0)(current): 461345264Sae * Request: [ ipfw_obj_header ] 462345264Sae * 463345264Sae * Returns 0 on success 464345264Sae */ 465345264Saestatic int 466345264Saenat64clat_reset_stats(struct ip_fw_chain *ch, ip_fw3_opheader *op, 467345264Sae struct sockopt_data *sd) 468345264Sae{ 469345264Sae struct nat64clat_cfg *cfg; 470345264Sae ipfw_obj_header *oh; 471345264Sae 472345264Sae if (sd->valsize != sizeof(*oh)) 473345264Sae return (EINVAL); 474345264Sae oh = (ipfw_obj_header *)sd->kbuf; 475345264Sae if (ipfw_check_object_name_generic(oh->ntlv.name) != 0 || 476345264Sae oh->ntlv.set >= IPFW_MAX_SETS) 477345264Sae return (EINVAL); 478345264Sae 479345264Sae IPFW_UH_WLOCK(ch); 480345264Sae cfg = nat64clat_find(CHAIN_TO_SRV(ch), oh->ntlv.name, oh->ntlv.set); 481345264Sae if (cfg == NULL) { 482345264Sae IPFW_UH_WUNLOCK(ch); 483345264Sae return (ENOENT); 484345264Sae } 485345264Sae COUNTER_ARRAY_ZERO(cfg->base.stats.cnt, NAT64STATS); 486345264Sae IPFW_UH_WUNLOCK(ch); 487345264Sae return (0); 488345264Sae} 489345264Sae 490345264Saestatic struct ipfw_sopt_handler scodes[] = { 491345264Sae 492345264Sae { IP_FW_NAT64CLAT_CREATE, 0, HDIR_SET, nat64clat_create }, 493345264Sae { IP_FW_NAT64CLAT_DESTROY,0, HDIR_SET, nat64clat_destroy }, 494345264Sae { IP_FW_NAT64CLAT_CONFIG, 0, HDIR_BOTH, nat64clat_config }, 495345264Sae { IP_FW_NAT64CLAT_LIST, 0, HDIR_GET, nat64clat_list }, 496345264Sae { IP_FW_NAT64CLAT_STATS, 0, HDIR_GET, nat64clat_stats }, 497345264Sae { IP_FW_NAT64CLAT_RESET_STATS,0, HDIR_SET, nat64clat_reset_stats }, 498345264Sae}; 499345264Sae 500345264Saestatic int 501345264Saenat64clat_classify(ipfw_insn *cmd, uint16_t *puidx, uint8_t *ptype) 502345264Sae{ 503345264Sae ipfw_insn *icmd; 504345264Sae 505345264Sae icmd = cmd - 1; 506345264Sae if (icmd->opcode != O_EXTERNAL_ACTION || 507345264Sae icmd->arg1 != V_nat64clat_eid) 508345264Sae return (1); 509345264Sae 510345264Sae *puidx = cmd->arg1; 511345264Sae *ptype = 0; 512345264Sae return (0); 513345264Sae} 514345264Sae 515345264Saestatic void 516345264Saenat64clat_update_arg1(ipfw_insn *cmd, uint16_t idx) 517345264Sae{ 518345264Sae 519345264Sae cmd->arg1 = idx; 520345264Sae} 521345264Sae 522345264Saestatic int 523345264Saenat64clat_findbyname(struct ip_fw_chain *ch, struct tid_info *ti, 524345264Sae struct named_object **pno) 525345264Sae{ 526345264Sae int err; 527345264Sae 528345264Sae err = ipfw_objhash_find_type(CHAIN_TO_SRV(ch), ti, 529345264Sae IPFW_TLV_NAT64CLAT_NAME, pno); 530345264Sae return (err); 531345264Sae} 532345264Sae 533345264Saestatic struct named_object * 534345264Saenat64clat_findbykidx(struct ip_fw_chain *ch, uint16_t idx) 535345264Sae{ 536345264Sae struct namedobj_instance *ni; 537345264Sae struct named_object *no; 538345264Sae 539345264Sae IPFW_UH_WLOCK_ASSERT(ch); 540345264Sae ni = CHAIN_TO_SRV(ch); 541345264Sae no = ipfw_objhash_lookup_kidx(ni, idx); 542345264Sae KASSERT(no != NULL, ("NAT with index %d not found", idx)); 543345264Sae 544345264Sae return (no); 545345264Sae} 546345264Sae 547345264Saestatic int 548345264Saenat64clat_manage_sets(struct ip_fw_chain *ch, uint16_t set, uint8_t new_set, 549345264Sae enum ipfw_sets_cmd cmd) 550345264Sae{ 551345264Sae 552345264Sae return (ipfw_obj_manage_sets(CHAIN_TO_SRV(ch), IPFW_TLV_NAT64CLAT_NAME, 553345264Sae set, new_set, cmd)); 554345264Sae} 555345264Sae 556345264Saestatic struct opcode_obj_rewrite opcodes[] = { 557345264Sae { 558345264Sae .opcode = O_EXTERNAL_INSTANCE, 559345264Sae .etlv = IPFW_TLV_EACTION /* just show it isn't table */, 560345264Sae .classifier = nat64clat_classify, 561345264Sae .update = nat64clat_update_arg1, 562345264Sae .find_byname = nat64clat_findbyname, 563345264Sae .find_bykidx = nat64clat_findbykidx, 564345264Sae .manage_sets = nat64clat_manage_sets, 565345264Sae }, 566345264Sae}; 567345264Sae 568345264Saestatic int 569345264Saedestroy_config_cb(struct namedobj_instance *ni, struct named_object *no, 570345264Sae void *arg) 571345264Sae{ 572345264Sae struct nat64clat_cfg *cfg; 573345264Sae struct ip_fw_chain *ch; 574345264Sae 575345264Sae ch = (struct ip_fw_chain *)arg; 576345264Sae cfg = (struct nat64clat_cfg *)SRV_OBJECT(ch, no->kidx); 577345264Sae SRV_OBJECT(ch, no->kidx) = NULL; 578345264Sae nat64clat_detach_config(ch, cfg); 579345264Sae nat64clat_free_config(cfg); 580345264Sae return (0); 581345264Sae} 582345264Sae 583345264Saeint 584345264Saenat64clat_init(struct ip_fw_chain *ch, int first) 585345264Sae{ 586345264Sae 587345264Sae V_nat64clat_eid = ipfw_add_eaction(ch, ipfw_nat64clat, "nat64clat"); 588345264Sae if (V_nat64clat_eid == 0) 589345264Sae return (ENXIO); 590345264Sae IPFW_ADD_SOPT_HANDLER(first, scodes); 591345264Sae IPFW_ADD_OBJ_REWRITER(first, opcodes); 592345264Sae return (0); 593345264Sae} 594345264Sae 595345264Saevoid 596345264Saenat64clat_uninit(struct ip_fw_chain *ch, int last) 597345264Sae{ 598345264Sae 599345264Sae IPFW_DEL_OBJ_REWRITER(last, opcodes); 600345264Sae IPFW_DEL_SOPT_HANDLER(last, scodes); 601345264Sae ipfw_del_eaction(ch, V_nat64clat_eid); 602345264Sae /* 603345264Sae * Since we already have deregistered external action, 604345264Sae * our named objects become unaccessible via rules, because 605345264Sae * all rules were truncated by ipfw_del_eaction(). 606345264Sae * So, we can unlink and destroy our named objects without holding 607345264Sae * IPFW_WLOCK(). 608345264Sae */ 609345264Sae IPFW_UH_WLOCK(ch); 610345264Sae ipfw_objhash_foreach_type(CHAIN_TO_SRV(ch), destroy_config_cb, ch, 611345264Sae IPFW_TLV_NAT64CLAT_NAME); 612345264Sae V_nat64clat_eid = 0; 613345264Sae IPFW_UH_WUNLOCK(ch); 614345264Sae} 615345264Sae 616