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