1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 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$"); 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 53 typedef int (nat64stl_cb_t)(ipfw_nat64stl_cfg *i, const char *name, 54 uint8_t set); 55 static int nat64stl_foreach(nat64stl_cb_t *f, const char *name, uint8_t set, 56 int sort); 57 58 static void nat64stl_create(const char *name, uint8_t set, int ac, char **av); 59 static void nat64stl_config(const char *name, uint8_t set, int ac, char **av); 60 static void nat64stl_destroy(const char *name, uint8_t set); 61 static void nat64stl_stats(const char *name, uint8_t set); 62 static void nat64stl_reset_stats(const char *name, uint8_t set); 63 static int nat64stl_show_cb(ipfw_nat64stl_cfg *cfg, const char *name, 64 uint8_t set); 65 static int nat64stl_destroy_cb(ipfw_nat64stl_cfg *cfg, const char *name, 66 uint8_t set); 67 68 static 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) 83 int 84 ipfw_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 111 static 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 124 void 125 ipfw_nat64stl_handler(int ac, char *av[]) 126 { 127 const char *name; 128 int tcmd; 129 uint8_t set; 130 131 if (g_co.use_set != 0) 132 set = g_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 183 static void 184 nat64stl_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 194 static 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 213 static void 214 nat64stl_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 */ 302 static void 303 nat64stl_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 */ 366 static void 367 nat64stl_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 */ 382 static int 383 nat64stl_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 403 static void 404 nat64stl_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 (g_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 */ 441 static void 442 nat64stl_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 452 static int 453 nat64stl_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 (g_co.use_set != 0 && cfg->set != set) 461 return (ESRCH); 462 463 if (g_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 478 static int 479 nat64stl_destroy_cb(ipfw_nat64stl_cfg *cfg, const char *name __unused, 480 uint8_t set) 481 { 482 483 if (g_co.use_set != 0 && cfg->set != set) 484 return (ESRCH); 485 486 nat64stl_destroy(cfg->name, cfg->set); 487 return (0); 488 } 489 490 491 /* 492 * Compare nat64stl instances names. 493 * Honor number comparison. 494 */ 495 static int 496 nat64name_cmp(const void *a, const void *b) 497 { 498 const ipfw_nat64stl_cfg *ca, *cb; 499 500 ca = (const ipfw_nat64stl_cfg *)a; 501 cb = (const ipfw_nat64stl_cfg *)b; 502 503 if (ca->set > cb->set) 504 return (1); 505 else if (ca->set < cb->set) 506 return (-1); 507 return (stringnum_cmp(ca->name, cb->name)); 508 } 509 510 /* 511 * Retrieves nat64stl instance list from kernel, 512 * optionally sorts it and calls requested function for each instance. 513 * 514 * Request: [ ipfw_obj_lheader ] 515 * Reply: [ ipfw_obj_lheader ipfw_nat64stl_cfg x N ] 516 */ 517 static int 518 nat64stl_foreach(nat64stl_cb_t *f, const char *name, uint8_t set, int sort) 519 { 520 ipfw_obj_lheader *olh; 521 ipfw_nat64stl_cfg *cfg; 522 size_t sz; 523 uint32_t i; 524 int error; 525 526 /* Start with reasonable default */ 527 sz = sizeof(*olh) + 16 * sizeof(*cfg); 528 for (;;) { 529 if ((olh = calloc(1, sz)) == NULL) 530 return (ENOMEM); 531 532 olh->size = sz; 533 if (do_get3(IP_FW_NAT64STL_LIST, &olh->opheader, &sz) != 0) { 534 sz = olh->size; 535 free(olh); 536 if (errno != ENOMEM) 537 return (errno); 538 continue; 539 } 540 541 if (sort != 0) 542 qsort(olh + 1, olh->count, olh->objsize, 543 nat64name_cmp); 544 545 cfg = (ipfw_nat64stl_cfg *)(olh + 1); 546 for (i = 0; i < olh->count; i++) { 547 error = f(cfg, name, set); /* Ignore errors for now */ 548 cfg = (ipfw_nat64stl_cfg *)((caddr_t)cfg + 549 olh->objsize); 550 } 551 free(olh); 552 break; 553 } 554 return (0); 555 } 556 557