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 else 307 errx(EX_USAGE, 308 "Prefix length required: %s", *av); 309 if (inet_pton(AF_INET6, *av, &prefix) != 1) 310 errx(EX_USAGE, 311 "Bad prefix: %s", *av); 312 plen = strtol(p, NULL, 10); 313 if (ipfw_check_nat64prefix(&prefix, plen) != 0) 314 errx(EX_USAGE, 315 "Bad prefix length: %s", p); 316 if (tcmd == TOK_PLAT_PREFIX) { 317 cfg->plat_prefix = prefix; 318 cfg->plat_plen = plen; 319 } else { 320 cfg->clat_prefix = prefix; 321 cfg->clat_plen = plen; 322 } 323 ac--; av++; 324 break; 325 case TOK_LOG: 326 cfg->flags |= NAT64_LOG; 327 break; 328 case TOK_LOGOFF: 329 cfg->flags &= ~NAT64_LOG; 330 break; 331 case TOK_PRIVATE: 332 cfg->flags |= NAT64_ALLOW_PRIVATE; 333 break; 334 case TOK_PRIVATEOFF: 335 cfg->flags &= ~NAT64_ALLOW_PRIVATE; 336 break; 337 default: 338 errx(EX_USAGE, "Can't change %s option", opt); 339 } 340 } 341 342 if (do_set3(IP_FW_NAT64CLAT_CONFIG, &oh->opheader, sizeof(buf)) != 0) 343 err(EX_OSERR, "nat64clat instance configuration failed"); 344 } 345 346 /* 347 * Destroys nat64clat instance. 348 * Request: [ ipfw_obj_header ] 349 */ 350 static void 351 nat64clat_destroy(const char *name, uint8_t set) 352 { 353 ipfw_obj_header oh; 354 355 memset(&oh, 0, sizeof(oh)); 356 nat64clat_fill_ntlv(&oh.ntlv, name, set); 357 if (do_set3(IP_FW_NAT64CLAT_DESTROY, &oh.opheader, sizeof(oh)) != 0) 358 err(EX_OSERR, "failed to destroy nat instance %s", name); 359 } 360 361 /* 362 * Get nat64clat instance statistics. 363 * Request: [ ipfw_obj_header ] 364 * Reply: [ ipfw_obj_header ipfw_obj_ctlv [ uint64_t x N ] ] 365 */ 366 static int 367 nat64clat_get_stats(const char *name, uint8_t set, 368 struct ipfw_nat64clat_stats *stats) 369 { 370 ipfw_obj_header *oh; 371 ipfw_obj_ctlv *oc; 372 size_t sz; 373 374 sz = sizeof(*oh) + sizeof(*oc) + sizeof(*stats); 375 oh = calloc(1, sz); 376 nat64clat_fill_ntlv(&oh->ntlv, name, set); 377 if (do_get3(IP_FW_NAT64CLAT_STATS, &oh->opheader, &sz) == 0) { 378 oc = (ipfw_obj_ctlv *)(oh + 1); 379 memcpy(stats, oc + 1, sizeof(*stats)); 380 free(oh); 381 return (0); 382 } 383 free(oh); 384 return (-1); 385 } 386 387 static void 388 nat64clat_stats(const char *name, uint8_t set) 389 { 390 struct ipfw_nat64clat_stats stats; 391 392 if (nat64clat_get_stats(name, set, &stats) != 0) 393 err(EX_OSERR, "Error retrieving stats"); 394 395 if (g_co.use_set != 0 || set != 0) 396 printf("set %u ", set); 397 printf("nat64clat %s\n", name); 398 399 printf("\t%ju packets translated from IPv6 to IPv4\n", 400 (uintmax_t)stats.opcnt64); 401 printf("\t%ju packets translated from IPv4 to IPv6\n", 402 (uintmax_t)stats.opcnt46); 403 printf("\t%ju IPv6 fragments created\n", 404 (uintmax_t)stats.ofrags); 405 printf("\t%ju IPv4 fragments received\n", 406 (uintmax_t)stats.ifrags); 407 printf("\t%ju output packets dropped due to no bufs, etc.\n", 408 (uintmax_t)stats.oerrors); 409 printf("\t%ju output packets discarded due to no IPv4 route\n", 410 (uintmax_t)stats.noroute4); 411 printf("\t%ju output packets discarded due to no IPv6 route\n", 412 (uintmax_t)stats.noroute6); 413 printf("\t%ju packets discarded due to unsupported protocol\n", 414 (uintmax_t)stats.noproto); 415 printf("\t%ju packets discarded due to memory allocation problems\n", 416 (uintmax_t)stats.nomem); 417 printf("\t%ju packets discarded due to some errors\n", 418 (uintmax_t)stats.dropped); 419 } 420 421 /* 422 * Reset nat64clat instance statistics specified by @oh->ntlv. 423 * Request: [ ipfw_obj_header ] 424 */ 425 static void 426 nat64clat_reset_stats(const char *name, uint8_t set) 427 { 428 ipfw_obj_header oh; 429 430 memset(&oh, 0, sizeof(oh)); 431 nat64clat_fill_ntlv(&oh.ntlv, name, set); 432 if (do_set3(IP_FW_NAT64CLAT_RESET_STATS, &oh.opheader, sizeof(oh)) != 0) 433 err(EX_OSERR, "failed to reset stats for instance %s", name); 434 } 435 436 static int 437 nat64clat_show_cb(ipfw_nat64clat_cfg *cfg, const char *name, uint8_t set) 438 { 439 char plat_buf[INET6_ADDRSTRLEN], clat_buf[INET6_ADDRSTRLEN]; 440 441 if (name != NULL && strcmp(cfg->name, name) != 0) 442 return (ESRCH); 443 444 if (g_co.use_set != 0 && cfg->set != set) 445 return (ESRCH); 446 447 if (g_co.use_set != 0 || cfg->set != 0) 448 printf("set %u ", cfg->set); 449 450 inet_ntop(AF_INET6, &cfg->clat_prefix, clat_buf, sizeof(clat_buf)); 451 inet_ntop(AF_INET6, &cfg->plat_prefix, plat_buf, sizeof(plat_buf)); 452 printf("nat64clat %s clat_prefix %s/%u plat_prefix %s/%u", 453 cfg->name, clat_buf, cfg->clat_plen, plat_buf, cfg->plat_plen); 454 if (cfg->flags & NAT64_LOG) 455 printf(" log"); 456 if (cfg->flags & NAT64_ALLOW_PRIVATE) 457 printf(" allow_private"); 458 printf("\n"); 459 return (0); 460 } 461 462 static int 463 nat64clat_destroy_cb(ipfw_nat64clat_cfg *cfg, const char *name __unused, 464 uint8_t set) 465 { 466 467 if (g_co.use_set != 0 && cfg->set != set) 468 return (ESRCH); 469 470 nat64clat_destroy(cfg->name, cfg->set); 471 return (0); 472 } 473 474 475 /* 476 * Compare nat64clat instances names. 477 * Honor number comparison. 478 */ 479 static int 480 nat64name_cmp(const void *a, const void *b) 481 { 482 const ipfw_nat64clat_cfg *ca, *cb; 483 484 ca = (const ipfw_nat64clat_cfg *)a; 485 cb = (const ipfw_nat64clat_cfg *)b; 486 487 if (ca->set > cb->set) 488 return (1); 489 else if (ca->set < cb->set) 490 return (-1); 491 return (stringnum_cmp(ca->name, cb->name)); 492 } 493 494 /* 495 * Retrieves nat64clat instance list from kernel, 496 * optionally sorts it and calls requested function for each instance. 497 * 498 * Request: [ ipfw_obj_lheader ] 499 * Reply: [ ipfw_obj_lheader ipfw_nat64clat_cfg x N ] 500 */ 501 static int 502 nat64clat_foreach(nat64clat_cb_t *f, const char *name, uint8_t set, int sort) 503 { 504 ipfw_obj_lheader *olh; 505 ipfw_nat64clat_cfg *cfg; 506 size_t sz; 507 uint32_t i; 508 int error; 509 510 /* Start with reasonable default */ 511 sz = sizeof(*olh) + 16 * sizeof(*cfg); 512 for (;;) { 513 if ((olh = calloc(1, sz)) == NULL) 514 return (ENOMEM); 515 516 olh->size = sz; 517 if (do_get3(IP_FW_NAT64CLAT_LIST, &olh->opheader, &sz) != 0) { 518 sz = olh->size; 519 free(olh); 520 if (errno != ENOMEM) 521 return (errno); 522 continue; 523 } 524 525 if (sort != 0) 526 qsort(olh + 1, olh->count, olh->objsize, 527 nat64name_cmp); 528 529 cfg = (ipfw_nat64clat_cfg *)(olh + 1); 530 for (i = 0; i < olh->count; i++) { 531 error = f(cfg, name, set); /* Ignore errors for now */ 532 cfg = (ipfw_nat64clat_cfg *)((caddr_t)cfg + 533 olh->objsize); 534 } 535 free(olh); 536 break; 537 } 538 return (0); 539 } 540 541