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/nat64lsn_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 48304046Sae#include <net/if.h> 49304046Sae#include <net/pfil.h> 50304046Sae 51304046Sae#include <netinet/in.h> 52304046Sae#include <netinet/ip.h> 53304046Sae#include <netinet/ip_var.h> 54304046Sae#include <netinet/ip_fw.h> 55334836Sae#include <netinet6/ip_fw_nat64.h> 56304046Sae 57304046Sae#include <netpfil/ipfw/ip_fw_private.h> 58304046Sae 59334836Sae#include "nat64lsn.h" 60334836Sae 61304046SaeVNET_DEFINE(uint16_t, nat64lsn_eid) = 0; 62304046Sae 63304046Saestatic struct nat64lsn_cfg * 64304046Saenat64lsn_find(struct namedobj_instance *ni, const char *name, uint8_t set) 65304046Sae{ 66304046Sae struct nat64lsn_cfg *cfg; 67304046Sae 68304046Sae cfg = (struct nat64lsn_cfg *)ipfw_objhash_lookup_name_type(ni, set, 69304046Sae IPFW_TLV_NAT64LSN_NAME, name); 70304046Sae 71304046Sae return (cfg); 72304046Sae} 73304046Sae 74304046Saestatic void 75304046Saenat64lsn_default_config(ipfw_nat64lsn_cfg *uc) 76304046Sae{ 77304046Sae 78304046Sae if (uc->max_ports == 0) 79304046Sae uc->max_ports = NAT64LSN_MAX_PORTS; 80304046Sae else 81304046Sae uc->max_ports = roundup(uc->max_ports, NAT64_CHUNK_SIZE); 82304046Sae if (uc->max_ports > NAT64_CHUNK_SIZE * NAT64LSN_MAXPGPTR) 83304046Sae uc->max_ports = NAT64_CHUNK_SIZE * NAT64LSN_MAXPGPTR; 84304046Sae if (uc->jmaxlen == 0) 85304046Sae uc->jmaxlen = NAT64LSN_JMAXLEN; 86304046Sae if (uc->jmaxlen > 65536) 87304046Sae uc->jmaxlen = 65536; 88304046Sae if (uc->nh_delete_delay == 0) 89304046Sae uc->nh_delete_delay = NAT64LSN_HOST_AGE; 90304046Sae if (uc->pg_delete_delay == 0) 91304046Sae uc->pg_delete_delay = NAT64LSN_PG_AGE; 92304046Sae if (uc->st_syn_ttl == 0) 93304046Sae uc->st_syn_ttl = NAT64LSN_TCP_SYN_AGE; 94304046Sae if (uc->st_close_ttl == 0) 95304046Sae uc->st_close_ttl = NAT64LSN_TCP_FIN_AGE; 96304046Sae if (uc->st_estab_ttl == 0) 97304046Sae uc->st_estab_ttl = NAT64LSN_TCP_EST_AGE; 98304046Sae if (uc->st_udp_ttl == 0) 99304046Sae uc->st_udp_ttl = NAT64LSN_UDP_AGE; 100304046Sae if (uc->st_icmp_ttl == 0) 101304046Sae uc->st_icmp_ttl = NAT64LSN_ICMP_AGE; 102304046Sae} 103304046Sae 104304046Sae/* 105304046Sae * Creates new nat64lsn instance. 106304046Sae * Data layout (v0)(current): 107304046Sae * Request: [ ipfw_obj_lheader ipfw_nat64lsn_cfg ] 108304046Sae * 109304046Sae * Returns 0 on success 110304046Sae */ 111304046Saestatic int 112304046Saenat64lsn_create(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 113304046Sae struct sockopt_data *sd) 114304046Sae{ 115304046Sae ipfw_obj_lheader *olh; 116304046Sae ipfw_nat64lsn_cfg *uc; 117304046Sae struct nat64lsn_cfg *cfg; 118304046Sae struct namedobj_instance *ni; 119304046Sae uint32_t addr4, mask4; 120304046Sae 121304046Sae if (sd->valsize != sizeof(*olh) + sizeof(*uc)) 122304046Sae return (EINVAL); 123304046Sae 124304046Sae olh = (ipfw_obj_lheader *)sd->kbuf; 125304046Sae uc = (ipfw_nat64lsn_cfg *)(olh + 1); 126304046Sae 127304046Sae if (ipfw_check_object_name_generic(uc->name) != 0) 128304046Sae return (EINVAL); 129304046Sae 130304046Sae if (uc->agg_prefix_len > 127 || uc->set >= IPFW_MAX_SETS) 131304046Sae return (EINVAL); 132304046Sae 133304046Sae if (uc->plen4 > 32) 134304046Sae return (EINVAL); 135334836Sae if (nat64_check_prefix6(&uc->prefix6, uc->plen6) != 0) 136304046Sae return (EINVAL); 137304046Sae 138304046Sae /* XXX: Check prefix4 to be global */ 139304046Sae addr4 = ntohl(uc->prefix4.s_addr); 140304046Sae mask4 = ~((1 << (32 - uc->plen4)) - 1); 141304046Sae if ((addr4 & mask4) != addr4) 142304046Sae return (EINVAL); 143304046Sae if (uc->min_port == 0) 144304046Sae uc->min_port = NAT64_MIN_PORT; 145304046Sae if (uc->max_port == 0) 146304046Sae uc->max_port = 65535; 147304046Sae if (uc->min_port > uc->max_port) 148304046Sae return (EINVAL); 149304046Sae uc->min_port = roundup(uc->min_port, NAT64_CHUNK_SIZE); 150304046Sae uc->max_port = roundup(uc->max_port, NAT64_CHUNK_SIZE); 151304046Sae 152304046Sae nat64lsn_default_config(uc); 153304046Sae 154304046Sae ni = CHAIN_TO_SRV(ch); 155304046Sae IPFW_UH_RLOCK(ch); 156304046Sae if (nat64lsn_find(ni, uc->name, uc->set) != NULL) { 157304046Sae IPFW_UH_RUNLOCK(ch); 158304046Sae return (EEXIST); 159304046Sae } 160304046Sae IPFW_UH_RUNLOCK(ch); 161304046Sae 162304046Sae cfg = nat64lsn_init_instance(ch, 1 << (32 - uc->plen4)); 163304046Sae strlcpy(cfg->name, uc->name, sizeof(cfg->name)); 164304046Sae cfg->no.name = cfg->name; 165304046Sae cfg->no.etlv = IPFW_TLV_NAT64LSN_NAME; 166304046Sae cfg->no.set = uc->set; 167304046Sae 168346210Sae cfg->base.plat_prefix = uc->prefix6; 169346210Sae cfg->base.plat_plen = uc->plen6; 170346210Sae cfg->base.flags = (uc->flags & NAT64LSN_FLAGSMASK) | NAT64_PLATPFX; 171346210Sae if (IN6_IS_ADDR_WKPFX(&cfg->base.plat_prefix)) 172334836Sae cfg->base.flags |= NAT64_WKPFX; 173334836Sae 174304046Sae cfg->prefix4 = addr4; 175304046Sae cfg->pmask4 = addr4 | ~mask4; 176304046Sae cfg->plen4 = uc->plen4; 177334836Sae 178304046Sae cfg->max_chunks = uc->max_ports / NAT64_CHUNK_SIZE; 179304046Sae cfg->agg_prefix_len = uc->agg_prefix_len; 180304046Sae cfg->agg_prefix_max = uc->agg_prefix_max; 181304046Sae 182304046Sae cfg->min_chunk = uc->min_port / NAT64_CHUNK_SIZE; 183304046Sae cfg->max_chunk = uc->max_port / NAT64_CHUNK_SIZE; 184304046Sae 185304046Sae cfg->jmaxlen = uc->jmaxlen; 186304046Sae cfg->nh_delete_delay = uc->nh_delete_delay; 187304046Sae cfg->pg_delete_delay = uc->pg_delete_delay; 188304046Sae cfg->st_syn_ttl = uc->st_syn_ttl; 189304046Sae cfg->st_close_ttl = uc->st_close_ttl; 190304046Sae cfg->st_estab_ttl = uc->st_estab_ttl; 191304046Sae cfg->st_udp_ttl = uc->st_udp_ttl; 192304046Sae cfg->st_icmp_ttl = uc->st_icmp_ttl; 193304046Sae 194304046Sae cfg->nomatch_verdict = IP_FW_DENY; 195304046Sae 196304046Sae IPFW_UH_WLOCK(ch); 197304046Sae 198304046Sae if (nat64lsn_find(ni, uc->name, uc->set) != NULL) { 199304046Sae IPFW_UH_WUNLOCK(ch); 200304046Sae nat64lsn_destroy_instance(cfg); 201304046Sae return (EEXIST); 202304046Sae } 203304046Sae 204304046Sae if (ipfw_objhash_alloc_idx(CHAIN_TO_SRV(ch), &cfg->no.kidx) != 0) { 205304046Sae IPFW_UH_WUNLOCK(ch); 206304046Sae nat64lsn_destroy_instance(cfg); 207304046Sae return (ENOSPC); 208304046Sae } 209304046Sae ipfw_objhash_add(CHAIN_TO_SRV(ch), &cfg->no); 210304046Sae 211304046Sae /* Okay, let's link data */ 212304046Sae SRV_OBJECT(ch, cfg->no.kidx) = cfg; 213304046Sae nat64lsn_start_instance(cfg); 214304046Sae 215304046Sae IPFW_UH_WUNLOCK(ch); 216304046Sae return (0); 217304046Sae} 218304046Sae 219304046Saestatic void 220304046Saenat64lsn_detach_config(struct ip_fw_chain *ch, struct nat64lsn_cfg *cfg) 221304046Sae{ 222304046Sae 223304046Sae IPFW_UH_WLOCK_ASSERT(ch); 224304046Sae 225304046Sae ipfw_objhash_del(CHAIN_TO_SRV(ch), &cfg->no); 226304046Sae ipfw_objhash_free_idx(CHAIN_TO_SRV(ch), cfg->no.kidx); 227304046Sae} 228304046Sae 229304046Sae/* 230304046Sae * Destroys nat64 instance. 231304046Sae * Data layout (v0)(current): 232304046Sae * Request: [ ipfw_obj_header ] 233304046Sae * 234304046Sae * Returns 0 on success 235304046Sae */ 236304046Saestatic int 237304046Saenat64lsn_destroy(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 238304046Sae struct sockopt_data *sd) 239304046Sae{ 240304046Sae struct nat64lsn_cfg *cfg; 241304046Sae ipfw_obj_header *oh; 242304046Sae 243304046Sae if (sd->valsize != sizeof(*oh)) 244304046Sae return (EINVAL); 245304046Sae 246304046Sae oh = (ipfw_obj_header *)op3; 247304046Sae 248304046Sae IPFW_UH_WLOCK(ch); 249304046Sae cfg = nat64lsn_find(CHAIN_TO_SRV(ch), oh->ntlv.name, oh->ntlv.set); 250304046Sae if (cfg == NULL) { 251304046Sae IPFW_UH_WUNLOCK(ch); 252304046Sae return (ESRCH); 253304046Sae } 254304046Sae 255304046Sae if (cfg->no.refcnt > 0) { 256304046Sae IPFW_UH_WUNLOCK(ch); 257304046Sae return (EBUSY); 258304046Sae } 259304046Sae 260346205Sae ipfw_reset_eaction_instance(ch, V_nat64lsn_eid, cfg->no.kidx); 261304046Sae SRV_OBJECT(ch, cfg->no.kidx) = NULL; 262304046Sae nat64lsn_detach_config(ch, cfg); 263304046Sae IPFW_UH_WUNLOCK(ch); 264304046Sae 265304046Sae nat64lsn_destroy_instance(cfg); 266304046Sae return (0); 267304046Sae} 268304046Sae 269304046Sae#define __COPY_STAT_FIELD(_cfg, _stats, _field) \ 270334836Sae (_stats)->_field = NAT64STAT_FETCH(&(_cfg)->base.stats, _field) 271304046Saestatic void 272304046Saeexport_stats(struct ip_fw_chain *ch, struct nat64lsn_cfg *cfg, 273304046Sae struct ipfw_nat64lsn_stats *stats) 274304046Sae{ 275304046Sae 276304046Sae __COPY_STAT_FIELD(cfg, stats, opcnt64); 277304046Sae __COPY_STAT_FIELD(cfg, stats, opcnt46); 278304046Sae __COPY_STAT_FIELD(cfg, stats, ofrags); 279304046Sae __COPY_STAT_FIELD(cfg, stats, ifrags); 280304046Sae __COPY_STAT_FIELD(cfg, stats, oerrors); 281304046Sae __COPY_STAT_FIELD(cfg, stats, noroute4); 282304046Sae __COPY_STAT_FIELD(cfg, stats, noroute6); 283304046Sae __COPY_STAT_FIELD(cfg, stats, nomatch4); 284304046Sae __COPY_STAT_FIELD(cfg, stats, noproto); 285304046Sae __COPY_STAT_FIELD(cfg, stats, nomem); 286304046Sae __COPY_STAT_FIELD(cfg, stats, dropped); 287304046Sae 288304046Sae __COPY_STAT_FIELD(cfg, stats, jcalls); 289304046Sae __COPY_STAT_FIELD(cfg, stats, jrequests); 290304046Sae __COPY_STAT_FIELD(cfg, stats, jhostsreq); 291304046Sae __COPY_STAT_FIELD(cfg, stats, jportreq); 292304046Sae __COPY_STAT_FIELD(cfg, stats, jhostfails); 293304046Sae __COPY_STAT_FIELD(cfg, stats, jportfails); 294304046Sae __COPY_STAT_FIELD(cfg, stats, jmaxlen); 295304046Sae __COPY_STAT_FIELD(cfg, stats, jnomem); 296304046Sae __COPY_STAT_FIELD(cfg, stats, jreinjected); 297304046Sae __COPY_STAT_FIELD(cfg, stats, screated); 298304046Sae __COPY_STAT_FIELD(cfg, stats, sdeleted); 299304046Sae __COPY_STAT_FIELD(cfg, stats, spgcreated); 300304046Sae __COPY_STAT_FIELD(cfg, stats, spgdeleted); 301304046Sae 302304046Sae stats->hostcount = cfg->ihcount; 303304046Sae stats->tcpchunks = cfg->protochunks[NAT_PROTO_TCP]; 304304046Sae stats->udpchunks = cfg->protochunks[NAT_PROTO_UDP]; 305304046Sae stats->icmpchunks = cfg->protochunks[NAT_PROTO_ICMP]; 306304046Sae} 307304046Sae#undef __COPY_STAT_FIELD 308304046Sae 309304046Saestatic void 310304046Saenat64lsn_export_config(struct ip_fw_chain *ch, struct nat64lsn_cfg *cfg, 311304046Sae ipfw_nat64lsn_cfg *uc) 312304046Sae{ 313304046Sae 314334836Sae uc->flags = cfg->base.flags & NAT64LSN_FLAGSMASK; 315304046Sae uc->max_ports = cfg->max_chunks * NAT64_CHUNK_SIZE; 316304046Sae uc->agg_prefix_len = cfg->agg_prefix_len; 317304046Sae uc->agg_prefix_max = cfg->agg_prefix_max; 318304046Sae 319304046Sae uc->jmaxlen = cfg->jmaxlen; 320304046Sae uc->nh_delete_delay = cfg->nh_delete_delay; 321304046Sae uc->pg_delete_delay = cfg->pg_delete_delay; 322304046Sae uc->st_syn_ttl = cfg->st_syn_ttl; 323304046Sae uc->st_close_ttl = cfg->st_close_ttl; 324304046Sae uc->st_estab_ttl = cfg->st_estab_ttl; 325304046Sae uc->st_udp_ttl = cfg->st_udp_ttl; 326304046Sae uc->st_icmp_ttl = cfg->st_icmp_ttl; 327304046Sae uc->prefix4.s_addr = htonl(cfg->prefix4); 328346210Sae uc->prefix6 = cfg->base.plat_prefix; 329304046Sae uc->plen4 = cfg->plen4; 330346210Sae uc->plen6 = cfg->base.plat_plen; 331304046Sae uc->set = cfg->no.set; 332304046Sae strlcpy(uc->name, cfg->no.name, sizeof(uc->name)); 333304046Sae} 334304046Sae 335304046Saestruct nat64_dump_arg { 336304046Sae struct ip_fw_chain *ch; 337304046Sae struct sockopt_data *sd; 338304046Sae}; 339304046Sae 340304046Saestatic int 341304046Saeexport_config_cb(struct namedobj_instance *ni, struct named_object *no, 342304046Sae void *arg) 343304046Sae{ 344304046Sae struct nat64_dump_arg *da = (struct nat64_dump_arg *)arg; 345304046Sae ipfw_nat64lsn_cfg *uc; 346304046Sae 347304046Sae uc = (struct _ipfw_nat64lsn_cfg *)ipfw_get_sopt_space(da->sd, 348304046Sae sizeof(*uc)); 349304046Sae nat64lsn_export_config(da->ch, (struct nat64lsn_cfg *)no, uc); 350304046Sae return (0); 351304046Sae} 352304046Sae 353304046Sae/* 354304046Sae * Lists all nat64 lsn instances currently available in kernel. 355304046Sae * Data layout (v0)(current): 356304046Sae * Request: [ ipfw_obj_lheader ] 357304046Sae * Reply: [ ipfw_obj_lheader ipfw_nat64lsn_cfg x N ] 358304046Sae * 359304046Sae * Returns 0 on success 360304046Sae */ 361304046Saestatic int 362304046Saenat64lsn_list(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 363304046Sae struct sockopt_data *sd) 364304046Sae{ 365304046Sae ipfw_obj_lheader *olh; 366304046Sae struct nat64_dump_arg da; 367304046Sae 368304046Sae /* Check minimum header size */ 369304046Sae if (sd->valsize < sizeof(ipfw_obj_lheader)) 370304046Sae return (EINVAL); 371304046Sae 372304046Sae olh = (ipfw_obj_lheader *)ipfw_get_sopt_header(sd, sizeof(*olh)); 373304046Sae 374304046Sae IPFW_UH_RLOCK(ch); 375304046Sae olh->count = ipfw_objhash_count_type(CHAIN_TO_SRV(ch), 376304046Sae IPFW_TLV_NAT64LSN_NAME); 377304046Sae olh->objsize = sizeof(ipfw_nat64lsn_cfg); 378304046Sae olh->size = sizeof(*olh) + olh->count * olh->objsize; 379304046Sae 380304046Sae if (sd->valsize < olh->size) { 381304046Sae IPFW_UH_RUNLOCK(ch); 382304046Sae return (ENOMEM); 383304046Sae } 384304046Sae memset(&da, 0, sizeof(da)); 385304046Sae da.ch = ch; 386304046Sae da.sd = sd; 387304046Sae ipfw_objhash_foreach_type(CHAIN_TO_SRV(ch), export_config_cb, &da, 388304046Sae IPFW_TLV_NAT64LSN_NAME); 389304046Sae IPFW_UH_RUNLOCK(ch); 390304046Sae 391304046Sae return (0); 392304046Sae} 393304046Sae 394304046Sae/* 395304046Sae * Change existing nat64lsn instance configuration. 396304046Sae * Data layout (v0)(current): 397304046Sae * Request: [ ipfw_obj_header ipfw_nat64lsn_cfg ] 398304046Sae * Reply: [ ipfw_obj_header ipfw_nat64lsn_cfg ] 399304046Sae * 400304046Sae * Returns 0 on success 401304046Sae */ 402304046Saestatic int 403304046Saenat64lsn_config(struct ip_fw_chain *ch, ip_fw3_opheader *op, 404304046Sae struct sockopt_data *sd) 405304046Sae{ 406304046Sae ipfw_obj_header *oh; 407304046Sae ipfw_nat64lsn_cfg *uc; 408304046Sae struct nat64lsn_cfg *cfg; 409304046Sae struct namedobj_instance *ni; 410304046Sae 411304046Sae if (sd->valsize != sizeof(*oh) + sizeof(*uc)) 412304046Sae return (EINVAL); 413304046Sae 414304046Sae oh = (ipfw_obj_header *)ipfw_get_sopt_space(sd, 415304046Sae sizeof(*oh) + sizeof(*uc)); 416304046Sae uc = (ipfw_nat64lsn_cfg *)(oh + 1); 417304046Sae 418304046Sae if (ipfw_check_object_name_generic(oh->ntlv.name) != 0 || 419304046Sae oh->ntlv.set >= IPFW_MAX_SETS) 420304046Sae return (EINVAL); 421304046Sae 422304046Sae ni = CHAIN_TO_SRV(ch); 423304046Sae if (sd->sopt->sopt_dir == SOPT_GET) { 424304046Sae IPFW_UH_RLOCK(ch); 425304046Sae cfg = nat64lsn_find(ni, oh->ntlv.name, oh->ntlv.set); 426304046Sae if (cfg == NULL) { 427304046Sae IPFW_UH_RUNLOCK(ch); 428304046Sae return (EEXIST); 429304046Sae } 430304046Sae nat64lsn_export_config(ch, cfg, uc); 431304046Sae IPFW_UH_RUNLOCK(ch); 432304046Sae return (0); 433304046Sae } 434304046Sae 435304046Sae nat64lsn_default_config(uc); 436304046Sae 437304046Sae IPFW_UH_WLOCK(ch); 438304046Sae cfg = nat64lsn_find(ni, oh->ntlv.name, oh->ntlv.set); 439304046Sae if (cfg == NULL) { 440304046Sae IPFW_UH_WUNLOCK(ch); 441304046Sae return (EEXIST); 442304046Sae } 443304046Sae 444304046Sae /* 445304046Sae * For now allow to change only following values: 446304046Sae * jmaxlen, nh_del_age, pg_del_age, tcp_syn_age, tcp_close_age, 447304046Sae * tcp_est_age, udp_age, icmp_age, flags, max_ports. 448304046Sae */ 449304046Sae 450304046Sae cfg->max_chunks = uc->max_ports / NAT64_CHUNK_SIZE; 451304046Sae cfg->jmaxlen = uc->jmaxlen; 452304046Sae cfg->nh_delete_delay = uc->nh_delete_delay; 453304046Sae cfg->pg_delete_delay = uc->pg_delete_delay; 454304046Sae cfg->st_syn_ttl = uc->st_syn_ttl; 455304046Sae cfg->st_close_ttl = uc->st_close_ttl; 456304046Sae cfg->st_estab_ttl = uc->st_estab_ttl; 457304046Sae cfg->st_udp_ttl = uc->st_udp_ttl; 458304046Sae cfg->st_icmp_ttl = uc->st_icmp_ttl; 459334836Sae cfg->base.flags &= ~NAT64LSN_FLAGSMASK; 460334836Sae cfg->base.flags |= uc->flags & NAT64LSN_FLAGSMASK; 461304046Sae 462304046Sae IPFW_UH_WUNLOCK(ch); 463304046Sae 464304046Sae return (0); 465304046Sae} 466304046Sae 467304046Sae/* 468304046Sae * Get nat64lsn statistics. 469304046Sae * Data layout (v0)(current): 470304046Sae * Request: [ ipfw_obj_header ] 471304046Sae * Reply: [ ipfw_obj_header ipfw_counter_tlv ] 472304046Sae * 473304046Sae * Returns 0 on success 474304046Sae */ 475304046Saestatic int 476304046Saenat64lsn_stats(struct ip_fw_chain *ch, ip_fw3_opheader *op, 477304046Sae struct sockopt_data *sd) 478304046Sae{ 479304046Sae struct ipfw_nat64lsn_stats stats; 480304046Sae struct nat64lsn_cfg *cfg; 481304046Sae ipfw_obj_header *oh; 482304046Sae ipfw_obj_ctlv *ctlv; 483304046Sae size_t sz; 484304046Sae 485304046Sae sz = sizeof(ipfw_obj_header) + sizeof(ipfw_obj_ctlv) + sizeof(stats); 486304046Sae if (sd->valsize % sizeof(uint64_t)) 487304046Sae return (EINVAL); 488304046Sae if (sd->valsize < sz) 489304046Sae return (ENOMEM); 490304046Sae oh = (ipfw_obj_header *)ipfw_get_sopt_header(sd, sz); 491304046Sae if (oh == NULL) 492304046Sae return (EINVAL); 493304046Sae memset(&stats, 0, sizeof(stats)); 494304046Sae 495304046Sae IPFW_UH_RLOCK(ch); 496304046Sae cfg = nat64lsn_find(CHAIN_TO_SRV(ch), oh->ntlv.name, oh->ntlv.set); 497304046Sae if (cfg == NULL) { 498304046Sae IPFW_UH_RUNLOCK(ch); 499304046Sae return (ESRCH); 500304046Sae } 501304046Sae 502304046Sae export_stats(ch, cfg, &stats); 503304046Sae IPFW_UH_RUNLOCK(ch); 504304046Sae 505304046Sae ctlv = (ipfw_obj_ctlv *)(oh + 1); 506304046Sae memset(ctlv, 0, sizeof(*ctlv)); 507304046Sae ctlv->head.type = IPFW_TLV_COUNTERS; 508304046Sae ctlv->head.length = sz - sizeof(ipfw_obj_header); 509304046Sae ctlv->count = sizeof(stats) / sizeof(uint64_t); 510304046Sae ctlv->objsize = sizeof(uint64_t); 511304046Sae ctlv->version = IPFW_NAT64_VERSION; 512304046Sae memcpy(ctlv + 1, &stats, sizeof(stats)); 513304046Sae return (0); 514304046Sae} 515304046Sae 516304046Sae/* 517304046Sae * Reset nat64lsn statistics. 518304046Sae * Data layout (v0)(current): 519304046Sae * Request: [ ipfw_obj_header ] 520304046Sae * 521304046Sae * Returns 0 on success 522304046Sae */ 523304046Saestatic int 524304046Saenat64lsn_reset_stats(struct ip_fw_chain *ch, ip_fw3_opheader *op, 525304046Sae struct sockopt_data *sd) 526304046Sae{ 527304046Sae struct nat64lsn_cfg *cfg; 528304046Sae ipfw_obj_header *oh; 529304046Sae 530304046Sae if (sd->valsize != sizeof(*oh)) 531304046Sae return (EINVAL); 532304046Sae oh = (ipfw_obj_header *)sd->kbuf; 533304046Sae if (ipfw_check_object_name_generic(oh->ntlv.name) != 0 || 534304046Sae oh->ntlv.set >= IPFW_MAX_SETS) 535304046Sae return (EINVAL); 536304046Sae 537304046Sae IPFW_UH_WLOCK(ch); 538304046Sae cfg = nat64lsn_find(CHAIN_TO_SRV(ch), oh->ntlv.name, oh->ntlv.set); 539304046Sae if (cfg == NULL) { 540304046Sae IPFW_UH_WUNLOCK(ch); 541304046Sae return (ESRCH); 542304046Sae } 543334836Sae COUNTER_ARRAY_ZERO(cfg->base.stats.cnt, NAT64STATS); 544304046Sae IPFW_UH_WUNLOCK(ch); 545304046Sae return (0); 546304046Sae} 547304046Sae 548304046Sae/* 549304046Sae * Reply: [ ipfw_obj_header ipfw_obj_data [ ipfw_nat64lsn_stg 550304046Sae * ipfw_nat64lsn_state x count, ... ] ] 551304046Sae */ 552304046Saestatic int 553304046Saeexport_pg_states(struct nat64lsn_cfg *cfg, struct nat64lsn_portgroup *pg, 554304046Sae ipfw_nat64lsn_stg *stg, struct sockopt_data *sd) 555304046Sae{ 556304046Sae ipfw_nat64lsn_state *ste; 557304046Sae struct nat64lsn_state *st; 558304046Sae int i, count; 559304046Sae 560304046Sae NAT64_LOCK(pg->host); 561304046Sae count = 0; 562304046Sae for (i = 0; i < 64; i++) { 563304046Sae if (PG_IS_BUSY_IDX(pg, i)) 564304046Sae count++; 565304046Sae } 566304046Sae DPRINTF(DP_STATE, "EXPORT PG %d, count %d", pg->idx, count); 567304046Sae 568304046Sae if (count == 0) { 569304046Sae stg->count = 0; 570304046Sae NAT64_UNLOCK(pg->host); 571304046Sae return (0); 572304046Sae } 573304046Sae ste = (ipfw_nat64lsn_state *)ipfw_get_sopt_space(sd, 574304046Sae count * sizeof(ipfw_nat64lsn_state)); 575304046Sae if (ste == NULL) { 576304046Sae NAT64_UNLOCK(pg->host); 577304046Sae return (1); 578304046Sae } 579304046Sae 580304046Sae stg->alias4.s_addr = pg->aaddr; 581304046Sae stg->proto = nat64lsn_rproto_map[pg->nat_proto]; 582304046Sae stg->flags = 0; 583304046Sae stg->host6 = pg->host->addr; 584304046Sae stg->count = count; 585304046Sae for (i = 0; i < 64; i++) { 586304046Sae if (PG_IS_FREE_IDX(pg, i)) 587304046Sae continue; 588304046Sae st = &pg->states[i]; 589304046Sae ste->daddr.s_addr = st->u.s.faddr; 590304046Sae ste->dport = st->u.s.fport; 591304046Sae ste->aport = pg->aport + i; 592304046Sae ste->sport = st->u.s.lport; 593304046Sae ste->flags = st->flags; /* XXX filter flags */ 594304046Sae ste->idle = GET_AGE(st->timestamp); 595304046Sae ste++; 596304046Sae } 597304046Sae NAT64_UNLOCK(pg->host); 598304046Sae 599304046Sae return (0); 600304046Sae} 601304046Sae 602304046Saestatic int 603304046Saeget_next_idx(struct nat64lsn_cfg *cfg, uint32_t *addr, uint8_t *nat_proto, 604304046Sae uint16_t *port) 605304046Sae{ 606304046Sae 607304046Sae if (*port < 65536 - NAT64_CHUNK_SIZE) { 608304046Sae *port += NAT64_CHUNK_SIZE; 609304046Sae return (0); 610304046Sae } 611304046Sae *port = 0; 612304046Sae 613304046Sae if (*nat_proto < NAT_MAX_PROTO - 1) { 614304046Sae *nat_proto += 1; 615304046Sae return (0); 616304046Sae } 617304046Sae *nat_proto = 1; 618304046Sae 619304046Sae if (*addr < cfg->pmask4) { 620304046Sae *addr += 1; 621304046Sae return (0); 622304046Sae } 623304046Sae 624304046Sae /* End of space. */ 625304046Sae return (1); 626304046Sae} 627304046Sae 628304046Sae#define PACK_IDX(addr, proto, port) \ 629304046Sae ((uint64_t)addr << 32) | ((uint32_t)port << 16) | (proto << 8) 630304046Sae#define UNPACK_IDX(idx, addr, proto, port) \ 631304046Sae (addr) = (uint32_t)((idx) >> 32); \ 632304046Sae (port) = (uint16_t)(((idx) >> 16) & 0xFFFF); \ 633304046Sae (proto) = (uint8_t)(((idx) >> 8) & 0xFF) 634304046Sae 635304046Saestatic struct nat64lsn_portgroup * 636304046Saeget_next_pg(struct nat64lsn_cfg *cfg, uint32_t *addr, uint8_t *nat_proto, 637304046Sae uint16_t *port) 638304046Sae{ 639304046Sae struct nat64lsn_portgroup *pg; 640304046Sae uint64_t pre_pack, post_pack; 641304046Sae 642304046Sae pg = NULL; 643304046Sae pre_pack = PACK_IDX(*addr, *nat_proto, *port); 644304046Sae for (;;) { 645304046Sae if (get_next_idx(cfg, addr, nat_proto, port) != 0) { 646304046Sae /* End of states */ 647304046Sae return (pg); 648304046Sae } 649304046Sae 650304046Sae pg = GET_PORTGROUP(cfg, *addr, *nat_proto, *port); 651304046Sae if (pg != NULL) 652304046Sae break; 653304046Sae } 654304046Sae 655304046Sae post_pack = PACK_IDX(*addr, *nat_proto, *port); 656304046Sae if (pre_pack == post_pack) 657304046Sae DPRINTF(DP_STATE, "XXX: PACK_IDX %u %d %d", 658304046Sae *addr, *nat_proto, *port); 659304046Sae return (pg); 660304046Sae} 661304046Sae 662316446Saestatic NAT64NOINLINE struct nat64lsn_portgroup * 663304046Saeget_first_pg(struct nat64lsn_cfg *cfg, uint32_t *addr, uint8_t *nat_proto, 664304046Sae uint16_t *port) 665304046Sae{ 666304046Sae struct nat64lsn_portgroup *pg; 667304046Sae 668304046Sae pg = GET_PORTGROUP(cfg, *addr, *nat_proto, *port); 669304046Sae if (pg == NULL) 670304046Sae pg = get_next_pg(cfg, addr, nat_proto, port); 671304046Sae 672304046Sae return (pg); 673304046Sae} 674304046Sae 675304046Sae/* 676304046Sae * Lists nat64lsn states. 677304046Sae * Data layout (v0)(current): 678304046Sae * Request: [ ipfw_obj_header ipfw_obj_data [ uint64_t ]] 679304046Sae * Reply: [ ipfw_obj_header ipfw_obj_data [ 680304046Sae * ipfw_nat64lsn_stg ipfw_nat64lsn_state x N] ] 681304046Sae * 682304046Sae * Returns 0 on success 683304046Sae */ 684304046Saestatic int 685304046Saenat64lsn_states(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 686304046Sae struct sockopt_data *sd) 687304046Sae{ 688304046Sae ipfw_obj_header *oh; 689304046Sae ipfw_obj_data *od; 690304046Sae ipfw_nat64lsn_stg *stg; 691304046Sae struct nat64lsn_cfg *cfg; 692304046Sae struct nat64lsn_portgroup *pg, *pg_next; 693304046Sae uint64_t next_idx; 694304046Sae size_t sz; 695304046Sae uint32_t addr, states; 696304046Sae uint16_t port; 697304046Sae uint8_t nat_proto; 698304046Sae 699304046Sae sz = sizeof(ipfw_obj_header) + sizeof(ipfw_obj_data) + 700304046Sae sizeof(uint64_t); 701304046Sae /* Check minimum header size */ 702304046Sae if (sd->valsize < sz) 703304046Sae return (EINVAL); 704304046Sae 705304046Sae oh = (ipfw_obj_header *)sd->kbuf; 706304046Sae od = (ipfw_obj_data *)(oh + 1); 707304046Sae if (od->head.type != IPFW_TLV_OBJDATA || 708304046Sae od->head.length != sz - sizeof(ipfw_obj_header)) 709304046Sae return (EINVAL); 710304046Sae 711304046Sae next_idx = *(uint64_t *)(od + 1); 712304046Sae /* Translate index to the request position to start from */ 713304046Sae UNPACK_IDX(next_idx, addr, nat_proto, port); 714304046Sae if (nat_proto >= NAT_MAX_PROTO) 715304046Sae return (EINVAL); 716304046Sae if (nat_proto == 0 && addr != 0) 717304046Sae return (EINVAL); 718304046Sae 719304046Sae IPFW_UH_RLOCK(ch); 720304046Sae cfg = nat64lsn_find(CHAIN_TO_SRV(ch), oh->ntlv.name, oh->ntlv.set); 721304046Sae if (cfg == NULL) { 722304046Sae IPFW_UH_RUNLOCK(ch); 723304046Sae return (ESRCH); 724304046Sae } 725304046Sae /* Fill in starting point */ 726304046Sae if (addr == 0) { 727304046Sae addr = cfg->prefix4; 728304046Sae nat_proto = 1; 729304046Sae port = 0; 730304046Sae } 731304046Sae if (addr < cfg->prefix4 || addr > cfg->pmask4) { 732304046Sae IPFW_UH_RUNLOCK(ch); 733316446Sae DPRINTF(DP_GENERIC | DP_STATE, "XXX: %ju %u %u", 734316446Sae (uintmax_t)next_idx, addr, cfg->pmask4); 735304046Sae return (EINVAL); 736304046Sae } 737304046Sae 738304046Sae sz = sizeof(ipfw_obj_header) + sizeof(ipfw_obj_data) + 739304046Sae sizeof(ipfw_nat64lsn_stg); 740304046Sae if (sd->valsize < sz) 741304046Sae return (ENOMEM); 742304046Sae oh = (ipfw_obj_header *)ipfw_get_sopt_space(sd, sz); 743304046Sae od = (ipfw_obj_data *)(oh + 1); 744304046Sae od->head.type = IPFW_TLV_OBJDATA; 745304046Sae od->head.length = sz - sizeof(ipfw_obj_header); 746304046Sae stg = (ipfw_nat64lsn_stg *)(od + 1); 747304046Sae 748304046Sae pg = get_first_pg(cfg, &addr, &nat_proto, &port); 749304046Sae if (pg == NULL) { 750304046Sae /* No states */ 751304046Sae stg->next_idx = 0xFF; 752304046Sae stg->count = 0; 753304046Sae IPFW_UH_RUNLOCK(ch); 754304046Sae return (0); 755304046Sae } 756304046Sae states = 0; 757304046Sae pg_next = NULL; 758304046Sae while (pg != NULL) { 759304046Sae pg_next = get_next_pg(cfg, &addr, &nat_proto, &port); 760304046Sae if (pg_next == NULL) 761304046Sae stg->next_idx = 0xFF; 762304046Sae else 763304046Sae stg->next_idx = PACK_IDX(addr, nat_proto, port); 764304046Sae 765304046Sae if (export_pg_states(cfg, pg, stg, sd) != 0) { 766304046Sae IPFW_UH_RUNLOCK(ch); 767304046Sae return (states == 0 ? ENOMEM: 0); 768304046Sae } 769304046Sae states += stg->count; 770304046Sae od->head.length += stg->count * sizeof(ipfw_nat64lsn_state); 771304046Sae sz += stg->count * sizeof(ipfw_nat64lsn_state); 772304046Sae if (pg_next != NULL) { 773304046Sae sz += sizeof(ipfw_nat64lsn_stg); 774304046Sae if (sd->valsize < sz) 775304046Sae break; 776304046Sae stg = (ipfw_nat64lsn_stg *)ipfw_get_sopt_space(sd, 777304046Sae sizeof(ipfw_nat64lsn_stg)); 778304046Sae } 779304046Sae pg = pg_next; 780304046Sae } 781304046Sae IPFW_UH_RUNLOCK(ch); 782304046Sae return (0); 783304046Sae} 784304046Sae 785304046Saestatic struct ipfw_sopt_handler scodes[] = { 786304046Sae { IP_FW_NAT64LSN_CREATE, 0, HDIR_BOTH, nat64lsn_create }, 787304046Sae { IP_FW_NAT64LSN_DESTROY,0, HDIR_SET, nat64lsn_destroy }, 788304046Sae { IP_FW_NAT64LSN_CONFIG, 0, HDIR_BOTH, nat64lsn_config }, 789304046Sae { IP_FW_NAT64LSN_LIST, 0, HDIR_GET, nat64lsn_list }, 790304046Sae { IP_FW_NAT64LSN_STATS, 0, HDIR_GET, nat64lsn_stats }, 791304046Sae { IP_FW_NAT64LSN_RESET_STATS,0, HDIR_SET, nat64lsn_reset_stats }, 792304046Sae { IP_FW_NAT64LSN_LIST_STATES,0, HDIR_GET, nat64lsn_states }, 793304046Sae}; 794304046Sae 795304046Saestatic int 796304046Saenat64lsn_classify(ipfw_insn *cmd, uint16_t *puidx, uint8_t *ptype) 797304046Sae{ 798304046Sae ipfw_insn *icmd; 799304046Sae 800304046Sae icmd = cmd - 1; 801304046Sae if (icmd->opcode != O_EXTERNAL_ACTION || 802304046Sae icmd->arg1 != V_nat64lsn_eid) 803304046Sae return (1); 804304046Sae 805304046Sae *puidx = cmd->arg1; 806304046Sae *ptype = 0; 807304046Sae return (0); 808304046Sae} 809304046Sae 810304046Saestatic void 811304046Saenat64lsn_update_arg1(ipfw_insn *cmd, uint16_t idx) 812304046Sae{ 813304046Sae 814304046Sae cmd->arg1 = idx; 815304046Sae} 816304046Sae 817304046Saestatic int 818304046Saenat64lsn_findbyname(struct ip_fw_chain *ch, struct tid_info *ti, 819304046Sae struct named_object **pno) 820304046Sae{ 821304046Sae int err; 822304046Sae 823304046Sae err = ipfw_objhash_find_type(CHAIN_TO_SRV(ch), ti, 824304046Sae IPFW_TLV_NAT64LSN_NAME, pno); 825304046Sae return (err); 826304046Sae} 827304046Sae 828304046Saestatic struct named_object * 829304046Saenat64lsn_findbykidx(struct ip_fw_chain *ch, uint16_t idx) 830304046Sae{ 831304046Sae struct namedobj_instance *ni; 832304046Sae struct named_object *no; 833304046Sae 834304046Sae IPFW_UH_WLOCK_ASSERT(ch); 835304046Sae ni = CHAIN_TO_SRV(ch); 836304046Sae no = ipfw_objhash_lookup_kidx(ni, idx); 837304046Sae KASSERT(no != NULL, ("NAT64LSN with index %d not found", idx)); 838304046Sae 839304046Sae return (no); 840304046Sae} 841304046Sae 842304046Saestatic int 843304046Saenat64lsn_manage_sets(struct ip_fw_chain *ch, uint16_t set, uint8_t new_set, 844304046Sae enum ipfw_sets_cmd cmd) 845304046Sae{ 846304046Sae 847304046Sae return (ipfw_obj_manage_sets(CHAIN_TO_SRV(ch), IPFW_TLV_NAT64LSN_NAME, 848304046Sae set, new_set, cmd)); 849304046Sae} 850304046Sae 851304046Saestatic struct opcode_obj_rewrite opcodes[] = { 852304046Sae { 853304046Sae .opcode = O_EXTERNAL_INSTANCE, 854304046Sae .etlv = IPFW_TLV_EACTION /* just show it isn't table */, 855304046Sae .classifier = nat64lsn_classify, 856304046Sae .update = nat64lsn_update_arg1, 857304046Sae .find_byname = nat64lsn_findbyname, 858304046Sae .find_bykidx = nat64lsn_findbykidx, 859304046Sae .manage_sets = nat64lsn_manage_sets, 860304046Sae }, 861304046Sae}; 862304046Sae 863304046Saestatic int 864304046Saedestroy_config_cb(struct namedobj_instance *ni, struct named_object *no, 865304046Sae void *arg) 866304046Sae{ 867304046Sae struct nat64lsn_cfg *cfg; 868304046Sae struct ip_fw_chain *ch; 869304046Sae 870304046Sae ch = (struct ip_fw_chain *)arg; 871304046Sae cfg = (struct nat64lsn_cfg *)SRV_OBJECT(ch, no->kidx); 872304046Sae SRV_OBJECT(ch, no->kidx) = NULL; 873304046Sae nat64lsn_detach_config(ch, cfg); 874304046Sae nat64lsn_destroy_instance(cfg); 875304046Sae return (0); 876304046Sae} 877304046Sae 878304046Saeint 879304046Saenat64lsn_init(struct ip_fw_chain *ch, int first) 880304046Sae{ 881304046Sae 882304046Sae if (first != 0) 883304046Sae nat64lsn_init_internal(); 884304046Sae V_nat64lsn_eid = ipfw_add_eaction(ch, ipfw_nat64lsn, "nat64lsn"); 885304046Sae if (V_nat64lsn_eid == 0) 886304046Sae return (ENXIO); 887304046Sae IPFW_ADD_SOPT_HANDLER(first, scodes); 888304046Sae IPFW_ADD_OBJ_REWRITER(first, opcodes); 889304046Sae return (0); 890304046Sae} 891304046Sae 892304046Saevoid 893304046Saenat64lsn_uninit(struct ip_fw_chain *ch, int last) 894304046Sae{ 895304046Sae 896304046Sae IPFW_DEL_OBJ_REWRITER(last, opcodes); 897304046Sae IPFW_DEL_SOPT_HANDLER(last, scodes); 898304046Sae ipfw_del_eaction(ch, V_nat64lsn_eid); 899304046Sae /* 900304046Sae * Since we already have deregistered external action, 901304046Sae * our named objects become unaccessible via rules, because 902304046Sae * all rules were truncated by ipfw_del_eaction(). 903304046Sae * So, we can unlink and destroy our named objects without holding 904304046Sae * IPFW_WLOCK(). 905304046Sae */ 906304046Sae IPFW_UH_WLOCK(ch); 907304046Sae ipfw_objhash_foreach_type(CHAIN_TO_SRV(ch), destroy_config_cb, ch, 908304046Sae IPFW_TLV_NAT64LSN_NAME); 909304046Sae V_nat64lsn_eid = 0; 910304046Sae IPFW_UH_WUNLOCK(ch); 911304046Sae if (last != 0) 912304046Sae nat64lsn_uninit_internal(); 913304046Sae} 914304046Sae 915