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 { NULL, 0 } 200 }; 201 202 /* 203 * Creates new nat64stl instance 204 * ipfw nat64stl <NAME> create table4 <name> table6 <name> [ prefix6 <prefix>] 205 * Request: [ ipfw_obj_lheader ipfw_nat64stl_cfg ] 206 */ 207 #define NAT64STL_HAS_TABLE4 0x01 208 #define NAT64STL_HAS_TABLE6 0x02 209 #define NAT64STL_HAS_PREFIX6 0x04 210 static void 211 nat64stl_create(const char *name, uint8_t set, int ac, char *av[]) 212 { 213 char buf[sizeof(ipfw_obj_lheader) + sizeof(ipfw_nat64stl_cfg)]; 214 ipfw_nat64stl_cfg *cfg; 215 ipfw_obj_lheader *olh; 216 int tcmd, flags; 217 char *p; 218 219 memset(buf, 0, sizeof(buf)); 220 olh = (ipfw_obj_lheader *)buf; 221 cfg = (ipfw_nat64stl_cfg *)(olh + 1); 222 223 /* Some reasonable defaults */ 224 inet_pton(AF_INET6, "64:ff9b::", &cfg->prefix6); 225 cfg->plen6 = 96; 226 cfg->set = set; 227 flags = NAT64STL_HAS_PREFIX6; 228 while (ac > 0) { 229 tcmd = get_token(nat64newcmds, *av, "option"); 230 ac--; av++; 231 232 switch (tcmd) { 233 case TOK_TABLE4: 234 NEED1("table name required"); 235 table_fill_ntlv(&cfg->ntlv4, *av, set, 4); 236 flags |= NAT64STL_HAS_TABLE4; 237 ac--; av++; 238 break; 239 case TOK_TABLE6: 240 NEED1("table name required"); 241 table_fill_ntlv(&cfg->ntlv6, *av, set, 6); 242 flags |= NAT64STL_HAS_TABLE6; 243 ac--; av++; 244 break; 245 case TOK_PREFIX6: 246 NEED1("IPv6 prefix6 required"); 247 if ((p = strchr(*av, '/')) != NULL) 248 *p++ = '\0'; 249 if (inet_pton(AF_INET6, *av, &cfg->prefix6) != 1) 250 errx(EX_USAGE, 251 "Bad prefix: %s", *av); 252 cfg->plen6 = strtol(p, NULL, 10); 253 if (ipfw_check_nat64prefix(&cfg->prefix6, 254 cfg->plen6) != 0) 255 errx(EX_USAGE, 256 "Bad prefix length: %s", p); 257 flags |= NAT64STL_HAS_PREFIX6; 258 ac--; av++; 259 break; 260 case TOK_LOG: 261 cfg->flags |= NAT64_LOG; 262 break; 263 case TOK_LOGOFF: 264 cfg->flags &= ~NAT64_LOG; 265 break; 266 } 267 } 268 269 /* Check validness */ 270 if ((flags & NAT64STL_HAS_TABLE4) != NAT64STL_HAS_TABLE4) 271 errx(EX_USAGE, "table4 required"); 272 if ((flags & NAT64STL_HAS_TABLE6) != NAT64STL_HAS_TABLE6) 273 errx(EX_USAGE, "table6 required"); 274 if ((flags & NAT64STL_HAS_PREFIX6) != NAT64STL_HAS_PREFIX6) 275 errx(EX_USAGE, "prefix6 required"); 276 277 olh->count = 1; 278 olh->objsize = sizeof(*cfg); 279 olh->size = sizeof(buf); 280 strlcpy(cfg->name, name, sizeof(cfg->name)); 281 if (do_set3(IP_FW_NAT64STL_CREATE, &olh->opheader, sizeof(buf)) != 0) 282 err(EX_OSERR, "nat64stl instance creation failed"); 283 } 284 285 /* 286 * Configures existing nat64stl instance 287 * ipfw nat64stl <NAME> config <options> 288 * Request: [ ipfw_obj_header ipfw_nat64stl_cfg ] 289 */ 290 static void 291 nat64stl_config(const char *name, uint8_t set, int ac, char **av) 292 { 293 char buf[sizeof(ipfw_obj_header) + sizeof(ipfw_nat64stl_cfg)]; 294 ipfw_nat64stl_cfg *cfg; 295 ipfw_obj_header *oh; 296 char *opt; 297 size_t sz; 298 int tcmd; 299 300 if (ac == 0) 301 errx(EX_USAGE, "config options required"); 302 memset(&buf, 0, sizeof(buf)); 303 oh = (ipfw_obj_header *)buf; 304 cfg = (ipfw_nat64stl_cfg *)(oh + 1); 305 sz = sizeof(buf); 306 307 nat64stl_fill_ntlv(&oh->ntlv, name, set); 308 if (do_get3(IP_FW_NAT64STL_CONFIG, &oh->opheader, &sz) != 0) 309 err(EX_OSERR, "failed to get config for instance %s", name); 310 311 while (ac > 0) { 312 tcmd = get_token(nat64newcmds, *av, "option"); 313 opt = *av; 314 ac--; av++; 315 316 switch (tcmd) { 317 #if 0 318 case TOK_TABLE4: 319 NEED1("table name required"); 320 table_fill_ntlv(&cfg->ntlv4, *av, set, 4); 321 ac--; av++; 322 break; 323 case TOK_TABLE6: 324 NEED1("table name required"); 325 table_fill_ntlv(&cfg->ntlv6, *av, set, 6); 326 ac--; av++; 327 break; 328 #endif 329 case TOK_LOG: 330 cfg->flags |= NAT64_LOG; 331 break; 332 case TOK_LOGOFF: 333 cfg->flags &= ~NAT64_LOG; 334 break; 335 default: 336 errx(EX_USAGE, "Can't change %s option", opt); 337 } 338 } 339 340 if (do_set3(IP_FW_NAT64STL_CONFIG, &oh->opheader, sizeof(buf)) != 0) 341 err(EX_OSERR, "nat64stl instance configuration failed"); 342 } 343 344 /* 345 * Destroys nat64stl instance. 346 * Request: [ ipfw_obj_header ] 347 */ 348 static void 349 nat64stl_destroy(const char *name, uint8_t set) 350 { 351 ipfw_obj_header oh; 352 353 memset(&oh, 0, sizeof(oh)); 354 nat64stl_fill_ntlv(&oh.ntlv, name, set); 355 if (do_set3(IP_FW_NAT64STL_DESTROY, &oh.opheader, sizeof(oh)) != 0) 356 err(EX_OSERR, "failed to destroy nat instance %s", name); 357 } 358 359 /* 360 * Get nat64stl instance statistics. 361 * Request: [ ipfw_obj_header ] 362 * Reply: [ ipfw_obj_header ipfw_obj_ctlv [ uint64_t x N ] ] 363 */ 364 static int 365 nat64stl_get_stats(const char *name, uint8_t set, 366 struct ipfw_nat64stl_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 nat64stl_fill_ntlv(&oh->ntlv, name, set); 375 if (do_get3(IP_FW_NAT64STL_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 nat64stl_stats(const char *name, uint8_t set) 387 { 388 struct ipfw_nat64stl_stats stats; 389 390 if (nat64stl_get_stats(name, set, &stats) != 0) 391 err(EX_OSERR, "Error retrieving stats"); 392 393 if (co.use_set != 0 || set != 0) 394 printf("set %u ", set); 395 printf("nat64stl %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 nat64stl instance statistics specified by @oh->ntlv. 421 * Request: [ ipfw_obj_header ] 422 */ 423 static void 424 nat64stl_reset_stats(const char *name, uint8_t set) 425 { 426 ipfw_obj_header oh; 427 428 memset(&oh, 0, sizeof(oh)); 429 nat64stl_fill_ntlv(&oh.ntlv, name, set); 430 if (do_set3(IP_FW_NAT64STL_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 nat64stl_show_cb(ipfw_nat64stl_cfg *cfg, const char *name, uint8_t set) 436 { 437 char abuf[INET6_ADDRSTRLEN]; 438 439 if (name != NULL && strcmp(cfg->name, name) != 0) 440 return (ESRCH); 441 442 if (co.use_set != 0 && cfg->set != set) 443 return (ESRCH); 444 445 if (co.use_set != 0 || cfg->set != 0) 446 printf("set %u ", cfg->set); 447 448 printf("nat64stl %s table4 %s table6 %s", 449 cfg->name, cfg->ntlv4.name, cfg->ntlv6.name); 450 inet_ntop(AF_INET6, &cfg->prefix6, abuf, sizeof(abuf)); 451 printf(" prefix6 %s/%u", abuf, cfg->plen6); 452 if (cfg->flags & NAT64_LOG) 453 printf(" log"); 454 printf("\n"); 455 return (0); 456 } 457 458 static int 459 nat64stl_destroy_cb(ipfw_nat64stl_cfg *cfg, const char *name, uint8_t set) 460 { 461 462 if (co.use_set != 0 && cfg->set != set) 463 return (ESRCH); 464 465 nat64stl_destroy(cfg->name, cfg->set); 466 return (0); 467 } 468 469 470 /* 471 * Compare nat64stl instances names. 472 * Honor number comparison. 473 */ 474 static int 475 nat64name_cmp(const void *a, const void *b) 476 { 477 ipfw_nat64stl_cfg *ca, *cb; 478 479 ca = (ipfw_nat64stl_cfg *)a; 480 cb = (ipfw_nat64stl_cfg *)b; 481 482 if (ca->set > cb->set) 483 return (1); 484 else if (ca->set < cb->set) 485 return (-1); 486 return (stringnum_cmp(ca->name, cb->name)); 487 } 488 489 /* 490 * Retrieves nat64stl instance list from kernel, 491 * optionally sorts it and calls requested function for each instance. 492 * 493 * Request: [ ipfw_obj_lheader ] 494 * Reply: [ ipfw_obj_lheader ipfw_nat64stl_cfg x N ] 495 */ 496 static int 497 nat64stl_foreach(nat64stl_cb_t *f, const char *name, uint8_t set, int sort) 498 { 499 ipfw_obj_lheader *olh; 500 ipfw_nat64stl_cfg *cfg; 501 size_t sz; 502 int i, error; 503 504 /* Start with reasonable default */ 505 sz = sizeof(*olh) + 16 * sizeof(*cfg); 506 for (;;) { 507 if ((olh = calloc(1, sz)) == NULL) 508 return (ENOMEM); 509 510 olh->size = sz; 511 if (do_get3(IP_FW_NAT64STL_LIST, &olh->opheader, &sz) != 0) { 512 sz = olh->size; 513 free(olh); 514 if (errno != ENOMEM) 515 return (errno); 516 continue; 517 } 518 519 if (sort != 0) 520 qsort(olh + 1, olh->count, olh->objsize, 521 nat64name_cmp); 522 523 cfg = (ipfw_nat64stl_cfg *)(olh + 1); 524 for (i = 0; i < olh->count; i++) { 525 error = f(cfg, name, set); /* Ignore errors for now */ 526 cfg = (ipfw_nat64stl_cfg *)((caddr_t)cfg + 527 olh->objsize); 528 } 529 free(olh); 530 break; 531 } 532 return (0); 533 } 534 535