1/*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2015-2019 Yandex LLC 5 * Copyright (c) 2015-2019 Andrey V. Elsukov <ae@FreeBSD.org> 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29#include <sys/cdefs.h> 30__FBSDID("$FreeBSD: stable/11/sbin/ipfw/nat64stl.c 364160 2020-08-12 12:06:01Z ae $"); 31 32#include <sys/types.h> 33#include <sys/socket.h> 34 35#include "ipfw2.h" 36 37#include <ctype.h> 38#include <err.h> 39#include <errno.h> 40#include <inttypes.h> 41#include <netdb.h> 42#include <stdio.h> 43#include <stdlib.h> 44#include <string.h> 45#include <sysexits.h> 46 47#include <net/if.h> 48#include <netinet/in.h> 49#include <netinet/ip_fw.h> 50#include <netinet6/ip_fw_nat64.h> 51#include <arpa/inet.h> 52 53typedef int (nat64stl_cb_t)(ipfw_nat64stl_cfg *i, const char *name, 54 uint8_t set); 55static int nat64stl_foreach(nat64stl_cb_t *f, const char *name, uint8_t set, 56 int sort); 57 58static void nat64stl_create(const char *name, uint8_t set, int ac, char **av); 59static void nat64stl_config(const char *name, uint8_t set, int ac, char **av); 60static void nat64stl_destroy(const char *name, uint8_t set); 61static void nat64stl_stats(const char *name, uint8_t set); 62static void nat64stl_reset_stats(const char *name, uint8_t set); 63static int nat64stl_show_cb(ipfw_nat64stl_cfg *cfg, const char *name, 64 uint8_t set); 65static int nat64stl_destroy_cb(ipfw_nat64stl_cfg *cfg, const char *name, 66 uint8_t set); 67 68static struct _s_x nat64cmds[] = { 69 { "create", TOK_CREATE }, 70 { "config", TOK_CONFIG }, 71 { "destroy", TOK_DESTROY }, 72 { "list", TOK_LIST }, 73 { "show", TOK_LIST }, 74 { "stats", TOK_STATS }, 75 { NULL, 0 } 76}; 77 78#define IPV6_ADDR_INT32_WKPFX htonl(0x64ff9b) 79#define IN6_IS_ADDR_WKPFX(a) \ 80 ((a)->__u6_addr.__u6_addr32[0] == IPV6_ADDR_INT32_WKPFX && \ 81 (a)->__u6_addr.__u6_addr32[1] == 0 && \ 82 (a)->__u6_addr.__u6_addr32[2] == 0) 83int 84ipfw_check_nat64prefix(const struct in6_addr *prefix, int length) 85{ 86 87 switch (length) { 88 case 32: 89 case 40: 90 case 48: 91 case 56: 92 case 64: 93 /* Well-known prefix has 96 prefix length */ 94 if (IN6_IS_ADDR_WKPFX(prefix)) 95 return (EINVAL); 96 /* FALLTHROUGH */ 97 case 96: 98 /* Bits 64 to 71 must be set to zero */ 99 if (prefix->__u6_addr.__u6_addr8[8] != 0) 100 return (EINVAL); 101 /* XXX: looks incorrect */ 102 if (IN6_IS_ADDR_MULTICAST(prefix) || 103 IN6_IS_ADDR_UNSPECIFIED(prefix) || 104 IN6_IS_ADDR_LOOPBACK(prefix)) 105 return (EINVAL); 106 return (0); 107 } 108 return (EINVAL); 109} 110 111static struct _s_x nat64statscmds[] = { 112 { "reset", TOK_RESET }, 113 { NULL, 0 } 114}; 115 116/* 117 * This one handles all nat64stl-related commands 118 * ipfw [set N] nat64stl NAME {create | config} ... 119 * ipfw [set N] nat64stl NAME stats [reset] 120 * ipfw [set N] nat64stl {NAME | all} destroy 121 * ipfw [set N] nat64stl {NAME | all} {list | show} 122 */ 123#define nat64stl_check_name table_check_name 124void 125ipfw_nat64stl_handler(int ac, char *av[]) 126{ 127 const char *name; 128 int tcmd; 129 uint8_t set; 130 131 if (co.use_set != 0) 132 set = co.use_set - 1; 133 else 134 set = 0; 135 ac--; av++; 136 137 NEED1("nat64stl needs instance name"); 138 name = *av; 139 if (nat64stl_check_name(name) != 0) { 140 if (strcmp(name, "all") == 0) 141 name = NULL; 142 else 143 errx(EX_USAGE, "nat64stl instance name %s is invalid", 144 name); 145 } 146 ac--; av++; 147 NEED1("nat64stl needs command"); 148 149 tcmd = get_token(nat64cmds, *av, "nat64stl command"); 150 if (name == NULL && tcmd != TOK_DESTROY && tcmd != TOK_LIST) 151 errx(EX_USAGE, "nat64stl instance name required"); 152 switch (tcmd) { 153 case TOK_CREATE: 154 ac--; av++; 155 nat64stl_create(name, set, ac, av); 156 break; 157 case TOK_CONFIG: 158 ac--; av++; 159 nat64stl_config(name, set, ac, av); 160 break; 161 case TOK_LIST: 162 nat64stl_foreach(nat64stl_show_cb, name, set, 1); 163 break; 164 case TOK_DESTROY: 165 if (name == NULL) 166 nat64stl_foreach(nat64stl_destroy_cb, NULL, set, 0); 167 else 168 nat64stl_destroy(name, set); 169 break; 170 case TOK_STATS: 171 ac--; av++; 172 if (ac == 0) { 173 nat64stl_stats(name, set); 174 break; 175 } 176 tcmd = get_token(nat64statscmds, *av, "stats command"); 177 if (tcmd == TOK_RESET) 178 nat64stl_reset_stats(name, set); 179 } 180} 181 182 183static void 184nat64stl_fill_ntlv(ipfw_obj_ntlv *ntlv, const char *name, uint8_t set) 185{ 186 187 ntlv->head.type = IPFW_TLV_EACTION_NAME(1); /* it doesn't matter */ 188 ntlv->head.length = sizeof(ipfw_obj_ntlv); 189 ntlv->idx = 1; 190 ntlv->set = set; 191 strlcpy(ntlv->name, name, sizeof(ntlv->name)); 192} 193 194static struct _s_x nat64newcmds[] = { 195 { "table4", TOK_TABLE4 }, 196 { "table6", TOK_TABLE6 }, 197 { "prefix6", TOK_PREFIX6 }, 198 { "log", TOK_LOG }, 199 { "-log", TOK_LOGOFF }, 200 { "allow_private", TOK_PRIVATE }, 201 { "-allow_private", TOK_PRIVATEOFF }, 202 { NULL, 0 } 203}; 204 205/* 206 * Creates new nat64stl instance 207 * ipfw nat64stl <NAME> create table4 <name> table6 <name> [ prefix6 <prefix>] 208 * Request: [ ipfw_obj_lheader ipfw_nat64stl_cfg ] 209 */ 210#define NAT64STL_HAS_TABLE4 0x01 211#define NAT64STL_HAS_TABLE6 0x02 212#define NAT64STL_HAS_PREFIX6 0x04 213static void 214nat64stl_create(const char *name, uint8_t set, int ac, char *av[]) 215{ 216 char buf[sizeof(ipfw_obj_lheader) + sizeof(ipfw_nat64stl_cfg)]; 217 ipfw_nat64stl_cfg *cfg; 218 ipfw_obj_lheader *olh; 219 int tcmd, flags; 220 char *p; 221 222 memset(buf, 0, sizeof(buf)); 223 olh = (ipfw_obj_lheader *)buf; 224 cfg = (ipfw_nat64stl_cfg *)(olh + 1); 225 226 /* Some reasonable defaults */ 227 inet_pton(AF_INET6, "64:ff9b::", &cfg->prefix6); 228 cfg->plen6 = 96; 229 cfg->set = set; 230 flags = NAT64STL_HAS_PREFIX6; 231 while (ac > 0) { 232 tcmd = get_token(nat64newcmds, *av, "option"); 233 ac--; av++; 234 235 switch (tcmd) { 236 case TOK_TABLE4: 237 NEED1("table name required"); 238 table_fill_ntlv(&cfg->ntlv4, *av, set, 4); 239 flags |= NAT64STL_HAS_TABLE4; 240 ac--; av++; 241 break; 242 case TOK_TABLE6: 243 NEED1("table name required"); 244 table_fill_ntlv(&cfg->ntlv6, *av, set, 6); 245 flags |= NAT64STL_HAS_TABLE6; 246 ac--; av++; 247 break; 248 case TOK_PREFIX6: 249 NEED1("IPv6 prefix6 required"); 250 if ((p = strchr(*av, '/')) != NULL) 251 *p++ = '\0'; 252 else 253 errx(EX_USAGE, 254 "Prefix length required: %s", *av); 255 if (inet_pton(AF_INET6, *av, &cfg->prefix6) != 1) 256 errx(EX_USAGE, 257 "Bad prefix: %s", *av); 258 cfg->plen6 = strtol(p, NULL, 10); 259 if (ipfw_check_nat64prefix(&cfg->prefix6, 260 cfg->plen6) != 0) 261 errx(EX_USAGE, 262 "Bad prefix length: %s", p); 263 flags |= NAT64STL_HAS_PREFIX6; 264 ac--; av++; 265 break; 266 case TOK_LOG: 267 cfg->flags |= NAT64_LOG; 268 break; 269 case TOK_LOGOFF: 270 cfg->flags &= ~NAT64_LOG; 271 break; 272 case TOK_PRIVATE: 273 cfg->flags |= NAT64_ALLOW_PRIVATE; 274 break; 275 case TOK_PRIVATEOFF: 276 cfg->flags &= ~NAT64_ALLOW_PRIVATE; 277 break; 278 } 279 } 280 281 /* Check validness */ 282 if ((flags & NAT64STL_HAS_TABLE4) != NAT64STL_HAS_TABLE4) 283 errx(EX_USAGE, "table4 required"); 284 if ((flags & NAT64STL_HAS_TABLE6) != NAT64STL_HAS_TABLE6) 285 errx(EX_USAGE, "table6 required"); 286 if ((flags & NAT64STL_HAS_PREFIX6) != NAT64STL_HAS_PREFIX6) 287 errx(EX_USAGE, "prefix6 required"); 288 289 olh->count = 1; 290 olh->objsize = sizeof(*cfg); 291 olh->size = sizeof(buf); 292 strlcpy(cfg->name, name, sizeof(cfg->name)); 293 if (do_set3(IP_FW_NAT64STL_CREATE, &olh->opheader, sizeof(buf)) != 0) 294 err(EX_OSERR, "nat64stl instance creation failed"); 295} 296 297/* 298 * Configures existing nat64stl instance 299 * ipfw nat64stl <NAME> config <options> 300 * Request: [ ipfw_obj_header ipfw_nat64stl_cfg ] 301 */ 302static void 303nat64stl_config(const char *name, uint8_t set, int ac, char **av) 304{ 305 char buf[sizeof(ipfw_obj_header) + sizeof(ipfw_nat64stl_cfg)]; 306 ipfw_nat64stl_cfg *cfg; 307 ipfw_obj_header *oh; 308 char *opt; 309 size_t sz; 310 int tcmd; 311 312 if (ac == 0) 313 errx(EX_USAGE, "config options required"); 314 memset(&buf, 0, sizeof(buf)); 315 oh = (ipfw_obj_header *)buf; 316 cfg = (ipfw_nat64stl_cfg *)(oh + 1); 317 sz = sizeof(buf); 318 319 nat64stl_fill_ntlv(&oh->ntlv, name, set); 320 if (do_get3(IP_FW_NAT64STL_CONFIG, &oh->opheader, &sz) != 0) 321 err(EX_OSERR, "failed to get config for instance %s", name); 322 323 while (ac > 0) { 324 tcmd = get_token(nat64newcmds, *av, "option"); 325 opt = *av; 326 ac--; av++; 327 328 switch (tcmd) { 329#if 0 330 case TOK_TABLE4: 331 NEED1("table name required"); 332 table_fill_ntlv(&cfg->ntlv4, *av, set, 4); 333 ac--; av++; 334 break; 335 case TOK_TABLE6: 336 NEED1("table name required"); 337 table_fill_ntlv(&cfg->ntlv6, *av, set, 6); 338 ac--; av++; 339 break; 340#endif 341 case TOK_LOG: 342 cfg->flags |= NAT64_LOG; 343 break; 344 case TOK_LOGOFF: 345 cfg->flags &= ~NAT64_LOG; 346 break; 347 case TOK_PRIVATE: 348 cfg->flags |= NAT64_ALLOW_PRIVATE; 349 break; 350 case TOK_PRIVATEOFF: 351 cfg->flags &= ~NAT64_ALLOW_PRIVATE; 352 break; 353 default: 354 errx(EX_USAGE, "Can't change %s option", opt); 355 } 356 } 357 358 if (do_set3(IP_FW_NAT64STL_CONFIG, &oh->opheader, sizeof(buf)) != 0) 359 err(EX_OSERR, "nat64stl instance configuration failed"); 360} 361 362/* 363 * Destroys nat64stl instance. 364 * Request: [ ipfw_obj_header ] 365 */ 366static void 367nat64stl_destroy(const char *name, uint8_t set) 368{ 369 ipfw_obj_header oh; 370 371 memset(&oh, 0, sizeof(oh)); 372 nat64stl_fill_ntlv(&oh.ntlv, name, set); 373 if (do_set3(IP_FW_NAT64STL_DESTROY, &oh.opheader, sizeof(oh)) != 0) 374 err(EX_OSERR, "failed to destroy nat instance %s", name); 375} 376 377/* 378 * Get nat64stl instance statistics. 379 * Request: [ ipfw_obj_header ] 380 * Reply: [ ipfw_obj_header ipfw_obj_ctlv [ uint64_t x N ] ] 381 */ 382static int 383nat64stl_get_stats(const char *name, uint8_t set, 384 struct ipfw_nat64stl_stats *stats) 385{ 386 ipfw_obj_header *oh; 387 ipfw_obj_ctlv *oc; 388 size_t sz; 389 390 sz = sizeof(*oh) + sizeof(*oc) + sizeof(*stats); 391 oh = calloc(1, sz); 392 nat64stl_fill_ntlv(&oh->ntlv, name, set); 393 if (do_get3(IP_FW_NAT64STL_STATS, &oh->opheader, &sz) == 0) { 394 oc = (ipfw_obj_ctlv *)(oh + 1); 395 memcpy(stats, oc + 1, sizeof(*stats)); 396 free(oh); 397 return (0); 398 } 399 free(oh); 400 return (-1); 401} 402 403static void 404nat64stl_stats(const char *name, uint8_t set) 405{ 406 struct ipfw_nat64stl_stats stats; 407 408 if (nat64stl_get_stats(name, set, &stats) != 0) 409 err(EX_OSERR, "Error retrieving stats"); 410 411 if (co.use_set != 0 || set != 0) 412 printf("set %u ", set); 413 printf("nat64stl %s\n", name); 414 415 printf("\t%ju packets translated from IPv6 to IPv4\n", 416 (uintmax_t)stats.opcnt64); 417 printf("\t%ju packets translated from IPv4 to IPv6\n", 418 (uintmax_t)stats.opcnt46); 419 printf("\t%ju IPv6 fragments created\n", 420 (uintmax_t)stats.ofrags); 421 printf("\t%ju IPv4 fragments received\n", 422 (uintmax_t)stats.ifrags); 423 printf("\t%ju output packets dropped due to no bufs, etc.\n", 424 (uintmax_t)stats.oerrors); 425 printf("\t%ju output packets discarded due to no IPv4 route\n", 426 (uintmax_t)stats.noroute4); 427 printf("\t%ju output packets discarded due to no IPv6 route\n", 428 (uintmax_t)stats.noroute6); 429 printf("\t%ju packets discarded due to unsupported protocol\n", 430 (uintmax_t)stats.noproto); 431 printf("\t%ju packets discarded due to memory allocation problems\n", 432 (uintmax_t)stats.nomem); 433 printf("\t%ju packets discarded due to some errors\n", 434 (uintmax_t)stats.dropped); 435} 436 437/* 438 * Reset nat64stl instance statistics specified by @oh->ntlv. 439 * Request: [ ipfw_obj_header ] 440 */ 441static void 442nat64stl_reset_stats(const char *name, uint8_t set) 443{ 444 ipfw_obj_header oh; 445 446 memset(&oh, 0, sizeof(oh)); 447 nat64stl_fill_ntlv(&oh.ntlv, name, set); 448 if (do_set3(IP_FW_NAT64STL_RESET_STATS, &oh.opheader, sizeof(oh)) != 0) 449 err(EX_OSERR, "failed to reset stats for instance %s", name); 450} 451 452static int 453nat64stl_show_cb(ipfw_nat64stl_cfg *cfg, const char *name, uint8_t set) 454{ 455 char abuf[INET6_ADDRSTRLEN]; 456 457 if (name != NULL && strcmp(cfg->name, name) != 0) 458 return (ESRCH); 459 460 if (co.use_set != 0 && cfg->set != set) 461 return (ESRCH); 462 463 if (co.use_set != 0 || cfg->set != 0) 464 printf("set %u ", cfg->set); 465 466 printf("nat64stl %s table4 %s table6 %s", 467 cfg->name, cfg->ntlv4.name, cfg->ntlv6.name); 468 inet_ntop(AF_INET6, &cfg->prefix6, abuf, sizeof(abuf)); 469 printf(" prefix6 %s/%u", abuf, cfg->plen6); 470 if (cfg->flags & NAT64_LOG) 471 printf(" log"); 472 if (cfg->flags & NAT64_ALLOW_PRIVATE) 473 printf(" allow_private"); 474 printf("\n"); 475 return (0); 476} 477 478static int 479nat64stl_destroy_cb(ipfw_nat64stl_cfg *cfg, const char *name, uint8_t set) 480{ 481 482 if (co.use_set != 0 && cfg->set != set) 483 return (ESRCH); 484 485 nat64stl_destroy(cfg->name, cfg->set); 486 return (0); 487} 488 489 490/* 491 * Compare nat64stl instances names. 492 * Honor number comparison. 493 */ 494static int 495nat64name_cmp(const void *a, const void *b) 496{ 497 ipfw_nat64stl_cfg *ca, *cb; 498 499 ca = (ipfw_nat64stl_cfg *)a; 500 cb = (ipfw_nat64stl_cfg *)b; 501 502 if (ca->set > cb->set) 503 return (1); 504 else if (ca->set < cb->set) 505 return (-1); 506 return (stringnum_cmp(ca->name, cb->name)); 507} 508 509/* 510 * Retrieves nat64stl instance list from kernel, 511 * optionally sorts it and calls requested function for each instance. 512 * 513 * Request: [ ipfw_obj_lheader ] 514 * Reply: [ ipfw_obj_lheader ipfw_nat64stl_cfg x N ] 515 */ 516static int 517nat64stl_foreach(nat64stl_cb_t *f, const char *name, uint8_t set, int sort) 518{ 519 ipfw_obj_lheader *olh; 520 ipfw_nat64stl_cfg *cfg; 521 size_t sz; 522 int i, error; 523 524 /* Start with reasonable default */ 525 sz = sizeof(*olh) + 16 * sizeof(*cfg); 526 for (;;) { 527 if ((olh = calloc(1, sz)) == NULL) 528 return (ENOMEM); 529 530 olh->size = sz; 531 if (do_get3(IP_FW_NAT64STL_LIST, &olh->opheader, &sz) != 0) { 532 sz = olh->size; 533 free(olh); 534 if (errno != ENOMEM) 535 return (errno); 536 continue; 537 } 538 539 if (sort != 0) 540 qsort(olh + 1, olh->count, olh->objsize, 541 nat64name_cmp); 542 543 cfg = (ipfw_nat64stl_cfg *)(olh + 1); 544 for (i = 0; i < olh->count; i++) { 545 error = f(cfg, name, set); /* Ignore errors for now */ 546 cfg = (ipfw_nat64stl_cfg *)((caddr_t)cfg + 547 olh->objsize); 548 } 549 free(olh); 550 break; 551 } 552 return (0); 553} 554 555