kernel/efi: Add a sysctl machdep.efi_map to dump the raw EFI memory map.
[dragonfly.git] / sbin / ipfw3 / ipfw3nat.c
1 /*
2  * Copyright (c) 2016 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Bill Yuan <bycn82@dragonflybsd.org>
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34
35 #include <sys/param.h>
36 #include <sys/mbuf.h>
37 #include <sys/socket.h>
38 #include <sys/sockio.h>
39 #include <sys/sysctl.h>
40 #include <sys/time.h>
41 #include <sys/wait.h>
42
43 #include <arpa/inet.h>
44 #include <ctype.h>
45 #include <dlfcn.h>
46 #include <err.h>
47 #include <errno.h>
48 #include <grp.h>
49 #include <limits.h>
50 #include <netdb.h>
51 #include <pwd.h>
52 #include <sysexits.h>
53 #include <signal.h>
54 #include <stdio.h>
55 #include <stdlib.h>
56 #include <stdarg.h>
57 #include <string.h>
58 #include <timeconv.h>
59 #include <unistd.h>
60
61 #include <netinet/in.h>
62 #include <netinet/in_systm.h>
63 #include <netinet/ip.h>
64 #include <netinet/ip_icmp.h>
65 #include <netinet/tcp.h>
66 #include <net/if.h>
67 #include <net/if_dl.h>
68 #include <net/route.h>
69 #include <net/ethernet.h>
70
71 #include "../../sys/net/ipfw3/ip_fw3.h"
72 #include "../../sys/net/ipfw3/ip_fw3_table.h"
73 #include "../../sys/net/ipfw3/ip_fw3_sync.h"
74 #include "../../sys/net/dummynet3/ip_dummynet3.h"
75 #include "../../sys/net/libalias/alias.h"
76 #include "../../sys/net/ipfw3_basic/ip_fw3_basic.h"
77 #include "../../sys/net/ipfw3_nat/ip_fw3_nat.h"
78
79 #include "ipfw3.h"
80 #include "ipfw3nat.h"
81
82 extern int verbose;
83 extern int do_time;
84 extern int do_quiet;
85 extern int do_force;
86
87 struct char_int_map nat_params[] = {
88         { "ip",                 TOK_IP },
89         { "if",                 TOK_IF },
90         { "log",                TOK_ALOG },
91         { "deny_in",            TOK_DENY_INC },
92         { "same_ports",         TOK_SAME_PORTS },
93         { "unreg_only",         TOK_UNREG_ONLY },
94         { "reset",              TOK_RESET_ADDR },
95         { "reverse",            TOK_ALIAS_REV },
96         { "proxy_only",         TOK_PROXY_ONLY },
97         { "redirect_addr",      TOK_REDIR_ADDR },
98         { "redirect_port",      TOK_REDIR_PORT },
99         { "redirect_proto",     TOK_REDIR_PROTO },
100         { NULL, 0 },
101 };
102
103
104 void
105 nat_config(int ac, char **av)
106 {
107         struct cfg_nat *n;      /* Nat instance configuration. */
108         int i, len, off, tok;
109         char *id, buf[NAT_BUF_LEN];     /* Buffer for serialized data. */
110
111         len = NAT_BUF_LEN;
112         /* Offset in buf: save space for n at the beginning. */
113         off = sizeof(struct cfg_nat);
114         memset(buf, 0, sizeof(buf));
115         n = (struct cfg_nat *)buf;
116
117         NEXT_ARG;
118         /* Nat id. */
119         if (ac && isdigit(**av)) {
120                 id = *av;
121                 i = atoi(*av);
122                 NEXT_ARG;
123                 n->id = i;
124         } else
125                 errx(EX_DATAERR, "missing nat id");
126         if (ac == 0)
127                 errx(EX_DATAERR, "missing option");
128
129         while (ac > 0) {
130                 tok = match_token(nat_params, *av);
131                 NEXT_ARG;
132                 switch (tok) {
133                 case TOK_IP:
134                         if (ac == 0)
135                                 errx(EX_DATAERR, "missing option");
136                         if (!inet_aton(av[0], &(n->ip)))
137                                 errx(EX_DATAERR, "bad ip addr `%s'", av[0]);
138                         NEXT_ARG;
139                         break;
140                 case TOK_IF:
141                         if (ac == 0)
142                                 errx(EX_DATAERR, "missing option");
143                         set_addr_dynamic(av[0], n);
144                         NEXT_ARG;
145                         break;
146                 case TOK_ALOG:
147                         n->mode |= PKT_ALIAS_LOG;
148                         break;
149                 case TOK_DENY_INC:
150                         n->mode |= PKT_ALIAS_DENY_INCOMING;
151                         break;
152                 case TOK_SAME_PORTS:
153                         n->mode |= PKT_ALIAS_SAME_PORTS;
154                         break;
155                 case TOK_UNREG_ONLY:
156                         n->mode |= PKT_ALIAS_UNREGISTERED_ONLY;
157                         break;
158                 case TOK_RESET_ADDR:
159                         n->mode |= PKT_ALIAS_RESET_ON_ADDR_CHANGE;
160                         break;
161                 case TOK_ALIAS_REV:
162                         n->mode |= PKT_ALIAS_REVERSE;
163                         break;
164                 case TOK_PROXY_ONLY:
165                         n->mode |= PKT_ALIAS_PROXY_ONLY;
166                         break;
167                 /*
168                  * All the setup_redir_* functions work
169                  * directly in the final
170                  * buffer, see above for details.
171                  */
172                 case TOK_REDIR_ADDR:
173                 case TOK_REDIR_PORT:
174                 case TOK_REDIR_PROTO:
175                         switch (tok) {
176                         case TOK_REDIR_ADDR:
177                                 i = setup_redir_addr(&buf[off], len, &ac, &av);
178                                 break;
179                         case TOK_REDIR_PORT:
180                                 i = setup_redir_port(&buf[off], len, &ac, &av);
181                                 break;
182                         case TOK_REDIR_PROTO:
183                                 i = setup_redir_proto(&buf[off], len, &ac, &av);
184                                 break;
185                         }
186                         n->redir_cnt++;
187                         off += i;
188                         len -= i;
189                         break;
190                 default:
191                         errx(EX_DATAERR, "unrecognised option ``%s''", av[-1]);
192                 }
193         }
194         i = do_set_x(IP_FW_NAT_ADD, buf, off);
195         if (i) {
196                 err(1, "do_set_x(%s)", "IP_FW_NAT_ADD");
197         }
198
199         /* After every modification, we show the resultant rule. */
200         int _ac = 2;
201         char *_av[] = {"config", id};
202         nat_show(_ac, _av);
203 }
204
205 void
206 nat_show_config(char *buf)
207 {
208         struct cfg_nat *n;
209         struct cfg_redir *t;
210         struct cfg_spool *s;
211         struct protoent *p;
212         int i, cnt, flag, off;
213
214         n = (struct cfg_nat *)buf;
215         flag = 1;
216         off = sizeof(*n);
217         printf("ipfw3 nat %u config", n->id);
218         if (strlen(n->if_name) != 0)
219                 printf(" if %s", n->if_name);
220         else if (n->ip.s_addr != 0)
221                 printf(" ip %s", inet_ntoa(n->ip));
222         while (n->mode != 0) {
223                 if (n->mode & PKT_ALIAS_LOG) {
224                         printf(" log");
225                         n->mode &= ~PKT_ALIAS_LOG;
226                 } else if (n->mode & PKT_ALIAS_DENY_INCOMING) {
227                         printf(" deny_in");
228                         n->mode &= ~PKT_ALIAS_DENY_INCOMING;
229                 } else if (n->mode & PKT_ALIAS_SAME_PORTS) {
230                         printf(" same_ports");
231                         n->mode &= ~PKT_ALIAS_SAME_PORTS;
232                 } else if (n->mode & PKT_ALIAS_UNREGISTERED_ONLY) {
233                         printf(" unreg_only");
234                         n->mode &= ~PKT_ALIAS_UNREGISTERED_ONLY;
235                 } else if (n->mode & PKT_ALIAS_RESET_ON_ADDR_CHANGE) {
236                         printf(" reset");
237                         n->mode &= ~PKT_ALIAS_RESET_ON_ADDR_CHANGE;
238                 } else if (n->mode & PKT_ALIAS_REVERSE) {
239                         printf(" reverse");
240                         n->mode &= ~PKT_ALIAS_REVERSE;
241                 } else if (n->mode & PKT_ALIAS_PROXY_ONLY) {
242                         printf(" proxy_only");
243                         n->mode &= ~PKT_ALIAS_PROXY_ONLY;
244                 }
245         }
246
247         /* Print all the redirect's data configuration. */
248         for (cnt = 0; cnt < n->redir_cnt; cnt++) {
249                 t = (struct cfg_redir *)&buf[off];
250                 off += SOF_REDIR;
251                 switch (t->mode) {
252                 case REDIR_ADDR:
253                         printf(" redirect_addr");
254                         if (t->spool_cnt == 0) {
255                                 printf(" %s", inet_ntoa(t->laddr));
256                         } else {
257                                 for (i = 0; i < t->spool_cnt; i++) {
258                                         s = (struct cfg_spool *)&buf[off];
259                                         if (i)
260                                                 printf(", ");
261                                         else
262                                                 printf(" ");
263                                         printf("%s", inet_ntoa(s->addr));
264                                         off += SOF_SPOOL;
265                                 }
266                         }
267                         printf(" %s", inet_ntoa(t->paddr));
268                         break;
269                 case REDIR_PORT:
270                         p = getprotobynumber(t->proto);
271                         printf(" redirect_port %s ", p->p_name);
272                         if (!t->spool_cnt) {
273                                 printf("%s:%u", inet_ntoa(t->laddr), t->lport);
274                                 if (t->pport_cnt > 1) {
275                                         printf("-%u", t->lport +
276                                                         t->pport_cnt - 1);
277                                 }
278                         } else
279                                 for (i=0; i < t->spool_cnt; i++) {
280                                         s = (struct cfg_spool *)&buf[off];
281                                         if (i) {
282                                                 printf(", ");
283                                         }
284                                         printf("%s:%u", inet_ntoa(s->addr),
285                                                         s->port);
286                                         off += SOF_SPOOL;
287                                 }
288
289                         printf(" ");
290                         if (t->paddr.s_addr) {
291                                 printf("%s:", inet_ntoa(t->paddr));
292                         }
293                         printf("%u", t->pport);
294                         if (!t->spool_cnt && t->pport_cnt > 1) {
295                                 printf("-%u", t->pport + t->pport_cnt - 1);
296                         }
297
298                         if (t->raddr.s_addr) {
299                                 printf(" %s", inet_ntoa(t->raddr));
300                                 if (t->rport) {
301                                         printf(":%u", t->rport);
302                                         if (!t->spool_cnt && t->rport_cnt > 1) {
303                                                 printf("-%u", t->rport +
304                                                         t->rport_cnt - 1);
305                                         }
306                                 }
307                         }
308                         break;
309                 case REDIR_PROTO:
310                         p = getprotobynumber(t->proto);
311                         printf(" redirect_proto %s %s", p->p_name,
312                                         inet_ntoa(t->laddr));
313                         if (t->paddr.s_addr != 0) {
314                                 printf(" %s", inet_ntoa(t->paddr));
315                                 if (t->raddr.s_addr) {
316                                         printf(" %s", inet_ntoa(t->raddr));
317                                 }
318                         }
319                         break;
320                 default:
321                         errx(EX_DATAERR, "unknown redir mode");
322                         break;
323                 }
324         }
325         printf("\n");
326 }
327
328
329 void
330 nat_show(int ac, char **av)
331 {
332         struct cfg_nat *n;
333         struct cfg_redir *e;
334         int i, nbytes, nalloc, size;
335         int nat_cnt, redir_cnt, nat_id;
336         uint8_t *data;
337
338         nalloc = 1024;
339         size = 0;
340         data = NULL;
341
342         NEXT_ARG;
343
344         if (ac == 0)
345                 nat_id = 0;
346         else
347                 nat_id = strtoul(*av, NULL, 10);
348
349         nbytes = nalloc;
350         while (nbytes >= nalloc) {
351                 nalloc = nalloc * 2;
352                 nbytes = nalloc;
353                 if ((data = realloc(data, nbytes)) == NULL) {
354                         err(EX_OSERR, "realloc");
355                 }
356                 if (do_get_x(IP_FW_NAT_GET, data, &nbytes) < 0) {
357                         err(EX_OSERR, "do_get_x(IP_FW_NAT_GET)");
358                 }
359         }
360
361         if (nbytes == 0) {
362                 exit(EX_OK);
363         }
364
365         nat_cnt = *((int *)data);
366         for (i = sizeof(nat_cnt); nat_cnt; nat_cnt--) {
367                 n = (struct cfg_nat *)&data[i];
368                 if (n->id >= 0 && n->id <= IPFW_DEFAULT_RULE) {
369                         if (nat_id == 0 || n->id == nat_id)
370                                 nat_show_config(&data[i]);
371                 }
372                 i += sizeof(struct cfg_nat);
373                 for (redir_cnt = 0; redir_cnt < n->redir_cnt; redir_cnt++) {
374                         e = (struct cfg_redir *)&data[i];
375                         i += sizeof(struct cfg_redir) +
376                                 e->spool_cnt * sizeof(struct cfg_spool);
377                 }
378         }
379 }
380
381 int
382 setup_redir_port(char *spool_buf, int len, int *_ac, char ***_av)
383 {
384         char **av, *sep, *protoName;
385         char tmp_spool_buf[NAT_BUF_LEN];
386         int ac, space, lsnat;
387         struct cfg_redir *r;
388         struct cfg_spool *tmp;
389         u_short numLocalPorts;
390         port_range portRange;
391
392         av = *_av;
393         ac = *_ac;
394         space = 0;
395         lsnat = 0;
396         numLocalPorts = 0;
397
398         if (len >= SOF_REDIR) {
399                 r = (struct cfg_redir *)spool_buf;
400                 /* Skip cfg_redir at beginning of buf. */
401                 spool_buf = &spool_buf[SOF_REDIR];
402                 space = SOF_REDIR;
403                 len -= SOF_REDIR;
404         } else {
405                 goto nospace;
406         }
407
408         r->mode = REDIR_PORT;
409         /*
410          * Extract protocol.
411          */
412         if (ac == 0)
413                 errx (EX_DATAERR, "redirect_port: missing protocol");
414
415         r->proto = str2proto(*av);
416         protoName = *av;
417         INC_ARGCV();
418
419         /*
420          * Extract local address.
421          */
422         if (ac == 0)
423                 errx (EX_DATAERR, "redirect_port: missing local address");
424
425         sep = strchr(*av, ',');
426         /* LSNAT redirection syntax. */
427         if (sep) {
428                 r->laddr.s_addr = INADDR_NONE;
429                 r->lport = ~0;
430                 numLocalPorts = 1;
431                 /* Preserve av, copy spool servers to tmp_spool_buf. */
432                 strncpy(tmp_spool_buf, *av, strlen(*av)+1);
433                 lsnat = 1;
434         } else {
435                 if (str2addr_portrange (*av, &r->laddr,
436                                         protoName, &portRange) != 0)
437                         errx(EX_DATAERR, "redirect_port:"
438                                 "invalid local port range");
439
440                 r->lport = GETLOPORT(portRange);
441                 numLocalPorts = GETNUMPORTS(portRange);
442         }
443         INC_ARGCV();
444
445         /*
446          * Extract public port and optionally address.
447          */
448         if (ac == 0)
449                 errx (EX_DATAERR, "redirect_port: missing public port");
450
451         sep = strchr (*av, ':');
452         if (sep) {
453                 if (str2addr_portrange (*av, &r->paddr, protoName, &portRange) != 0)
454                         errx(EX_DATAERR, "redirect_port:"
455                                 "invalid public port range");
456         } else {
457                 r->paddr.s_addr = INADDR_ANY;
458                 if (str2portrange(*av, protoName, &portRange) != 0)
459                         errx(EX_DATAERR, "redirect_port:"
460                                 "invalid public port range");
461         }
462
463         r->pport = GETLOPORT(portRange);
464         r->pport_cnt = GETNUMPORTS(portRange);
465         INC_ARGCV();
466
467         /*
468          * Extract remote address and optionally port.
469          */
470         /*
471          * NB: isalpha(**av) => we've to check that next parameter is really an
472          * option for this redirect entry, else stop here processing arg[cv].
473          */
474         if (ac != 0 && !isalpha(**av)) {
475                 sep = strchr (*av, ':');
476                 if (sep) {
477                         if (str2addr_portrange (*av, &r->raddr,
478                                                 protoName, &portRange) != 0) {
479                                 errx(EX_DATAERR, "redirect_port:"
480                                         "invalid remote port range");
481                         }
482                 } else {
483                         SETLOPORT(portRange, 0);
484                         SETNUMPORTS(portRange, 1);
485                         str2addr (*av, &r->raddr);
486                 }
487                 INC_ARGCV();
488         } else {
489                 SETLOPORT(portRange, 0);
490                 SETNUMPORTS(portRange, 1);
491                 r->raddr.s_addr = INADDR_ANY;
492         }
493         r->rport = GETLOPORT(portRange);
494         r->rport_cnt = GETNUMPORTS(portRange);
495
496         /*
497          * Make sure port ranges match up, then add the redirect ports.
498          */
499         if (numLocalPorts != r->pport_cnt) {
500                 errx(EX_DATAERR, "redirect_port:"
501                         "port ranges must be equal in size");
502         }
503
504         /* Remote port range is allowed to be '0' which means all ports. */
505         if (r->rport_cnt != numLocalPorts &&
506                         (r->rport_cnt != 1 || r->rport != 0)) {
507                 errx(EX_DATAERR, "redirect_port: remote port must"
508                                 "be 0 or equal to local port range in size");
509         }
510
511         /*
512          * Setup LSNAT server pool.
513          */
514         if (lsnat) {
515                 sep = strtok(tmp_spool_buf, ", ");
516                 while (sep != NULL) {
517                         tmp = (struct cfg_spool *)spool_buf;
518                         if (len < SOF_SPOOL)
519                                 goto nospace;
520
521                         len -= SOF_SPOOL;
522                         space += SOF_SPOOL;
523                         if (str2addr_portrange(sep,
524                                 &tmp->addr, protoName, &portRange) != 0)
525                                 errx(EX_DATAERR, "redirect_port:"
526                                         "invalid local port range");
527                         if (GETNUMPORTS(portRange) != 1)
528                                 errx(EX_DATAERR, "redirect_port: local port"
529                                         "must be single in this context");
530                         tmp->port = GETLOPORT(portRange);
531                         r->spool_cnt++;
532                         /* Point to the next possible cfg_spool. */
533                         spool_buf = &spool_buf[SOF_SPOOL];
534                         sep = strtok(NULL, ", ");
535                 }
536         }
537         return (space);
538
539 nospace:
540         errx(EX_DATAERR, "redirect_port: buf is too small\n");
541 }
542
543 int
544 setup_redir_proto(char *spool_buf, int len, int *_ac, char ***_av)
545 {
546         struct protoent *protoent;
547         struct cfg_redir *r;
548         int ac, i, space;
549         char **av;
550
551         i=0;
552         av = *_av;
553         ac = *_ac;
554         if (len >= SOF_REDIR) {
555                 r = (struct cfg_redir *)spool_buf;
556                 /* Skip cfg_redir at beginning of buf. */
557                 spool_buf = &spool_buf[SOF_REDIR];
558                 space = SOF_REDIR;
559                 len -= SOF_REDIR;
560         } else {
561                 goto nospace;
562         }
563         r->mode = REDIR_PROTO;
564         /*
565          * Extract protocol.
566          */
567         if (ac == 0)
568                 errx(EX_DATAERR, "redirect_proto: missing protocol");
569
570         protoent = getprotobyname(*av);
571         if (protoent == NULL)
572                 errx(EX_DATAERR, "redirect_proto: unknown protocol %s", *av);
573         else
574                 r->proto = protoent->p_proto;
575
576         INC_ARGCV();
577
578         /*
579          * Extract local address.
580          */
581         if (ac == 0)
582                 errx(EX_DATAERR, "redirect_proto: missing local address");
583         else
584                 str2addr(*av, &r->laddr);
585         INC_ARGCV();
586
587         /*
588          * Extract optional public address.
589          */
590         if (ac == 0) {
591                 r->paddr.s_addr = INADDR_ANY;
592                 r->raddr.s_addr = INADDR_ANY;
593         } else {
594                 /* see above in setup_redir_port() */
595                 if (!isalpha(**av)) {
596                         str2addr(*av, &r->paddr);
597                         INC_ARGCV();
598
599                         /*
600                          * Extract optional remote address.
601                          */
602                         /* see above in setup_redir_port() */
603                         if (ac != 0 && !isalpha(**av)) {
604                                 str2addr(*av, &r->raddr);
605                                 INC_ARGCV();
606                         }
607                 }
608         }
609         return (space);
610
611 nospace:
612         errx(EX_DATAERR, "redirect_proto: buf is too small\n");
613 }
614
615 int
616 str2proto(const char* str)
617 {
618         if (!strcmp (str, "tcp"))
619                 return IPPROTO_TCP;
620         if (!strcmp (str, "udp"))
621                 return IPPROTO_UDP;
622         errx (EX_DATAERR, "unknown protocol %s. Expected tcp or udp", str);
623 }
624
625 int
626 str2addr_portrange (const char* str, struct in_addr* addr,
627         char* proto, port_range *portRange)
628 {
629         char*   ptr;
630
631         ptr = strchr (str, ':');
632         if (!ptr)
633                 errx (EX_DATAERR, "%s is missing port number", str);
634
635         *ptr = '\0';
636         ++ptr;
637
638         str2addr (str, addr);
639         return str2portrange (ptr, proto, portRange);
640 }
641
642 /*
643  * Search for interface with name "ifn", and fill n accordingly:
644  *
645  * n->ip                ip address of interface "ifn"
646  * n->if_name copy of interface name "ifn"
647  */
648 void
649 set_addr_dynamic(const char *ifn, struct cfg_nat *n)
650 {
651         struct if_msghdr *ifm;
652         struct ifa_msghdr *ifam;
653         struct sockaddr_dl *sdl;
654         struct sockaddr_in *sin;
655         char *buf, *lim, *next;
656         size_t needed;
657         int mib[6];
658         int ifIndex, ifMTU;
659
660         mib[0] = CTL_NET;
661         mib[1] = PF_ROUTE;
662         mib[2] = 0;
663         mib[3] = AF_INET;
664         mib[4] = NET_RT_IFLIST;
665         mib[5] = 0;
666
667         /*
668          * Get interface data.
669          */
670         if (sysctl(mib, 6, NULL, &needed, NULL, 0) == -1)
671                 err(1, "iflist-sysctl-estimate");
672         if ((buf = malloc(needed)) == NULL)
673                 errx(1, "malloc failed");
674         if (sysctl(mib, 6, buf, &needed, NULL, 0) == -1)
675                 err(1, "iflist-sysctl-get");
676         lim = buf + needed;
677         /*
678          * Loop through interfaces until one with
679          * given name is found. This is done to
680          * find correct interface index for routing
681          * message processing.
682          */
683         ifIndex = 0;
684         next = buf;
685         while (next < lim) {
686                 ifm = (struct if_msghdr *)next;
687                 next += ifm->ifm_msglen;
688                 if (ifm->ifm_version != RTM_VERSION) {
689                         if (verbose)
690                                 warnx("routing message version %d "
691                                         "not understood", ifm->ifm_version);
692                         continue;
693                 }
694                 if (ifm->ifm_type == RTM_IFINFO) {
695                         sdl = (struct sockaddr_dl *)(ifm + 1);
696                         if (strlen(ifn) == sdl->sdl_nlen &&
697                                 strncmp(ifn, sdl->sdl_data,
698                                         sdl->sdl_nlen) == 0) {
699                                 ifIndex = ifm->ifm_index;
700                                 ifMTU = ifm->ifm_data.ifi_mtu;
701                                 break;
702                         }
703                 }
704         }
705         if (!ifIndex)
706                 errx(1, "unknown interface name %s", ifn);
707         /*
708          * Get interface address.
709          */
710         sin = NULL;
711         while (next < lim) {
712                 ifam = (struct ifa_msghdr *)next;
713                 next += ifam->ifam_msglen;
714                 if (ifam->ifam_version != RTM_VERSION) {
715                         if (verbose)
716                                 warnx("routing message version %d "
717                                         "not understood", ifam->ifam_version);
718                         continue;
719                 }
720                 if (ifam->ifam_type != RTM_NEWADDR)
721                         break;
722                 if (ifam->ifam_addrs & RTA_IFA) {
723                         int i;
724                         char *cp = (char *)(ifam + 1);
725
726                         for (i = 1; i < RTA_IFA; i <<= 1) {
727                                 if (ifam->ifam_addrs & i)
728                                         cp += SA_SIZE((struct sockaddr *)cp);
729                         }
730                         if (((struct sockaddr *)cp)->sa_family == AF_INET) {
731                                 sin = (struct sockaddr_in *)cp;
732                                 break;
733                         }
734                 }
735         }
736         if (sin == NULL)
737                 errx(1, "%s: cannot get interface address", ifn);
738
739         n->ip = sin->sin_addr;
740         strncpy(n->if_name, ifn, IF_NAMESIZE);
741
742         free(buf);
743 }
744
745 int
746 setup_redir_addr(char *spool_buf, int len, int *_ac, char ***_av)
747 {
748         struct cfg_redir *r;
749         struct cfg_spool *tmp;
750         char **av, *sep;
751         char tmp_spool_buf[NAT_BUF_LEN];
752         int ac, i, space, lsnat;
753
754         i=0;
755         av = *_av;
756         ac = *_ac;
757         space = 0;
758         lsnat = 0;
759         if (len >= SOF_REDIR) {
760                 r = (struct cfg_redir *)spool_buf;
761                 /* Skip cfg_redir at beginning of buf. */
762                 spool_buf = &spool_buf[SOF_REDIR];
763                 space = SOF_REDIR;
764                 len -= SOF_REDIR;
765         } else {
766                 goto nospace;
767         }
768
769         r->mode = REDIR_ADDR;
770         /* Extract local address. */
771         if (ac == 0)
772                 errx(EX_DATAERR, "redirect_addr: missing local address");
773
774         sep = strchr(*av, ',');
775         if (sep) {              /* LSNAT redirection syntax. */
776                 r->laddr.s_addr = INADDR_NONE;
777                 /* Preserve av, copy spool servers to tmp_spool_buf. */
778                 strncpy(tmp_spool_buf, *av, strlen(*av)+1);
779                 lsnat = 1;
780         } else {
781                 str2addr(*av, &r->laddr);
782         }
783         INC_ARGCV();
784
785         /* Extract public address. */
786         if (ac == 0)
787                 errx(EX_DATAERR, "redirect_addr: missing public address");
788
789         str2addr(*av, &r->paddr);
790         INC_ARGCV();
791
792         /* Setup LSNAT server pool. */
793         if (sep) {
794                 sep = strtok(tmp_spool_buf, ", ");
795                 while (sep != NULL) {
796                         tmp = (struct cfg_spool *)spool_buf;
797                         if (len < SOF_SPOOL)
798                                 goto nospace;
799
800                         len -= SOF_SPOOL;
801                         space += SOF_SPOOL;
802                         str2addr(sep, &tmp->addr);
803                         tmp->port = ~0;
804                         r->spool_cnt++;
805                         /* Point to the next possible cfg_spool. */
806                         spool_buf = &spool_buf[SOF_SPOOL];
807                         sep = strtok(NULL, ", ");
808                 }
809         }
810         return(space);
811
812 nospace:
813         errx(EX_DATAERR, "redirect_addr: buf is too small\n");
814 }
815
816 void
817 str2addr(const char* str, struct in_addr* addr)
818 {
819         struct hostent* hp;
820
821         if (inet_aton (str, addr))
822                 return;
823
824         hp = gethostbyname (str);
825         if (!hp)
826                 errx (1, "unknown host %s", str);
827
828         memcpy (addr, hp->h_addr, sizeof (struct in_addr));
829 }
830
831 int
832 str2portrange(const char* str, const char* proto, port_range *portRange)
833 {
834         struct servent* sp;
835         char*   sep;
836         char*   end;
837         u_short loPort, hiPort;
838
839         /* First see if this is a service, return corresponding port if so. */
840         sp = getservbyname (str, proto);
841         if (sp) {
842                 SETLOPORT(*portRange, ntohs(sp->s_port));
843                 SETNUMPORTS(*portRange, 1);
844                 return 0;
845         }
846
847         /* Not a service, see if it's a single port or port range. */
848         sep = strchr (str, '-');
849         if (sep == NULL) {
850                 SETLOPORT(*portRange, strtol(str, &end, 10));
851                 if (end != str) {
852                         /* Single port. */
853                         SETNUMPORTS(*portRange, 1);
854                         return 0;
855                 }
856
857                 /* Error in port range field. */
858                 errx (EX_DATAERR, "%s/%s: unknown service", str, proto);
859         }
860
861         /* Port range, get the values and sanity check. */
862         sscanf (str, "%hu-%hu", &loPort, &hiPort);
863         SETLOPORT(*portRange, loPort);
864         SETNUMPORTS(*portRange, 0);     /* Error by default */
865         if (loPort <= hiPort)
866                 SETNUMPORTS(*portRange, hiPort - loPort + 1);
867
868         if (GETNUMPORTS(*portRange) == 0)
869                 errx (EX_DATAERR, "invalid port range %s", str);
870
871         return 0;
872 }
873
874 void
875 nat_delete_config(int ac, char *av[])
876 {
877         NEXT_ARG;
878         int i = 0;
879         if (ac > 0) {
880                 i = atoi(*av);
881         }
882         if (do_set_x(IP_FW_NAT_DEL, &i, sizeof(i)) == -1)
883                 errx(EX_USAGE, "NAT %d in use or not exists", i);
884 }
885
886 void
887 nat_show_state(int ac, char **av)
888 {
889         int nbytes, nalloc;
890         int nat_id;
891         uint8_t *data;
892
893         nalloc = 1024;
894         data = NULL;
895
896         NEXT_ARG;
897         if (ac == 0)
898                 nat_id = 0;
899         else
900                 nat_id = strtoul(*av, NULL, 10);
901
902         nbytes = nalloc;
903         while (nbytes >= nalloc) {
904                 nalloc = nalloc * 2;
905                 nbytes = nalloc;
906                 if ((data = realloc(data, nbytes)) == NULL) {
907                         err(EX_OSERR, "realloc");
908                 }
909                 memcpy(data, &nat_id, sizeof(int));
910                 if (do_get_x(IP_FW_NAT_GET_RECORD, data, &nbytes) < 0) {
911                         err(EX_OSERR, "do_get_x(IP_FW_NAT_GET_RECORD)");
912                 }
913         }
914         if (nbytes == 0)
915                 exit(EX_OK);
916         struct ipfw_ioc_nat_state *nat_state;
917         nat_state =(struct ipfw_ioc_nat_state *)data;
918         int count = nbytes / sizeof( struct ipfw_ioc_nat_state);
919         int i, uptime_sec;
920         uptime_sec = get_kern_boottime();
921         for (i = 0; i < count; i ++) {
922                 printf("#%d ", nat_state->cpuid);
923                 printf("%s:%hu => ",inet_ntoa(nat_state->src_addr),
924                                 htons(nat_state->src_port));
925                 printf("%s:%hu",inet_ntoa(nat_state->alias_addr),
926                                 htons(nat_state->alias_port));
927                 printf(" -> %s:%hu ",inet_ntoa(nat_state->dst_addr),
928                                 htons(nat_state->dst_port));
929                 if (do_time == 1) {
930                         char timestr[30];
931                         time_t t = _long_to_time(uptime_sec +
932                                         nat_state->timestamp);
933                         strcpy(timestr, ctime(&t));
934                         *strchr(timestr, '\n') = '\0';
935                         printf("%s ", timestr);
936                 } else if (do_time == 2) {
937                         printf( "%10u ", uptime_sec + nat_state->timestamp);
938                 }
939                 struct protoent *pe = getprotobynumber(nat_state->link_type);
940                 printf("%s ", pe->p_name);
941                 printf(" %s", nat_state->is_outgoing? "out": "in");
942                 printf("\n");
943                 nat_state++;
944         }
945 }
946
947 int
948 get_kern_boottime(void)
949 {
950         struct timeval boottime;
951         size_t size;
952         int mib[2];
953         mib[0] = CTL_KERN;
954         mib[1] = KERN_BOOTTIME;
955         size = sizeof(boottime);
956         if (sysctl(mib, 2, &boottime, &size, NULL, 0) != -1 &&
957                         boottime.tv_sec != 0) {
958                 return boottime.tv_sec;
959         }
960         return -1;
961 }
962
963 void
964 nat_flush()
965 {
966         int cmd = IP_FW_NAT_FLUSH;
967         if (!do_force) {
968                 int c;
969
970                 printf("Are you sure? [yn] ");
971                 fflush(stdout);
972                 do {
973                         c = toupper(getc(stdin));
974                         while (c != '\n' && getc(stdin) != '\n')
975                                 if (feof(stdin))
976                                         return; /* and do not flush */
977                 } while (c != 'Y' && c != 'N');
978                 if (c == 'N')   /* user said no */
979                         return;
980         }
981         if (do_set_x(cmd, NULL, 0) < 0 ) {
982                 errx(EX_USAGE, "NAT configuration in use");
983         }
984         if (!do_quiet) {
985                 printf("Flushed all nat configurations");
986         }
987 }
988
989 void
990 nat_main(int ac, char **av)
991 {
992         if (!strncmp(*av, "config", strlen(*av))) {
993                 nat_config(ac, av);
994         } else if (!strncmp(*av, "flush", strlen(*av))) {
995                 nat_flush();
996         } else if (!strncmp(*av, "show", strlen(*av)) ||
997                         !strncmp(*av, "list", strlen(*av))) {
998                 if (ac > 2 && isdigit(*(av[1]))) {
999                         char *p = av[1];
1000                         av[1] = av[2];
1001                         av[2] = p;
1002                 }
1003                 NEXT_ARG;
1004                 if (!strncmp(*av, "config", strlen(*av))) {
1005                         nat_show(ac, av);
1006                 } else if (!strncmp(*av, "state", strlen(*av))) {
1007                         nat_show_state(ac,av);
1008                 } else {
1009                         errx(EX_USAGE,
1010                                         "bad nat show command `%s'", *av);
1011                 }
1012         } else if (!strncmp(*av, "delete", strlen(*av))) {
1013                 nat_delete_config(ac, av);
1014         } else {
1015                 errx(EX_USAGE, "bad ipfw nat command `%s'", *av);
1016         }
1017 }