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