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 static int nat64stl_check_prefix(struct in6_addr *prefix, int length); 53 typedef int (nat64stl_cb_t)(ipfw_nat64stl_cfg *i, const char *name, 54 uint8_t set); 55 static int nat64stl_foreach(nat64stl_cb_t *f, const char *name, uint8_t set, 56 int sort); 57 58 static void nat64stl_create(const char *name, uint8_t set, int ac, char **av); 59 static void nat64stl_config(const char *name, uint8_t set, int ac, char **av); 60 static void nat64stl_destroy(const char *name, uint8_t set); 61 static void nat64stl_stats(const char *name, uint8_t set); 62 static void nat64stl_reset_stats(const char *name, uint8_t set); 63 static int nat64stl_show_cb(ipfw_nat64stl_cfg *cfg, const char *name, 64 uint8_t set); 65 static int nat64stl_destroy_cb(ipfw_nat64stl_cfg *cfg, const char *name, 66 uint8_t set); 67 68 static struct _s_x nat64cmds[] = { 69 { "create", TOK_CREATE }, 70 { "config", TOK_CONFIG }, 71 { "destroy", TOK_DESTROY }, 72 { "list", TOK_LIST }, 73 { "show", TOK_LIST }, 74 { "stats", TOK_STATS }, 75 { NULL, 0 } 76 }; 77 78 #define IPV6_ADDR_INT32_WKPFX htonl(0x64ff9b) 79 #define IN6_IS_ADDR_WKPFX(a) \ 80 ((a)->__u6_addr.__u6_addr32[0] == IPV6_ADDR_INT32_WKPFX && \ 81 (a)->__u6_addr.__u6_addr32[1] == 0 && \ 82 (a)->__u6_addr.__u6_addr32[2] == 0) 83 static int 84 nat64stl_check_prefix(struct in6_addr *prefix, int length) 85 { 86 87 if (IN6_IS_ADDR_WKPFX(prefix) && length == 96) 88 return (0); 89 #if 0 90 switch (length) { 91 case 32: 92 case 40: 93 case 48: 94 case 56: 95 case 64: 96 /* Well-known prefix has 96 prefix length */ 97 if (IN6_IS_ADDR_WKPFX(prefix)) 98 return (1); 99 /* FALLTHROUGH */ 100 case 96: 101 /* Bits 64 to 71 must be set to zero */ 102 if (prefix->__u6_addr.__u6_addr8[8] != 0) 103 return (1); 104 /* XXX: looks incorrect */ 105 if (IN6_IS_ADDR_MULTICAST(prefix) || 106 IN6_IS_ADDR_UNSPECIFIED(prefix) || 107 IN6_IS_ADDR_LOOPBACK(prefix)) 108 return (1); 109 return (0); 110 } 111 #endif 112 return (1); 113 } 114 115 static struct _s_x nat64statscmds[] = { 116 { "reset", TOK_RESET }, 117 { NULL, 0 } 118 }; 119 120 /* 121 * This one handles all nat64stl-related commands 122 * ipfw [set N] nat64stl NAME {create | config} ... 123 * ipfw [set N] nat64stl NAME stats [reset] 124 * ipfw [set N] nat64stl {NAME | all} destroy 125 * ipfw [set N] nat64stl {NAME | all} {list | show} 126 */ 127 #define nat64stl_check_name table_check_name 128 void 129 ipfw_nat64stl_handler(int ac, char *av[]) 130 { 131 const char *name; 132 int tcmd; 133 uint8_t set; 134 135 if (co.use_set != 0) 136 set = co.use_set - 1; 137 else 138 set = 0; 139 ac--; av++; 140 141 NEED1("nat64stl needs instance name"); 142 name = *av; 143 if (nat64stl_check_name(name) != 0) { 144 if (strcmp(name, "all") == 0) 145 name = NULL; 146 else 147 errx(EX_USAGE, "nat64stl instance name %s is invalid", 148 name); 149 } 150 ac--; av++; 151 NEED1("nat64stl needs command"); 152 153 tcmd = get_token(nat64cmds, *av, "nat64stl command"); 154 if (name == NULL && tcmd != TOK_DESTROY && tcmd != TOK_LIST) 155 errx(EX_USAGE, "nat64stl instance name required"); 156 switch (tcmd) { 157 case TOK_CREATE: 158 ac--; av++; 159 nat64stl_create(name, set, ac, av); 160 break; 161 case TOK_CONFIG: 162 ac--; av++; 163 nat64stl_config(name, set, ac, av); 164 break; 165 case TOK_LIST: 166 nat64stl_foreach(nat64stl_show_cb, name, set, 1); 167 break; 168 case TOK_DESTROY: 169 if (name == NULL) 170 nat64stl_foreach(nat64stl_destroy_cb, NULL, set, 0); 171 else 172 nat64stl_destroy(name, set); 173 break; 174 case TOK_STATS: 175 ac--; av++; 176 if (ac == 0) { 177 nat64stl_stats(name, set); 178 break; 179 } 180 tcmd = get_token(nat64statscmds, *av, "stats command"); 181 if (tcmd == TOK_RESET) 182 nat64stl_reset_stats(name, set); 183 } 184 } 185 186 187 static void 188 nat64stl_fill_ntlv(ipfw_obj_ntlv *ntlv, const char *name, uint8_t set) 189 { 190 191 ntlv->head.type = IPFW_TLV_EACTION_NAME(1); /* it doesn't matter */ 192 ntlv->head.length = sizeof(ipfw_obj_ntlv); 193 ntlv->idx = 1; 194 ntlv->set = set; 195 strlcpy(ntlv->name, name, sizeof(ntlv->name)); 196 } 197 198 static struct _s_x nat64newcmds[] = { 199 { "table4", TOK_TABLE4 }, 200 { "table6", TOK_TABLE6 }, 201 { "prefix6", TOK_PREFIX6 }, 202 { "log", TOK_LOG }, 203 { "-log", TOK_LOGOFF }, 204 { NULL, 0 } 205 }; 206 207 /* 208 * Creates new nat64stl instance 209 * ipfw nat64stl <NAME> create table4 <name> table6 <name> [ prefix6 <prefix>] 210 * Request: [ ipfw_obj_lheader ipfw_nat64stl_cfg ] 211 */ 212 #define NAT64STL_HAS_TABLE4 0x01 213 #define NAT64STL_HAS_TABLE6 0x02 214 #define NAT64STL_HAS_PREFIX6 0x04 215 static void 216 nat64stl_create(const char *name, uint8_t set, int ac, char *av[]) 217 { 218 char buf[sizeof(ipfw_obj_lheader) + sizeof(ipfw_nat64stl_cfg)]; 219 ipfw_nat64stl_cfg *cfg; 220 ipfw_obj_lheader *olh; 221 int tcmd, flags; 222 char *p; 223 224 memset(buf, 0, sizeof(buf)); 225 olh = (ipfw_obj_lheader *)buf; 226 cfg = (ipfw_nat64stl_cfg *)(olh + 1); 227 228 /* Some reasonable defaults */ 229 inet_pton(AF_INET6, "64:ff9b::", &cfg->prefix6); 230 cfg->plen6 = 96; 231 cfg->set = set; 232 flags = NAT64STL_HAS_PREFIX6; 233 while (ac > 0) { 234 tcmd = get_token(nat64newcmds, *av, "option"); 235 ac--; av++; 236 237 switch (tcmd) { 238 case TOK_TABLE4: 239 NEED1("table name required"); 240 table_fill_ntlv(&cfg->ntlv4, *av, set, 4); 241 flags |= NAT64STL_HAS_TABLE4; 242 ac--; av++; 243 break; 244 case TOK_TABLE6: 245 NEED1("table name required"); 246 table_fill_ntlv(&cfg->ntlv6, *av, set, 6); 247 flags |= NAT64STL_HAS_TABLE6; 248 ac--; av++; 249 break; 250 case TOK_PREFIX6: 251 NEED1("IPv6 prefix6 required"); 252 if ((p = strchr(*av, '/')) != NULL) 253 *p++ = '\0'; 254 if (inet_pton(AF_INET6, *av, &cfg->prefix6) != 1) 255 errx(EX_USAGE, 256 "Bad prefix: %s", *av); 257 cfg->plen6 = strtol(p, NULL, 10); 258 if (nat64stl_check_prefix(&cfg->prefix6, 259 cfg->plen6) != 0) 260 errx(EX_USAGE, 261 "Bad prefix length: %s", p); 262 flags |= NAT64STL_HAS_PREFIX6; 263 ac--; av++; 264 break; 265 case TOK_LOG: 266 cfg->flags |= NAT64_LOG; 267 break; 268 case TOK_LOGOFF: 269 cfg->flags &= ~NAT64_LOG; 270 break; 271 } 272 } 273 274 /* Check validness */ 275 if ((flags & NAT64STL_HAS_TABLE4) != NAT64STL_HAS_TABLE4) 276 errx(EX_USAGE, "table4 required"); 277 if ((flags & NAT64STL_HAS_TABLE6) != NAT64STL_HAS_TABLE6) 278 errx(EX_USAGE, "table6 required"); 279 if ((flags & NAT64STL_HAS_PREFIX6) != NAT64STL_HAS_PREFIX6) 280 errx(EX_USAGE, "prefix6 required"); 281 282 olh->count = 1; 283 olh->objsize = sizeof(*cfg); 284 olh->size = sizeof(buf); 285 strlcpy(cfg->name, name, sizeof(cfg->name)); 286 if (do_set3(IP_FW_NAT64STL_CREATE, &olh->opheader, sizeof(buf)) != 0) 287 err(EX_OSERR, "nat64stl instance creation failed"); 288 } 289 290 /* 291 * Configures existing nat64stl instance 292 * ipfw nat64stl <NAME> config <options> 293 * Request: [ ipfw_obj_header ipfw_nat64stl_cfg ] 294 */ 295 static void 296 nat64stl_config(const char *name, uint8_t set, int ac, char **av) 297 { 298 char buf[sizeof(ipfw_obj_header) + sizeof(ipfw_nat64stl_cfg)]; 299 ipfw_nat64stl_cfg *cfg; 300 ipfw_obj_header *oh; 301 char *opt; 302 size_t sz; 303 int tcmd; 304 305 if (ac == 0) 306 errx(EX_USAGE, "config options required"); 307 memset(&buf, 0, sizeof(buf)); 308 oh = (ipfw_obj_header *)buf; 309 cfg = (ipfw_nat64stl_cfg *)(oh + 1); 310 sz = sizeof(buf); 311 312 nat64stl_fill_ntlv(&oh->ntlv, name, set); 313 if (do_get3(IP_FW_NAT64STL_CONFIG, &oh->opheader, &sz) != 0) 314 err(EX_OSERR, "failed to get config for instance %s", name); 315 316 while (ac > 0) { 317 tcmd = get_token(nat64newcmds, *av, "option"); 318 opt = *av; 319 ac--; av++; 320 321 switch (tcmd) { 322 #if 0 323 case TOK_TABLE4: 324 NEED1("table name required"); 325 table_fill_ntlv(&cfg->ntlv4, *av, set, 4); 326 ac--; av++; 327 break; 328 case TOK_TABLE6: 329 NEED1("table name required"); 330 table_fill_ntlv(&cfg->ntlv6, *av, set, 6); 331 ac--; av++; 332 break; 333 #endif 334 case TOK_LOG: 335 cfg->flags |= NAT64_LOG; 336 break; 337 case TOK_LOGOFF: 338 cfg->flags &= ~NAT64_LOG; 339 break; 340 default: 341 errx(EX_USAGE, "Can't change %s option", opt); 342 } 343 } 344 345 if (do_set3(IP_FW_NAT64STL_CONFIG, &oh->opheader, sizeof(buf)) != 0) 346 err(EX_OSERR, "nat64stl instance configuration failed"); 347 } 348 349 /* 350 * Destroys nat64stl instance. 351 * Request: [ ipfw_obj_header ] 352 */ 353 static void 354 nat64stl_destroy(const char *name, uint8_t set) 355 { 356 ipfw_obj_header oh; 357 358 memset(&oh, 0, sizeof(oh)); 359 nat64stl_fill_ntlv(&oh.ntlv, name, set); 360 if (do_set3(IP_FW_NAT64STL_DESTROY, &oh.opheader, sizeof(oh)) != 0) 361 err(EX_OSERR, "failed to destroy nat instance %s", name); 362 } 363 364 /* 365 * Get nat64stl instance statistics. 366 * Request: [ ipfw_obj_header ] 367 * Reply: [ ipfw_obj_header ipfw_obj_ctlv [ uint64_t x N ] ] 368 */ 369 static int 370 nat64stl_get_stats(const char *name, uint8_t set, 371 struct ipfw_nat64stl_stats *stats) 372 { 373 ipfw_obj_header *oh; 374 ipfw_obj_ctlv *oc; 375 size_t sz; 376 377 sz = sizeof(*oh) + sizeof(*oc) + sizeof(*stats); 378 oh = calloc(1, sz); 379 nat64stl_fill_ntlv(&oh->ntlv, name, set); 380 if (do_get3(IP_FW_NAT64STL_STATS, &oh->opheader, &sz) == 0) { 381 oc = (ipfw_obj_ctlv *)(oh + 1); 382 memcpy(stats, oc + 1, sizeof(*stats)); 383 free(oh); 384 return (0); 385 } 386 free(oh); 387 return (-1); 388 } 389 390 static void 391 nat64stl_stats(const char *name, uint8_t set) 392 { 393 struct ipfw_nat64stl_stats stats; 394 395 if (nat64stl_get_stats(name, set, &stats) != 0) 396 err(EX_OSERR, "Error retrieving stats"); 397 398 if (co.use_set != 0 || set != 0) 399 printf("set %u ", set); 400 printf("nat64stl %s\n", name); 401 402 printf("\t%ju packets translated from IPv6 to IPv4\n", 403 (uintmax_t)stats.opcnt64); 404 printf("\t%ju packets translated from IPv4 to IPv6\n", 405 (uintmax_t)stats.opcnt46); 406 printf("\t%ju IPv6 fragments created\n", 407 (uintmax_t)stats.ofrags); 408 printf("\t%ju IPv4 fragments received\n", 409 (uintmax_t)stats.ifrags); 410 printf("\t%ju output packets dropped due to no bufs, etc.\n", 411 (uintmax_t)stats.oerrors); 412 printf("\t%ju output packets discarded due to no IPv4 route\n", 413 (uintmax_t)stats.noroute4); 414 printf("\t%ju output packets discarded due to no IPv6 route\n", 415 (uintmax_t)stats.noroute6); 416 printf("\t%ju packets discarded due to unsupported protocol\n", 417 (uintmax_t)stats.noproto); 418 printf("\t%ju packets discarded due to memory allocation problems\n", 419 (uintmax_t)stats.nomem); 420 printf("\t%ju packets discarded due to some errors\n", 421 (uintmax_t)stats.dropped); 422 } 423 424 /* 425 * Reset nat64stl instance statistics specified by @oh->ntlv. 426 * Request: [ ipfw_obj_header ] 427 */ 428 static void 429 nat64stl_reset_stats(const char *name, uint8_t set) 430 { 431 ipfw_obj_header oh; 432 433 memset(&oh, 0, sizeof(oh)); 434 nat64stl_fill_ntlv(&oh.ntlv, name, set); 435 if (do_set3(IP_FW_NAT64STL_RESET_STATS, &oh.opheader, sizeof(oh)) != 0) 436 err(EX_OSERR, "failed to reset stats for instance %s", name); 437 } 438 439 static int 440 nat64stl_show_cb(ipfw_nat64stl_cfg *cfg, const char *name, uint8_t set) 441 { 442 443 if (name != NULL && strcmp(cfg->name, name) != 0) 444 return (ESRCH); 445 446 if (co.use_set != 0 && cfg->set != set) 447 return (ESRCH); 448 449 if (co.use_set != 0 || cfg->set != 0) 450 printf("set %u ", cfg->set); 451 printf("nat64stl %s table4 %s table6 %s", 452 cfg->name, cfg->ntlv4.name, cfg->ntlv6.name); 453 if (cfg->flags & NAT64_LOG) 454 printf(" log"); 455 printf("\n"); 456 return (0); 457 } 458 459 static int 460 nat64stl_destroy_cb(ipfw_nat64stl_cfg *cfg, const char *name, uint8_t set) 461 { 462 463 if (co.use_set != 0 && cfg->set != set) 464 return (ESRCH); 465 466 nat64stl_destroy(cfg->name, cfg->set); 467 return (0); 468 } 469 470 471 /* 472 * Compare nat64stl instances names. 473 * Honor number comparison. 474 */ 475 static int 476 nat64name_cmp(const void *a, const void *b) 477 { 478 ipfw_nat64stl_cfg *ca, *cb; 479 480 ca = (ipfw_nat64stl_cfg *)a; 481 cb = (ipfw_nat64stl_cfg *)b; 482 483 if (ca->set > cb->set) 484 return (1); 485 else if (ca->set < cb->set) 486 return (-1); 487 return (stringnum_cmp(ca->name, cb->name)); 488 } 489 490 /* 491 * Retrieves nat64stl instance list from kernel, 492 * optionally sorts it and calls requested function for each instance. 493 * 494 * Request: [ ipfw_obj_lheader ] 495 * Reply: [ ipfw_obj_lheader ipfw_nat64stl_cfg x N ] 496 */ 497 static int 498 nat64stl_foreach(nat64stl_cb_t *f, const char *name, uint8_t set, int sort) 499 { 500 ipfw_obj_lheader *olh; 501 ipfw_nat64stl_cfg *cfg; 502 size_t sz; 503 int i, error; 504 505 /* Start with reasonable default */ 506 sz = sizeof(*olh) + 16 * sizeof(*cfg); 507 for (;;) { 508 if ((olh = calloc(1, sz)) == NULL) 509 return (ENOMEM); 510 511 olh->size = sz; 512 if (do_get3(IP_FW_NAT64STL_LIST, &olh->opheader, &sz) != 0) { 513 sz = olh->size; 514 free(olh); 515 if (errno != ENOMEM) 516 return (errno); 517 continue; 518 } 519 520 if (sort != 0) 521 qsort(olh + 1, olh->count, olh->objsize, 522 nat64name_cmp); 523 524 cfg = (ipfw_nat64stl_cfg *)(olh + 1); 525 for (i = 0; i < olh->count; i++) { 526 error = f(cfg, name, set); /* Ignore errors for now */ 527 cfg = (ipfw_nat64stl_cfg *)((caddr_t)cfg + 528 olh->objsize); 529 } 530 free(olh); 531 break; 532 } 533 return (0); 534 } 535 536