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