1d8caf56eSAndrey V. Elsukov /*- 2d8caf56eSAndrey V. Elsukov * Copyright (c) 2015-2016 Yandex LLC 3d8caf56eSAndrey V. Elsukov * Copyright (c) 2015-2016 Alexander V. Chernikov <melifaro@FreeBSD.org> 4d8caf56eSAndrey V. Elsukov * Copyright (c) 2015-2016 Andrey V. Elsukov <ae@FreeBSD.org> 5d8caf56eSAndrey V. Elsukov * All rights reserved. 6d8caf56eSAndrey V. Elsukov * 7d8caf56eSAndrey V. Elsukov * Redistribution and use in source and binary forms, with or without 8d8caf56eSAndrey V. Elsukov * modification, are permitted provided that the following conditions 9d8caf56eSAndrey V. Elsukov * are met: 10d8caf56eSAndrey V. Elsukov * 11d8caf56eSAndrey V. Elsukov * 1. Redistributions of source code must retain the above copyright 12d8caf56eSAndrey V. Elsukov * notice, this list of conditions and the following disclaimer. 13d8caf56eSAndrey V. Elsukov * 2. Redistributions in binary form must reproduce the above copyright 14d8caf56eSAndrey V. Elsukov * notice, this list of conditions and the following disclaimer in the 15d8caf56eSAndrey V. Elsukov * documentation and/or other materials provided with the distribution. 16d8caf56eSAndrey V. Elsukov * 17d8caf56eSAndrey V. Elsukov * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18d8caf56eSAndrey V. Elsukov * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19d8caf56eSAndrey V. Elsukov * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20d8caf56eSAndrey V. Elsukov * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21d8caf56eSAndrey V. Elsukov * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22d8caf56eSAndrey V. Elsukov * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23d8caf56eSAndrey V. Elsukov * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24d8caf56eSAndrey V. Elsukov * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25d8caf56eSAndrey V. Elsukov * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26d8caf56eSAndrey V. Elsukov * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27d8caf56eSAndrey V. Elsukov */ 28d8caf56eSAndrey V. Elsukov 29d8caf56eSAndrey V. Elsukov #include <sys/cdefs.h> 30d8caf56eSAndrey V. Elsukov __FBSDID("$FreeBSD$"); 31d8caf56eSAndrey V. Elsukov 32d8caf56eSAndrey V. Elsukov #include <sys/types.h> 33d8caf56eSAndrey V. Elsukov #include <sys/socket.h> 34d8caf56eSAndrey V. Elsukov 35d8caf56eSAndrey V. Elsukov #include "ipfw2.h" 36d8caf56eSAndrey V. Elsukov 37d8caf56eSAndrey V. Elsukov #include <ctype.h> 38d8caf56eSAndrey V. Elsukov #include <err.h> 39d8caf56eSAndrey V. Elsukov #include <errno.h> 40c5e85276SAndrey V. Elsukov #include <inttypes.h> 41d8caf56eSAndrey V. Elsukov #include <netdb.h> 42d8caf56eSAndrey V. Elsukov #include <stdio.h> 43d8caf56eSAndrey V. Elsukov #include <stdlib.h> 44d8caf56eSAndrey V. Elsukov #include <string.h> 45d8caf56eSAndrey V. Elsukov #include <sysexits.h> 46d8caf56eSAndrey V. Elsukov 47d8caf56eSAndrey V. Elsukov #include <net/if.h> 48d8caf56eSAndrey V. Elsukov #include <netinet/in.h> 49d8caf56eSAndrey V. Elsukov #include <netinet/ip_fw.h> 50d8caf56eSAndrey V. Elsukov #include <netinet6/ip_fw_nat64.h> 51d8caf56eSAndrey V. Elsukov #include <arpa/inet.h> 52d8caf56eSAndrey V. Elsukov 53d8caf56eSAndrey V. Elsukov static void nat64lsn_fill_ntlv(ipfw_obj_ntlv *ntlv, const char *name, 54d8caf56eSAndrey V. Elsukov uint8_t set); 55d8caf56eSAndrey V. Elsukov typedef int (nat64lsn_cb_t)(ipfw_nat64lsn_cfg *cfg, const char *name, 56d8caf56eSAndrey V. Elsukov uint8_t set); 57d8caf56eSAndrey V. Elsukov static int nat64lsn_foreach(nat64lsn_cb_t *f, const char *name, uint8_t set, 58d8caf56eSAndrey V. Elsukov int sort); 59d8caf56eSAndrey V. Elsukov 60d8caf56eSAndrey V. Elsukov static void nat64lsn_create(const char *name, uint8_t set, int ac, char **av); 61d8caf56eSAndrey V. Elsukov static void nat64lsn_config(const char *name, uint8_t set, int ac, char **av); 62d8caf56eSAndrey V. Elsukov static void nat64lsn_destroy(const char *name, uint8_t set); 63d8caf56eSAndrey V. Elsukov static void nat64lsn_stats(const char *name, uint8_t set); 64d8caf56eSAndrey V. Elsukov static void nat64lsn_reset_stats(const char *name, uint8_t set); 65d8caf56eSAndrey V. Elsukov static int nat64lsn_show_cb(ipfw_nat64lsn_cfg *cfg, const char *name, 66d8caf56eSAndrey V. Elsukov uint8_t set); 67d8caf56eSAndrey V. Elsukov static int nat64lsn_destroy_cb(ipfw_nat64lsn_cfg *cfg, const char *name, 68d8caf56eSAndrey V. Elsukov uint8_t set); 69d8caf56eSAndrey V. Elsukov static int nat64lsn_states_cb(ipfw_nat64lsn_cfg *cfg, const char *name, 70d8caf56eSAndrey V. Elsukov uint8_t set); 71d8caf56eSAndrey V. Elsukov 72d8caf56eSAndrey V. Elsukov static struct _s_x nat64cmds[] = { 73d8caf56eSAndrey V. Elsukov { "create", TOK_CREATE }, 74d8caf56eSAndrey V. Elsukov { "config", TOK_CONFIG }, 75d8caf56eSAndrey V. Elsukov { "destroy", TOK_DESTROY }, 76d8caf56eSAndrey V. Elsukov { "list", TOK_LIST }, 77d8caf56eSAndrey V. Elsukov { "show", TOK_LIST }, 78d8caf56eSAndrey V. Elsukov { "stats", TOK_STATS }, 79d8caf56eSAndrey V. Elsukov { NULL, 0 } 80d8caf56eSAndrey V. Elsukov }; 81d8caf56eSAndrey V. Elsukov 82d8caf56eSAndrey V. Elsukov static uint64_t 83d8caf56eSAndrey V. Elsukov nat64lsn_print_states(void *buf) 84d8caf56eSAndrey V. Elsukov { 85d8caf56eSAndrey V. Elsukov char s[INET6_ADDRSTRLEN], a[INET_ADDRSTRLEN], f[INET_ADDRSTRLEN]; 86d8caf56eSAndrey V. Elsukov char sflags[4], *sf, *proto; 87d8caf56eSAndrey V. Elsukov ipfw_obj_header *oh; 88d8caf56eSAndrey V. Elsukov ipfw_obj_data *od; 89d8caf56eSAndrey V. Elsukov ipfw_nat64lsn_stg *stg; 90d8caf56eSAndrey V. Elsukov ipfw_nat64lsn_state *ste; 91d8caf56eSAndrey V. Elsukov uint64_t next_idx; 92d8caf56eSAndrey V. Elsukov int i, sz; 93d8caf56eSAndrey V. Elsukov 94d8caf56eSAndrey V. Elsukov oh = (ipfw_obj_header *)buf; 95d8caf56eSAndrey V. Elsukov od = (ipfw_obj_data *)(oh + 1); 96d8caf56eSAndrey V. Elsukov stg = (ipfw_nat64lsn_stg *)(od + 1); 97d8caf56eSAndrey V. Elsukov sz = od->head.length - sizeof(*od); 98d8caf56eSAndrey V. Elsukov next_idx = 0; 99d8caf56eSAndrey V. Elsukov while (sz > 0 && next_idx != 0xFF) { 100d8caf56eSAndrey V. Elsukov next_idx = stg->next_idx; 101d8caf56eSAndrey V. Elsukov sz -= sizeof(*stg); 102d8caf56eSAndrey V. Elsukov if (stg->count == 0) { 103d8caf56eSAndrey V. Elsukov stg++; 104d8caf56eSAndrey V. Elsukov continue; 105d8caf56eSAndrey V. Elsukov } 106d8caf56eSAndrey V. Elsukov switch (stg->proto) { 107d8caf56eSAndrey V. Elsukov case IPPROTO_TCP: 108d8caf56eSAndrey V. Elsukov proto = "TCP"; 109d8caf56eSAndrey V. Elsukov break; 110d8caf56eSAndrey V. Elsukov case IPPROTO_UDP: 111d8caf56eSAndrey V. Elsukov proto = "UDP"; 112d8caf56eSAndrey V. Elsukov break; 113d8caf56eSAndrey V. Elsukov case IPPROTO_ICMPV6: 114d8caf56eSAndrey V. Elsukov proto = "ICMPv6"; 115d8caf56eSAndrey V. Elsukov break; 116d8caf56eSAndrey V. Elsukov } 117d8caf56eSAndrey V. Elsukov inet_ntop(AF_INET6, &stg->host6, s, sizeof(s)); 118d8caf56eSAndrey V. Elsukov inet_ntop(AF_INET, &stg->alias4, a, sizeof(a)); 119d8caf56eSAndrey V. Elsukov ste = (ipfw_nat64lsn_state *)(stg + 1); 120d8caf56eSAndrey V. Elsukov for (i = 0; i < stg->count && sz > 0; i++) { 121d8caf56eSAndrey V. Elsukov sf = sflags; 122d8caf56eSAndrey V. Elsukov inet_ntop(AF_INET, &ste->daddr, f, sizeof(f)); 123d8caf56eSAndrey V. Elsukov if (stg->proto == IPPROTO_TCP) { 124d8caf56eSAndrey V. Elsukov if (ste->flags & 0x02) 125d8caf56eSAndrey V. Elsukov *sf++ = 'S'; 126d8caf56eSAndrey V. Elsukov if (ste->flags & 0x04) 127d8caf56eSAndrey V. Elsukov *sf++ = 'E'; 128d8caf56eSAndrey V. Elsukov if (ste->flags & 0x01) 129d8caf56eSAndrey V. Elsukov *sf++ = 'F'; 130d8caf56eSAndrey V. Elsukov } 131d8caf56eSAndrey V. Elsukov *sf = '\0'; 132d8caf56eSAndrey V. Elsukov switch (stg->proto) { 133d8caf56eSAndrey V. Elsukov case IPPROTO_TCP: 134d8caf56eSAndrey V. Elsukov case IPPROTO_UDP: 135d8caf56eSAndrey V. Elsukov printf("%s:%d\t%s:%d\t%s\t%s\t%d\t%s:%d\n", 136d8caf56eSAndrey V. Elsukov s, ste->sport, a, ste->aport, proto, 137d8caf56eSAndrey V. Elsukov sflags, ste->idle, f, ste->dport); 138d8caf56eSAndrey V. Elsukov break; 139d8caf56eSAndrey V. Elsukov case IPPROTO_ICMPV6: 140d8caf56eSAndrey V. Elsukov printf("%s\t%s\t%s\t\t%d\t%s\n", 141d8caf56eSAndrey V. Elsukov s, a, proto, ste->idle, f); 142d8caf56eSAndrey V. Elsukov break; 143d8caf56eSAndrey V. Elsukov default: 144d8caf56eSAndrey V. Elsukov printf("%s\t%s\t%d\t\t%d\t%s\n", 145d8caf56eSAndrey V. Elsukov s, a, stg->proto, ste->idle, f); 146d8caf56eSAndrey V. Elsukov } 147d8caf56eSAndrey V. Elsukov ste++; 148d8caf56eSAndrey V. Elsukov sz -= sizeof(*ste); 149d8caf56eSAndrey V. Elsukov } 150d8caf56eSAndrey V. Elsukov stg = (ipfw_nat64lsn_stg *)ste; 151d8caf56eSAndrey V. Elsukov } 152d8caf56eSAndrey V. Elsukov return (next_idx); 153d8caf56eSAndrey V. Elsukov } 154d8caf56eSAndrey V. Elsukov 155d8caf56eSAndrey V. Elsukov static int 156d8caf56eSAndrey V. Elsukov nat64lsn_states_cb(ipfw_nat64lsn_cfg *cfg, const char *name, uint8_t set) 157d8caf56eSAndrey V. Elsukov { 158d8caf56eSAndrey V. Elsukov ipfw_obj_header *oh; 159d8caf56eSAndrey V. Elsukov ipfw_obj_data *od; 160d8caf56eSAndrey V. Elsukov void *buf; 161d8caf56eSAndrey V. Elsukov uint64_t next_idx; 162d8caf56eSAndrey V. Elsukov size_t sz; 163d8caf56eSAndrey V. Elsukov 164d8caf56eSAndrey V. Elsukov if (name != NULL && strcmp(cfg->name, name) != 0) 165d8caf56eSAndrey V. Elsukov return (ESRCH); 166d8caf56eSAndrey V. Elsukov 167d8caf56eSAndrey V. Elsukov if (set != 0 && cfg->set != set) 168d8caf56eSAndrey V. Elsukov return (ESRCH); 169d8caf56eSAndrey V. Elsukov 170d8caf56eSAndrey V. Elsukov next_idx = 0; 171d8caf56eSAndrey V. Elsukov sz = 4096; 172d8caf56eSAndrey V. Elsukov if ((buf = calloc(1, sz)) == NULL) 173d8caf56eSAndrey V. Elsukov err(EX_OSERR, NULL); 174d8caf56eSAndrey V. Elsukov do { 175d8caf56eSAndrey V. Elsukov oh = (ipfw_obj_header *)buf; 176d8caf56eSAndrey V. Elsukov od = (ipfw_obj_data *)(oh + 1); 177d8caf56eSAndrey V. Elsukov nat64lsn_fill_ntlv(&oh->ntlv, cfg->name, set); 178d8caf56eSAndrey V. Elsukov od->head.type = IPFW_TLV_OBJDATA; 179d8caf56eSAndrey V. Elsukov od->head.length = sizeof(*od) + sizeof(next_idx); 180d8caf56eSAndrey V. Elsukov *((uint64_t *)(od + 1)) = next_idx; 181d8caf56eSAndrey V. Elsukov if (do_get3(IP_FW_NAT64LSN_LIST_STATES, &oh->opheader, &sz)) 182d8caf56eSAndrey V. Elsukov err(EX_OSERR, "Error reading nat64lsn states"); 183d8caf56eSAndrey V. Elsukov next_idx = nat64lsn_print_states(buf); 184d8caf56eSAndrey V. Elsukov sz = 4096; 185d8caf56eSAndrey V. Elsukov memset(buf, 0, sz); 186d8caf56eSAndrey V. Elsukov } while (next_idx != 0xFF); 187d8caf56eSAndrey V. Elsukov 188d8caf56eSAndrey V. Elsukov free(buf); 189d8caf56eSAndrey V. Elsukov return (0); 190d8caf56eSAndrey V. Elsukov } 191d8caf56eSAndrey V. Elsukov 192d8caf56eSAndrey V. Elsukov static struct _s_x nat64statscmds[] = { 193d8caf56eSAndrey V. Elsukov { "reset", TOK_RESET }, 194d8caf56eSAndrey V. Elsukov { NULL, 0 } 195d8caf56eSAndrey V. Elsukov }; 196d8caf56eSAndrey V. Elsukov 197d8caf56eSAndrey V. Elsukov static void 198d8caf56eSAndrey V. Elsukov ipfw_nat64lsn_stats_handler(const char *name, uint8_t set, int ac, char *av[]) 199d8caf56eSAndrey V. Elsukov { 200d8caf56eSAndrey V. Elsukov int tcmd; 201d8caf56eSAndrey V. Elsukov 202d8caf56eSAndrey V. Elsukov if (ac == 0) { 203d8caf56eSAndrey V. Elsukov nat64lsn_stats(name, set); 204d8caf56eSAndrey V. Elsukov return; 205d8caf56eSAndrey V. Elsukov } 206d8caf56eSAndrey V. Elsukov NEED1("nat64lsn stats needs command"); 207d8caf56eSAndrey V. Elsukov tcmd = get_token(nat64statscmds, *av, "nat64lsn stats command"); 208d8caf56eSAndrey V. Elsukov switch (tcmd) { 209d8caf56eSAndrey V. Elsukov case TOK_RESET: 210d8caf56eSAndrey V. Elsukov nat64lsn_reset_stats(name, set); 211d8caf56eSAndrey V. Elsukov } 212d8caf56eSAndrey V. Elsukov } 213d8caf56eSAndrey V. Elsukov 214d8caf56eSAndrey V. Elsukov static struct _s_x nat64listcmds[] = { 215d8caf56eSAndrey V. Elsukov { "states", TOK_STATES }, 216d8caf56eSAndrey V. Elsukov { "config", TOK_CONFIG }, 217d8caf56eSAndrey V. Elsukov { NULL, 0 } 218d8caf56eSAndrey V. Elsukov }; 219d8caf56eSAndrey V. Elsukov 220d8caf56eSAndrey V. Elsukov static void 221d8caf56eSAndrey V. Elsukov ipfw_nat64lsn_list_handler(const char *name, uint8_t set, int ac, char *av[]) 222d8caf56eSAndrey V. Elsukov { 223d8caf56eSAndrey V. Elsukov int tcmd; 224d8caf56eSAndrey V. Elsukov 225d8caf56eSAndrey V. Elsukov if (ac == 0) { 226d8caf56eSAndrey V. Elsukov nat64lsn_foreach(nat64lsn_show_cb, name, set, 1); 227d8caf56eSAndrey V. Elsukov return; 228d8caf56eSAndrey V. Elsukov } 229d8caf56eSAndrey V. Elsukov NEED1("nat64lsn list needs command"); 230d8caf56eSAndrey V. Elsukov tcmd = get_token(nat64listcmds, *av, "nat64lsn list command"); 231d8caf56eSAndrey V. Elsukov switch (tcmd) { 232d8caf56eSAndrey V. Elsukov case TOK_STATES: 233d8caf56eSAndrey V. Elsukov nat64lsn_foreach(nat64lsn_states_cb, name, set, 1); 234d8caf56eSAndrey V. Elsukov break; 235d8caf56eSAndrey V. Elsukov case TOK_CONFIG: 236d8caf56eSAndrey V. Elsukov nat64lsn_foreach(nat64lsn_show_cb, name, set, 1); 237d8caf56eSAndrey V. Elsukov } 238d8caf56eSAndrey V. Elsukov } 239d8caf56eSAndrey V. Elsukov 240d8caf56eSAndrey V. Elsukov /* 241d8caf56eSAndrey V. Elsukov * This one handles all nat64lsn-related commands 242d8caf56eSAndrey V. Elsukov * ipfw [set N] nat64lsn NAME {create | config} ... 243d8caf56eSAndrey V. Elsukov * ipfw [set N] nat64lsn NAME stats 244d8caf56eSAndrey V. Elsukov * ipfw [set N] nat64lsn {NAME | all} destroy 245d8caf56eSAndrey V. Elsukov * ipfw [set N] nat64lsn {NAME | all} {list | show} [config | states] 246d8caf56eSAndrey V. Elsukov */ 247d8caf56eSAndrey V. Elsukov #define nat64lsn_check_name table_check_name 248d8caf56eSAndrey V. Elsukov void 249d8caf56eSAndrey V. Elsukov ipfw_nat64lsn_handler(int ac, char *av[]) 250d8caf56eSAndrey V. Elsukov { 251d8caf56eSAndrey V. Elsukov const char *name; 252d8caf56eSAndrey V. Elsukov int tcmd; 253d8caf56eSAndrey V. Elsukov uint8_t set; 254d8caf56eSAndrey V. Elsukov 255d8caf56eSAndrey V. Elsukov if (co.use_set != 0) 256d8caf56eSAndrey V. Elsukov set = co.use_set - 1; 257d8caf56eSAndrey V. Elsukov else 258d8caf56eSAndrey V. Elsukov set = 0; 259d8caf56eSAndrey V. Elsukov ac--; av++; 260d8caf56eSAndrey V. Elsukov 261d8caf56eSAndrey V. Elsukov NEED1("nat64lsn needs instance name"); 262d8caf56eSAndrey V. Elsukov name = *av; 263d8caf56eSAndrey V. Elsukov if (nat64lsn_check_name(name) != 0) { 264d8caf56eSAndrey V. Elsukov if (strcmp(name, "all") == 0) 265d8caf56eSAndrey V. Elsukov name = NULL; 266d8caf56eSAndrey V. Elsukov else 267d8caf56eSAndrey V. Elsukov errx(EX_USAGE, "nat64lsn instance name %s is invalid", 268d8caf56eSAndrey V. Elsukov name); 269d8caf56eSAndrey V. Elsukov } 270d8caf56eSAndrey V. Elsukov ac--; av++; 271d8caf56eSAndrey V. Elsukov NEED1("nat64lsn needs command"); 272d8caf56eSAndrey V. Elsukov 273d8caf56eSAndrey V. Elsukov tcmd = get_token(nat64cmds, *av, "nat64lsn command"); 274d8caf56eSAndrey V. Elsukov if (name == NULL && tcmd != TOK_DESTROY && tcmd != TOK_LIST) 275d8caf56eSAndrey V. Elsukov errx(EX_USAGE, "nat64lsn instance name required"); 276d8caf56eSAndrey V. Elsukov switch (tcmd) { 277d8caf56eSAndrey V. Elsukov case TOK_CREATE: 278d8caf56eSAndrey V. Elsukov ac--; av++; 279d8caf56eSAndrey V. Elsukov nat64lsn_create(name, set, ac, av); 280d8caf56eSAndrey V. Elsukov break; 281d8caf56eSAndrey V. Elsukov case TOK_CONFIG: 282d8caf56eSAndrey V. Elsukov ac--; av++; 283d8caf56eSAndrey V. Elsukov nat64lsn_config(name, set, ac, av); 284d8caf56eSAndrey V. Elsukov break; 285d8caf56eSAndrey V. Elsukov case TOK_LIST: 286d8caf56eSAndrey V. Elsukov ac--; av++; 287d8caf56eSAndrey V. Elsukov ipfw_nat64lsn_list_handler(name, set, ac, av); 288d8caf56eSAndrey V. Elsukov break; 289d8caf56eSAndrey V. Elsukov case TOK_DESTROY: 290d8caf56eSAndrey V. Elsukov if (name == NULL) 291d8caf56eSAndrey V. Elsukov nat64lsn_foreach(nat64lsn_destroy_cb, NULL, set, 0); 292d8caf56eSAndrey V. Elsukov else 293d8caf56eSAndrey V. Elsukov nat64lsn_destroy(name, set); 294d8caf56eSAndrey V. Elsukov break; 295d8caf56eSAndrey V. Elsukov case TOK_STATS: 296d8caf56eSAndrey V. Elsukov ac--; av++; 297d8caf56eSAndrey V. Elsukov ipfw_nat64lsn_stats_handler(name, set, ac, av); 298d8caf56eSAndrey V. Elsukov } 299d8caf56eSAndrey V. Elsukov } 300d8caf56eSAndrey V. Elsukov 301d8caf56eSAndrey V. Elsukov static void 302d8caf56eSAndrey V. Elsukov nat64lsn_fill_ntlv(ipfw_obj_ntlv *ntlv, const char *name, uint8_t set) 303d8caf56eSAndrey V. Elsukov { 304d8caf56eSAndrey V. Elsukov 305d8caf56eSAndrey V. Elsukov ntlv->head.type = IPFW_TLV_EACTION_NAME(1); /* it doesn't matter */ 306d8caf56eSAndrey V. Elsukov ntlv->head.length = sizeof(ipfw_obj_ntlv); 307d8caf56eSAndrey V. Elsukov ntlv->idx = 1; 308d8caf56eSAndrey V. Elsukov ntlv->set = set; 309d8caf56eSAndrey V. Elsukov strlcpy(ntlv->name, name, sizeof(ntlv->name)); 310d8caf56eSAndrey V. Elsukov } 311d8caf56eSAndrey V. Elsukov 312d8caf56eSAndrey V. Elsukov static void 313d8caf56eSAndrey V. Elsukov nat64lsn_apply_mask(int af, void *prefix, uint16_t plen) 314d8caf56eSAndrey V. Elsukov { 315d8caf56eSAndrey V. Elsukov struct in6_addr mask6, *p6; 316d8caf56eSAndrey V. Elsukov struct in_addr mask4, *p4; 317d8caf56eSAndrey V. Elsukov 318d8caf56eSAndrey V. Elsukov if (af == AF_INET) { 319d8caf56eSAndrey V. Elsukov p4 = (struct in_addr *)prefix; 320d8caf56eSAndrey V. Elsukov mask4.s_addr = htonl(~((1 << (32 - plen)) - 1)); 321d8caf56eSAndrey V. Elsukov p4->s_addr &= mask4.s_addr; 322d8caf56eSAndrey V. Elsukov } else if (af == AF_INET6) { 323d8caf56eSAndrey V. Elsukov p6 = (struct in6_addr *)prefix; 324d8caf56eSAndrey V. Elsukov n2mask(&mask6, plen); 325d8caf56eSAndrey V. Elsukov APPLY_MASK(p6, &mask6); 326d8caf56eSAndrey V. Elsukov } 327d8caf56eSAndrey V. Elsukov } 328d8caf56eSAndrey V. Elsukov 329d8caf56eSAndrey V. Elsukov static void 330d8caf56eSAndrey V. Elsukov nat64lsn_parse_prefix(const char *arg, int af, void *prefix, uint16_t *plen) 331d8caf56eSAndrey V. Elsukov { 332d8caf56eSAndrey V. Elsukov char *p, *l; 333d8caf56eSAndrey V. Elsukov 334d8caf56eSAndrey V. Elsukov p = strdup(arg); 335d8caf56eSAndrey V. Elsukov if (p == NULL) 336d8caf56eSAndrey V. Elsukov err(EX_OSERR, NULL); 337d8caf56eSAndrey V. Elsukov if ((l = strchr(p, '/')) != NULL) 338d8caf56eSAndrey V. Elsukov *l++ = '\0'; 339d8caf56eSAndrey V. Elsukov if (l == NULL) 340d8caf56eSAndrey V. Elsukov errx(EX_USAGE, "Prefix length required"); 341d8caf56eSAndrey V. Elsukov if (inet_pton(af, p, prefix) != 1) 342d8caf56eSAndrey V. Elsukov errx(EX_USAGE, "Bad prefix: %s", p); 343d8caf56eSAndrey V. Elsukov *plen = (uint16_t)strtol(l, &l, 10); 344d8caf56eSAndrey V. Elsukov if (*l != '\0' || *plen == 0 || (af == AF_INET && *plen > 32) || 345d8caf56eSAndrey V. Elsukov (af == AF_INET6 && *plen > 96)) 346d8caf56eSAndrey V. Elsukov errx(EX_USAGE, "Bad prefix length: %s", arg); 347d8caf56eSAndrey V. Elsukov nat64lsn_apply_mask(af, prefix, *plen); 348d8caf56eSAndrey V. Elsukov free(p); 349d8caf56eSAndrey V. Elsukov } 350d8caf56eSAndrey V. Elsukov 351d8caf56eSAndrey V. Elsukov static uint32_t 352d8caf56eSAndrey V. Elsukov nat64lsn_parse_int(const char *arg, const char *desc) 353d8caf56eSAndrey V. Elsukov { 354d8caf56eSAndrey V. Elsukov char *p; 355d8caf56eSAndrey V. Elsukov uint32_t val; 356d8caf56eSAndrey V. Elsukov 357d8caf56eSAndrey V. Elsukov val = (uint32_t)strtol(arg, &p, 10); 358d8caf56eSAndrey V. Elsukov if (*p != '\0') 359d8caf56eSAndrey V. Elsukov errx(EX_USAGE, "Invalid %s value: %s\n", desc, arg); 360d8caf56eSAndrey V. Elsukov return (val); 361d8caf56eSAndrey V. Elsukov } 362d8caf56eSAndrey V. Elsukov 363d8caf56eSAndrey V. Elsukov static struct _s_x nat64newcmds[] = { 364d8caf56eSAndrey V. Elsukov { "prefix6", TOK_PREFIX6 }, 365d8caf56eSAndrey V. Elsukov { "agg_len", TOK_AGG_LEN }, /* not yet */ 366d8caf56eSAndrey V. Elsukov { "agg_count", TOK_AGG_COUNT }, /* not yet */ 367d8caf56eSAndrey V. Elsukov { "port_range", TOK_PORT_RANGE }, /* not yet */ 368d8caf56eSAndrey V. Elsukov { "jmaxlen", TOK_JMAXLEN }, 369d8caf56eSAndrey V. Elsukov { "prefix4", TOK_PREFIX4 }, 370d8caf56eSAndrey V. Elsukov { "max_ports", TOK_MAX_PORTS }, 371d8caf56eSAndrey V. Elsukov { "host_del_age", TOK_HOST_DEL_AGE }, 372d8caf56eSAndrey V. Elsukov { "pg_del_age", TOK_PG_DEL_AGE }, 373d8caf56eSAndrey V. Elsukov { "tcp_syn_age", TOK_TCP_SYN_AGE }, 374d8caf56eSAndrey V. Elsukov { "tcp_close_age",TOK_TCP_CLOSE_AGE }, 375d8caf56eSAndrey V. Elsukov { "tcp_est_age", TOK_TCP_EST_AGE }, 376d8caf56eSAndrey V. Elsukov { "udp_age", TOK_UDP_AGE }, 377d8caf56eSAndrey V. Elsukov { "icmp_age", TOK_ICMP_AGE }, 378d8caf56eSAndrey V. Elsukov { "log", TOK_LOG }, 379d8caf56eSAndrey V. Elsukov { "-log", TOK_LOGOFF }, 380d8caf56eSAndrey V. Elsukov { NULL, 0 } 381d8caf56eSAndrey V. Elsukov }; 382d8caf56eSAndrey V. Elsukov 383d8caf56eSAndrey V. Elsukov /* 384d8caf56eSAndrey V. Elsukov * Creates new nat64lsn instance 385d8caf56eSAndrey V. Elsukov * ipfw nat64lsn <NAME> create 386d8caf56eSAndrey V. Elsukov * [ max_ports <N> ] 387d8caf56eSAndrey V. Elsukov * Request: [ ipfw_obj_lheader ipfw_nat64lsn_cfg ] 388d8caf56eSAndrey V. Elsukov */ 389d8caf56eSAndrey V. Elsukov #define NAT64LSN_HAS_PREFIX4 0x01 390d8caf56eSAndrey V. Elsukov #define NAT64LSN_HAS_PREFIX6 0x02 391d8caf56eSAndrey V. Elsukov static void 392d8caf56eSAndrey V. Elsukov nat64lsn_create(const char *name, uint8_t set, int ac, char **av) 393d8caf56eSAndrey V. Elsukov { 394d8caf56eSAndrey V. Elsukov char buf[sizeof(ipfw_obj_lheader) + sizeof(ipfw_nat64lsn_cfg)]; 395d8caf56eSAndrey V. Elsukov ipfw_nat64lsn_cfg *cfg; 396d8caf56eSAndrey V. Elsukov ipfw_obj_lheader *olh; 397d8caf56eSAndrey V. Elsukov int tcmd, flags; 398d8caf56eSAndrey V. Elsukov char *opt; 399d8caf56eSAndrey V. Elsukov 400d8caf56eSAndrey V. Elsukov memset(&buf, 0, sizeof(buf)); 401d8caf56eSAndrey V. Elsukov olh = (ipfw_obj_lheader *)buf; 402d8caf56eSAndrey V. Elsukov cfg = (ipfw_nat64lsn_cfg *)(olh + 1); 403d8caf56eSAndrey V. Elsukov 404d8caf56eSAndrey V. Elsukov /* Some reasonable defaults */ 405d8caf56eSAndrey V. Elsukov inet_pton(AF_INET6, "64:ff9b::", &cfg->prefix6); 406d8caf56eSAndrey V. Elsukov cfg->plen6 = 96; 407d8caf56eSAndrey V. Elsukov cfg->set = set; 408d8caf56eSAndrey V. Elsukov cfg->max_ports = NAT64LSN_MAX_PORTS; 409d8caf56eSAndrey V. Elsukov cfg->jmaxlen = NAT64LSN_JMAXLEN; 410d8caf56eSAndrey V. Elsukov cfg->nh_delete_delay = NAT64LSN_HOST_AGE; 411d8caf56eSAndrey V. Elsukov cfg->pg_delete_delay = NAT64LSN_PG_AGE; 412d8caf56eSAndrey V. Elsukov cfg->st_syn_ttl = NAT64LSN_TCP_SYN_AGE; 413d8caf56eSAndrey V. Elsukov cfg->st_estab_ttl = NAT64LSN_TCP_EST_AGE; 414d8caf56eSAndrey V. Elsukov cfg->st_close_ttl = NAT64LSN_TCP_FIN_AGE; 415d8caf56eSAndrey V. Elsukov cfg->st_udp_ttl = NAT64LSN_UDP_AGE; 416d8caf56eSAndrey V. Elsukov cfg->st_icmp_ttl = NAT64LSN_ICMP_AGE; 417d8caf56eSAndrey V. Elsukov flags = NAT64LSN_HAS_PREFIX6; 418d8caf56eSAndrey V. Elsukov while (ac > 0) { 419d8caf56eSAndrey V. Elsukov tcmd = get_token(nat64newcmds, *av, "option"); 420d8caf56eSAndrey V. Elsukov opt = *av; 421d8caf56eSAndrey V. Elsukov ac--; av++; 422d8caf56eSAndrey V. Elsukov 423d8caf56eSAndrey V. Elsukov switch (tcmd) { 424d8caf56eSAndrey V. Elsukov case TOK_PREFIX4: 425d8caf56eSAndrey V. Elsukov NEED1("IPv4 prefix required"); 426d8caf56eSAndrey V. Elsukov nat64lsn_parse_prefix(*av, AF_INET, &cfg->prefix4, 427d8caf56eSAndrey V. Elsukov &cfg->plen4); 428d8caf56eSAndrey V. Elsukov flags |= NAT64LSN_HAS_PREFIX4; 429d8caf56eSAndrey V. Elsukov ac--; av++; 430d8caf56eSAndrey V. Elsukov break; 431d8caf56eSAndrey V. Elsukov case TOK_PREFIX6: 432d8caf56eSAndrey V. Elsukov NEED1("IPv6 prefix required"); 433d8caf56eSAndrey V. Elsukov nat64lsn_parse_prefix(*av, AF_INET6, &cfg->prefix6, 434d8caf56eSAndrey V. Elsukov &cfg->plen6); 435*782360deSAndrey V. Elsukov if (ipfw_check_nat64prefix(&cfg->prefix6, 436*782360deSAndrey V. Elsukov cfg->plen6) != 0) 437*782360deSAndrey V. Elsukov errx(EX_USAGE, "Bad prefix6 %s", *av); 438*782360deSAndrey V. Elsukov 439d8caf56eSAndrey V. Elsukov ac--; av++; 440d8caf56eSAndrey V. Elsukov break; 441*782360deSAndrey V. Elsukov #if 0 442d8caf56eSAndrey V. Elsukov case TOK_AGG_LEN: 443d8caf56eSAndrey V. Elsukov NEED1("Aggregation prefix len required"); 444d8caf56eSAndrey V. Elsukov cfg->agg_prefix_len = nat64lsn_parse_int(*av, opt); 445d8caf56eSAndrey V. Elsukov ac--; av++; 446d8caf56eSAndrey V. Elsukov break; 447d8caf56eSAndrey V. Elsukov case TOK_AGG_COUNT: 448d8caf56eSAndrey V. Elsukov NEED1("Max per-prefix count required"); 449d8caf56eSAndrey V. Elsukov cfg->agg_prefix_max = nat64lsn_parse_int(*av, opt); 450d8caf56eSAndrey V. Elsukov ac--; av++; 451d8caf56eSAndrey V. Elsukov break; 452d8caf56eSAndrey V. Elsukov case TOK_PORT_RANGE: 453d8caf56eSAndrey V. Elsukov NEED1("port range x[:y] required"); 454d8caf56eSAndrey V. Elsukov if ((p = strchr(*av, ':')) == NULL) 455d8caf56eSAndrey V. Elsukov cfg->min_port = (uint16_t)nat64lsn_parse_int( 456d8caf56eSAndrey V. Elsukov *av, opt); 457d8caf56eSAndrey V. Elsukov else { 458d8caf56eSAndrey V. Elsukov *p++ = '\0'; 459d8caf56eSAndrey V. Elsukov cfg->min_port = (uint16_t)nat64lsn_parse_int( 460d8caf56eSAndrey V. Elsukov *av, opt); 461d8caf56eSAndrey V. Elsukov cfg->max_port = (uint16_t)nat64lsn_parse_int( 462d8caf56eSAndrey V. Elsukov p, opt); 463d8caf56eSAndrey V. Elsukov } 464d8caf56eSAndrey V. Elsukov ac--; av++; 465d8caf56eSAndrey V. Elsukov break; 466d8caf56eSAndrey V. Elsukov case TOK_JMAXLEN: 467d8caf56eSAndrey V. Elsukov NEED1("job queue length required"); 468d8caf56eSAndrey V. Elsukov cfg->jmaxlen = nat64lsn_parse_int(*av, opt); 469d8caf56eSAndrey V. Elsukov ac--; av++; 470d8caf56eSAndrey V. Elsukov break; 471d8caf56eSAndrey V. Elsukov #endif 472d8caf56eSAndrey V. Elsukov case TOK_MAX_PORTS: 473d8caf56eSAndrey V. Elsukov NEED1("Max per-user ports required"); 474d8caf56eSAndrey V. Elsukov cfg->max_ports = nat64lsn_parse_int(*av, opt); 475d8caf56eSAndrey V. Elsukov ac--; av++; 476d8caf56eSAndrey V. Elsukov break; 477d8caf56eSAndrey V. Elsukov case TOK_HOST_DEL_AGE: 478d8caf56eSAndrey V. Elsukov NEED1("host delete delay required"); 479d8caf56eSAndrey V. Elsukov cfg->nh_delete_delay = (uint16_t)nat64lsn_parse_int( 480d8caf56eSAndrey V. Elsukov *av, opt); 481d8caf56eSAndrey V. Elsukov ac--; av++; 482d8caf56eSAndrey V. Elsukov break; 483d8caf56eSAndrey V. Elsukov case TOK_PG_DEL_AGE: 484d8caf56eSAndrey V. Elsukov NEED1("portgroup delete delay required"); 485d8caf56eSAndrey V. Elsukov cfg->pg_delete_delay = (uint16_t)nat64lsn_parse_int( 486d8caf56eSAndrey V. Elsukov *av, opt); 487d8caf56eSAndrey V. Elsukov ac--; av++; 488d8caf56eSAndrey V. Elsukov break; 489d8caf56eSAndrey V. Elsukov case TOK_TCP_SYN_AGE: 490d8caf56eSAndrey V. Elsukov NEED1("tcp syn age required"); 491d8caf56eSAndrey V. Elsukov cfg->st_syn_ttl = (uint16_t)nat64lsn_parse_int( 492d8caf56eSAndrey V. Elsukov *av, opt); 493d8caf56eSAndrey V. Elsukov ac--; av++; 494d8caf56eSAndrey V. Elsukov break; 495d8caf56eSAndrey V. Elsukov case TOK_TCP_CLOSE_AGE: 496d8caf56eSAndrey V. Elsukov NEED1("tcp close age required"); 497d8caf56eSAndrey V. Elsukov cfg->st_close_ttl = (uint16_t)nat64lsn_parse_int( 498d8caf56eSAndrey V. Elsukov *av, opt); 499d8caf56eSAndrey V. Elsukov ac--; av++; 500d8caf56eSAndrey V. Elsukov break; 501d8caf56eSAndrey V. Elsukov case TOK_TCP_EST_AGE: 502d8caf56eSAndrey V. Elsukov NEED1("tcp est age required"); 503d8caf56eSAndrey V. Elsukov cfg->st_estab_ttl = (uint16_t)nat64lsn_parse_int( 504d8caf56eSAndrey V. Elsukov *av, opt); 505d8caf56eSAndrey V. Elsukov ac--; av++; 506d8caf56eSAndrey V. Elsukov break; 507d8caf56eSAndrey V. Elsukov case TOK_UDP_AGE: 508d8caf56eSAndrey V. Elsukov NEED1("udp age required"); 509d8caf56eSAndrey V. Elsukov cfg->st_udp_ttl = (uint16_t)nat64lsn_parse_int( 510d8caf56eSAndrey V. Elsukov *av, opt); 511d8caf56eSAndrey V. Elsukov ac--; av++; 512d8caf56eSAndrey V. Elsukov break; 513d8caf56eSAndrey V. Elsukov case TOK_ICMP_AGE: 514d8caf56eSAndrey V. Elsukov NEED1("icmp age required"); 515d8caf56eSAndrey V. Elsukov cfg->st_icmp_ttl = (uint16_t)nat64lsn_parse_int( 516d8caf56eSAndrey V. Elsukov *av, opt); 517d8caf56eSAndrey V. Elsukov ac--; av++; 518d8caf56eSAndrey V. Elsukov break; 519d8caf56eSAndrey V. Elsukov case TOK_LOG: 520d8caf56eSAndrey V. Elsukov cfg->flags |= NAT64_LOG; 521d8caf56eSAndrey V. Elsukov break; 522d8caf56eSAndrey V. Elsukov case TOK_LOGOFF: 523d8caf56eSAndrey V. Elsukov cfg->flags &= ~NAT64_LOG; 524d8caf56eSAndrey V. Elsukov break; 525d8caf56eSAndrey V. Elsukov } 526d8caf56eSAndrey V. Elsukov } 527d8caf56eSAndrey V. Elsukov 528d8caf56eSAndrey V. Elsukov /* Check validness */ 529d8caf56eSAndrey V. Elsukov if ((flags & NAT64LSN_HAS_PREFIX4) != NAT64LSN_HAS_PREFIX4) 530d8caf56eSAndrey V. Elsukov errx(EX_USAGE, "prefix4 required"); 531d8caf56eSAndrey V. Elsukov 532d8caf56eSAndrey V. Elsukov olh->count = 1; 533d8caf56eSAndrey V. Elsukov olh->objsize = sizeof(*cfg); 534d8caf56eSAndrey V. Elsukov olh->size = sizeof(buf); 535d8caf56eSAndrey V. Elsukov strlcpy(cfg->name, name, sizeof(cfg->name)); 536d8caf56eSAndrey V. Elsukov if (do_set3(IP_FW_NAT64LSN_CREATE, &olh->opheader, sizeof(buf)) != 0) 537d8caf56eSAndrey V. Elsukov err(EX_OSERR, "nat64lsn instance creation failed"); 538d8caf56eSAndrey V. Elsukov } 539d8caf56eSAndrey V. Elsukov 540d8caf56eSAndrey V. Elsukov /* 541d8caf56eSAndrey V. Elsukov * Configures existing nat64lsn instance 542d8caf56eSAndrey V. Elsukov * ipfw nat64lsn <NAME> config <options> 543d8caf56eSAndrey V. Elsukov * Request: [ ipfw_obj_header ipfw_nat64lsn_cfg ] 544d8caf56eSAndrey V. Elsukov */ 545d8caf56eSAndrey V. Elsukov static void 546d8caf56eSAndrey V. Elsukov nat64lsn_config(const char *name, uint8_t set, int ac, char **av) 547d8caf56eSAndrey V. Elsukov { 548d8caf56eSAndrey V. Elsukov char buf[sizeof(ipfw_obj_header) + sizeof(ipfw_nat64lsn_cfg)]; 549d8caf56eSAndrey V. Elsukov ipfw_nat64lsn_cfg *cfg; 550d8caf56eSAndrey V. Elsukov ipfw_obj_header *oh; 551d8caf56eSAndrey V. Elsukov size_t sz; 552d8caf56eSAndrey V. Elsukov char *opt; 553d8caf56eSAndrey V. Elsukov int tcmd; 554d8caf56eSAndrey V. Elsukov 555d8caf56eSAndrey V. Elsukov if (ac == 0) 556d8caf56eSAndrey V. Elsukov errx(EX_USAGE, "config options required"); 557d8caf56eSAndrey V. Elsukov memset(&buf, 0, sizeof(buf)); 558d8caf56eSAndrey V. Elsukov oh = (ipfw_obj_header *)buf; 559d8caf56eSAndrey V. Elsukov cfg = (ipfw_nat64lsn_cfg *)(oh + 1); 560d8caf56eSAndrey V. Elsukov sz = sizeof(buf); 561d8caf56eSAndrey V. Elsukov 562d8caf56eSAndrey V. Elsukov nat64lsn_fill_ntlv(&oh->ntlv, name, set); 563d8caf56eSAndrey V. Elsukov if (do_get3(IP_FW_NAT64LSN_CONFIG, &oh->opheader, &sz) != 0) 564d8caf56eSAndrey V. Elsukov err(EX_OSERR, "failed to get config for instance %s", name); 565d8caf56eSAndrey V. Elsukov 566d8caf56eSAndrey V. Elsukov while (ac > 0) { 567d8caf56eSAndrey V. Elsukov tcmd = get_token(nat64newcmds, *av, "option"); 568d8caf56eSAndrey V. Elsukov opt = *av; 569d8caf56eSAndrey V. Elsukov ac--; av++; 570d8caf56eSAndrey V. Elsukov 571d8caf56eSAndrey V. Elsukov switch (tcmd) { 572d8caf56eSAndrey V. Elsukov case TOK_MAX_PORTS: 573d8caf56eSAndrey V. Elsukov NEED1("Max per-user ports required"); 574d8caf56eSAndrey V. Elsukov cfg->max_ports = nat64lsn_parse_int(*av, opt); 575d8caf56eSAndrey V. Elsukov ac--; av++; 576d8caf56eSAndrey V. Elsukov break; 577d8caf56eSAndrey V. Elsukov case TOK_JMAXLEN: 578d8caf56eSAndrey V. Elsukov NEED1("job queue length required"); 579d8caf56eSAndrey V. Elsukov cfg->jmaxlen = nat64lsn_parse_int(*av, opt); 580d8caf56eSAndrey V. Elsukov ac--; av++; 581d8caf56eSAndrey V. Elsukov break; 582d8caf56eSAndrey V. Elsukov case TOK_HOST_DEL_AGE: 583d8caf56eSAndrey V. Elsukov NEED1("host delete delay required"); 584d8caf56eSAndrey V. Elsukov cfg->nh_delete_delay = (uint16_t)nat64lsn_parse_int( 585d8caf56eSAndrey V. Elsukov *av, opt); 586d8caf56eSAndrey V. Elsukov ac--; av++; 587d8caf56eSAndrey V. Elsukov break; 588d8caf56eSAndrey V. Elsukov case TOK_PG_DEL_AGE: 589d8caf56eSAndrey V. Elsukov NEED1("portgroup delete delay required"); 590d8caf56eSAndrey V. Elsukov cfg->pg_delete_delay = (uint16_t)nat64lsn_parse_int( 591d8caf56eSAndrey V. Elsukov *av, opt); 592d8caf56eSAndrey V. Elsukov ac--; av++; 593d8caf56eSAndrey V. Elsukov break; 594d8caf56eSAndrey V. Elsukov case TOK_TCP_SYN_AGE: 595d8caf56eSAndrey V. Elsukov NEED1("tcp syn age required"); 596d8caf56eSAndrey V. Elsukov cfg->st_syn_ttl = (uint16_t)nat64lsn_parse_int( 597d8caf56eSAndrey V. Elsukov *av, opt); 598d8caf56eSAndrey V. Elsukov ac--; av++; 599d8caf56eSAndrey V. Elsukov break; 600d8caf56eSAndrey V. Elsukov case TOK_TCP_CLOSE_AGE: 601d8caf56eSAndrey V. Elsukov NEED1("tcp close age required"); 602d8caf56eSAndrey V. Elsukov cfg->st_close_ttl = (uint16_t)nat64lsn_parse_int( 603d8caf56eSAndrey V. Elsukov *av, opt); 604d8caf56eSAndrey V. Elsukov ac--; av++; 605d8caf56eSAndrey V. Elsukov break; 606d8caf56eSAndrey V. Elsukov case TOK_TCP_EST_AGE: 607d8caf56eSAndrey V. Elsukov NEED1("tcp est age required"); 608d8caf56eSAndrey V. Elsukov cfg->st_estab_ttl = (uint16_t)nat64lsn_parse_int( 609d8caf56eSAndrey V. Elsukov *av, opt); 610d8caf56eSAndrey V. Elsukov ac--; av++; 611d8caf56eSAndrey V. Elsukov break; 612d8caf56eSAndrey V. Elsukov case TOK_UDP_AGE: 613d8caf56eSAndrey V. Elsukov NEED1("udp age required"); 614d8caf56eSAndrey V. Elsukov cfg->st_udp_ttl = (uint16_t)nat64lsn_parse_int( 615d8caf56eSAndrey V. Elsukov *av, opt); 616d8caf56eSAndrey V. Elsukov ac--; av++; 617d8caf56eSAndrey V. Elsukov break; 618d8caf56eSAndrey V. Elsukov case TOK_ICMP_AGE: 619d8caf56eSAndrey V. Elsukov NEED1("icmp age required"); 620d8caf56eSAndrey V. Elsukov cfg->st_icmp_ttl = (uint16_t)nat64lsn_parse_int( 621d8caf56eSAndrey V. Elsukov *av, opt); 622d8caf56eSAndrey V. Elsukov ac--; av++; 623d8caf56eSAndrey V. Elsukov break; 624d8caf56eSAndrey V. Elsukov case TOK_LOG: 625d8caf56eSAndrey V. Elsukov cfg->flags |= NAT64_LOG; 626d8caf56eSAndrey V. Elsukov break; 627d8caf56eSAndrey V. Elsukov case TOK_LOGOFF: 628d8caf56eSAndrey V. Elsukov cfg->flags &= ~NAT64_LOG; 629d8caf56eSAndrey V. Elsukov break; 630d8caf56eSAndrey V. Elsukov default: 631d8caf56eSAndrey V. Elsukov errx(EX_USAGE, "Can't change %s option", opt); 632d8caf56eSAndrey V. Elsukov } 633d8caf56eSAndrey V. Elsukov } 634d8caf56eSAndrey V. Elsukov 635d8caf56eSAndrey V. Elsukov if (do_set3(IP_FW_NAT64LSN_CONFIG, &oh->opheader, sizeof(buf)) != 0) 636d8caf56eSAndrey V. Elsukov err(EX_OSERR, "nat64lsn instance configuration failed"); 637d8caf56eSAndrey V. Elsukov } 638d8caf56eSAndrey V. Elsukov 639d8caf56eSAndrey V. Elsukov /* 640d8caf56eSAndrey V. Elsukov * Reset nat64lsn instance statistics specified by @oh->ntlv. 641d8caf56eSAndrey V. Elsukov * Request: [ ipfw_obj_header ] 642d8caf56eSAndrey V. Elsukov */ 643d8caf56eSAndrey V. Elsukov static void 644d8caf56eSAndrey V. Elsukov nat64lsn_reset_stats(const char *name, uint8_t set) 645d8caf56eSAndrey V. Elsukov { 646d8caf56eSAndrey V. Elsukov ipfw_obj_header oh; 647d8caf56eSAndrey V. Elsukov 648d8caf56eSAndrey V. Elsukov memset(&oh, 0, sizeof(oh)); 649d8caf56eSAndrey V. Elsukov nat64lsn_fill_ntlv(&oh.ntlv, name, set); 650d8caf56eSAndrey V. Elsukov if (do_set3(IP_FW_NAT64LSN_RESET_STATS, &oh.opheader, sizeof(oh)) != 0) 651d8caf56eSAndrey V. Elsukov err(EX_OSERR, "failed to reset stats for instance %s", name); 652d8caf56eSAndrey V. Elsukov } 653d8caf56eSAndrey V. Elsukov 654d8caf56eSAndrey V. Elsukov /* 655d8caf56eSAndrey V. Elsukov * Destroys nat64lsn instance specified by @oh->ntlv. 656d8caf56eSAndrey V. Elsukov * Request: [ ipfw_obj_header ] 657d8caf56eSAndrey V. Elsukov */ 658d8caf56eSAndrey V. Elsukov static void 659d8caf56eSAndrey V. Elsukov nat64lsn_destroy(const char *name, uint8_t set) 660d8caf56eSAndrey V. Elsukov { 661d8caf56eSAndrey V. Elsukov ipfw_obj_header oh; 662d8caf56eSAndrey V. Elsukov 663d8caf56eSAndrey V. Elsukov memset(&oh, 0, sizeof(oh)); 664d8caf56eSAndrey V. Elsukov nat64lsn_fill_ntlv(&oh.ntlv, name, set); 665d8caf56eSAndrey V. Elsukov if (do_set3(IP_FW_NAT64LSN_DESTROY, &oh.opheader, sizeof(oh)) != 0) 666d8caf56eSAndrey V. Elsukov err(EX_OSERR, "failed to destroy nat instance %s", name); 667d8caf56eSAndrey V. Elsukov } 668d8caf56eSAndrey V. Elsukov 669d8caf56eSAndrey V. Elsukov /* 670d8caf56eSAndrey V. Elsukov * Get nat64lsn instance statistics. 671d8caf56eSAndrey V. Elsukov * Request: [ ipfw_obj_header ] 672d8caf56eSAndrey V. Elsukov * Reply: [ ipfw_obj_header ipfw_obj_ctlv [ uint64_t x N ] ] 673d8caf56eSAndrey V. Elsukov */ 674d8caf56eSAndrey V. Elsukov static int 675d8caf56eSAndrey V. Elsukov nat64lsn_get_stats(const char *name, uint8_t set, 676d8caf56eSAndrey V. Elsukov struct ipfw_nat64lsn_stats *stats) 677d8caf56eSAndrey V. Elsukov { 678d8caf56eSAndrey V. Elsukov ipfw_obj_header *oh; 679d8caf56eSAndrey V. Elsukov ipfw_obj_ctlv *oc; 680d8caf56eSAndrey V. Elsukov size_t sz; 681d8caf56eSAndrey V. Elsukov 682d8caf56eSAndrey V. Elsukov sz = sizeof(*oh) + sizeof(*oc) + sizeof(*stats); 683d8caf56eSAndrey V. Elsukov oh = calloc(1, sz); 684d8caf56eSAndrey V. Elsukov nat64lsn_fill_ntlv(&oh->ntlv, name, set); 685d8caf56eSAndrey V. Elsukov if (do_get3(IP_FW_NAT64LSN_STATS, &oh->opheader, &sz) == 0) { 686d8caf56eSAndrey V. Elsukov oc = (ipfw_obj_ctlv *)(oh + 1); 687d8caf56eSAndrey V. Elsukov memcpy(stats, oc + 1, sizeof(*stats)); 688d8caf56eSAndrey V. Elsukov free(oh); 689d8caf56eSAndrey V. Elsukov return (0); 690d8caf56eSAndrey V. Elsukov } 691d8caf56eSAndrey V. Elsukov free(oh); 692d8caf56eSAndrey V. Elsukov return (-1); 693d8caf56eSAndrey V. Elsukov } 694d8caf56eSAndrey V. Elsukov 695d8caf56eSAndrey V. Elsukov static void 696d8caf56eSAndrey V. Elsukov nat64lsn_stats(const char *name, uint8_t set) 697d8caf56eSAndrey V. Elsukov { 698d8caf56eSAndrey V. Elsukov struct ipfw_nat64lsn_stats stats; 699d8caf56eSAndrey V. Elsukov 700d8caf56eSAndrey V. Elsukov if (nat64lsn_get_stats(name, set, &stats) != 0) 701d8caf56eSAndrey V. Elsukov err(EX_OSERR, "Error retrieving stats"); 702d8caf56eSAndrey V. Elsukov 703c5e85276SAndrey V. Elsukov if (co.use_set != 0 || set != 0) 704c5e85276SAndrey V. Elsukov printf("set %u ", set); 705c5e85276SAndrey V. Elsukov printf("nat64lsn %s\n", name); 706c5e85276SAndrey V. Elsukov printf("\t%ju packets translated from IPv6 to IPv4\n", 707c5e85276SAndrey V. Elsukov (uintmax_t)stats.opcnt64); 708c5e85276SAndrey V. Elsukov printf("\t%ju packets translated from IPv4 to IPv6\n", 709c5e85276SAndrey V. Elsukov (uintmax_t)stats.opcnt46); 710c5e85276SAndrey V. Elsukov printf("\t%ju IPv6 fragments created\n", 711c5e85276SAndrey V. Elsukov (uintmax_t)stats.ofrags); 712c5e85276SAndrey V. Elsukov printf("\t%ju IPv4 fragments received\n", 713c5e85276SAndrey V. Elsukov (uintmax_t)stats.ifrags); 714c5e85276SAndrey V. Elsukov printf("\t%ju output packets dropped due to no bufs, etc.\n", 715c5e85276SAndrey V. Elsukov (uintmax_t)stats.oerrors); 716c5e85276SAndrey V. Elsukov printf("\t%ju output packets discarded due to no IPv4 route\n", 717c5e85276SAndrey V. Elsukov (uintmax_t)stats.noroute4); 718c5e85276SAndrey V. Elsukov printf("\t%ju output packets discarded due to no IPv6 route\n", 719c5e85276SAndrey V. Elsukov (uintmax_t)stats.noroute6); 720c5e85276SAndrey V. Elsukov printf("\t%ju packets discarded due to unsupported protocol\n", 721c5e85276SAndrey V. Elsukov (uintmax_t)stats.noproto); 722c5e85276SAndrey V. Elsukov printf("\t%ju packets discarded due to memory allocation problems\n", 723c5e85276SAndrey V. Elsukov (uintmax_t)stats.nomem); 724c5e85276SAndrey V. Elsukov printf("\t%ju packets discarded due to some errors\n", 725c5e85276SAndrey V. Elsukov (uintmax_t)stats.dropped); 726c5e85276SAndrey V. Elsukov printf("\t%ju packets not matched with IPv4 prefix\n", 727c5e85276SAndrey V. Elsukov (uintmax_t)stats.nomatch4); 728d8caf56eSAndrey V. Elsukov 729c5e85276SAndrey V. Elsukov printf("\t%ju mbufs queued for post processing\n", 730c5e85276SAndrey V. Elsukov (uintmax_t)stats.jreinjected); 731c5e85276SAndrey V. Elsukov printf("\t%ju times the job queue was processed\n", 732c5e85276SAndrey V. Elsukov (uintmax_t)stats.jcalls); 733c5e85276SAndrey V. Elsukov printf("\t%ju job requests queued\n", 734c5e85276SAndrey V. Elsukov (uintmax_t)stats.jrequests); 735c5e85276SAndrey V. Elsukov printf("\t%ju job requests queue limit reached\n", 736c5e85276SAndrey V. Elsukov (uintmax_t)stats.jmaxlen); 737c5e85276SAndrey V. Elsukov printf("\t%ju job requests failed due to memory allocation problems\n", 738c5e85276SAndrey V. Elsukov (uintmax_t)stats.jnomem); 739c5e85276SAndrey V. Elsukov 740c5e85276SAndrey V. Elsukov printf("\t%ju hosts allocated\n", (uintmax_t)stats.hostcount); 741c5e85276SAndrey V. Elsukov printf("\t%ju hosts requested\n", (uintmax_t)stats.jhostsreq); 742c5e85276SAndrey V. Elsukov printf("\t%ju host requests failed\n", (uintmax_t)stats.jhostfails); 743c5e85276SAndrey V. Elsukov 744c5e85276SAndrey V. Elsukov printf("\t%ju portgroups requested\n", (uintmax_t)stats.jportreq); 745c5e85276SAndrey V. Elsukov printf("\t%ju portgroups allocated\n", (uintmax_t)stats.spgcreated); 746c5e85276SAndrey V. Elsukov printf("\t%ju portgroups deleted\n", (uintmax_t)stats.spgdeleted); 747c5e85276SAndrey V. Elsukov printf("\t%ju portgroup requests failed\n", 748c5e85276SAndrey V. Elsukov (uintmax_t)stats.jportfails); 749c5e85276SAndrey V. Elsukov printf("\t%ju portgroups allocated for TCP\n", 750c5e85276SAndrey V. Elsukov (uintmax_t)stats.tcpchunks); 751c5e85276SAndrey V. Elsukov printf("\t%ju portgroups allocated for UDP\n", 752c5e85276SAndrey V. Elsukov (uintmax_t)stats.udpchunks); 753c5e85276SAndrey V. Elsukov printf("\t%ju portgroups allocated for ICMP\n", 754c5e85276SAndrey V. Elsukov (uintmax_t)stats.icmpchunks); 755c5e85276SAndrey V. Elsukov 756c5e85276SAndrey V. Elsukov printf("\t%ju states created\n", (uintmax_t)stats.screated); 757c5e85276SAndrey V. Elsukov printf("\t%ju states deleted\n", (uintmax_t)stats.sdeleted); 758d8caf56eSAndrey V. Elsukov } 759d8caf56eSAndrey V. Elsukov 760d8caf56eSAndrey V. Elsukov static int 761d8caf56eSAndrey V. Elsukov nat64lsn_show_cb(ipfw_nat64lsn_cfg *cfg, const char *name, uint8_t set) 762d8caf56eSAndrey V. Elsukov { 763d8caf56eSAndrey V. Elsukov char abuf[INET6_ADDRSTRLEN]; 764d8caf56eSAndrey V. Elsukov 765d8caf56eSAndrey V. Elsukov if (name != NULL && strcmp(cfg->name, name) != 0) 766d8caf56eSAndrey V. Elsukov return (ESRCH); 767d8caf56eSAndrey V. Elsukov 768d8caf56eSAndrey V. Elsukov if (co.use_set != 0 && cfg->set != set) 769d8caf56eSAndrey V. Elsukov return (ESRCH); 770d8caf56eSAndrey V. Elsukov 771d8caf56eSAndrey V. Elsukov if (co.use_set != 0 || cfg->set != 0) 772d8caf56eSAndrey V. Elsukov printf("set %u ", cfg->set); 773d8caf56eSAndrey V. Elsukov inet_ntop(AF_INET, &cfg->prefix4, abuf, sizeof(abuf)); 774d8caf56eSAndrey V. Elsukov printf("nat64lsn %s prefix4 %s/%u", cfg->name, abuf, cfg->plen4); 775d8caf56eSAndrey V. Elsukov inet_ntop(AF_INET6, &cfg->prefix6, abuf, sizeof(abuf)); 776d8caf56eSAndrey V. Elsukov printf(" prefix6 %s/%u", abuf, cfg->plen6); 777*782360deSAndrey V. Elsukov #if 0 778d8caf56eSAndrey V. Elsukov printf("agg_len %u agg_count %u ", cfg->agg_prefix_len, 779d8caf56eSAndrey V. Elsukov cfg->agg_prefix_max); 780d8caf56eSAndrey V. Elsukov if (cfg->min_port != NAT64LSN_PORT_MIN || 781d8caf56eSAndrey V. Elsukov cfg->max_port != NAT64LSN_PORT_MAX) 782d8caf56eSAndrey V. Elsukov printf(" port_range %u:%u", cfg->min_port, cfg->max_port); 783d8caf56eSAndrey V. Elsukov if (cfg->jmaxlen != NAT64LSN_JMAXLEN) 784d8caf56eSAndrey V. Elsukov printf(" jmaxlen %u ", cfg->jmaxlen); 785d8caf56eSAndrey V. Elsukov #endif 786d8caf56eSAndrey V. Elsukov if (cfg->max_ports != NAT64LSN_MAX_PORTS) 787d8caf56eSAndrey V. Elsukov printf(" max_ports %u", cfg->max_ports); 788d8caf56eSAndrey V. Elsukov if (cfg->nh_delete_delay != NAT64LSN_HOST_AGE) 789d8caf56eSAndrey V. Elsukov printf(" host_del_age %u", cfg->nh_delete_delay); 790d8caf56eSAndrey V. Elsukov if (cfg->pg_delete_delay != NAT64LSN_PG_AGE) 791d8caf56eSAndrey V. Elsukov printf(" pg_del_age %u ", cfg->pg_delete_delay); 792d8caf56eSAndrey V. Elsukov if (cfg->st_syn_ttl != NAT64LSN_TCP_SYN_AGE) 793d8caf56eSAndrey V. Elsukov printf(" tcp_syn_age %u", cfg->st_syn_ttl); 794d8caf56eSAndrey V. Elsukov if (cfg->st_close_ttl != NAT64LSN_TCP_FIN_AGE) 795d8caf56eSAndrey V. Elsukov printf(" tcp_close_age %u", cfg->st_close_ttl); 796d8caf56eSAndrey V. Elsukov if (cfg->st_estab_ttl != NAT64LSN_TCP_EST_AGE) 797d8caf56eSAndrey V. Elsukov printf(" tcp_est_age %u", cfg->st_estab_ttl); 798d8caf56eSAndrey V. Elsukov if (cfg->st_udp_ttl != NAT64LSN_UDP_AGE) 799d8caf56eSAndrey V. Elsukov printf(" udp_age %u", cfg->st_udp_ttl); 800d8caf56eSAndrey V. Elsukov if (cfg->st_icmp_ttl != NAT64LSN_ICMP_AGE) 801d8caf56eSAndrey V. Elsukov printf(" icmp_age %u", cfg->st_icmp_ttl); 802d8caf56eSAndrey V. Elsukov if (cfg->flags & NAT64_LOG) 803d8caf56eSAndrey V. Elsukov printf(" log"); 804d8caf56eSAndrey V. Elsukov printf("\n"); 805d8caf56eSAndrey V. Elsukov return (0); 806d8caf56eSAndrey V. Elsukov } 807d8caf56eSAndrey V. Elsukov 808d8caf56eSAndrey V. Elsukov static int 809d8caf56eSAndrey V. Elsukov nat64lsn_destroy_cb(ipfw_nat64lsn_cfg *cfg, const char *name, uint8_t set) 810d8caf56eSAndrey V. Elsukov { 811d8caf56eSAndrey V. Elsukov 812d8caf56eSAndrey V. Elsukov if (co.use_set != 0 && cfg->set != set) 813d8caf56eSAndrey V. Elsukov return (ESRCH); 814d8caf56eSAndrey V. Elsukov 815d8caf56eSAndrey V. Elsukov nat64lsn_destroy(cfg->name, cfg->set); 816d8caf56eSAndrey V. Elsukov return (0); 817d8caf56eSAndrey V. Elsukov } 818d8caf56eSAndrey V. Elsukov 819d8caf56eSAndrey V. Elsukov 820d8caf56eSAndrey V. Elsukov /* 821d8caf56eSAndrey V. Elsukov * Compare nat64lsn instances names. 822d8caf56eSAndrey V. Elsukov * Honor number comparison. 823d8caf56eSAndrey V. Elsukov */ 824d8caf56eSAndrey V. Elsukov static int 825d8caf56eSAndrey V. Elsukov nat64name_cmp(const void *a, const void *b) 826d8caf56eSAndrey V. Elsukov { 827d8caf56eSAndrey V. Elsukov ipfw_nat64lsn_cfg *ca, *cb; 828d8caf56eSAndrey V. Elsukov 829d8caf56eSAndrey V. Elsukov ca = (ipfw_nat64lsn_cfg *)a; 830d8caf56eSAndrey V. Elsukov cb = (ipfw_nat64lsn_cfg *)b; 831d8caf56eSAndrey V. Elsukov 832d8caf56eSAndrey V. Elsukov if (ca->set > cb->set) 833d8caf56eSAndrey V. Elsukov return (1); 834d8caf56eSAndrey V. Elsukov else if (ca->set < cb->set) 835d8caf56eSAndrey V. Elsukov return (-1); 836d8caf56eSAndrey V. Elsukov return (stringnum_cmp(ca->name, cb->name)); 837d8caf56eSAndrey V. Elsukov } 838d8caf56eSAndrey V. Elsukov 839d8caf56eSAndrey V. Elsukov /* 840d8caf56eSAndrey V. Elsukov * Retrieves nat64lsn instance list from kernel, 841d8caf56eSAndrey V. Elsukov * optionally sorts it and calls requested function for each instance. 842d8caf56eSAndrey V. Elsukov * 843d8caf56eSAndrey V. Elsukov * Request: [ ipfw_obj_lheader ] 844d8caf56eSAndrey V. Elsukov * Reply: [ ipfw_obj_lheader ipfw_nat64lsn_cfg x N ] 845d8caf56eSAndrey V. Elsukov */ 846d8caf56eSAndrey V. Elsukov static int 847d8caf56eSAndrey V. Elsukov nat64lsn_foreach(nat64lsn_cb_t *f, const char *name, uint8_t set, int sort) 848d8caf56eSAndrey V. Elsukov { 849d8caf56eSAndrey V. Elsukov ipfw_obj_lheader *olh; 850d8caf56eSAndrey V. Elsukov ipfw_nat64lsn_cfg *cfg; 851d8caf56eSAndrey V. Elsukov size_t sz; 852d8caf56eSAndrey V. Elsukov int i, error; 853d8caf56eSAndrey V. Elsukov 854d8caf56eSAndrey V. Elsukov /* Start with reasonable default */ 855d8caf56eSAndrey V. Elsukov sz = sizeof(*olh) + 16 * sizeof(ipfw_nat64lsn_cfg); 856d8caf56eSAndrey V. Elsukov 857d8caf56eSAndrey V. Elsukov for (;;) { 858d8caf56eSAndrey V. Elsukov if ((olh = calloc(1, sz)) == NULL) 859d8caf56eSAndrey V. Elsukov return (ENOMEM); 860d8caf56eSAndrey V. Elsukov 861d8caf56eSAndrey V. Elsukov olh->size = sz; 862d8caf56eSAndrey V. Elsukov if (do_get3(IP_FW_NAT64LSN_LIST, &olh->opheader, &sz) != 0) { 863d8caf56eSAndrey V. Elsukov sz = olh->size; 864d8caf56eSAndrey V. Elsukov free(olh); 865d8caf56eSAndrey V. Elsukov if (errno != ENOMEM) 866d8caf56eSAndrey V. Elsukov return (errno); 867d8caf56eSAndrey V. Elsukov continue; 868d8caf56eSAndrey V. Elsukov } 869d8caf56eSAndrey V. Elsukov 870d8caf56eSAndrey V. Elsukov if (sort != 0) 871d8caf56eSAndrey V. Elsukov qsort(olh + 1, olh->count, olh->objsize, 872d8caf56eSAndrey V. Elsukov nat64name_cmp); 873d8caf56eSAndrey V. Elsukov 874d8caf56eSAndrey V. Elsukov cfg = (ipfw_nat64lsn_cfg *)(olh + 1); 875d8caf56eSAndrey V. Elsukov for (i = 0; i < olh->count; i++) { 876d8caf56eSAndrey V. Elsukov error = f(cfg, name, set); /* Ignore errors for now */ 877d8caf56eSAndrey V. Elsukov cfg = (ipfw_nat64lsn_cfg *)((caddr_t)cfg + 878d8caf56eSAndrey V. Elsukov olh->objsize); 879d8caf56eSAndrey V. Elsukov } 880d8caf56eSAndrey V. Elsukov free(olh); 881d8caf56eSAndrey V. Elsukov break; 882d8caf56eSAndrey V. Elsukov } 883d8caf56eSAndrey V. Elsukov return (0); 884d8caf56eSAndrey V. Elsukov } 885d8caf56eSAndrey V. Elsukov 886