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