1 /*- 2 * Copyright (c) 2016 Yandex LLC 3 * Copyright (c) 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/param.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 <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_nptv6.h> 49 #include <arpa/inet.h> 50 51 52 typedef int (nptv6_cb_t)(ipfw_nptv6_cfg *i, const char *name, uint8_t set); 53 static int nptv6_foreach(nptv6_cb_t *f, const char *name, uint8_t set, 54 int sort); 55 56 static void nptv6_create(const char *name, uint8_t set, int ac, char **av); 57 static void nptv6_destroy(const char *name, uint8_t set); 58 static void nptv6_stats(const char *name, uint8_t set); 59 static void nptv6_reset_stats(const char *name, uint8_t set); 60 static int nptv6_show_cb(ipfw_nptv6_cfg *cfg, const char *name, uint8_t set); 61 static int nptv6_destroy_cb(ipfw_nptv6_cfg *cfg, const char *name, uint8_t set); 62 63 static struct _s_x nptv6cmds[] = { 64 { "create", TOK_CREATE }, 65 { "destroy", TOK_DESTROY }, 66 { "list", TOK_LIST }, 67 { "show", TOK_LIST }, 68 { "stats", TOK_STATS }, 69 { NULL, 0 } 70 }; 71 72 static struct _s_x nptv6statscmds[] = { 73 { "reset", TOK_RESET }, 74 { NULL, 0 } 75 }; 76 77 /* 78 * This one handles all NPTv6-related commands 79 * ipfw [set N] nptv6 NAME {create | config} ... 80 * ipfw [set N] nptv6 NAME stats [reset] 81 * ipfw [set N] nptv6 {NAME | all} destroy 82 * ipfw [set N] nptv6 {NAME | all} {list | show} 83 */ 84 #define nptv6_check_name table_check_name 85 void 86 ipfw_nptv6_handler(int ac, char *av[]) 87 { 88 const char *name; 89 int tcmd; 90 uint8_t set; 91 92 if (co.use_set != 0) 93 set = co.use_set - 1; 94 else 95 set = 0; 96 ac--; av++; 97 98 NEED1("nptv6 needs instance name"); 99 name = *av; 100 if (nptv6_check_name(name) != 0) { 101 if (strcmp(name, "all") == 0) { 102 name = NULL; 103 } else 104 errx(EX_USAGE, "nptv6 instance name %s is invalid", 105 name); 106 } 107 ac--; av++; 108 NEED1("nptv6 needs command"); 109 110 tcmd = get_token(nptv6cmds, *av, "nptv6 command"); 111 if (name == NULL && tcmd != TOK_DESTROY && tcmd != TOK_LIST) 112 errx(EX_USAGE, "nptv6 instance name required"); 113 switch (tcmd) { 114 case TOK_CREATE: 115 ac--; av++; 116 nptv6_create(name, set, ac, av); 117 break; 118 case TOK_LIST: 119 nptv6_foreach(nptv6_show_cb, name, set, 1); 120 break; 121 case TOK_DESTROY: 122 if (name == NULL) 123 nptv6_foreach(nptv6_destroy_cb, NULL, set, 0); 124 else 125 nptv6_destroy(name, set); 126 break; 127 case TOK_STATS: 128 ac--; av++; 129 if (ac == 0) { 130 nptv6_stats(name, set); 131 break; 132 } 133 tcmd = get_token(nptv6statscmds, *av, "stats command"); 134 if (tcmd == TOK_RESET) 135 nptv6_reset_stats(name, set); 136 } 137 } 138 139 140 static void 141 nptv6_fill_ntlv(ipfw_obj_ntlv *ntlv, const char *name, uint8_t set) 142 { 143 144 ntlv->head.type = IPFW_TLV_EACTION_NAME(1); /* it doesn't matter */ 145 ntlv->head.length = sizeof(ipfw_obj_ntlv); 146 ntlv->idx = 1; 147 ntlv->set = set; 148 strlcpy(ntlv->name, name, sizeof(ntlv->name)); 149 } 150 151 static struct _s_x nptv6newcmds[] = { 152 { "int_prefix", TOK_INTPREFIX }, 153 { "ext_prefix", TOK_EXTPREFIX }, 154 { "prefixlen", TOK_PREFIXLEN }, 155 { NULL, 0 } 156 }; 157 158 159 static void 160 nptv6_parse_prefix(const char *arg, struct in6_addr *prefix, int *len) 161 { 162 char *p, *l; 163 164 p = strdup(arg); 165 if (p == NULL) 166 err(EX_OSERR, NULL); 167 if ((l = strchr(p, '/')) != NULL) 168 *l++ = '\0'; 169 if (inet_pton(AF_INET6, p, prefix) != 1) 170 errx(EX_USAGE, "Bad prefix: %s", p); 171 if (l != NULL) { 172 *len = (int)strtol(l, &l, 10); 173 if (*l != '\0' || *len <= 0 || *len > 64) 174 errx(EX_USAGE, "Bad prefix length: %s", arg); 175 } else 176 *len = 0; 177 free(p); 178 } 179 /* 180 * Creates new nptv6 instance 181 * ipfw nptv6 <NAME> create int_prefix <prefix> ext_prefix <prefix> 182 * Request: [ ipfw_obj_lheader ipfw_nptv6_cfg ] 183 */ 184 #define NPTV6_HAS_INTPREFIX 0x01 185 #define NPTV6_HAS_EXTPREFIX 0x02 186 #define NPTV6_HAS_PREFIXLEN 0x04 187 static void 188 nptv6_create(const char *name, uint8_t set, int ac, char *av[]) 189 { 190 char buf[sizeof(ipfw_obj_lheader) + sizeof(ipfw_nptv6_cfg)]; 191 struct in6_addr mask; 192 ipfw_nptv6_cfg *cfg; 193 ipfw_obj_lheader *olh; 194 int tcmd, flags, plen; 195 char *p = "\0"; 196 197 plen = 0; 198 memset(buf, 0, sizeof(buf)); 199 olh = (ipfw_obj_lheader *)buf; 200 cfg = (ipfw_nptv6_cfg *)(olh + 1); 201 cfg->set = set; 202 flags = 0; 203 while (ac > 0) { 204 tcmd = get_token(nptv6newcmds, *av, "option"); 205 ac--; av++; 206 207 switch (tcmd) { 208 case TOK_INTPREFIX: 209 NEED1("IPv6 prefix required"); 210 nptv6_parse_prefix(*av, &cfg->internal, &plen); 211 flags |= NPTV6_HAS_INTPREFIX; 212 if (plen > 0) 213 goto check_prefix; 214 ac--; av++; 215 break; 216 case TOK_EXTPREFIX: 217 NEED1("IPv6 prefix required"); 218 nptv6_parse_prefix(*av, &cfg->external, &plen); 219 flags |= NPTV6_HAS_EXTPREFIX; 220 if (plen > 0) 221 goto check_prefix; 222 ac--; av++; 223 break; 224 case TOK_PREFIXLEN: 225 NEED1("IPv6 prefix length required"); 226 plen = strtol(*av, &p, 10); 227 check_prefix: 228 if (*p != '\0' || plen < 8 || plen > 64) 229 errx(EX_USAGE, "wrong prefix length: %s", *av); 230 /* RFC 6296 Sec. 3.1 */ 231 if (cfg->plen > 0 && cfg->plen != plen) { 232 warnx("Prefix length mismatch (%d vs %d). " 233 "It was extended up to %d", 234 cfg->plen, plen, MAX(plen, cfg->plen)); 235 plen = MAX(plen, cfg->plen); 236 } 237 cfg->plen = plen; 238 flags |= NPTV6_HAS_PREFIXLEN; 239 ac--; av++; 240 break; 241 } 242 } 243 244 /* Check validness */ 245 if ((flags & NPTV6_HAS_INTPREFIX) != NPTV6_HAS_INTPREFIX) 246 errx(EX_USAGE, "int_prefix required"); 247 if ((flags & NPTV6_HAS_EXTPREFIX) != NPTV6_HAS_EXTPREFIX) 248 errx(EX_USAGE, "ext_prefix required"); 249 if ((flags & NPTV6_HAS_PREFIXLEN) != NPTV6_HAS_PREFIXLEN) 250 errx(EX_USAGE, "prefixlen required"); 251 252 n2mask(&mask, cfg->plen); 253 APPLY_MASK(&cfg->internal, &mask); 254 APPLY_MASK(&cfg->external, &mask); 255 256 olh->count = 1; 257 olh->objsize = sizeof(*cfg); 258 olh->size = sizeof(buf); 259 strlcpy(cfg->name, name, sizeof(cfg->name)); 260 if (do_set3(IP_FW_NPTV6_CREATE, &olh->opheader, sizeof(buf)) != 0) 261 err(EX_OSERR, "nptv6 instance creation failed"); 262 } 263 264 /* 265 * Destroys NPTv6 instance. 266 * Request: [ ipfw_obj_header ] 267 */ 268 static void 269 nptv6_destroy(const char *name, uint8_t set) 270 { 271 ipfw_obj_header oh; 272 273 memset(&oh, 0, sizeof(oh)); 274 nptv6_fill_ntlv(&oh.ntlv, name, set); 275 if (do_set3(IP_FW_NPTV6_DESTROY, &oh.opheader, sizeof(oh)) != 0) 276 err(EX_OSERR, "failed to destroy nat instance %s", name); 277 } 278 279 /* 280 * Get NPTv6 instance statistics. 281 * Request: [ ipfw_obj_header ] 282 * Reply: [ ipfw_obj_header ipfw_obj_ctlv [ uint64_t x N ] ] 283 */ 284 static int 285 nptv6_get_stats(const char *name, uint8_t set, struct ipfw_nptv6_stats *stats) 286 { 287 ipfw_obj_header *oh; 288 ipfw_obj_ctlv *oc; 289 size_t sz; 290 291 sz = sizeof(*oh) + sizeof(*oc) + sizeof(*stats); 292 oh = calloc(1, sz); 293 nptv6_fill_ntlv(&oh->ntlv, name, set); 294 if (do_get3(IP_FW_NPTV6_STATS, &oh->opheader, &sz) == 0) { 295 oc = (ipfw_obj_ctlv *)(oh + 1); 296 memcpy(stats, oc + 1, sizeof(*stats)); 297 free(oh); 298 return (0); 299 } 300 free(oh); 301 return (-1); 302 } 303 304 static void 305 nptv6_stats(const char *name, uint8_t set) 306 { 307 struct ipfw_nptv6_stats stats; 308 309 if (nptv6_get_stats(name, set, &stats) != 0) 310 err(EX_OSERR, "Error retrieving stats"); 311 312 if (co.use_set != 0 || set != 0) 313 printf("set %u ", set); 314 printf("nptv6 %s\n", name); 315 printf("\t%ju packets translated (internal to external)\n", 316 (uintmax_t)stats.in2ex); 317 printf("\t%ju packets translated (external to internal)\n", 318 (uintmax_t)stats.ex2in); 319 printf("\t%ju packets dropped due to some error\n", 320 (uintmax_t)stats.dropped); 321 } 322 323 /* 324 * Reset NPTv6 instance statistics specified by @oh->ntlv. 325 * Request: [ ipfw_obj_header ] 326 */ 327 static void 328 nptv6_reset_stats(const char *name, uint8_t set) 329 { 330 ipfw_obj_header oh; 331 332 memset(&oh, 0, sizeof(oh)); 333 nptv6_fill_ntlv(&oh.ntlv, name, set); 334 if (do_set3(IP_FW_NPTV6_RESET_STATS, &oh.opheader, sizeof(oh)) != 0) 335 err(EX_OSERR, "failed to reset stats for instance %s", name); 336 } 337 338 static int 339 nptv6_show_cb(ipfw_nptv6_cfg *cfg, const char *name, uint8_t set) 340 { 341 char abuf[INET6_ADDRSTRLEN]; 342 343 if (name != NULL && strcmp(cfg->name, name) != 0) 344 return (ESRCH); 345 346 if (co.use_set != 0 && cfg->set != set) 347 return (ESRCH); 348 349 if (co.use_set != 0 || cfg->set != 0) 350 printf("set %u ", cfg->set); 351 inet_ntop(AF_INET6, &cfg->internal, abuf, sizeof(abuf)); 352 printf("nptv6 %s int_prefix %s ", cfg->name, abuf); 353 inet_ntop(AF_INET6, &cfg->external, abuf, sizeof(abuf)); 354 printf("ext_prefix %s prefixlen %u\n", abuf, cfg->plen); 355 return (0); 356 } 357 358 static int 359 nptv6_destroy_cb(ipfw_nptv6_cfg *cfg, const char *name, uint8_t set) 360 { 361 362 if (co.use_set != 0 && cfg->set != set) 363 return (ESRCH); 364 365 nptv6_destroy(cfg->name, cfg->set); 366 return (0); 367 } 368 369 370 /* 371 * Compare NPTv6 instances names. 372 * Honor number comparison. 373 */ 374 static int 375 nptv6name_cmp(const void *a, const void *b) 376 { 377 ipfw_nptv6_cfg *ca, *cb; 378 379 ca = (ipfw_nptv6_cfg *)a; 380 cb = (ipfw_nptv6_cfg *)b; 381 382 if (ca->set > cb->set) 383 return (1); 384 else if (ca->set < cb->set) 385 return (-1); 386 return (stringnum_cmp(ca->name, cb->name)); 387 } 388 389 /* 390 * Retrieves NPTv6 instance list from kernel, 391 * Request: [ ipfw_obj_lheader ] 392 * Reply: [ ipfw_obj_lheader ipfw_nptv6_cfg x N ] 393 */ 394 static int 395 nptv6_foreach(nptv6_cb_t *f, const char *name, uint8_t set, int sort) 396 { 397 ipfw_obj_lheader *olh; 398 ipfw_nptv6_cfg *cfg; 399 size_t sz; 400 int i, error; 401 402 /* Start with reasonable default */ 403 sz = sizeof(*olh) + 16 * sizeof(*cfg); 404 for (;;) { 405 if ((olh = calloc(1, sz)) == NULL) 406 return (ENOMEM); 407 408 olh->size = sz; 409 if (do_get3(IP_FW_NPTV6_LIST, &olh->opheader, &sz) != 0) { 410 sz = olh->size; 411 free(olh); 412 if (errno != ENOMEM) 413 return (errno); 414 continue; 415 } 416 417 if (sort != 0) 418 qsort(olh + 1, olh->count, olh->objsize, nptv6name_cmp); 419 420 cfg = (ipfw_nptv6_cfg *)(olh + 1); 421 for (i = 0; i < olh->count; i++) { 422 error = f(cfg, name, set); 423 cfg = (ipfw_nptv6_cfg *)((caddr_t)cfg + olh->objsize); 424 } 425 free(olh); 426 break; 427 } 428 return (0); 429 } 430 431