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