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