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