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