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$"); 32 33 #include <sys/param.h> 34 #include <sys/systm.h> 35 #include <sys/counter.h> 36 #include <sys/ck.h> 37 #include <sys/epoch.h> 38 #include <sys/errno.h> 39 #include <sys/kernel.h> 40 #include <sys/lock.h> 41 #include <sys/malloc.h> 42 #include <sys/mbuf.h> 43 #include <sys/module.h> 44 #include <sys/rmlock.h> 45 #include <sys/rwlock.h> 46 #include <sys/socket.h> 47 #include <sys/sockopt.h> 48 49 #include <net/if.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 61 VNET_DEFINE(uint16_t, nat64lsn_eid) = 0; 62 63 static struct nat64lsn_cfg * 64 nat64lsn_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 74 static void 75 nat64lsn_default_config(ipfw_nat64lsn_cfg *uc) 76 { 77 78 if (uc->jmaxlen == 0) 79 uc->jmaxlen = NAT64LSN_JMAXLEN; 80 if (uc->jmaxlen > 65536) 81 uc->jmaxlen = 65536; 82 if (uc->nh_delete_delay == 0) 83 uc->nh_delete_delay = NAT64LSN_HOST_AGE; 84 if (uc->pg_delete_delay == 0) 85 uc->pg_delete_delay = NAT64LSN_PG_AGE; 86 if (uc->st_syn_ttl == 0) 87 uc->st_syn_ttl = NAT64LSN_TCP_SYN_AGE; 88 if (uc->st_close_ttl == 0) 89 uc->st_close_ttl = NAT64LSN_TCP_FIN_AGE; 90 if (uc->st_estab_ttl == 0) 91 uc->st_estab_ttl = NAT64LSN_TCP_EST_AGE; 92 if (uc->st_udp_ttl == 0) 93 uc->st_udp_ttl = NAT64LSN_UDP_AGE; 94 if (uc->st_icmp_ttl == 0) 95 uc->st_icmp_ttl = NAT64LSN_ICMP_AGE; 96 97 if (uc->states_chunks == 0) 98 uc->states_chunks = 1; 99 else if (uc->states_chunks >= 128) 100 uc->states_chunks = 128; 101 else if (!powerof2(uc->states_chunks)) 102 uc->states_chunks = 1 << fls(uc->states_chunks); 103 } 104 105 /* 106 * Creates new nat64lsn instance. 107 * Data layout (v0)(current): 108 * Request: [ ipfw_obj_lheader ipfw_nat64lsn_cfg ] 109 * 110 * Returns 0 on success 111 */ 112 static int 113 nat64lsn_create(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 114 struct sockopt_data *sd) 115 { 116 ipfw_obj_lheader *olh; 117 ipfw_nat64lsn_cfg *uc; 118 struct nat64lsn_cfg *cfg; 119 struct namedobj_instance *ni; 120 uint32_t addr4, mask4; 121 122 if (sd->valsize != sizeof(*olh) + sizeof(*uc)) 123 return (EINVAL); 124 125 olh = (ipfw_obj_lheader *)sd->kbuf; 126 uc = (ipfw_nat64lsn_cfg *)(olh + 1); 127 128 if (ipfw_check_object_name_generic(uc->name) != 0) 129 return (EINVAL); 130 131 if (uc->set >= IPFW_MAX_SETS) 132 return (EINVAL); 133 134 if (uc->plen4 > 32) 135 return (EINVAL); 136 137 /* 138 * Unspecified address has special meaning. But it must 139 * have valid prefix length. This length will be used to 140 * correctly extract and embedd IPv4 address into IPv6. 141 */ 142 if (nat64_check_prefix6(&uc->prefix6, uc->plen6) != 0 && 143 IN6_IS_ADDR_UNSPECIFIED(&uc->prefix6) && 144 nat64_check_prefixlen(uc->plen6) != 0) 145 return (EINVAL); 146 147 /* XXX: Check prefix4 to be global */ 148 addr4 = ntohl(uc->prefix4.s_addr); 149 mask4 = ~((1 << (32 - uc->plen4)) - 1); 150 if ((addr4 & mask4) != addr4) 151 return (EINVAL); 152 153 nat64lsn_default_config(uc); 154 155 ni = CHAIN_TO_SRV(ch); 156 IPFW_UH_RLOCK(ch); 157 if (nat64lsn_find(ni, uc->name, uc->set) != NULL) { 158 IPFW_UH_RUNLOCK(ch); 159 return (EEXIST); 160 } 161 IPFW_UH_RUNLOCK(ch); 162 163 cfg = nat64lsn_init_instance(ch, addr4, uc->plen4); 164 strlcpy(cfg->name, uc->name, sizeof(cfg->name)); 165 cfg->no.name = cfg->name; 166 cfg->no.etlv = IPFW_TLV_NAT64LSN_NAME; 167 cfg->no.set = uc->set; 168 169 cfg->base.plat_prefix = uc->prefix6; 170 cfg->base.plat_plen = uc->plen6; 171 cfg->base.flags = (uc->flags & NAT64LSN_FLAGSMASK) | NAT64_PLATPFX; 172 if (IN6_IS_ADDR_WKPFX(&cfg->base.plat_prefix)) 173 cfg->base.flags |= NAT64_WKPFX; 174 else if (IN6_IS_ADDR_UNSPECIFIED(&cfg->base.plat_prefix)) 175 cfg->base.flags |= NAT64LSN_ANYPREFIX; 176 177 cfg->states_chunks = uc->states_chunks; 178 cfg->jmaxlen = uc->jmaxlen; 179 cfg->host_delete_delay = uc->nh_delete_delay; 180 cfg->pg_delete_delay = uc->pg_delete_delay; 181 cfg->st_syn_ttl = uc->st_syn_ttl; 182 cfg->st_close_ttl = uc->st_close_ttl; 183 cfg->st_estab_ttl = uc->st_estab_ttl; 184 cfg->st_udp_ttl = uc->st_udp_ttl; 185 cfg->st_icmp_ttl = uc->st_icmp_ttl; 186 187 cfg->nomatch_verdict = IP_FW_DENY; 188 189 IPFW_UH_WLOCK(ch); 190 191 if (nat64lsn_find(ni, uc->name, uc->set) != NULL) { 192 IPFW_UH_WUNLOCK(ch); 193 nat64lsn_destroy_instance(cfg); 194 return (EEXIST); 195 } 196 197 if (ipfw_objhash_alloc_idx(CHAIN_TO_SRV(ch), &cfg->no.kidx) != 0) { 198 IPFW_UH_WUNLOCK(ch); 199 nat64lsn_destroy_instance(cfg); 200 return (ENOSPC); 201 } 202 ipfw_objhash_add(CHAIN_TO_SRV(ch), &cfg->no); 203 204 /* Okay, let's link data */ 205 SRV_OBJECT(ch, cfg->no.kidx) = cfg; 206 nat64lsn_start_instance(cfg); 207 208 IPFW_UH_WUNLOCK(ch); 209 return (0); 210 } 211 212 static void 213 nat64lsn_detach_config(struct ip_fw_chain *ch, struct nat64lsn_cfg *cfg) 214 { 215 216 IPFW_UH_WLOCK_ASSERT(ch); 217 218 ipfw_objhash_del(CHAIN_TO_SRV(ch), &cfg->no); 219 ipfw_objhash_free_idx(CHAIN_TO_SRV(ch), cfg->no.kidx); 220 } 221 222 /* 223 * Destroys nat64 instance. 224 * Data layout (v0)(current): 225 * Request: [ ipfw_obj_header ] 226 * 227 * Returns 0 on success 228 */ 229 static int 230 nat64lsn_destroy(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 231 struct sockopt_data *sd) 232 { 233 struct nat64lsn_cfg *cfg; 234 ipfw_obj_header *oh; 235 236 if (sd->valsize != sizeof(*oh)) 237 return (EINVAL); 238 239 oh = (ipfw_obj_header *)op3; 240 241 IPFW_UH_WLOCK(ch); 242 cfg = nat64lsn_find(CHAIN_TO_SRV(ch), oh->ntlv.name, oh->ntlv.set); 243 if (cfg == NULL) { 244 IPFW_UH_WUNLOCK(ch); 245 return (ENOENT); 246 } 247 248 if (cfg->no.refcnt > 0) { 249 IPFW_UH_WUNLOCK(ch); 250 return (EBUSY); 251 } 252 253 ipfw_reset_eaction_instance(ch, V_nat64lsn_eid, cfg->no.kidx); 254 SRV_OBJECT(ch, cfg->no.kidx) = NULL; 255 nat64lsn_detach_config(ch, cfg); 256 IPFW_UH_WUNLOCK(ch); 257 258 nat64lsn_destroy_instance(cfg); 259 return (0); 260 } 261 262 #define __COPY_STAT_FIELD(_cfg, _stats, _field) \ 263 (_stats)->_field = NAT64STAT_FETCH(&(_cfg)->base.stats, _field) 264 static void 265 export_stats(struct ip_fw_chain *ch, struct nat64lsn_cfg *cfg, 266 struct ipfw_nat64lsn_stats *stats) 267 { 268 struct nat64lsn_alias *alias; 269 int i, j; 270 271 __COPY_STAT_FIELD(cfg, stats, opcnt64); 272 __COPY_STAT_FIELD(cfg, stats, opcnt46); 273 __COPY_STAT_FIELD(cfg, stats, ofrags); 274 __COPY_STAT_FIELD(cfg, stats, ifrags); 275 __COPY_STAT_FIELD(cfg, stats, oerrors); 276 __COPY_STAT_FIELD(cfg, stats, noroute4); 277 __COPY_STAT_FIELD(cfg, stats, noroute6); 278 __COPY_STAT_FIELD(cfg, stats, nomatch4); 279 __COPY_STAT_FIELD(cfg, stats, noproto); 280 __COPY_STAT_FIELD(cfg, stats, nomem); 281 __COPY_STAT_FIELD(cfg, stats, dropped); 282 283 __COPY_STAT_FIELD(cfg, stats, jcalls); 284 __COPY_STAT_FIELD(cfg, stats, jrequests); 285 __COPY_STAT_FIELD(cfg, stats, jhostsreq); 286 __COPY_STAT_FIELD(cfg, stats, jportreq); 287 __COPY_STAT_FIELD(cfg, stats, jhostfails); 288 __COPY_STAT_FIELD(cfg, stats, jportfails); 289 __COPY_STAT_FIELD(cfg, stats, jmaxlen); 290 __COPY_STAT_FIELD(cfg, stats, jnomem); 291 __COPY_STAT_FIELD(cfg, stats, jreinjected); 292 __COPY_STAT_FIELD(cfg, stats, screated); 293 __COPY_STAT_FIELD(cfg, stats, sdeleted); 294 __COPY_STAT_FIELD(cfg, stats, spgcreated); 295 __COPY_STAT_FIELD(cfg, stats, spgdeleted); 296 297 stats->hostcount = cfg->hosts_count; 298 for (i = 0; i < (1 << (32 - cfg->plen4)); i++) { 299 alias = &cfg->aliases[i]; 300 for (j = 0; j < 32 && ISSET32(alias->tcp_chunkmask, j); j++) 301 stats->tcpchunks += bitcount32(alias->tcp_pgmask[j]); 302 for (j = 0; j < 32 && ISSET32(alias->udp_chunkmask, j); j++) 303 stats->udpchunks += bitcount32(alias->udp_pgmask[j]); 304 for (j = 0; j < 32 && ISSET32(alias->icmp_chunkmask, j); j++) 305 stats->icmpchunks += bitcount32(alias->icmp_pgmask[j]); 306 } 307 } 308 #undef __COPY_STAT_FIELD 309 310 static void 311 nat64lsn_export_config(struct ip_fw_chain *ch, struct nat64lsn_cfg *cfg, 312 ipfw_nat64lsn_cfg *uc) 313 { 314 315 uc->flags = cfg->base.flags & NAT64LSN_FLAGSMASK; 316 uc->states_chunks = cfg->states_chunks; 317 uc->jmaxlen = cfg->jmaxlen; 318 uc->nh_delete_delay = cfg->host_delete_delay; 319 uc->pg_delete_delay = cfg->pg_delete_delay; 320 uc->st_syn_ttl = cfg->st_syn_ttl; 321 uc->st_close_ttl = cfg->st_close_ttl; 322 uc->st_estab_ttl = cfg->st_estab_ttl; 323 uc->st_udp_ttl = cfg->st_udp_ttl; 324 uc->st_icmp_ttl = cfg->st_icmp_ttl; 325 uc->prefix4.s_addr = htonl(cfg->prefix4); 326 uc->prefix6 = cfg->base.plat_prefix; 327 uc->plen4 = cfg->plen4; 328 uc->plen6 = cfg->base.plat_plen; 329 uc->set = cfg->no.set; 330 strlcpy(uc->name, cfg->no.name, sizeof(uc->name)); 331 } 332 333 struct nat64_dump_arg { 334 struct ip_fw_chain *ch; 335 struct sockopt_data *sd; 336 }; 337 338 static int 339 export_config_cb(struct namedobj_instance *ni, struct named_object *no, 340 void *arg) 341 { 342 struct nat64_dump_arg *da = (struct nat64_dump_arg *)arg; 343 ipfw_nat64lsn_cfg *uc; 344 345 uc = (struct _ipfw_nat64lsn_cfg *)ipfw_get_sopt_space(da->sd, 346 sizeof(*uc)); 347 nat64lsn_export_config(da->ch, (struct nat64lsn_cfg *)no, uc); 348 return (0); 349 } 350 351 /* 352 * Lists all nat64 lsn instances currently available in kernel. 353 * Data layout (v0)(current): 354 * Request: [ ipfw_obj_lheader ] 355 * Reply: [ ipfw_obj_lheader ipfw_nat64lsn_cfg x N ] 356 * 357 * Returns 0 on success 358 */ 359 static int 360 nat64lsn_list(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 361 struct sockopt_data *sd) 362 { 363 ipfw_obj_lheader *olh; 364 struct nat64_dump_arg da; 365 366 /* Check minimum header size */ 367 if (sd->valsize < sizeof(ipfw_obj_lheader)) 368 return (EINVAL); 369 370 olh = (ipfw_obj_lheader *)ipfw_get_sopt_header(sd, sizeof(*olh)); 371 372 IPFW_UH_RLOCK(ch); 373 olh->count = ipfw_objhash_count_type(CHAIN_TO_SRV(ch), 374 IPFW_TLV_NAT64LSN_NAME); 375 olh->objsize = sizeof(ipfw_nat64lsn_cfg); 376 olh->size = sizeof(*olh) + olh->count * olh->objsize; 377 378 if (sd->valsize < olh->size) { 379 IPFW_UH_RUNLOCK(ch); 380 return (ENOMEM); 381 } 382 memset(&da, 0, sizeof(da)); 383 da.ch = ch; 384 da.sd = sd; 385 ipfw_objhash_foreach_type(CHAIN_TO_SRV(ch), export_config_cb, &da, 386 IPFW_TLV_NAT64LSN_NAME); 387 IPFW_UH_RUNLOCK(ch); 388 389 return (0); 390 } 391 392 /* 393 * Change existing nat64lsn instance configuration. 394 * Data layout (v0)(current): 395 * Request: [ ipfw_obj_header ipfw_nat64lsn_cfg ] 396 * Reply: [ ipfw_obj_header ipfw_nat64lsn_cfg ] 397 * 398 * Returns 0 on success 399 */ 400 static int 401 nat64lsn_config(struct ip_fw_chain *ch, ip_fw3_opheader *op, 402 struct sockopt_data *sd) 403 { 404 ipfw_obj_header *oh; 405 ipfw_nat64lsn_cfg *uc; 406 struct nat64lsn_cfg *cfg; 407 struct namedobj_instance *ni; 408 409 if (sd->valsize != sizeof(*oh) + sizeof(*uc)) 410 return (EINVAL); 411 412 oh = (ipfw_obj_header *)ipfw_get_sopt_space(sd, 413 sizeof(*oh) + sizeof(*uc)); 414 uc = (ipfw_nat64lsn_cfg *)(oh + 1); 415 416 if (ipfw_check_object_name_generic(oh->ntlv.name) != 0 || 417 oh->ntlv.set >= IPFW_MAX_SETS) 418 return (EINVAL); 419 420 ni = CHAIN_TO_SRV(ch); 421 if (sd->sopt->sopt_dir == SOPT_GET) { 422 IPFW_UH_RLOCK(ch); 423 cfg = nat64lsn_find(ni, oh->ntlv.name, oh->ntlv.set); 424 if (cfg == NULL) { 425 IPFW_UH_RUNLOCK(ch); 426 return (ENOENT); 427 } 428 nat64lsn_export_config(ch, cfg, uc); 429 IPFW_UH_RUNLOCK(ch); 430 return (0); 431 } 432 433 nat64lsn_default_config(uc); 434 435 IPFW_UH_WLOCK(ch); 436 cfg = nat64lsn_find(ni, oh->ntlv.name, oh->ntlv.set); 437 if (cfg == NULL) { 438 IPFW_UH_WUNLOCK(ch); 439 return (ENOENT); 440 } 441 442 /* 443 * For now allow to change only following values: 444 * jmaxlen, nh_del_age, pg_del_age, tcp_syn_age, tcp_close_age, 445 * tcp_est_age, udp_age, icmp_age, flags, states_chunks. 446 */ 447 448 cfg->states_chunks = uc->states_chunks; 449 cfg->jmaxlen = uc->jmaxlen; 450 cfg->host_delete_delay = uc->nh_delete_delay; 451 cfg->pg_delete_delay = uc->pg_delete_delay; 452 cfg->st_syn_ttl = uc->st_syn_ttl; 453 cfg->st_close_ttl = uc->st_close_ttl; 454 cfg->st_estab_ttl = uc->st_estab_ttl; 455 cfg->st_udp_ttl = uc->st_udp_ttl; 456 cfg->st_icmp_ttl = uc->st_icmp_ttl; 457 cfg->base.flags &= ~NAT64LSN_FLAGSMASK; 458 cfg->base.flags |= uc->flags & NAT64LSN_FLAGSMASK; 459 460 IPFW_UH_WUNLOCK(ch); 461 462 return (0); 463 } 464 465 /* 466 * Get nat64lsn statistics. 467 * Data layout (v0)(current): 468 * Request: [ ipfw_obj_header ] 469 * Reply: [ ipfw_obj_header ipfw_counter_tlv ] 470 * 471 * Returns 0 on success 472 */ 473 static int 474 nat64lsn_stats(struct ip_fw_chain *ch, ip_fw3_opheader *op, 475 struct sockopt_data *sd) 476 { 477 struct ipfw_nat64lsn_stats stats; 478 struct nat64lsn_cfg *cfg; 479 ipfw_obj_header *oh; 480 ipfw_obj_ctlv *ctlv; 481 size_t sz; 482 483 sz = sizeof(ipfw_obj_header) + sizeof(ipfw_obj_ctlv) + sizeof(stats); 484 if (sd->valsize % sizeof(uint64_t)) 485 return (EINVAL); 486 if (sd->valsize < sz) 487 return (ENOMEM); 488 oh = (ipfw_obj_header *)ipfw_get_sopt_header(sd, sz); 489 if (oh == NULL) 490 return (EINVAL); 491 memset(&stats, 0, sizeof(stats)); 492 493 IPFW_UH_RLOCK(ch); 494 cfg = nat64lsn_find(CHAIN_TO_SRV(ch), oh->ntlv.name, oh->ntlv.set); 495 if (cfg == NULL) { 496 IPFW_UH_RUNLOCK(ch); 497 return (ENOENT); 498 } 499 500 export_stats(ch, cfg, &stats); 501 IPFW_UH_RUNLOCK(ch); 502 503 ctlv = (ipfw_obj_ctlv *)(oh + 1); 504 memset(ctlv, 0, sizeof(*ctlv)); 505 ctlv->head.type = IPFW_TLV_COUNTERS; 506 ctlv->head.length = sz - sizeof(ipfw_obj_header); 507 ctlv->count = sizeof(stats) / sizeof(uint64_t); 508 ctlv->objsize = sizeof(uint64_t); 509 ctlv->version = IPFW_NAT64_VERSION; 510 memcpy(ctlv + 1, &stats, sizeof(stats)); 511 return (0); 512 } 513 514 /* 515 * Reset nat64lsn statistics. 516 * Data layout (v0)(current): 517 * Request: [ ipfw_obj_header ] 518 * 519 * Returns 0 on success 520 */ 521 static int 522 nat64lsn_reset_stats(struct ip_fw_chain *ch, ip_fw3_opheader *op, 523 struct sockopt_data *sd) 524 { 525 struct nat64lsn_cfg *cfg; 526 ipfw_obj_header *oh; 527 528 if (sd->valsize != sizeof(*oh)) 529 return (EINVAL); 530 oh = (ipfw_obj_header *)sd->kbuf; 531 if (ipfw_check_object_name_generic(oh->ntlv.name) != 0 || 532 oh->ntlv.set >= IPFW_MAX_SETS) 533 return (EINVAL); 534 535 IPFW_UH_WLOCK(ch); 536 cfg = nat64lsn_find(CHAIN_TO_SRV(ch), oh->ntlv.name, oh->ntlv.set); 537 if (cfg == NULL) { 538 IPFW_UH_WUNLOCK(ch); 539 return (ENOENT); 540 } 541 COUNTER_ARRAY_ZERO(cfg->base.stats.cnt, NAT64STATS); 542 IPFW_UH_WUNLOCK(ch); 543 return (0); 544 } 545 546 #ifdef __LP64__ 547 #define FREEMASK_COPY(pg, n, out) (out) = *FREEMASK_CHUNK((pg), (n)) 548 #else 549 #define FREEMASK_COPY(pg, n, out) (out) = *FREEMASK_CHUNK((pg), (n)) | \ 550 ((uint64_t)*(FREEMASK_CHUNK((pg), (n)) + 1) << 32) 551 #endif 552 /* 553 * Reply: [ ipfw_obj_header ipfw_obj_data [ ipfw_nat64lsn_stg 554 * ipfw_nat64lsn_state x count, ... ] ] 555 */ 556 static int 557 nat64lsn_export_states_v1(struct nat64lsn_cfg *cfg, union nat64lsn_pgidx *idx, 558 struct nat64lsn_pg *pg, struct sockopt_data *sd, uint32_t *ret_count) 559 { 560 ipfw_nat64lsn_state_v1 *s; 561 struct nat64lsn_state *state; 562 uint64_t freemask; 563 uint32_t i, count; 564 565 /* validate user input */ 566 if (idx->chunk > pg->chunks_count - 1) 567 return (EINVAL); 568 569 FREEMASK_COPY(pg, idx->chunk, freemask); 570 count = 64 - bitcount64(freemask); 571 if (count == 0) 572 return (0); /* Try next PG/chunk */ 573 574 DPRINTF(DP_STATE, "EXPORT PG 0x%16jx, count %d", 575 (uintmax_t)idx->index, count); 576 577 s = (ipfw_nat64lsn_state_v1 *)ipfw_get_sopt_space(sd, 578 count * sizeof(ipfw_nat64lsn_state_v1)); 579 if (s == NULL) 580 return (ENOMEM); 581 582 for (i = 0; i < 64; i++) { 583 if (ISSET64(freemask, i)) 584 continue; 585 state = pg->chunks_count == 1 ? &pg->states->state[i] : 586 &pg->states_chunk[idx->chunk]->state[i]; 587 588 s->host6 = state->host->addr; 589 s->daddr.s_addr = htonl(state->ip_dst); 590 s->dport = state->dport; 591 s->sport = state->sport; 592 s->aport = state->aport; 593 s->flags = (uint8_t)(state->flags & 7); 594 s->proto = state->proto; 595 s->idle = GET_AGE(state->timestamp); 596 s++; 597 } 598 *ret_count = count; 599 return (0); 600 } 601 602 #define LAST_IDX 0xFF 603 static int 604 nat64lsn_next_pgidx(struct nat64lsn_cfg *cfg, struct nat64lsn_pg *pg, 605 union nat64lsn_pgidx *idx) 606 { 607 608 /* First iterate over chunks */ 609 if (pg != NULL) { 610 if (idx->chunk < pg->chunks_count - 1) { 611 idx->chunk++; 612 return (0); 613 } 614 } 615 idx->chunk = 0; 616 /* Then over PGs */ 617 if (idx->port < UINT16_MAX - 64) { 618 idx->port += 64; 619 return (0); 620 } 621 idx->port = NAT64_MIN_PORT; 622 /* Then over supported protocols */ 623 switch (idx->proto) { 624 case IPPROTO_ICMP: 625 idx->proto = IPPROTO_TCP; 626 return (0); 627 case IPPROTO_TCP: 628 idx->proto = IPPROTO_UDP; 629 return (0); 630 default: 631 idx->proto = IPPROTO_ICMP; 632 } 633 /* And then over IPv4 alias addresses */ 634 if (idx->addr < cfg->pmask4) { 635 idx->addr++; 636 return (1); /* New states group is needed */ 637 } 638 idx->index = LAST_IDX; 639 return (-1); /* No more states */ 640 } 641 642 static struct nat64lsn_pg* 643 nat64lsn_get_pg_byidx(struct nat64lsn_cfg *cfg, union nat64lsn_pgidx *idx) 644 { 645 struct nat64lsn_alias *alias; 646 int pg_idx; 647 648 alias = &cfg->aliases[idx->addr & ((1 << (32 - cfg->plen4)) - 1)]; 649 MPASS(alias->addr == idx->addr); 650 651 pg_idx = (idx->port - NAT64_MIN_PORT) / 64; 652 switch (idx->proto) { 653 case IPPROTO_ICMP: 654 if (ISSET32(alias->icmp_pgmask[pg_idx / 32], pg_idx % 32)) 655 return (alias->icmp[pg_idx / 32]->pgptr[pg_idx % 32]); 656 break; 657 case IPPROTO_TCP: 658 if (ISSET32(alias->tcp_pgmask[pg_idx / 32], pg_idx % 32)) 659 return (alias->tcp[pg_idx / 32]->pgptr[pg_idx % 32]); 660 break; 661 case IPPROTO_UDP: 662 if (ISSET32(alias->udp_pgmask[pg_idx / 32], pg_idx % 32)) 663 return (alias->udp[pg_idx / 32]->pgptr[pg_idx % 32]); 664 break; 665 } 666 return (NULL); 667 } 668 669 /* 670 * Lists nat64lsn states. 671 * Data layout (v0): 672 * Request: [ ipfw_obj_header ipfw_obj_data [ uint64_t ]] 673 * Reply: [ ipfw_obj_header ipfw_obj_data [ 674 * ipfw_nat64lsn_stg ipfw_nat64lsn_state x N] ] 675 * 676 * Returns 0 on success 677 */ 678 static int 679 nat64lsn_states_v0(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 680 struct sockopt_data *sd) 681 { 682 683 /* TODO: implement states listing for old ipfw(8) binaries */ 684 return (EOPNOTSUPP); 685 } 686 687 /* 688 * Lists nat64lsn states. 689 * Data layout (v1)(current): 690 * Request: [ ipfw_obj_header ipfw_obj_data [ uint64_t ]] 691 * Reply: [ ipfw_obj_header ipfw_obj_data [ 692 * ipfw_nat64lsn_stg_v1 ipfw_nat64lsn_state_v1 x N] ] 693 * 694 * Returns 0 on success 695 */ 696 static int 697 nat64lsn_states_v1(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 698 struct sockopt_data *sd) 699 { 700 ipfw_obj_header *oh; 701 ipfw_obj_data *od; 702 ipfw_nat64lsn_stg_v1 *stg; 703 struct nat64lsn_cfg *cfg; 704 struct nat64lsn_pg *pg; 705 union nat64lsn_pgidx idx; 706 size_t sz; 707 uint32_t count, total; 708 int ret; 709 710 sz = sizeof(ipfw_obj_header) + sizeof(ipfw_obj_data) + 711 sizeof(uint64_t); 712 /* Check minimum header size */ 713 if (sd->valsize < sz) 714 return (EINVAL); 715 716 oh = (ipfw_obj_header *)sd->kbuf; 717 od = (ipfw_obj_data *)(oh + 1); 718 if (od->head.type != IPFW_TLV_OBJDATA || 719 od->head.length != sz - sizeof(ipfw_obj_header)) 720 return (EINVAL); 721 722 idx.index = *(uint64_t *)(od + 1); 723 if (idx.index != 0 && idx.proto != IPPROTO_ICMP && 724 idx.proto != IPPROTO_TCP && idx.proto != IPPROTO_UDP) 725 return (EINVAL); 726 if (idx.index == LAST_IDX) 727 return (EINVAL); 728 729 IPFW_UH_RLOCK(ch); 730 cfg = nat64lsn_find(CHAIN_TO_SRV(ch), oh->ntlv.name, oh->ntlv.set); 731 if (cfg == NULL) { 732 IPFW_UH_RUNLOCK(ch); 733 return (ENOENT); 734 } 735 if (idx.index == 0) { /* Fill in starting point */ 736 idx.addr = cfg->prefix4; 737 idx.proto = IPPROTO_ICMP; 738 idx.port = NAT64_MIN_PORT; 739 } 740 if (idx.addr < cfg->prefix4 || idx.addr > cfg->pmask4 || 741 idx.port < NAT64_MIN_PORT) { 742 IPFW_UH_RUNLOCK(ch); 743 return (EINVAL); 744 } 745 sz = sizeof(ipfw_obj_header) + sizeof(ipfw_obj_data) + 746 sizeof(ipfw_nat64lsn_stg_v1); 747 if (sd->valsize < sz) { 748 IPFW_UH_RUNLOCK(ch); 749 return (ENOMEM); 750 } 751 oh = (ipfw_obj_header *)ipfw_get_sopt_space(sd, sz); 752 od = (ipfw_obj_data *)(oh + 1); 753 od->head.type = IPFW_TLV_OBJDATA; 754 od->head.length = sz - sizeof(ipfw_obj_header); 755 stg = (ipfw_nat64lsn_stg_v1 *)(od + 1); 756 stg->count = total = 0; 757 stg->next.index = idx.index; 758 /* 759 * Acquire CALLOUT_LOCK to avoid races with expiration code. 760 * Thus states, hosts and PGs will not expire while we hold it. 761 */ 762 CALLOUT_LOCK(cfg); 763 ret = 0; 764 do { 765 pg = nat64lsn_get_pg_byidx(cfg, &idx); 766 if (pg != NULL) { 767 count = 0; 768 ret = nat64lsn_export_states_v1(cfg, &idx, pg, 769 sd, &count); 770 if (ret != 0) 771 break; 772 if (count > 0) { 773 stg->count += count; 774 total += count; 775 /* Update total size of reply */ 776 od->head.length += 777 count * sizeof(ipfw_nat64lsn_state_v1); 778 sz += count * sizeof(ipfw_nat64lsn_state_v1); 779 } 780 stg->alias4.s_addr = htonl(idx.addr); 781 } 782 /* Determine new index */ 783 switch (nat64lsn_next_pgidx(cfg, pg, &idx)) { 784 case -1: 785 ret = ENOENT; /* End of search */ 786 break; 787 case 1: /* 788 * Next alias address, new group may be needed. 789 * If states count is zero, use this group. 790 */ 791 if (stg->count == 0) 792 continue; 793 /* Otherwise try to create new group */ 794 sz += sizeof(ipfw_nat64lsn_stg_v1); 795 if (sd->valsize < sz) { 796 ret = ENOMEM; 797 break; 798 } 799 /* Save next index in current group */ 800 stg->next.index = idx.index; 801 stg = (ipfw_nat64lsn_stg_v1 *)ipfw_get_sopt_space(sd, 802 sizeof(ipfw_nat64lsn_stg_v1)); 803 od->head.length += sizeof(ipfw_nat64lsn_stg_v1); 804 stg->count = 0; 805 break; 806 } 807 stg->next.index = idx.index; 808 } while (ret == 0); 809 CALLOUT_UNLOCK(cfg); 810 IPFW_UH_RUNLOCK(ch); 811 return ((total > 0 || idx.index == LAST_IDX) ? 0: ret); 812 } 813 814 static struct ipfw_sopt_handler scodes[] = { 815 { IP_FW_NAT64LSN_CREATE, 0, HDIR_BOTH, nat64lsn_create }, 816 { IP_FW_NAT64LSN_DESTROY,0, HDIR_SET, nat64lsn_destroy }, 817 { IP_FW_NAT64LSN_CONFIG, 0, HDIR_BOTH, nat64lsn_config }, 818 { IP_FW_NAT64LSN_LIST, 0, HDIR_GET, nat64lsn_list }, 819 { IP_FW_NAT64LSN_STATS, 0, HDIR_GET, nat64lsn_stats }, 820 { IP_FW_NAT64LSN_RESET_STATS,0, HDIR_SET, nat64lsn_reset_stats }, 821 { IP_FW_NAT64LSN_LIST_STATES,0, HDIR_GET, nat64lsn_states_v0 }, 822 { IP_FW_NAT64LSN_LIST_STATES,1, HDIR_GET, nat64lsn_states_v1 }, 823 }; 824 825 static int 826 nat64lsn_classify(ipfw_insn *cmd, uint16_t *puidx, uint8_t *ptype) 827 { 828 ipfw_insn *icmd; 829 830 icmd = cmd - 1; 831 if (icmd->opcode != O_EXTERNAL_ACTION || 832 icmd->arg1 != V_nat64lsn_eid) 833 return (1); 834 835 *puidx = cmd->arg1; 836 *ptype = 0; 837 return (0); 838 } 839 840 static void 841 nat64lsn_update_arg1(ipfw_insn *cmd, uint16_t idx) 842 { 843 844 cmd->arg1 = idx; 845 } 846 847 static int 848 nat64lsn_findbyname(struct ip_fw_chain *ch, struct tid_info *ti, 849 struct named_object **pno) 850 { 851 int err; 852 853 err = ipfw_objhash_find_type(CHAIN_TO_SRV(ch), ti, 854 IPFW_TLV_NAT64LSN_NAME, pno); 855 return (err); 856 } 857 858 static struct named_object * 859 nat64lsn_findbykidx(struct ip_fw_chain *ch, uint16_t idx) 860 { 861 struct namedobj_instance *ni; 862 struct named_object *no; 863 864 IPFW_UH_WLOCK_ASSERT(ch); 865 ni = CHAIN_TO_SRV(ch); 866 no = ipfw_objhash_lookup_kidx(ni, idx); 867 KASSERT(no != NULL, ("NAT64LSN with index %d not found", idx)); 868 869 return (no); 870 } 871 872 static int 873 nat64lsn_manage_sets(struct ip_fw_chain *ch, uint16_t set, uint8_t new_set, 874 enum ipfw_sets_cmd cmd) 875 { 876 877 return (ipfw_obj_manage_sets(CHAIN_TO_SRV(ch), IPFW_TLV_NAT64LSN_NAME, 878 set, new_set, cmd)); 879 } 880 881 static struct opcode_obj_rewrite opcodes[] = { 882 { 883 .opcode = O_EXTERNAL_INSTANCE, 884 .etlv = IPFW_TLV_EACTION /* just show it isn't table */, 885 .classifier = nat64lsn_classify, 886 .update = nat64lsn_update_arg1, 887 .find_byname = nat64lsn_findbyname, 888 .find_bykidx = nat64lsn_findbykidx, 889 .manage_sets = nat64lsn_manage_sets, 890 }, 891 }; 892 893 static int 894 destroy_config_cb(struct namedobj_instance *ni, struct named_object *no, 895 void *arg) 896 { 897 struct nat64lsn_cfg *cfg; 898 struct ip_fw_chain *ch; 899 900 ch = (struct ip_fw_chain *)arg; 901 cfg = (struct nat64lsn_cfg *)SRV_OBJECT(ch, no->kidx); 902 SRV_OBJECT(ch, no->kidx) = NULL; 903 nat64lsn_detach_config(ch, cfg); 904 nat64lsn_destroy_instance(cfg); 905 return (0); 906 } 907 908 int 909 nat64lsn_init(struct ip_fw_chain *ch, int first) 910 { 911 912 if (first != 0) 913 nat64lsn_init_internal(); 914 V_nat64lsn_eid = ipfw_add_eaction(ch, ipfw_nat64lsn, "nat64lsn"); 915 if (V_nat64lsn_eid == 0) 916 return (ENXIO); 917 IPFW_ADD_SOPT_HANDLER(first, scodes); 918 IPFW_ADD_OBJ_REWRITER(first, opcodes); 919 return (0); 920 } 921 922 void 923 nat64lsn_uninit(struct ip_fw_chain *ch, int last) 924 { 925 926 IPFW_DEL_OBJ_REWRITER(last, opcodes); 927 IPFW_DEL_SOPT_HANDLER(last, scodes); 928 ipfw_del_eaction(ch, V_nat64lsn_eid); 929 /* 930 * Since we already have deregistered external action, 931 * our named objects become unaccessible via rules, because 932 * all rules were truncated by ipfw_del_eaction(). 933 * So, we can unlink and destroy our named objects without holding 934 * IPFW_WLOCK(). 935 */ 936 IPFW_UH_WLOCK(ch); 937 ipfw_objhash_foreach_type(CHAIN_TO_SRV(ch), destroy_config_cb, ch, 938 IPFW_TLV_NAT64LSN_NAME); 939 V_nat64lsn_eid = 0; 940 IPFW_UH_WUNLOCK(ch); 941 if (last != 0) 942 nat64lsn_uninit_internal(); 943 } 944