nat64lsn_control.c revision 346211
1/*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2015-2019 Yandex LLC 5 * Copyright (c) 2015 Alexander V. Chernikov <melifaro@FreeBSD.org> 6 * Copyright (c) 2015-2019 Andrey V. Elsukov <ae@FreeBSD.org> 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30#include <sys/cdefs.h> 31__FBSDID("$FreeBSD: stable/11/sys/netpfil/ipfw/nat64/nat64lsn_control.c 346211 2019-04-14 12:35:58Z ae $"); 32 33#include <sys/param.h> 34#include <sys/systm.h> 35#include <sys/counter.h> 36#include <sys/errno.h> 37#include <sys/kernel.h> 38#include <sys/lock.h> 39#include <sys/malloc.h> 40#include <sys/mbuf.h> 41#include <sys/module.h> 42#include <sys/rmlock.h> 43#include <sys/rwlock.h> 44#include <sys/socket.h> 45#include <sys/sockopt.h> 46#include <sys/queue.h> 47 48#include <net/if.h> 49#include <net/pfil.h> 50 51#include <netinet/in.h> 52#include <netinet/ip.h> 53#include <netinet/ip_var.h> 54#include <netinet/ip_fw.h> 55#include <netinet6/ip_fw_nat64.h> 56 57#include <netpfil/ipfw/ip_fw_private.h> 58 59#include "nat64lsn.h" 60 61VNET_DEFINE(uint16_t, nat64lsn_eid) = 0; 62 63static struct nat64lsn_cfg * 64nat64lsn_find(struct namedobj_instance *ni, const char *name, uint8_t set) 65{ 66 struct nat64lsn_cfg *cfg; 67 68 cfg = (struct nat64lsn_cfg *)ipfw_objhash_lookup_name_type(ni, set, 69 IPFW_TLV_NAT64LSN_NAME, name); 70 71 return (cfg); 72} 73 74static void 75nat64lsn_default_config(ipfw_nat64lsn_cfg *uc) 76{ 77 78 if (uc->max_ports == 0) 79 uc->max_ports = NAT64LSN_MAX_PORTS; 80 else 81 uc->max_ports = roundup(uc->max_ports, NAT64_CHUNK_SIZE); 82 if (uc->max_ports > NAT64_CHUNK_SIZE * NAT64LSN_MAXPGPTR) 83 uc->max_ports = NAT64_CHUNK_SIZE * NAT64LSN_MAXPGPTR; 84 if (uc->jmaxlen == 0) 85 uc->jmaxlen = NAT64LSN_JMAXLEN; 86 if (uc->jmaxlen > 65536) 87 uc->jmaxlen = 65536; 88 if (uc->nh_delete_delay == 0) 89 uc->nh_delete_delay = NAT64LSN_HOST_AGE; 90 if (uc->pg_delete_delay == 0) 91 uc->pg_delete_delay = NAT64LSN_PG_AGE; 92 if (uc->st_syn_ttl == 0) 93 uc->st_syn_ttl = NAT64LSN_TCP_SYN_AGE; 94 if (uc->st_close_ttl == 0) 95 uc->st_close_ttl = NAT64LSN_TCP_FIN_AGE; 96 if (uc->st_estab_ttl == 0) 97 uc->st_estab_ttl = NAT64LSN_TCP_EST_AGE; 98 if (uc->st_udp_ttl == 0) 99 uc->st_udp_ttl = NAT64LSN_UDP_AGE; 100 if (uc->st_icmp_ttl == 0) 101 uc->st_icmp_ttl = NAT64LSN_ICMP_AGE; 102} 103 104/* 105 * Creates new nat64lsn instance. 106 * Data layout (v0)(current): 107 * Request: [ ipfw_obj_lheader ipfw_nat64lsn_cfg ] 108 * 109 * Returns 0 on success 110 */ 111static int 112nat64lsn_create(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 113 struct sockopt_data *sd) 114{ 115 ipfw_obj_lheader *olh; 116 ipfw_nat64lsn_cfg *uc; 117 struct nat64lsn_cfg *cfg; 118 struct namedobj_instance *ni; 119 uint32_t addr4, mask4; 120 121 if (sd->valsize != sizeof(*olh) + sizeof(*uc)) 122 return (EINVAL); 123 124 olh = (ipfw_obj_lheader *)sd->kbuf; 125 uc = (ipfw_nat64lsn_cfg *)(olh + 1); 126 127 if (ipfw_check_object_name_generic(uc->name) != 0) 128 return (EINVAL); 129 130 if (uc->agg_prefix_len > 127 || uc->set >= IPFW_MAX_SETS) 131 return (EINVAL); 132 133 if (uc->plen4 > 32) 134 return (EINVAL); 135 if (nat64_check_prefix6(&uc->prefix6, uc->plen6) != 0) 136 return (EINVAL); 137 138 /* XXX: Check prefix4 to be global */ 139 addr4 = ntohl(uc->prefix4.s_addr); 140 mask4 = ~((1 << (32 - uc->plen4)) - 1); 141 if ((addr4 & mask4) != addr4) 142 return (EINVAL); 143 if (uc->min_port == 0) 144 uc->min_port = NAT64_MIN_PORT; 145 if (uc->max_port == 0) 146 uc->max_port = 65535; 147 if (uc->min_port > uc->max_port) 148 return (EINVAL); 149 uc->min_port = roundup(uc->min_port, NAT64_CHUNK_SIZE); 150 uc->max_port = roundup(uc->max_port, NAT64_CHUNK_SIZE); 151 152 nat64lsn_default_config(uc); 153 154 ni = CHAIN_TO_SRV(ch); 155 IPFW_UH_RLOCK(ch); 156 if (nat64lsn_find(ni, uc->name, uc->set) != NULL) { 157 IPFW_UH_RUNLOCK(ch); 158 return (EEXIST); 159 } 160 IPFW_UH_RUNLOCK(ch); 161 162 cfg = nat64lsn_init_instance(ch, 1 << (32 - uc->plen4)); 163 strlcpy(cfg->name, uc->name, sizeof(cfg->name)); 164 cfg->no.name = cfg->name; 165 cfg->no.etlv = IPFW_TLV_NAT64LSN_NAME; 166 cfg->no.set = uc->set; 167 168 cfg->base.plat_prefix = uc->prefix6; 169 cfg->base.plat_plen = uc->plen6; 170 cfg->base.flags = (uc->flags & NAT64LSN_FLAGSMASK) | NAT64_PLATPFX; 171 if (IN6_IS_ADDR_WKPFX(&cfg->base.plat_prefix)) 172 cfg->base.flags |= NAT64_WKPFX; 173 174 cfg->prefix4 = addr4; 175 cfg->pmask4 = addr4 | ~mask4; 176 cfg->plen4 = uc->plen4; 177 178 cfg->max_chunks = uc->max_ports / NAT64_CHUNK_SIZE; 179 cfg->agg_prefix_len = uc->agg_prefix_len; 180 cfg->agg_prefix_max = uc->agg_prefix_max; 181 182 cfg->min_chunk = uc->min_port / NAT64_CHUNK_SIZE; 183 cfg->max_chunk = uc->max_port / NAT64_CHUNK_SIZE; 184 185 cfg->jmaxlen = uc->jmaxlen; 186 cfg->nh_delete_delay = uc->nh_delete_delay; 187 cfg->pg_delete_delay = uc->pg_delete_delay; 188 cfg->st_syn_ttl = uc->st_syn_ttl; 189 cfg->st_close_ttl = uc->st_close_ttl; 190 cfg->st_estab_ttl = uc->st_estab_ttl; 191 cfg->st_udp_ttl = uc->st_udp_ttl; 192 cfg->st_icmp_ttl = uc->st_icmp_ttl; 193 194 cfg->nomatch_verdict = IP_FW_DENY; 195 196 IPFW_UH_WLOCK(ch); 197 198 if (nat64lsn_find(ni, uc->name, uc->set) != NULL) { 199 IPFW_UH_WUNLOCK(ch); 200 nat64lsn_destroy_instance(cfg); 201 return (EEXIST); 202 } 203 204 if (ipfw_objhash_alloc_idx(CHAIN_TO_SRV(ch), &cfg->no.kidx) != 0) { 205 IPFW_UH_WUNLOCK(ch); 206 nat64lsn_destroy_instance(cfg); 207 return (ENOSPC); 208 } 209 ipfw_objhash_add(CHAIN_TO_SRV(ch), &cfg->no); 210 211 /* Okay, let's link data */ 212 SRV_OBJECT(ch, cfg->no.kidx) = cfg; 213 nat64lsn_start_instance(cfg); 214 215 IPFW_UH_WUNLOCK(ch); 216 return (0); 217} 218 219static void 220nat64lsn_detach_config(struct ip_fw_chain *ch, struct nat64lsn_cfg *cfg) 221{ 222 223 IPFW_UH_WLOCK_ASSERT(ch); 224 225 ipfw_objhash_del(CHAIN_TO_SRV(ch), &cfg->no); 226 ipfw_objhash_free_idx(CHAIN_TO_SRV(ch), cfg->no.kidx); 227} 228 229/* 230 * Destroys nat64 instance. 231 * Data layout (v0)(current): 232 * Request: [ ipfw_obj_header ] 233 * 234 * Returns 0 on success 235 */ 236static int 237nat64lsn_destroy(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 238 struct sockopt_data *sd) 239{ 240 struct nat64lsn_cfg *cfg; 241 ipfw_obj_header *oh; 242 243 if (sd->valsize != sizeof(*oh)) 244 return (EINVAL); 245 246 oh = (ipfw_obj_header *)op3; 247 248 IPFW_UH_WLOCK(ch); 249 cfg = nat64lsn_find(CHAIN_TO_SRV(ch), oh->ntlv.name, oh->ntlv.set); 250 if (cfg == NULL) { 251 IPFW_UH_WUNLOCK(ch); 252 return (ESRCH); 253 } 254 255 if (cfg->no.refcnt > 0) { 256 IPFW_UH_WUNLOCK(ch); 257 return (EBUSY); 258 } 259 260 ipfw_reset_eaction_instance(ch, V_nat64lsn_eid, cfg->no.kidx); 261 SRV_OBJECT(ch, cfg->no.kidx) = NULL; 262 nat64lsn_detach_config(ch, cfg); 263 IPFW_UH_WUNLOCK(ch); 264 265 nat64lsn_destroy_instance(cfg); 266 return (0); 267} 268 269#define __COPY_STAT_FIELD(_cfg, _stats, _field) \ 270 (_stats)->_field = NAT64STAT_FETCH(&(_cfg)->base.stats, _field) 271static void 272export_stats(struct ip_fw_chain *ch, struct nat64lsn_cfg *cfg, 273 struct ipfw_nat64lsn_stats *stats) 274{ 275 276 __COPY_STAT_FIELD(cfg, stats, opcnt64); 277 __COPY_STAT_FIELD(cfg, stats, opcnt46); 278 __COPY_STAT_FIELD(cfg, stats, ofrags); 279 __COPY_STAT_FIELD(cfg, stats, ifrags); 280 __COPY_STAT_FIELD(cfg, stats, oerrors); 281 __COPY_STAT_FIELD(cfg, stats, noroute4); 282 __COPY_STAT_FIELD(cfg, stats, noroute6); 283 __COPY_STAT_FIELD(cfg, stats, nomatch4); 284 __COPY_STAT_FIELD(cfg, stats, noproto); 285 __COPY_STAT_FIELD(cfg, stats, nomem); 286 __COPY_STAT_FIELD(cfg, stats, dropped); 287 288 __COPY_STAT_FIELD(cfg, stats, jcalls); 289 __COPY_STAT_FIELD(cfg, stats, jrequests); 290 __COPY_STAT_FIELD(cfg, stats, jhostsreq); 291 __COPY_STAT_FIELD(cfg, stats, jportreq); 292 __COPY_STAT_FIELD(cfg, stats, jhostfails); 293 __COPY_STAT_FIELD(cfg, stats, jportfails); 294 __COPY_STAT_FIELD(cfg, stats, jmaxlen); 295 __COPY_STAT_FIELD(cfg, stats, jnomem); 296 __COPY_STAT_FIELD(cfg, stats, jreinjected); 297 __COPY_STAT_FIELD(cfg, stats, screated); 298 __COPY_STAT_FIELD(cfg, stats, sdeleted); 299 __COPY_STAT_FIELD(cfg, stats, spgcreated); 300 __COPY_STAT_FIELD(cfg, stats, spgdeleted); 301 302 stats->hostcount = cfg->ihcount; 303 stats->tcpchunks = cfg->protochunks[NAT_PROTO_TCP]; 304 stats->udpchunks = cfg->protochunks[NAT_PROTO_UDP]; 305 stats->icmpchunks = cfg->protochunks[NAT_PROTO_ICMP]; 306} 307#undef __COPY_STAT_FIELD 308 309static void 310nat64lsn_export_config(struct ip_fw_chain *ch, struct nat64lsn_cfg *cfg, 311 ipfw_nat64lsn_cfg *uc) 312{ 313 314 uc->flags = cfg->base.flags & NAT64LSN_FLAGSMASK; 315 uc->max_ports = cfg->max_chunks * NAT64_CHUNK_SIZE; 316 uc->agg_prefix_len = cfg->agg_prefix_len; 317 uc->agg_prefix_max = cfg->agg_prefix_max; 318 319 uc->jmaxlen = cfg->jmaxlen; 320 uc->nh_delete_delay = cfg->nh_delete_delay; 321 uc->pg_delete_delay = cfg->pg_delete_delay; 322 uc->st_syn_ttl = cfg->st_syn_ttl; 323 uc->st_close_ttl = cfg->st_close_ttl; 324 uc->st_estab_ttl = cfg->st_estab_ttl; 325 uc->st_udp_ttl = cfg->st_udp_ttl; 326 uc->st_icmp_ttl = cfg->st_icmp_ttl; 327 uc->prefix4.s_addr = htonl(cfg->prefix4); 328 uc->prefix6 = cfg->base.plat_prefix; 329 uc->plen4 = cfg->plen4; 330 uc->plen6 = cfg->base.plat_plen; 331 uc->set = cfg->no.set; 332 strlcpy(uc->name, cfg->no.name, sizeof(uc->name)); 333} 334 335struct nat64_dump_arg { 336 struct ip_fw_chain *ch; 337 struct sockopt_data *sd; 338}; 339 340static int 341export_config_cb(struct namedobj_instance *ni, struct named_object *no, 342 void *arg) 343{ 344 struct nat64_dump_arg *da = (struct nat64_dump_arg *)arg; 345 ipfw_nat64lsn_cfg *uc; 346 347 uc = (struct _ipfw_nat64lsn_cfg *)ipfw_get_sopt_space(da->sd, 348 sizeof(*uc)); 349 nat64lsn_export_config(da->ch, (struct nat64lsn_cfg *)no, uc); 350 return (0); 351} 352 353/* 354 * Lists all nat64 lsn instances currently available in kernel. 355 * Data layout (v0)(current): 356 * Request: [ ipfw_obj_lheader ] 357 * Reply: [ ipfw_obj_lheader ipfw_nat64lsn_cfg x N ] 358 * 359 * Returns 0 on success 360 */ 361static int 362nat64lsn_list(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 363 struct sockopt_data *sd) 364{ 365 ipfw_obj_lheader *olh; 366 struct nat64_dump_arg da; 367 368 /* Check minimum header size */ 369 if (sd->valsize < sizeof(ipfw_obj_lheader)) 370 return (EINVAL); 371 372 olh = (ipfw_obj_lheader *)ipfw_get_sopt_header(sd, sizeof(*olh)); 373 374 IPFW_UH_RLOCK(ch); 375 olh->count = ipfw_objhash_count_type(CHAIN_TO_SRV(ch), 376 IPFW_TLV_NAT64LSN_NAME); 377 olh->objsize = sizeof(ipfw_nat64lsn_cfg); 378 olh->size = sizeof(*olh) + olh->count * olh->objsize; 379 380 if (sd->valsize < olh->size) { 381 IPFW_UH_RUNLOCK(ch); 382 return (ENOMEM); 383 } 384 memset(&da, 0, sizeof(da)); 385 da.ch = ch; 386 da.sd = sd; 387 ipfw_objhash_foreach_type(CHAIN_TO_SRV(ch), export_config_cb, &da, 388 IPFW_TLV_NAT64LSN_NAME); 389 IPFW_UH_RUNLOCK(ch); 390 391 return (0); 392} 393 394/* 395 * Change existing nat64lsn instance configuration. 396 * Data layout (v0)(current): 397 * Request: [ ipfw_obj_header ipfw_nat64lsn_cfg ] 398 * Reply: [ ipfw_obj_header ipfw_nat64lsn_cfg ] 399 * 400 * Returns 0 on success 401 */ 402static int 403nat64lsn_config(struct ip_fw_chain *ch, ip_fw3_opheader *op, 404 struct sockopt_data *sd) 405{ 406 ipfw_obj_header *oh; 407 ipfw_nat64lsn_cfg *uc; 408 struct nat64lsn_cfg *cfg; 409 struct namedobj_instance *ni; 410 411 if (sd->valsize != sizeof(*oh) + sizeof(*uc)) 412 return (EINVAL); 413 414 oh = (ipfw_obj_header *)ipfw_get_sopt_space(sd, 415 sizeof(*oh) + sizeof(*uc)); 416 uc = (ipfw_nat64lsn_cfg *)(oh + 1); 417 418 if (ipfw_check_object_name_generic(oh->ntlv.name) != 0 || 419 oh->ntlv.set >= IPFW_MAX_SETS) 420 return (EINVAL); 421 422 ni = CHAIN_TO_SRV(ch); 423 if (sd->sopt->sopt_dir == SOPT_GET) { 424 IPFW_UH_RLOCK(ch); 425 cfg = nat64lsn_find(ni, oh->ntlv.name, oh->ntlv.set); 426 if (cfg == NULL) { 427 IPFW_UH_RUNLOCK(ch); 428 return (EEXIST); 429 } 430 nat64lsn_export_config(ch, cfg, uc); 431 IPFW_UH_RUNLOCK(ch); 432 return (0); 433 } 434 435 nat64lsn_default_config(uc); 436 437 IPFW_UH_WLOCK(ch); 438 cfg = nat64lsn_find(ni, oh->ntlv.name, oh->ntlv.set); 439 if (cfg == NULL) { 440 IPFW_UH_WUNLOCK(ch); 441 return (EEXIST); 442 } 443 444 /* 445 * For now allow to change only following values: 446 * jmaxlen, nh_del_age, pg_del_age, tcp_syn_age, tcp_close_age, 447 * tcp_est_age, udp_age, icmp_age, flags, max_ports. 448 */ 449 450 cfg->max_chunks = uc->max_ports / NAT64_CHUNK_SIZE; 451 cfg->jmaxlen = uc->jmaxlen; 452 cfg->nh_delete_delay = uc->nh_delete_delay; 453 cfg->pg_delete_delay = uc->pg_delete_delay; 454 cfg->st_syn_ttl = uc->st_syn_ttl; 455 cfg->st_close_ttl = uc->st_close_ttl; 456 cfg->st_estab_ttl = uc->st_estab_ttl; 457 cfg->st_udp_ttl = uc->st_udp_ttl; 458 cfg->st_icmp_ttl = uc->st_icmp_ttl; 459 cfg->base.flags &= ~NAT64LSN_FLAGSMASK; 460 cfg->base.flags |= uc->flags & NAT64LSN_FLAGSMASK; 461 462 IPFW_UH_WUNLOCK(ch); 463 464 return (0); 465} 466 467/* 468 * Get nat64lsn statistics. 469 * Data layout (v0)(current): 470 * Request: [ ipfw_obj_header ] 471 * Reply: [ ipfw_obj_header ipfw_counter_tlv ] 472 * 473 * Returns 0 on success 474 */ 475static int 476nat64lsn_stats(struct ip_fw_chain *ch, ip_fw3_opheader *op, 477 struct sockopt_data *sd) 478{ 479 struct ipfw_nat64lsn_stats stats; 480 struct nat64lsn_cfg *cfg; 481 ipfw_obj_header *oh; 482 ipfw_obj_ctlv *ctlv; 483 size_t sz; 484 485 sz = sizeof(ipfw_obj_header) + sizeof(ipfw_obj_ctlv) + sizeof(stats); 486 if (sd->valsize % sizeof(uint64_t)) 487 return (EINVAL); 488 if (sd->valsize < sz) 489 return (ENOMEM); 490 oh = (ipfw_obj_header *)ipfw_get_sopt_header(sd, sz); 491 if (oh == NULL) 492 return (EINVAL); 493 memset(&stats, 0, sizeof(stats)); 494 495 IPFW_UH_RLOCK(ch); 496 cfg = nat64lsn_find(CHAIN_TO_SRV(ch), oh->ntlv.name, oh->ntlv.set); 497 if (cfg == NULL) { 498 IPFW_UH_RUNLOCK(ch); 499 return (ESRCH); 500 } 501 502 export_stats(ch, cfg, &stats); 503 IPFW_UH_RUNLOCK(ch); 504 505 ctlv = (ipfw_obj_ctlv *)(oh + 1); 506 memset(ctlv, 0, sizeof(*ctlv)); 507 ctlv->head.type = IPFW_TLV_COUNTERS; 508 ctlv->head.length = sz - sizeof(ipfw_obj_header); 509 ctlv->count = sizeof(stats) / sizeof(uint64_t); 510 ctlv->objsize = sizeof(uint64_t); 511 ctlv->version = IPFW_NAT64_VERSION; 512 memcpy(ctlv + 1, &stats, sizeof(stats)); 513 return (0); 514} 515 516/* 517 * Reset nat64lsn statistics. 518 * Data layout (v0)(current): 519 * Request: [ ipfw_obj_header ] 520 * 521 * Returns 0 on success 522 */ 523static int 524nat64lsn_reset_stats(struct ip_fw_chain *ch, ip_fw3_opheader *op, 525 struct sockopt_data *sd) 526{ 527 struct nat64lsn_cfg *cfg; 528 ipfw_obj_header *oh; 529 530 if (sd->valsize != sizeof(*oh)) 531 return (EINVAL); 532 oh = (ipfw_obj_header *)sd->kbuf; 533 if (ipfw_check_object_name_generic(oh->ntlv.name) != 0 || 534 oh->ntlv.set >= IPFW_MAX_SETS) 535 return (EINVAL); 536 537 IPFW_UH_WLOCK(ch); 538 cfg = nat64lsn_find(CHAIN_TO_SRV(ch), oh->ntlv.name, oh->ntlv.set); 539 if (cfg == NULL) { 540 IPFW_UH_WUNLOCK(ch); 541 return (ESRCH); 542 } 543 COUNTER_ARRAY_ZERO(cfg->base.stats.cnt, NAT64STATS); 544 IPFW_UH_WUNLOCK(ch); 545 return (0); 546} 547 548/* 549 * Reply: [ ipfw_obj_header ipfw_obj_data [ ipfw_nat64lsn_stg 550 * ipfw_nat64lsn_state x count, ... ] ] 551 */ 552static int 553export_pg_states(struct nat64lsn_cfg *cfg, struct nat64lsn_portgroup *pg, 554 ipfw_nat64lsn_stg *stg, struct sockopt_data *sd) 555{ 556 ipfw_nat64lsn_state *ste; 557 struct nat64lsn_state *st; 558 int i, count; 559 560 NAT64_LOCK(pg->host); 561 count = 0; 562 for (i = 0; i < 64; i++) { 563 if (PG_IS_BUSY_IDX(pg, i)) 564 count++; 565 } 566 DPRINTF(DP_STATE, "EXPORT PG %d, count %d", pg->idx, count); 567 568 if (count == 0) { 569 stg->count = 0; 570 NAT64_UNLOCK(pg->host); 571 return (0); 572 } 573 ste = (ipfw_nat64lsn_state *)ipfw_get_sopt_space(sd, 574 count * sizeof(ipfw_nat64lsn_state)); 575 if (ste == NULL) { 576 NAT64_UNLOCK(pg->host); 577 return (1); 578 } 579 580 stg->alias4.s_addr = pg->aaddr; 581 stg->proto = nat64lsn_rproto_map[pg->nat_proto]; 582 stg->flags = 0; 583 stg->host6 = pg->host->addr; 584 stg->count = count; 585 for (i = 0; i < 64; i++) { 586 if (PG_IS_FREE_IDX(pg, i)) 587 continue; 588 st = &pg->states[i]; 589 ste->daddr.s_addr = st->u.s.faddr; 590 ste->dport = st->u.s.fport; 591 ste->aport = pg->aport + i; 592 ste->sport = st->u.s.lport; 593 ste->flags = st->flags; /* XXX filter flags */ 594 ste->idle = GET_AGE(st->timestamp); 595 ste++; 596 } 597 NAT64_UNLOCK(pg->host); 598 599 return (0); 600} 601 602static int 603get_next_idx(struct nat64lsn_cfg *cfg, uint32_t *addr, uint8_t *nat_proto, 604 uint16_t *port) 605{ 606 607 if (*port < 65536 - NAT64_CHUNK_SIZE) { 608 *port += NAT64_CHUNK_SIZE; 609 return (0); 610 } 611 *port = 0; 612 613 if (*nat_proto < NAT_MAX_PROTO - 1) { 614 *nat_proto += 1; 615 return (0); 616 } 617 *nat_proto = 1; 618 619 if (*addr < cfg->pmask4) { 620 *addr += 1; 621 return (0); 622 } 623 624 /* End of space. */ 625 return (1); 626} 627 628#define PACK_IDX(addr, proto, port) \ 629 ((uint64_t)addr << 32) | ((uint32_t)port << 16) | (proto << 8) 630#define UNPACK_IDX(idx, addr, proto, port) \ 631 (addr) = (uint32_t)((idx) >> 32); \ 632 (port) = (uint16_t)(((idx) >> 16) & 0xFFFF); \ 633 (proto) = (uint8_t)(((idx) >> 8) & 0xFF) 634 635static struct nat64lsn_portgroup * 636get_next_pg(struct nat64lsn_cfg *cfg, uint32_t *addr, uint8_t *nat_proto, 637 uint16_t *port) 638{ 639 struct nat64lsn_portgroup *pg; 640 uint64_t pre_pack, post_pack; 641 642 pg = NULL; 643 pre_pack = PACK_IDX(*addr, *nat_proto, *port); 644 for (;;) { 645 if (get_next_idx(cfg, addr, nat_proto, port) != 0) { 646 /* End of states */ 647 return (pg); 648 } 649 650 pg = GET_PORTGROUP(cfg, *addr, *nat_proto, *port); 651 if (pg != NULL) 652 break; 653 } 654 655 post_pack = PACK_IDX(*addr, *nat_proto, *port); 656 if (pre_pack == post_pack) 657 DPRINTF(DP_STATE, "XXX: PACK_IDX %u %d %d", 658 *addr, *nat_proto, *port); 659 return (pg); 660} 661 662static NAT64NOINLINE struct nat64lsn_portgroup * 663get_first_pg(struct nat64lsn_cfg *cfg, uint32_t *addr, uint8_t *nat_proto, 664 uint16_t *port) 665{ 666 struct nat64lsn_portgroup *pg; 667 668 pg = GET_PORTGROUP(cfg, *addr, *nat_proto, *port); 669 if (pg == NULL) 670 pg = get_next_pg(cfg, addr, nat_proto, port); 671 672 return (pg); 673} 674 675/* 676 * Lists nat64lsn states. 677 * Data layout (v0)(current): 678 * Request: [ ipfw_obj_header ipfw_obj_data [ uint64_t ]] 679 * Reply: [ ipfw_obj_header ipfw_obj_data [ 680 * ipfw_nat64lsn_stg ipfw_nat64lsn_state x N] ] 681 * 682 * Returns 0 on success 683 */ 684static int 685nat64lsn_states(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 686 struct sockopt_data *sd) 687{ 688 ipfw_obj_header *oh; 689 ipfw_obj_data *od; 690 ipfw_nat64lsn_stg *stg; 691 struct nat64lsn_cfg *cfg; 692 struct nat64lsn_portgroup *pg, *pg_next; 693 uint64_t next_idx; 694 size_t sz; 695 uint32_t addr, states; 696 uint16_t port; 697 uint8_t nat_proto; 698 699 sz = sizeof(ipfw_obj_header) + sizeof(ipfw_obj_data) + 700 sizeof(uint64_t); 701 /* Check minimum header size */ 702 if (sd->valsize < sz) 703 return (EINVAL); 704 705 oh = (ipfw_obj_header *)sd->kbuf; 706 od = (ipfw_obj_data *)(oh + 1); 707 if (od->head.type != IPFW_TLV_OBJDATA || 708 od->head.length != sz - sizeof(ipfw_obj_header)) 709 return (EINVAL); 710 711 next_idx = *(uint64_t *)(od + 1); 712 /* Translate index to the request position to start from */ 713 UNPACK_IDX(next_idx, addr, nat_proto, port); 714 if (nat_proto >= NAT_MAX_PROTO) 715 return (EINVAL); 716 if (nat_proto == 0 && addr != 0) 717 return (EINVAL); 718 719 IPFW_UH_RLOCK(ch); 720 cfg = nat64lsn_find(CHAIN_TO_SRV(ch), oh->ntlv.name, oh->ntlv.set); 721 if (cfg == NULL) { 722 IPFW_UH_RUNLOCK(ch); 723 return (ESRCH); 724 } 725 /* Fill in starting point */ 726 if (addr == 0) { 727 addr = cfg->prefix4; 728 nat_proto = 1; 729 port = 0; 730 } 731 if (addr < cfg->prefix4 || addr > cfg->pmask4) { 732 IPFW_UH_RUNLOCK(ch); 733 DPRINTF(DP_GENERIC | DP_STATE, "XXX: %ju %u %u", 734 (uintmax_t)next_idx, addr, cfg->pmask4); 735 return (EINVAL); 736 } 737 738 sz = sizeof(ipfw_obj_header) + sizeof(ipfw_obj_data) + 739 sizeof(ipfw_nat64lsn_stg); 740 if (sd->valsize < sz) 741 return (ENOMEM); 742 oh = (ipfw_obj_header *)ipfw_get_sopt_space(sd, sz); 743 od = (ipfw_obj_data *)(oh + 1); 744 od->head.type = IPFW_TLV_OBJDATA; 745 od->head.length = sz - sizeof(ipfw_obj_header); 746 stg = (ipfw_nat64lsn_stg *)(od + 1); 747 748 pg = get_first_pg(cfg, &addr, &nat_proto, &port); 749 if (pg == NULL) { 750 /* No states */ 751 stg->next_idx = 0xFF; 752 stg->count = 0; 753 IPFW_UH_RUNLOCK(ch); 754 return (0); 755 } 756 states = 0; 757 pg_next = NULL; 758 while (pg != NULL) { 759 pg_next = get_next_pg(cfg, &addr, &nat_proto, &port); 760 if (pg_next == NULL) 761 stg->next_idx = 0xFF; 762 else 763 stg->next_idx = PACK_IDX(addr, nat_proto, port); 764 765 if (export_pg_states(cfg, pg, stg, sd) != 0) { 766 IPFW_UH_RUNLOCK(ch); 767 return (states == 0 ? ENOMEM: 0); 768 } 769 states += stg->count; 770 od->head.length += stg->count * sizeof(ipfw_nat64lsn_state); 771 sz += stg->count * sizeof(ipfw_nat64lsn_state); 772 if (pg_next != NULL) { 773 sz += sizeof(ipfw_nat64lsn_stg); 774 if (sd->valsize < sz) 775 break; 776 stg = (ipfw_nat64lsn_stg *)ipfw_get_sopt_space(sd, 777 sizeof(ipfw_nat64lsn_stg)); 778 } 779 pg = pg_next; 780 } 781 IPFW_UH_RUNLOCK(ch); 782 return (0); 783} 784 785static struct ipfw_sopt_handler scodes[] = { 786 { IP_FW_NAT64LSN_CREATE, 0, HDIR_BOTH, nat64lsn_create }, 787 { IP_FW_NAT64LSN_DESTROY,0, HDIR_SET, nat64lsn_destroy }, 788 { IP_FW_NAT64LSN_CONFIG, 0, HDIR_BOTH, nat64lsn_config }, 789 { IP_FW_NAT64LSN_LIST, 0, HDIR_GET, nat64lsn_list }, 790 { IP_FW_NAT64LSN_STATS, 0, HDIR_GET, nat64lsn_stats }, 791 { IP_FW_NAT64LSN_RESET_STATS,0, HDIR_SET, nat64lsn_reset_stats }, 792 { IP_FW_NAT64LSN_LIST_STATES,0, HDIR_GET, nat64lsn_states }, 793}; 794 795static int 796nat64lsn_classify(ipfw_insn *cmd, uint16_t *puidx, uint8_t *ptype) 797{ 798 ipfw_insn *icmd; 799 800 icmd = cmd - 1; 801 if (icmd->opcode != O_EXTERNAL_ACTION || 802 icmd->arg1 != V_nat64lsn_eid) 803 return (1); 804 805 *puidx = cmd->arg1; 806 *ptype = 0; 807 return (0); 808} 809 810static void 811nat64lsn_update_arg1(ipfw_insn *cmd, uint16_t idx) 812{ 813 814 cmd->arg1 = idx; 815} 816 817static int 818nat64lsn_findbyname(struct ip_fw_chain *ch, struct tid_info *ti, 819 struct named_object **pno) 820{ 821 int err; 822 823 err = ipfw_objhash_find_type(CHAIN_TO_SRV(ch), ti, 824 IPFW_TLV_NAT64LSN_NAME, pno); 825 return (err); 826} 827 828static struct named_object * 829nat64lsn_findbykidx(struct ip_fw_chain *ch, uint16_t idx) 830{ 831 struct namedobj_instance *ni; 832 struct named_object *no; 833 834 IPFW_UH_WLOCK_ASSERT(ch); 835 ni = CHAIN_TO_SRV(ch); 836 no = ipfw_objhash_lookup_kidx(ni, idx); 837 KASSERT(no != NULL, ("NAT64LSN with index %d not found", idx)); 838 839 return (no); 840} 841 842static int 843nat64lsn_manage_sets(struct ip_fw_chain *ch, uint16_t set, uint8_t new_set, 844 enum ipfw_sets_cmd cmd) 845{ 846 847 return (ipfw_obj_manage_sets(CHAIN_TO_SRV(ch), IPFW_TLV_NAT64LSN_NAME, 848 set, new_set, cmd)); 849} 850 851static struct opcode_obj_rewrite opcodes[] = { 852 { 853 .opcode = O_EXTERNAL_INSTANCE, 854 .etlv = IPFW_TLV_EACTION /* just show it isn't table */, 855 .classifier = nat64lsn_classify, 856 .update = nat64lsn_update_arg1, 857 .find_byname = nat64lsn_findbyname, 858 .find_bykidx = nat64lsn_findbykidx, 859 .manage_sets = nat64lsn_manage_sets, 860 }, 861}; 862 863static int 864destroy_config_cb(struct namedobj_instance *ni, struct named_object *no, 865 void *arg) 866{ 867 struct nat64lsn_cfg *cfg; 868 struct ip_fw_chain *ch; 869 870 ch = (struct ip_fw_chain *)arg; 871 cfg = (struct nat64lsn_cfg *)SRV_OBJECT(ch, no->kidx); 872 SRV_OBJECT(ch, no->kidx) = NULL; 873 nat64lsn_detach_config(ch, cfg); 874 nat64lsn_destroy_instance(cfg); 875 return (0); 876} 877 878int 879nat64lsn_init(struct ip_fw_chain *ch, int first) 880{ 881 882 if (first != 0) 883 nat64lsn_init_internal(); 884 V_nat64lsn_eid = ipfw_add_eaction(ch, ipfw_nat64lsn, "nat64lsn"); 885 if (V_nat64lsn_eid == 0) 886 return (ENXIO); 887 IPFW_ADD_SOPT_HANDLER(first, scodes); 888 IPFW_ADD_OBJ_REWRITER(first, opcodes); 889 return (0); 890} 891 892void 893nat64lsn_uninit(struct ip_fw_chain *ch, int last) 894{ 895 896 IPFW_DEL_OBJ_REWRITER(last, opcodes); 897 IPFW_DEL_SOPT_HANDLER(last, scodes); 898 ipfw_del_eaction(ch, V_nat64lsn_eid); 899 /* 900 * Since we already have deregistered external action, 901 * our named objects become unaccessible via rules, because 902 * all rules were truncated by ipfw_del_eaction(). 903 * So, we can unlink and destroy our named objects without holding 904 * IPFW_WLOCK(). 905 */ 906 IPFW_UH_WLOCK(ch); 907 ipfw_objhash_foreach_type(CHAIN_TO_SRV(ch), destroy_config_cb, ch, 908 IPFW_TLV_NAT64LSN_NAME); 909 V_nat64lsn_eid = 0; 910 IPFW_UH_WUNLOCK(ch); 911 if (last != 0) 912 nat64lsn_uninit_internal(); 913} 914 915