1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 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 #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 (nat64clat_cb_t)(ipfw_nat64clat_cfg *i, const char *name, 53 uint8_t set); 54 static int nat64clat_foreach(nat64clat_cb_t *f, const char *name, uint8_t set, 55 int sort); 56 57 static void nat64clat_create(const char *name, uint8_t set, int ac, char **av); 58 static void nat64clat_config(const char *name, uint8_t set, int ac, char **av); 59 static void nat64clat_destroy(const char *name, uint8_t set); 60 static void nat64clat_stats(const char *name, uint8_t set); 61 static void nat64clat_reset_stats(const char *name, uint8_t set); 62 static int nat64clat_show_cb(ipfw_nat64clat_cfg *cfg, const char *name, 63 uint8_t set); 64 static int nat64clat_destroy_cb(ipfw_nat64clat_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 static struct _s_x nat64statscmds[] = { 78 { "reset", TOK_RESET }, 79 { NULL, 0 } 80 }; 81 82 /* 83 * This one handles all nat64clat-related commands 84 * ipfw [set N] nat64clat NAME {create | config} ... 85 * ipfw [set N] nat64clat NAME stats [reset] 86 * ipfw [set N] nat64clat {NAME | all} destroy 87 * ipfw [set N] nat64clat {NAME | all} {list | show} 88 */ 89 #define nat64clat_check_name table_check_name 90 void 91 ipfw_nat64clat_handler(int ac, char *av[]) 92 { 93 const char *name; 94 int tcmd; 95 uint8_t set; 96 97 if (g_co.use_set != 0) 98 set = g_co.use_set - 1; 99 else 100 set = 0; 101 ac--; av++; 102 103 NEED1("nat64clat needs instance name"); 104 name = *av; 105 if (nat64clat_check_name(name) != 0) { 106 if (strcmp(name, "all") == 0) 107 name = NULL; 108 else 109 errx(EX_USAGE, "nat64clat instance name %s is invalid", 110 name); 111 } 112 ac--; av++; 113 NEED1("nat64clat needs command"); 114 115 tcmd = get_token(nat64cmds, *av, "nat64clat command"); 116 if (name == NULL && tcmd != TOK_DESTROY && tcmd != TOK_LIST) 117 errx(EX_USAGE, "nat64clat instance name required"); 118 switch (tcmd) { 119 case TOK_CREATE: 120 ac--; av++; 121 nat64clat_create(name, set, ac, av); 122 break; 123 case TOK_CONFIG: 124 ac--; av++; 125 nat64clat_config(name, set, ac, av); 126 break; 127 case TOK_LIST: 128 nat64clat_foreach(nat64clat_show_cb, name, set, 1); 129 break; 130 case TOK_DESTROY: 131 if (name == NULL) 132 nat64clat_foreach(nat64clat_destroy_cb, NULL, set, 0); 133 else 134 nat64clat_destroy(name, set); 135 break; 136 case TOK_STATS: 137 ac--; av++; 138 if (ac == 0) { 139 nat64clat_stats(name, set); 140 break; 141 } 142 tcmd = get_token(nat64statscmds, *av, "stats command"); 143 if (tcmd == TOK_RESET) 144 nat64clat_reset_stats(name, set); 145 } 146 } 147 148 149 static void 150 nat64clat_fill_ntlv(ipfw_obj_ntlv *ntlv, const char *name, uint8_t set) 151 { 152 153 ntlv->head.type = IPFW_TLV_EACTION_NAME(1); /* it doesn't matter */ 154 ntlv->head.length = sizeof(ipfw_obj_ntlv); 155 ntlv->idx = 1; 156 ntlv->set = set; 157 strlcpy(ntlv->name, name, sizeof(ntlv->name)); 158 } 159 160 static struct _s_x nat64newcmds[] = { 161 { "plat_prefix", TOK_PLAT_PREFIX }, 162 { "clat_prefix", TOK_CLAT_PREFIX }, 163 { "log", TOK_LOG }, 164 { "-log", TOK_LOGOFF }, 165 { "allow_private", TOK_PRIVATE }, 166 { "-allow_private", TOK_PRIVATEOFF }, 167 { NULL, 0 } 168 }; 169 170 /* 171 * Creates new nat64clat instance 172 * ipfw nat64clat <NAME> create clat_prefix <prefix> plat_prefix <prefix> 173 * Request: [ ipfw_obj_lheader ipfw_nat64clat_cfg ] 174 */ 175 #define NAT64CLAT_HAS_CLAT_PREFIX 0x01 176 #define NAT64CLAT_HAS_PLAT_PREFIX 0x02 177 static void 178 nat64clat_create(const char *name, uint8_t set, int ac, char *av[]) 179 { 180 char buf[sizeof(ipfw_obj_lheader) + sizeof(ipfw_nat64clat_cfg)]; 181 ipfw_nat64clat_cfg *cfg; 182 ipfw_obj_lheader *olh; 183 int tcmd, flags; 184 char *p; 185 struct in6_addr prefix; 186 uint8_t plen; 187 188 memset(buf, 0, sizeof(buf)); 189 olh = (ipfw_obj_lheader *)buf; 190 cfg = (ipfw_nat64clat_cfg *)(olh + 1); 191 192 /* Some reasonable defaults */ 193 inet_pton(AF_INET6, "64:ff9b::", &cfg->plat_prefix); 194 cfg->plat_plen = 96; 195 cfg->set = set; 196 flags = NAT64CLAT_HAS_PLAT_PREFIX; 197 while (ac > 0) { 198 tcmd = get_token(nat64newcmds, *av, "option"); 199 ac--; av++; 200 201 switch (tcmd) { 202 case TOK_PLAT_PREFIX: 203 case TOK_CLAT_PREFIX: 204 if (tcmd == TOK_PLAT_PREFIX) { 205 NEED1("IPv6 plat_prefix required"); 206 } else { 207 NEED1("IPv6 clat_prefix required"); 208 } 209 210 if ((p = strchr(*av, '/')) != NULL) 211 *p++ = '\0'; 212 if (inet_pton(AF_INET6, *av, &prefix) != 1) 213 errx(EX_USAGE, 214 "Bad prefix: %s", *av); 215 plen = strtol(p, NULL, 10); 216 if (ipfw_check_nat64prefix(&prefix, plen) != 0) 217 errx(EX_USAGE, 218 "Bad prefix length: %s", p); 219 if (tcmd == TOK_PLAT_PREFIX) { 220 flags |= NAT64CLAT_HAS_PLAT_PREFIX; 221 cfg->plat_prefix = prefix; 222 cfg->plat_plen = plen; 223 } else { 224 flags |= NAT64CLAT_HAS_CLAT_PREFIX; 225 cfg->clat_prefix = prefix; 226 cfg->clat_plen = plen; 227 } 228 ac--; av++; 229 break; 230 case TOK_LOG: 231 cfg->flags |= NAT64_LOG; 232 break; 233 case TOK_LOGOFF: 234 cfg->flags &= ~NAT64_LOG; 235 break; 236 case TOK_PRIVATE: 237 cfg->flags |= NAT64_ALLOW_PRIVATE; 238 break; 239 case TOK_PRIVATEOFF: 240 cfg->flags &= ~NAT64_ALLOW_PRIVATE; 241 break; 242 } 243 } 244 245 /* Check validness */ 246 if ((flags & NAT64CLAT_HAS_PLAT_PREFIX) != NAT64CLAT_HAS_PLAT_PREFIX) 247 errx(EX_USAGE, "plat_prefix required"); 248 if ((flags & NAT64CLAT_HAS_CLAT_PREFIX) != NAT64CLAT_HAS_CLAT_PREFIX) 249 errx(EX_USAGE, "clat_prefix required"); 250 251 olh->count = 1; 252 olh->objsize = sizeof(*cfg); 253 olh->size = sizeof(buf); 254 strlcpy(cfg->name, name, sizeof(cfg->name)); 255 if (do_set3(IP_FW_NAT64CLAT_CREATE, &olh->opheader, sizeof(buf)) != 0) 256 err(EX_OSERR, "nat64clat instance creation failed"); 257 } 258 259 /* 260 * Configures existing nat64clat instance 261 * ipfw nat64clat <NAME> config <options> 262 * Request: [ ipfw_obj_header ipfw_nat64clat_cfg ] 263 */ 264 static void 265 nat64clat_config(const char *name, uint8_t set, int ac, char **av) 266 { 267 char buf[sizeof(ipfw_obj_header) + sizeof(ipfw_nat64clat_cfg)]; 268 ipfw_nat64clat_cfg *cfg; 269 ipfw_obj_header *oh; 270 char *opt; 271 char *p; 272 size_t sz; 273 int tcmd; 274 struct in6_addr prefix; 275 uint8_t plen; 276 277 if (ac == 0) 278 errx(EX_USAGE, "config options required"); 279 memset(&buf, 0, sizeof(buf)); 280 oh = (ipfw_obj_header *)buf; 281 cfg = (ipfw_nat64clat_cfg *)(oh + 1); 282 sz = sizeof(buf); 283 284 nat64clat_fill_ntlv(&oh->ntlv, name, set); 285 if (do_get3(IP_FW_NAT64CLAT_CONFIG, &oh->opheader, &sz) != 0) 286 err(EX_OSERR, "failed to get config for instance %s", name); 287 288 while (ac > 0) { 289 tcmd = get_token(nat64newcmds, *av, "option"); 290 opt = *av; 291 ac--; av++; 292 293 switch (tcmd) { 294 case TOK_PLAT_PREFIX: 295 case TOK_CLAT_PREFIX: 296 if (tcmd == TOK_PLAT_PREFIX) { 297 NEED1("IPv6 plat_prefix required"); 298 } else { 299 NEED1("IPv6 clat_prefix required"); 300 } 301 302 if ((p = strchr(*av, '/')) != NULL) 303 *p++ = '\0'; 304 else 305 errx(EX_USAGE, 306 "Prefix length required: %s", *av); 307 if (inet_pton(AF_INET6, *av, &prefix) != 1) 308 errx(EX_USAGE, 309 "Bad prefix: %s", *av); 310 plen = strtol(p, NULL, 10); 311 if (ipfw_check_nat64prefix(&prefix, plen) != 0) 312 errx(EX_USAGE, 313 "Bad prefix length: %s", p); 314 if (tcmd == TOK_PLAT_PREFIX) { 315 cfg->plat_prefix = prefix; 316 cfg->plat_plen = plen; 317 } else { 318 cfg->clat_prefix = prefix; 319 cfg->clat_plen = plen; 320 } 321 ac--; av++; 322 break; 323 case TOK_LOG: 324 cfg->flags |= NAT64_LOG; 325 break; 326 case TOK_LOGOFF: 327 cfg->flags &= ~NAT64_LOG; 328 break; 329 case TOK_PRIVATE: 330 cfg->flags |= NAT64_ALLOW_PRIVATE; 331 break; 332 case TOK_PRIVATEOFF: 333 cfg->flags &= ~NAT64_ALLOW_PRIVATE; 334 break; 335 default: 336 errx(EX_USAGE, "Can't change %s option", opt); 337 } 338 } 339 340 if (do_set3(IP_FW_NAT64CLAT_CONFIG, &oh->opheader, sizeof(buf)) != 0) 341 err(EX_OSERR, "nat64clat instance configuration failed"); 342 } 343 344 /* 345 * Destroys nat64clat instance. 346 * Request: [ ipfw_obj_header ] 347 */ 348 static void 349 nat64clat_destroy(const char *name, uint8_t set) 350 { 351 ipfw_obj_header oh; 352 353 memset(&oh, 0, sizeof(oh)); 354 nat64clat_fill_ntlv(&oh.ntlv, name, set); 355 if (do_set3(IP_FW_NAT64CLAT_DESTROY, &oh.opheader, sizeof(oh)) != 0) 356 err(EX_OSERR, "failed to destroy nat instance %s", name); 357 } 358 359 /* 360 * Get nat64clat instance statistics. 361 * Request: [ ipfw_obj_header ] 362 * Reply: [ ipfw_obj_header ipfw_obj_ctlv [ uint64_t x N ] ] 363 */ 364 static int 365 nat64clat_get_stats(const char *name, uint8_t set, 366 struct ipfw_nat64clat_stats *stats) 367 { 368 ipfw_obj_header *oh; 369 ipfw_obj_ctlv *oc; 370 size_t sz; 371 372 sz = sizeof(*oh) + sizeof(*oc) + sizeof(*stats); 373 oh = calloc(1, sz); 374 nat64clat_fill_ntlv(&oh->ntlv, name, set); 375 if (do_get3(IP_FW_NAT64CLAT_STATS, &oh->opheader, &sz) == 0) { 376 oc = (ipfw_obj_ctlv *)(oh + 1); 377 memcpy(stats, oc + 1, sizeof(*stats)); 378 free(oh); 379 return (0); 380 } 381 free(oh); 382 return (-1); 383 } 384 385 static void 386 nat64clat_stats(const char *name, uint8_t set) 387 { 388 struct ipfw_nat64clat_stats stats; 389 390 if (nat64clat_get_stats(name, set, &stats) != 0) 391 err(EX_OSERR, "Error retrieving stats"); 392 393 if (g_co.use_set != 0 || set != 0) 394 printf("set %u ", set); 395 printf("nat64clat %s\n", name); 396 397 printf("\t%ju packets translated from IPv6 to IPv4\n", 398 (uintmax_t)stats.opcnt64); 399 printf("\t%ju packets translated from IPv4 to IPv6\n", 400 (uintmax_t)stats.opcnt46); 401 printf("\t%ju IPv6 fragments created\n", 402 (uintmax_t)stats.ofrags); 403 printf("\t%ju IPv4 fragments received\n", 404 (uintmax_t)stats.ifrags); 405 printf("\t%ju output packets dropped due to no bufs, etc.\n", 406 (uintmax_t)stats.oerrors); 407 printf("\t%ju output packets discarded due to no IPv4 route\n", 408 (uintmax_t)stats.noroute4); 409 printf("\t%ju output packets discarded due to no IPv6 route\n", 410 (uintmax_t)stats.noroute6); 411 printf("\t%ju packets discarded due to unsupported protocol\n", 412 (uintmax_t)stats.noproto); 413 printf("\t%ju packets discarded due to memory allocation problems\n", 414 (uintmax_t)stats.nomem); 415 printf("\t%ju packets discarded due to some errors\n", 416 (uintmax_t)stats.dropped); 417 } 418 419 /* 420 * Reset nat64clat instance statistics specified by @oh->ntlv. 421 * Request: [ ipfw_obj_header ] 422 */ 423 static void 424 nat64clat_reset_stats(const char *name, uint8_t set) 425 { 426 ipfw_obj_header oh; 427 428 memset(&oh, 0, sizeof(oh)); 429 nat64clat_fill_ntlv(&oh.ntlv, name, set); 430 if (do_set3(IP_FW_NAT64CLAT_RESET_STATS, &oh.opheader, sizeof(oh)) != 0) 431 err(EX_OSERR, "failed to reset stats for instance %s", name); 432 } 433 434 static int 435 nat64clat_show_cb(ipfw_nat64clat_cfg *cfg, const char *name, uint8_t set) 436 { 437 char plat_buf[INET6_ADDRSTRLEN], clat_buf[INET6_ADDRSTRLEN]; 438 439 if (name != NULL && strcmp(cfg->name, name) != 0) 440 return (ESRCH); 441 442 if (g_co.use_set != 0 && cfg->set != set) 443 return (ESRCH); 444 445 if (g_co.use_set != 0 || cfg->set != 0) 446 printf("set %u ", cfg->set); 447 448 inet_ntop(AF_INET6, &cfg->clat_prefix, clat_buf, sizeof(clat_buf)); 449 inet_ntop(AF_INET6, &cfg->plat_prefix, plat_buf, sizeof(plat_buf)); 450 printf("nat64clat %s clat_prefix %s/%u plat_prefix %s/%u", 451 cfg->name, clat_buf, cfg->clat_plen, plat_buf, cfg->plat_plen); 452 if (cfg->flags & NAT64_LOG) 453 printf(" log"); 454 if (cfg->flags & NAT64_ALLOW_PRIVATE) 455 printf(" allow_private"); 456 printf("\n"); 457 return (0); 458 } 459 460 static int 461 nat64clat_destroy_cb(ipfw_nat64clat_cfg *cfg, const char *name __unused, 462 uint8_t set) 463 { 464 465 if (g_co.use_set != 0 && cfg->set != set) 466 return (ESRCH); 467 468 nat64clat_destroy(cfg->name, cfg->set); 469 return (0); 470 } 471 472 473 /* 474 * Compare nat64clat instances names. 475 * Honor number comparison. 476 */ 477 static int 478 nat64name_cmp(const void *a, const void *b) 479 { 480 const ipfw_nat64clat_cfg *ca, *cb; 481 482 ca = (const ipfw_nat64clat_cfg *)a; 483 cb = (const ipfw_nat64clat_cfg *)b; 484 485 if (ca->set > cb->set) 486 return (1); 487 else if (ca->set < cb->set) 488 return (-1); 489 return (stringnum_cmp(ca->name, cb->name)); 490 } 491 492 /* 493 * Retrieves nat64clat instance list from kernel, 494 * optionally sorts it and calls requested function for each instance. 495 * 496 * Request: [ ipfw_obj_lheader ] 497 * Reply: [ ipfw_obj_lheader ipfw_nat64clat_cfg x N ] 498 */ 499 static int 500 nat64clat_foreach(nat64clat_cb_t *f, const char *name, uint8_t set, int sort) 501 { 502 ipfw_obj_lheader *olh; 503 ipfw_nat64clat_cfg *cfg; 504 size_t sz; 505 uint32_t i; 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 (void)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