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/types.h> 31 #include <sys/socket.h> 32 33 #include "ipfw2.h" 34 35 #include <ctype.h> 36 #include <err.h> 37 #include <errno.h> 38 #include <inttypes.h> 39 #include <netdb.h> 40 #include <stdio.h> 41 #include <stdlib.h> 42 #include <string.h> 43 #include <sysexits.h> 44 45 #include <net/if.h> 46 #include <netinet/in.h> 47 #include <netinet/ip_fw.h> 48 #include <netinet6/ip_fw_nat64.h> 49 #include <arpa/inet.h> 50 51 typedef int (nat64clat_cb_t)(ipfw_nat64clat_cfg *i, const char *name, 52 uint8_t set); 53 static int nat64clat_foreach(nat64clat_cb_t *f, const char *name, uint8_t set, 54 int sort); 55 56 static void nat64clat_create(const char *name, uint8_t set, int ac, char **av); 57 static void nat64clat_config(const char *name, uint8_t set, int ac, char **av); 58 static void nat64clat_destroy(const char *name, uint8_t set); 59 static void nat64clat_stats(const char *name, uint8_t set); 60 static void nat64clat_reset_stats(const char *name, uint8_t set); 61 static int nat64clat_show_cb(ipfw_nat64clat_cfg *cfg, const char *name, 62 uint8_t set); 63 static int nat64clat_destroy_cb(ipfw_nat64clat_cfg *cfg, const char *name, 64 uint8_t set); 65 66 static struct _s_x nat64cmds[] = { 67 { "create", TOK_CREATE }, 68 { "config", TOK_CONFIG }, 69 { "destroy", TOK_DESTROY }, 70 { "list", TOK_LIST }, 71 { "show", TOK_LIST }, 72 { "stats", TOK_STATS }, 73 { NULL, 0 } 74 }; 75 76 static struct _s_x nat64statscmds[] = { 77 { "reset", TOK_RESET }, 78 { NULL, 0 } 79 }; 80 81 /* 82 * This one handles all nat64clat-related commands 83 * ipfw [set N] nat64clat NAME {create | config} ... 84 * ipfw [set N] nat64clat NAME stats [reset] 85 * ipfw [set N] nat64clat {NAME | all} destroy 86 * ipfw [set N] nat64clat {NAME | all} {list | show} 87 */ 88 #define nat64clat_check_name table_check_name 89 void 90 ipfw_nat64clat_handler(int ac, char *av[]) 91 { 92 const char *name; 93 int tcmd; 94 uint8_t set; 95 96 if (g_co.use_set != 0) 97 set = g_co.use_set - 1; 98 else 99 set = 0; 100 ac--; av++; 101 102 NEED1("nat64clat needs instance name"); 103 name = *av; 104 if (nat64clat_check_name(name) != 0) { 105 if (strcmp(name, "all") == 0) 106 name = NULL; 107 else 108 errx(EX_USAGE, "nat64clat instance name %s is invalid", 109 name); 110 } 111 ac--; av++; 112 NEED1("nat64clat needs command"); 113 114 tcmd = get_token(nat64cmds, *av, "nat64clat command"); 115 if (name == NULL && tcmd != TOK_DESTROY && tcmd != TOK_LIST) 116 errx(EX_USAGE, "nat64clat instance name required"); 117 switch (tcmd) { 118 case TOK_CREATE: 119 ac--; av++; 120 nat64clat_create(name, set, ac, av); 121 break; 122 case TOK_CONFIG: 123 ac--; av++; 124 nat64clat_config(name, set, ac, av); 125 break; 126 case TOK_LIST: 127 nat64clat_foreach(nat64clat_show_cb, name, set, 1); 128 break; 129 case TOK_DESTROY: 130 if (name == NULL) 131 nat64clat_foreach(nat64clat_destroy_cb, NULL, set, 0); 132 else 133 nat64clat_destroy(name, set); 134 break; 135 case TOK_STATS: 136 ac--; av++; 137 if (ac == 0) { 138 nat64clat_stats(name, set); 139 break; 140 } 141 tcmd = get_token(nat64statscmds, *av, "stats command"); 142 if (tcmd == TOK_RESET) 143 nat64clat_reset_stats(name, set); 144 } 145 } 146 147 148 static void 149 nat64clat_fill_ntlv(ipfw_obj_ntlv *ntlv, const char *name, uint8_t set) 150 { 151 152 ntlv->head.type = IPFW_TLV_EACTION_NAME(1); /* it doesn't matter */ 153 ntlv->head.length = sizeof(ipfw_obj_ntlv); 154 ntlv->idx = 1; 155 ntlv->set = set; 156 strlcpy(ntlv->name, name, sizeof(ntlv->name)); 157 } 158 159 static struct _s_x nat64newcmds[] = { 160 { "plat_prefix", TOK_PLAT_PREFIX }, 161 { "clat_prefix", TOK_CLAT_PREFIX }, 162 { "log", TOK_LOG }, 163 { "-log", TOK_LOGOFF }, 164 { "allow_private", TOK_PRIVATE }, 165 { "-allow_private", TOK_PRIVATEOFF }, 166 { NULL, 0 } 167 }; 168 169 /* 170 * Creates new nat64clat instance 171 * ipfw nat64clat <NAME> create clat_prefix <prefix> plat_prefix <prefix> 172 * Request: [ ipfw_obj_lheader ipfw_nat64clat_cfg ] 173 */ 174 #define NAT64CLAT_HAS_CLAT_PREFIX 0x01 175 #define NAT64CLAT_HAS_PLAT_PREFIX 0x02 176 static void 177 nat64clat_create(const char *name, uint8_t set, int ac, char *av[]) 178 { 179 char buf[sizeof(ipfw_obj_lheader) + sizeof(ipfw_nat64clat_cfg)]; 180 ipfw_nat64clat_cfg *cfg; 181 ipfw_obj_lheader *olh; 182 int tcmd, flags; 183 char *p; 184 struct in6_addr prefix; 185 uint8_t plen; 186 187 memset(buf, 0, sizeof(buf)); 188 olh = (ipfw_obj_lheader *)buf; 189 cfg = (ipfw_nat64clat_cfg *)(olh + 1); 190 191 /* Some reasonable defaults */ 192 inet_pton(AF_INET6, "64:ff9b::", &cfg->plat_prefix); 193 cfg->plat_plen = 96; 194 cfg->set = set; 195 flags = NAT64CLAT_HAS_PLAT_PREFIX; 196 while (ac > 0) { 197 tcmd = get_token(nat64newcmds, *av, "option"); 198 ac--; av++; 199 200 switch (tcmd) { 201 case TOK_PLAT_PREFIX: 202 case TOK_CLAT_PREFIX: 203 if (tcmd == TOK_PLAT_PREFIX) { 204 NEED1("IPv6 plat_prefix required"); 205 } else { 206 NEED1("IPv6 clat_prefix required"); 207 } 208 209 if ((p = strchr(*av, '/')) != NULL) 210 *p++ = '\0'; 211 if (inet_pton(AF_INET6, *av, &prefix) != 1) 212 errx(EX_USAGE, 213 "Bad prefix: %s", *av); 214 plen = strtol(p, NULL, 10); 215 if (ipfw_check_nat64prefix(&prefix, plen) != 0) 216 errx(EX_USAGE, 217 "Bad prefix length: %s", p); 218 if (tcmd == TOK_PLAT_PREFIX) { 219 flags |= NAT64CLAT_HAS_PLAT_PREFIX; 220 cfg->plat_prefix = prefix; 221 cfg->plat_plen = plen; 222 } else { 223 flags |= NAT64CLAT_HAS_CLAT_PREFIX; 224 cfg->clat_prefix = prefix; 225 cfg->clat_plen = plen; 226 } 227 ac--; av++; 228 break; 229 case TOK_LOG: 230 cfg->flags |= NAT64_LOG; 231 break; 232 case TOK_LOGOFF: 233 cfg->flags &= ~NAT64_LOG; 234 break; 235 case TOK_PRIVATE: 236 cfg->flags |= NAT64_ALLOW_PRIVATE; 237 break; 238 case TOK_PRIVATEOFF: 239 cfg->flags &= ~NAT64_ALLOW_PRIVATE; 240 break; 241 } 242 } 243 244 /* Check validness */ 245 if ((flags & NAT64CLAT_HAS_PLAT_PREFIX) != NAT64CLAT_HAS_PLAT_PREFIX) 246 errx(EX_USAGE, "plat_prefix required"); 247 if ((flags & NAT64CLAT_HAS_CLAT_PREFIX) != NAT64CLAT_HAS_CLAT_PREFIX) 248 errx(EX_USAGE, "clat_prefix required"); 249 250 olh->count = 1; 251 olh->objsize = sizeof(*cfg); 252 olh->size = sizeof(buf); 253 strlcpy(cfg->name, name, sizeof(cfg->name)); 254 if (do_set3(IP_FW_NAT64CLAT_CREATE, &olh->opheader, sizeof(buf)) != 0) 255 err(EX_OSERR, "nat64clat instance creation failed"); 256 } 257 258 /* 259 * Configures existing nat64clat instance 260 * ipfw nat64clat <NAME> config <options> 261 * Request: [ ipfw_obj_header ipfw_nat64clat_cfg ] 262 */ 263 static void 264 nat64clat_config(const char *name, uint8_t set, int ac, char **av) 265 { 266 char buf[sizeof(ipfw_obj_header) + sizeof(ipfw_nat64clat_cfg)]; 267 ipfw_nat64clat_cfg *cfg; 268 ipfw_obj_header *oh; 269 char *opt; 270 char *p; 271 size_t sz; 272 int tcmd; 273 struct in6_addr prefix; 274 uint8_t plen; 275 276 if (ac == 0) 277 errx(EX_USAGE, "config options required"); 278 memset(&buf, 0, sizeof(buf)); 279 oh = (ipfw_obj_header *)buf; 280 cfg = (ipfw_nat64clat_cfg *)(oh + 1); 281 sz = sizeof(buf); 282 283 nat64clat_fill_ntlv(&oh->ntlv, name, set); 284 if (do_get3(IP_FW_NAT64CLAT_CONFIG, &oh->opheader, &sz) != 0) 285 err(EX_OSERR, "failed to get config for instance %s", name); 286 287 while (ac > 0) { 288 tcmd = get_token(nat64newcmds, *av, "option"); 289 opt = *av; 290 ac--; av++; 291 292 switch (tcmd) { 293 case TOK_PLAT_PREFIX: 294 case TOK_CLAT_PREFIX: 295 if (tcmd == TOK_PLAT_PREFIX) { 296 NEED1("IPv6 plat_prefix required"); 297 } else { 298 NEED1("IPv6 clat_prefix required"); 299 } 300 301 if ((p = strchr(*av, '/')) != NULL) 302 *p++ = '\0'; 303 else 304 errx(EX_USAGE, 305 "Prefix length required: %s", *av); 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 506 /* Start with reasonable default */ 507 sz = sizeof(*olh) + 16 * sizeof(*cfg); 508 for (;;) { 509 if ((olh = calloc(1, sz)) == NULL) 510 return (ENOMEM); 511 512 olh->size = sz; 513 if (do_get3(IP_FW_NAT64CLAT_LIST, &olh->opheader, &sz) != 0) { 514 sz = olh->size; 515 free(olh); 516 if (errno != ENOMEM) 517 return (errno); 518 continue; 519 } 520 521 if (sort != 0) 522 qsort(olh + 1, olh->count, olh->objsize, 523 nat64name_cmp); 524 525 cfg = (ipfw_nat64clat_cfg *)(olh + 1); 526 for (i = 0; i < olh->count; i++) { 527 (void)f(cfg, name, set); /* Ignore errors for now */ 528 cfg = (ipfw_nat64clat_cfg *)((caddr_t)cfg + 529 olh->objsize); 530 } 531 free(olh); 532 break; 533 } 534 return (0); 535 } 536 537