Use system's RT_ROUNDUP and RT_ADVANCE macros instead of local copies.
[dragonfly.git] / sbin / natd / natd.c
1 /*
2  * natd - Network Address Translation Daemon for FreeBSD.
3  *
4  * This software is provided free of charge, with no
5  * warranty of any kind, either expressed or implied.
6  * Use at your own risk.
7  *
8  * You may copy, modify and distribute this software (natd.c) freely.
9  *
10  * Ari Suutari <suutari@iki.fi>
11  *
12  * $FreeBSD: src/sbin/natd/natd.c,v 1.25.2.5 2002/02/01 09:18:32 ru Exp $
13  */
14
15 #define SYSLOG_NAMES
16
17 #include <sys/param.h>
18 #include <sys/socket.h>
19 #include <sys/sysctl.h>
20 #include <sys/time.h>
21
22 #include <netinet/in.h>
23 #include <netinet/in_systm.h>
24 #include <netinet/ip.h>
25 #include <netinet/tcp.h>
26 #include <netinet/udp.h>
27 #include <netinet/ip_icmp.h>
28 #include <net/if.h>
29 #include <net/if_dl.h>
30 #include <net/route.h>
31 #include <arpa/inet.h>
32
33 #include <alias.h>
34 #include <ctype.h>
35 #include <err.h>
36 #include <errno.h>
37 #include <netdb.h>
38 #include <signal.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <syslog.h>
43 #include <unistd.h>
44
45 #include "natd.h"
46
47 /*
48  * Default values for input and output
49  * divert socket ports.
50  */
51
52 #define DEFAULT_SERVICE "natd"
53
54 /*
55  * Definition of a port range, and macros to deal with values.
56  * FORMAT:  HI 16-bits == first port in range, 0 == all ports.
57  *          LO 16-bits == number of ports in range
58  * NOTES:   - Port values are not stored in network byte order.
59  */
60
61 typedef u_long port_range;
62
63 #define GETLOPORT(x)     ((x) >> 0x10)
64 #define GETNUMPORTS(x)   ((x) & 0x0000ffff)
65 #define GETHIPORT(x)     (GETLOPORT((x)) + GETNUMPORTS((x)))
66
67 /* Set y to be the low-port value in port_range variable x. */
68 #define SETLOPORT(x,y)   ((x) = ((x) & 0x0000ffff) | ((y) << 0x10))
69
70 /* Set y to be the number of ports in port_range variable x. */
71 #define SETNUMPORTS(x,y) ((x) = ((x) & 0xffff0000) | (y))
72
73 /*
74  * Function prototypes.
75  */
76
77 static void     DoAliasing(int, int);
78 static void     DaemonMode(void);
79 static void     HandleRoutingInfo(int);
80 static void     Usage(void);
81 static char*    FormatPacket(struct ip *);
82 static void     PrintPacket(struct ip *);
83 static void     SyslogPacket(struct ip *, int, const char *);
84 static void     SetAliasAddressFromIfName(const char *);
85 static void     InitiateShutdown(int);
86 static void     Shutdown(int);
87 static void     RefreshAddr(int);
88 static void     ParseOption(const char *, const char *);
89 static void     ReadConfigFile(const char *);
90 static void     SetupPortRedirect(const char *);
91 static void     SetupProtoRedirect(const char *);
92 static void     SetupAddressRedirect(const char *);
93 static void     StrToAddr(const char *, struct in_addr *);
94 static u_short  StrToPort(const char *, const char *);
95 static int      StrToPortRange(const char *, const char *, port_range *);
96 static int      StrToProto(const char *);
97 static int      StrToAddrAndPortRange(const char *, struct in_addr *, char *, port_range *);
98 static void     ParseArgs(int, char **);
99 static void     SetupPunchFW(const char *);
100
101 /*
102  * Globals.
103  */
104
105 static  int                     verbose;
106 static  int                     background;
107 static  volatile sig_atomic_t   running;
108 static  volatile sig_atomic_t   assignAliasAddr;
109 static  char*                   ifName;
110 static  int                     ifIndex;
111 static  u_short                 inPort;
112 static  u_short                 outPort;
113 static  u_short                 inOutPort;
114 static  struct in_addr          aliasAddr;
115 static  int                     dynamicMode;
116 static  int                     ifMTU;
117 static  int                     aliasOverhead;
118 static  int                     icmpSock;
119 static  int                     dropIgnoredIncoming;
120 static  int                     logDropped;
121 static  int                     logFacility;
122 static  int                     logIpfwDenied;
123 static  int                     exitDelay;
124
125 int
126 main(int argc, char **argv)
127 {
128         int                     divertIn;
129         int                     divertOut;
130         int                     divertInOut;
131         int                     routeSock;
132         struct sockaddr_in      addr;
133         fd_set                  readMask;
134         int                     fdMax;
135         struct sigaction        sa;
136 /*
137  * Initialize packet aliasing software.
138  * Done already here to be able to alter option bits
139  * during command line and configuration file processing.
140  */
141         PacketAliasInit();
142 /*
143  * Parse options.
144  */
145         inPort                  = 0;
146         outPort                 = 0;
147         verbose                 = 0;
148         inOutPort               = 0;
149         ifName                  = NULL;
150         ifMTU                   = -1;
151         background              = 0;
152         running                 = 1;
153         assignAliasAddr         = 0;
154         aliasAddr.s_addr        = INADDR_NONE;
155         aliasOverhead           = 12;
156         dynamicMode             = 0;
157         logDropped              = 0;
158         logFacility             = LOG_DAEMON;
159         logIpfwDenied           = -1;
160         exitDelay               = EXIT_DELAY;
161
162         ParseArgs(argc, argv);
163 /*
164  * Log ipfw(8) denied packets by default in verbose mode.
165  */
166         if (logIpfwDenied == -1)
167                 logIpfwDenied = verbose;
168 /*
169  * Open syslog channel.
170  */
171         openlog("natd", LOG_CONS | LOG_PID | (verbose ? LOG_PERROR : 0),
172                 logFacility);
173 /*
174  * Check that valid aliasing address has been given.
175  */
176         if (aliasAddr.s_addr == INADDR_NONE && ifName == NULL)
177                 errx(1, "aliasing address not given");
178
179         if (aliasAddr.s_addr != INADDR_NONE && ifName != NULL)
180                 errx(1, "both alias address and interface "
181                         "name are not allowed");
182 /*
183  * Check that valid port number is known.
184  */
185         if (inPort != 0 || outPort != 0)
186                 if (inPort == 0 || outPort == 0)
187                         errx(1, "both input and output ports are required");
188
189         if (inPort == 0 && outPort == 0 && inOutPort == 0)
190                 ParseOption("port", DEFAULT_SERVICE);
191
192 /*
193  * Check if ignored packets should be dropped.
194  */
195         dropIgnoredIncoming = PacketAliasSetMode(0, 0);
196         dropIgnoredIncoming &= PKT_ALIAS_DENY_INCOMING;
197 /*
198  * Create divert sockets. Use only one socket if -p was specified
199  * on command line. Otherwise, create separate sockets for
200  * outgoing and incoming connnections.
201  */
202         if (inOutPort) {
203                 divertInOut = socket(PF_INET, SOCK_RAW, IPPROTO_DIVERT);
204                 if (divertInOut == -1)
205                         Quit("Unable to create divert socket.");
206
207                 divertIn  = -1;
208                 divertOut = -1;
209 /*
210  * Bind socket.
211  */
212
213                 addr.sin_family         = AF_INET;
214                 addr.sin_addr.s_addr    = INADDR_ANY;
215                 addr.sin_port           = inOutPort;
216
217                 if (bind(divertInOut,
218                          (struct sockaddr *)&addr,
219                          sizeof addr) == -1)
220                         Quit("Unable to bind divert socket.");
221         } else {
222                 divertIn = socket(PF_INET, SOCK_RAW, IPPROTO_DIVERT);
223                 if (divertIn == -1)
224                         Quit("Unable to create incoming divert socket.");
225
226                 divertOut = socket(PF_INET, SOCK_RAW, IPPROTO_DIVERT);
227                 if (divertOut == -1)
228                         Quit("Unable to create outgoing divert socket.");
229
230                 divertInOut = -1;
231
232 /*
233  * Bind divert sockets.
234  */
235
236                 addr.sin_family         = AF_INET;
237                 addr.sin_addr.s_addr    = INADDR_ANY;
238                 addr.sin_port           = inPort;
239
240                 if (bind(divertIn,
241                          (struct sockaddr *)&addr,
242                          sizeof addr) == -1)
243                         Quit("Unable to bind incoming divert socket.");
244
245                 addr.sin_family         = AF_INET;
246                 addr.sin_addr.s_addr    = INADDR_ANY;
247                 addr.sin_port           = outPort;
248
249                 if (bind(divertOut,
250                          (struct sockaddr *)&addr,
251                          sizeof addr) == -1)
252                         Quit("Unable to bind outgoing divert socket.");
253         }
254 /*
255  * Create routing socket if interface name specified and in dynamic mode.
256  */
257         routeSock = -1;
258         if (ifName) {
259                 if (dynamicMode) {
260                         routeSock = socket(PF_ROUTE, SOCK_RAW, 0);
261                         if (routeSock == -1)
262                                 Quit("Unable to create routing info socket.");
263
264                         assignAliasAddr = 1;
265                 } else
266                         SetAliasAddressFromIfName(ifName);
267         }
268 /*
269  * Create socket for sending ICMP messages.
270  */
271         icmpSock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
272         if (icmpSock == -1)
273                 Quit("Unable to create ICMP socket.");
274
275 /*
276  * And disable reads for the socket, otherwise it slowly fills
277  * up with received icmps which we do not use.
278  */
279         shutdown(icmpSock, SHUT_RD);
280
281 /*
282  * Become a daemon unless verbose mode was requested.
283  */
284         if (!verbose)
285                 DaemonMode();
286 /*
287  * Catch signals to manage shutdown and
288  * refresh of interface address.
289  */
290         sa.sa_flags = 0;
291         sigemptyset(&sa.sa_mask);
292         if (exitDelay)
293                 sa.sa_handler = InitiateShutdown;
294         else
295                 sa.sa_handler = Shutdown;
296         sigaction(SIGTERM, &sa, NULL);
297         sa.sa_handler = RefreshAddr;
298         sigaction(SIGHUP, &sa, NULL);
299 /*
300  * Set alias address if it has been given.
301  */
302         if (aliasAddr.s_addr != INADDR_NONE)
303                 PacketAliasSetAddress(aliasAddr);
304 /*
305  * We need largest descriptor number for select.
306  */
307
308         fdMax = -1;
309
310         if (divertIn > fdMax)
311                 fdMax = divertIn;
312
313         if (divertOut > fdMax)
314                 fdMax = divertOut;
315
316         if (divertInOut > fdMax)
317                 fdMax = divertInOut;
318
319         if (routeSock > fdMax)
320                 fdMax = routeSock;
321
322         while (running) {
323                 if (divertInOut != -1 && !ifName) {
324 /*
325  * When using only one socket, just call
326  * DoAliasing repeatedly to process packets.
327  */
328                         DoAliasing(divertInOut, DONT_KNOW);
329                         continue;
330                 }
331 /*
332  * Build read mask from socket descriptors to select.
333  */
334                 FD_ZERO(&readMask);
335 /*
336  * Check if new packets are available.
337  */
338                 if (divertIn != -1)
339                         FD_SET(divertIn, &readMask);
340
341                 if (divertOut != -1)
342                         FD_SET(divertOut, &readMask);
343
344                 if (divertInOut != -1)
345                         FD_SET(divertInOut, &readMask);
346 /*
347  * Routing info is processed always.
348  */
349                 if (routeSock != -1)
350                         FD_SET(routeSock, &readMask);
351
352                 if (select(fdMax + 1,
353                            &readMask,
354                            NULL,
355                            NULL,
356                            NULL) == -1) {
357                         if (errno == EINTR)
358                                 continue;
359
360                         Quit("Select failed.");
361                 }
362
363                 if (divertIn != -1)
364                         if (FD_ISSET(divertIn, &readMask))
365                                 DoAliasing(divertIn, INPUT);
366
367                 if (divertOut != -1)
368                         if (FD_ISSET(divertOut, &readMask))
369                                 DoAliasing(divertOut, OUTPUT);
370
371                 if (divertInOut != -1)
372                         if (FD_ISSET(divertInOut, &readMask))
373                                 DoAliasing(divertInOut, DONT_KNOW);
374
375                 if (routeSock != -1)
376                         if (FD_ISSET(routeSock, &readMask))
377                                 HandleRoutingInfo(routeSock);
378         }
379
380         if (background)
381                 unlink(PIDFILE);
382
383         return 0;
384 }
385
386 static void
387 DaemonMode(void)
388 {
389         FILE*   pidFile;
390
391         daemon(0, 0);
392         background = 1;
393
394         pidFile = fopen(PIDFILE, "w");
395         if (pidFile) {
396                 fprintf(pidFile, "%d\n", getpid());
397                 fclose(pidFile);
398         }
399 }
400
401 static void
402 ParseArgs(int argc, char **argv)
403 {
404         int             arg;
405         char*           opt;
406         char            parmBuf[256];
407         int             len; /* bounds checking */
408
409         for (arg = 1; arg < argc; arg++) {
410                 opt  = argv[arg];
411                 if (*opt != '-') {
412                         warnx("invalid option %s", opt);
413                         Usage();
414                 }
415
416                 parmBuf[0] = '\0';
417                 len = 0;
418
419                 while (arg < argc - 1) {
420                         if (argv[arg + 1][0] == '-')
421                                 break;
422
423                         if (len) {
424                                 strncat(parmBuf, " ", sizeof(parmBuf) - (len + 1));
425                                 len += strlen(parmBuf + len);
426                         }
427
428                         ++arg;
429                         strncat(parmBuf, argv[arg], sizeof(parmBuf) - (len + 1));
430                         len += strlen(parmBuf + len);
431
432                 }
433
434                 ParseOption(opt + 1, (len ? parmBuf : NULL));
435
436         }
437 }
438
439 static void
440 DoAliasing(int fd, int direction)
441 {
442         int                     bytes;
443         int                     origBytes;
444         char                    buf[IP_MAXPACKET];
445         struct sockaddr_in      addr;
446         int                     wrote;
447         int                     status;
448         int                     addrSize;
449         struct ip*              ip;
450         char                    msgBuf[80];
451
452         if (assignAliasAddr) {
453                 SetAliasAddressFromIfName(ifName);
454                 assignAliasAddr = 0;
455         }
456 /*
457  * Get packet from socket.
458  */
459         addrSize  = sizeof addr;
460         origBytes = recvfrom(fd,
461                              buf,
462                              sizeof buf,
463                              0,
464                              (struct sockaddr *)&addr,
465                              &addrSize);
466
467         if (origBytes == -1) {
468                 if (errno != EINTR)
469                         Warn("read from divert socket failed");
470
471                 return;
472         }
473 /*
474  * This is a IP packet.
475  */
476         ip = (struct ip *)buf;
477         if (direction == DONT_KNOW) {
478                 if (addr.sin_addr.s_addr == INADDR_ANY)
479                         direction = OUTPUT;
480                 else
481                         direction = INPUT;
482         }
483
484         if (verbose) {
485 /*
486  * Print packet direction and protocol type.
487  */
488                 printf(direction == OUTPUT ? "Out " : "In  ");
489
490                 switch (ip->ip_p) {
491                 case IPPROTO_TCP:
492                         printf("[TCP]  ");
493                         break;
494
495                 case IPPROTO_UDP:
496                         printf("[UDP]  ");
497                         break;
498
499                 case IPPROTO_ICMP:
500                         printf("[ICMP] ");
501                         break;
502
503                 default:
504                         printf("[%d]    ", ip->ip_p);
505                         break;
506                 }
507 /*
508  * Print addresses.
509  */
510                 PrintPacket(ip);
511         }
512
513         if (direction == OUTPUT) {
514 /*
515  * Outgoing packets. Do aliasing.
516  */
517                 PacketAliasOut(buf, IP_MAXPACKET);
518         } else {
519 /*
520  * Do aliasing.
521  */
522                 status = PacketAliasIn(buf, IP_MAXPACKET);
523                 if (status == PKT_ALIAS_IGNORED &&
524                     dropIgnoredIncoming) {
525                         if (verbose)
526                                 printf(" dropped.\n");
527
528                         if (logDropped)
529                                 SyslogPacket(ip, LOG_WARNING, "denied");
530
531                         return;
532                 }
533         }
534 /*
535  * Length might have changed during aliasing.
536  */
537         bytes = ntohs(ip->ip_len);
538 /*
539  * Update alias overhead size for outgoing packets.
540  */
541         if (direction == OUTPUT &&
542             bytes - origBytes > aliasOverhead)
543                 aliasOverhead = bytes - origBytes;
544
545         if (verbose) {
546 /*
547  * Print addresses after aliasing.
548  */
549                 printf(" aliased to\n");
550                 printf("           ");
551                 PrintPacket(ip);
552                 printf("\n");
553         }
554
555 /*
556  * Put packet back for processing.
557  */
558         wrote = sendto(fd,
559                        buf,
560                        bytes,
561                        0,
562                        (struct sockaddr *)&addr,
563                        sizeof addr);
564
565         if (wrote != bytes) {
566                 if (errno == EMSGSIZE) {
567                         if (direction == OUTPUT &&
568                             ifMTU != -1)
569                                 SendNeedFragIcmp(icmpSock,
570                                                  (struct ip *)buf,
571                                                  ifMTU - aliasOverhead);
572                 } else if (errno == EACCES && logIpfwDenied) {
573                         sprintf(msgBuf, "failed to write packet back");
574                         Warn(msgBuf);
575                 }
576         }
577 }
578
579 static void
580 HandleRoutingInfo(int fd)
581 {
582         int                     bytes;
583         struct if_msghdr        ifMsg;
584 /*
585  * Get packet from socket.
586  */
587         bytes = read(fd, &ifMsg, sizeof ifMsg);
588         if (bytes == -1) {
589                 Warn("read from routing socket failed");
590                 return;
591         }
592
593         if (ifMsg.ifm_version != RTM_VERSION) {
594                 Warn("unexpected packet read from routing socket");
595                 return;
596         }
597
598         if (verbose)
599                 printf("Routing message %#x received.\n", ifMsg.ifm_type);
600
601         if ((ifMsg.ifm_type == RTM_NEWADDR || ifMsg.ifm_type == RTM_IFINFO) &&
602             ifMsg.ifm_index == ifIndex) {
603                 if (verbose)
604                         printf("Interface address/MTU has probably changed.\n");
605                 assignAliasAddr = 1;
606         }
607 }
608
609 static void
610 PrintPacket(struct ip *ip)
611 {
612         printf("%s", FormatPacket(ip));
613 }
614
615 static void
616 SyslogPacket(struct ip *ip, int priority, const char *label)
617 {
618         syslog(priority, "%s %s", label, FormatPacket(ip));
619 }
620
621 static char*
622 FormatPacket(struct ip *ip)
623 {
624         static char     buf[256];
625         struct tcphdr*  tcphdr;
626         struct udphdr*  udphdr;
627         struct icmp*    icmphdr;
628         char            src[20];
629         char            dst[20];
630
631         strcpy(src, inet_ntoa(ip->ip_src));
632         strcpy(dst, inet_ntoa(ip->ip_dst));
633
634         switch (ip->ip_p) {
635         case IPPROTO_TCP:
636                 tcphdr = (struct tcphdr *)((char *)ip + (ip->ip_hl << 2));
637                 sprintf(buf, "[TCP] %s:%d -> %s:%d",
638                              src,
639                              ntohs(tcphdr->th_sport),
640                              dst,
641                              ntohs(tcphdr->th_dport));
642                 break;
643
644         case IPPROTO_UDP:
645                 udphdr = (struct udphdr *)((char *)ip + (ip->ip_hl << 2));
646                 sprintf(buf, "[UDP] %s:%d -> %s:%d",
647                              src,
648                              ntohs(udphdr->uh_sport),
649                              dst,
650                              ntohs(udphdr->uh_dport));
651                 break;
652
653         case IPPROTO_ICMP:
654                 icmphdr = (struct icmp *)((char *)ip + (ip->ip_hl << 2));
655                 sprintf(buf, "[ICMP] %s -> %s %u(%u)",
656                              src,
657                              dst,
658                              icmphdr->icmp_type,
659                              icmphdr->icmp_code);
660                 break;
661
662         default:
663                 sprintf(buf, "[%d] %s -> %s ", ip->ip_p, src, dst);
664                 break;
665         }
666
667         return buf;
668 }
669
670 static void
671 SetAliasAddressFromIfName(const char *ifn)
672 {
673         size_t needed;
674         int mib[6];
675         char *buf, *lim, *next;
676         struct if_msghdr *ifm;
677         struct ifa_msghdr *ifam;
678         struct sockaddr_dl *s_dl;
679         struct sockaddr_in *s_in;
680
681         mib[0] = CTL_NET;
682         mib[1] = PF_ROUTE;
683         mib[2] = 0;
684         mib[3] = AF_INET;       /* Only IP addresses please */
685         mib[4] = NET_RT_IFLIST;
686         mib[5] = 0;             /* ifIndex??? */
687 /*
688  * Get interface data.
689  */
690         if (sysctl(mib, 6, NULL, &needed, NULL, 0) == -1)
691                 err(1, "iflist-sysctl-estimate");
692         if ((buf = malloc(needed)) == NULL)
693                 errx(1, "malloc failed");
694         if (sysctl(mib, 6, buf, &needed, NULL, 0) == -1)
695                 err(1, "iflist-sysctl-get");
696         lim = buf + needed;
697 /*
698  * Loop through interfaces until one with
699  * given name is found. This is done to
700  * find correct interface index for routing
701  * message processing.
702  */
703         ifIndex = 0;
704         next = buf;
705         while (next < lim) {
706                 ifm = (struct if_msghdr *)next;
707                 next += ifm->ifm_msglen;
708                 if (ifm->ifm_version != RTM_VERSION) {
709                         if (verbose)
710                                 warnx("routing message version %d "
711                                       "not understood", ifm->ifm_version);
712                         continue;
713                 }
714                 if (ifm->ifm_type == RTM_IFINFO) {
715                         s_dl = (struct sockaddr_dl *)(ifm + 1);
716                         if (strlen(ifn) == s_dl->sdl_nlen &&
717                             strncmp(ifn, s_dl->sdl_data, s_dl->sdl_nlen) == 0) {
718                                 ifIndex = ifm->ifm_index;
719                                 ifMTU = ifm->ifm_data.ifi_mtu;
720                                 break;
721                         }
722                 }
723         }
724         if (!ifIndex)
725                 errx(1, "unknown interface name %s", ifn);
726 /*
727  * Get interface address.
728  */
729         s_in = NULL;
730         while (next < lim) {
731                 ifam = (struct ifa_msghdr *)next;
732                 next += ifam->ifam_msglen;
733                 if (ifam->ifam_version != RTM_VERSION) {
734                         if (verbose)
735                                 warnx("routing message version %d "
736                                       "not understood", ifam->ifam_version);
737                         continue;
738                 }
739                 if (ifam->ifam_type != RTM_NEWADDR)
740                         break;
741                 if (ifam->ifam_addrs & RTA_IFA) {
742                         int i;
743                         char *cp = (char *)(ifam + 1);
744
745                         for (i = 1; i < RTA_IFA; i <<= 1)
746                                 if (ifam->ifam_addrs & i)
747                                         RT_ADVANCE(cp, (struct sockaddr *)cp);
748                         if (((struct sockaddr *)cp)->sa_family == AF_INET) {
749                                 s_in = (struct sockaddr_in *)cp;
750                                 break;
751                         }
752                 }
753         }
754         if (s_in == NULL)
755                 errx(1, "%s: cannot get interface address", ifn);
756
757         PacketAliasSetAddress(s_in->sin_addr);
758         syslog(LOG_INFO, "Aliasing to %s, mtu %d bytes",
759                inet_ntoa(s_in->sin_addr), ifMTU);
760
761         free(buf);
762 }
763
764 void
765 Quit(const char *msg)
766 {
767         Warn(msg);
768         exit(1);
769 }
770
771 void
772 Warn(const char *msg)
773 {
774         if (background)
775                 syslog(LOG_ALERT, "%s (%m)", msg);
776         else
777                 warn("%s", msg);
778 }
779
780 static void
781 RefreshAddr(int sig __unused)
782 {
783         if (ifName)
784                 assignAliasAddr = 1;
785 }
786
787 static void
788 InitiateShutdown(int sig __unused)
789 {
790         struct sigaction sa;
791 /*
792  * Start timer to allow kernel gracefully
793  * shutdown existing connections when system
794  * is shut down.
795  */
796         sa.sa_handler = Shutdown;
797         sa.sa_flags = 0;
798         sigemptyset(&sa.sa_mask);
799         sigaction(SIGALRM, &sa, NULL);
800         ualarm(exitDelay*1000, 1000);
801 }
802
803 static void
804 Shutdown(int sig __unused)
805 {
806         running = 0;
807 }
808
809 /*
810  * Different options recognized by this program.
811  */
812
813 enum Option {
814         PacketAliasOption,
815         Verbose,
816         InPort,
817         OutPort,
818         Port,
819         AliasAddress,
820         TargetAddress,
821         InterfaceName,
822         RedirectPort,
823         RedirectProto,
824         RedirectAddress,
825         ConfigFile,
826         DynamicMode,
827         ProxyRule,
828         LogDenied,
829         LogFacility,
830         PunchFW,
831         LogIpfwDenied,
832         ExitDelay
833 };
834
835 enum Param {
836         YesNo,
837         Numeric,
838         String,
839         None,
840         Address,
841         Service
842 };
843
844 /*
845  * Option information structure (used by ParseOption).
846  */
847
848 struct OptionInfo {
849         enum Option             type;
850         int                     packetAliasOpt;
851         enum Param              parm;
852         const char*             parmDescription;
853         const char*             description;
854         const char*             name;
855         const char*             shortName;
856 };
857
858 /*
859  * Table of known options.
860  */
861
862 static struct OptionInfo optionTable[] = {
863         { PacketAliasOption,
864                 PKT_ALIAS_UNREGISTERED_ONLY,
865                 YesNo,
866                 "[yes|no]",
867                 "alias only unregistered addresses",
868                 "unregistered_only",
869                 "u" },
870
871         { PacketAliasOption,
872                 PKT_ALIAS_LOG,
873                 YesNo,
874                 "[yes|no]",
875                 "enable logging",
876                 "log",
877                 "l" },
878
879         { PacketAliasOption,
880                 PKT_ALIAS_PROXY_ONLY,
881                 YesNo,
882                 "[yes|no]",
883                 "proxy only",
884                 "proxy_only",
885                 NULL },
886
887         { PacketAliasOption,
888                 PKT_ALIAS_REVERSE,
889                 YesNo,
890                 "[yes|no]",
891                 "operate in reverse mode",
892                 "reverse",
893                 NULL },
894
895         { PacketAliasOption,
896                 PKT_ALIAS_DENY_INCOMING,
897                 YesNo,
898                 "[yes|no]",
899                 "allow incoming connections",
900                 "deny_incoming",
901                 "d" },
902
903         { PacketAliasOption,
904                 PKT_ALIAS_USE_SOCKETS,
905                 YesNo,
906                 "[yes|no]",
907                 "use sockets to inhibit port conflict",
908                 "use_sockets",
909                 "s" },
910
911         { PacketAliasOption,
912                 PKT_ALIAS_SAME_PORTS,
913                 YesNo,
914                 "[yes|no]",
915                 "try to keep original port numbers for connections",
916                 "same_ports",
917                 "m" },
918
919         { Verbose,
920                 0,
921                 YesNo,
922                 "[yes|no]",
923                 "verbose mode, dump packet information",
924                 "verbose",
925                 "v" },
926
927         { DynamicMode,
928                 0,
929                 YesNo,
930                 "[yes|no]",
931                 "dynamic mode, automatically detect interface address changes",
932                 "dynamic",
933                 NULL },
934
935         { InPort,
936                 0,
937                 Service,
938                 "number|service_name",
939                 "set port for incoming packets",
940                 "in_port",
941                 "i" },
942
943         { OutPort,
944                 0,
945                 Service,
946                 "number|service_name",
947                 "set port for outgoing packets",
948                 "out_port",
949                 "o" },
950
951         { Port,
952                 0,
953                 Service,
954                 "number|service_name",
955                 "set port (defaults to natd/divert)",
956                 "port",
957                 "p" },
958
959         { AliasAddress,
960                 0,
961                 Address,
962                 "x.x.x.x",
963                 "address to use for aliasing",
964                 "alias_address",
965                 "a" },
966
967         { TargetAddress,
968                 0,
969                 Address,
970                 "x.x.x.x",
971                 "address to use for incoming sessions",
972                 "target_address",
973                 "t" },
974
975         { InterfaceName,
976                 0,
977                 String,
978                 "network_if_name",
979                 "take aliasing address from interface",
980                 "interface",
981                 "n" },
982
983         { ProxyRule,
984                 0,
985                 String,
986                 "[type encode_ip_hdr|encode_tcp_stream] port xxxx server "
987                 "a.b.c.d:yyyy",
988                 "add transparent proxying / destination NAT",
989                 "proxy_rule",
990                 NULL },
991
992         { RedirectPort,
993                 0,
994                 String,
995                 "tcp|udp local_addr:local_port_range[,...] [public_addr:]public_port_range"
996                 " [remote_addr[:remote_port_range]]",
997                 "redirect a port (or ports) for incoming traffic",
998                 "redirect_port",
999                 NULL },
1000
1001         { RedirectProto,
1002                 0,
1003                 String,
1004                 "proto local_addr [public_addr] [remote_addr]",
1005                 "redirect packets of a given proto",
1006                 "redirect_proto",
1007                 NULL },
1008
1009         { RedirectAddress,
1010                 0,
1011                 String,
1012                 "local_addr[,...] public_addr",
1013                 "define mapping between local and public addresses",
1014                 "redirect_address",
1015                 NULL },
1016
1017         { ConfigFile,
1018                 0,
1019                 String,
1020                 "file_name",
1021                 "read options from configuration file",
1022                 "config",
1023                 "f" },
1024
1025         { LogDenied,
1026                 0,
1027                 YesNo,
1028                 "[yes|no]",
1029                 "enable logging of denied incoming packets",
1030                 "log_denied",
1031                 NULL },
1032
1033         { LogFacility,
1034                 0,
1035                 String,
1036                 "facility",
1037                 "name of syslog facility to use for logging",
1038                 "log_facility",
1039                 NULL },
1040
1041         { PunchFW,
1042                 0,
1043                 String,
1044                 "basenumber:count",
1045                 "punch holes in the firewall for incoming FTP/IRC DCC connections",
1046                 "punch_fw",
1047                 NULL },
1048
1049         { LogIpfwDenied,
1050                 0,
1051                 YesNo,
1052                 "[yes|no]",
1053                 "log packets converted by natd, but denied by ipfw",
1054                 "log_ipfw_denied",
1055                 NULL },
1056         { ExitDelay,
1057                 0,
1058                 Numeric,
1059                 "ms",
1060                 "delay in ms before daemon exit after signal",
1061                 "exit_delay",
1062                 NULL },
1063 };
1064
1065 static void
1066 ParseOption(const char *option, const char *parms)
1067 {
1068         int                     i;
1069         struct OptionInfo*      info;
1070         int                     yesNoValue;
1071         int                     aliasValue;
1072         int                     numValue;
1073         u_short                 uNumValue;
1074         const char*             strValue;
1075         struct in_addr          addrValue;
1076         int                     max;
1077         char*                   end;
1078         CODE*                   fac_record = NULL;
1079 /*
1080  * Find option from table.
1081  */
1082         max = sizeof(optionTable) / sizeof(struct OptionInfo);
1083         for (i = 0, info = optionTable; i < max; i++, info++) {
1084                 if (!strcmp(info->name, option))
1085                         break;
1086
1087                 if (info->shortName)
1088                         if (!strcmp(info->shortName, option))
1089                                 break;
1090         }
1091
1092         if (i >= max) {
1093                 warnx("unknown option %s", option);
1094                 Usage();
1095         }
1096
1097         uNumValue       = 0;
1098         yesNoValue      = 0;
1099         numValue        = 0;
1100         strValue        = NULL;
1101 /*
1102  * Check parameters.
1103  */
1104         switch (info->parm) {
1105         case YesNo:
1106                 if (!parms)
1107                         parms = "yes";
1108
1109                 if (!strcmp(parms, "yes"))
1110                         yesNoValue = 1;
1111                 else
1112                         if (!strcmp(parms, "no"))
1113                                 yesNoValue = 0;
1114                         else
1115                                 errx(1, "%s needs yes/no parameter", option);
1116                 break;
1117
1118         case Service:
1119                 if (!parms)
1120                         errx(1, "%s needs service name or "
1121                                 "port number parameter",
1122                                 option);
1123
1124                 uNumValue = StrToPort(parms, "divert");
1125                 break;
1126
1127         case Numeric:
1128                 if (parms)
1129                         numValue = strtol(parms, &end, 10);
1130                 else
1131                         end = NULL;
1132
1133                 if (end == parms)
1134                         errx(1, "%s needs numeric parameter", option);
1135                 break;
1136
1137         case String:
1138                 strValue = parms;
1139                 if (!strValue)
1140                         errx(1, "%s needs parameter", option);
1141                 break;
1142
1143         case None:
1144                 if (parms)
1145                         errx(1, "%s does not take parameters", option);
1146                 break;
1147
1148         case Address:
1149                 if (!parms)
1150                         errx(1, "%s needs address/host parameter", option);
1151
1152                 StrToAddr(parms, &addrValue);
1153                 break;
1154         }
1155
1156         switch (info->type) {
1157         case PacketAliasOption:
1158
1159                 aliasValue = yesNoValue ? info->packetAliasOpt : 0;
1160                 PacketAliasSetMode(aliasValue, info->packetAliasOpt);
1161                 break;
1162
1163         case Verbose:
1164                 verbose = yesNoValue;
1165                 break;
1166
1167         case DynamicMode:
1168                 dynamicMode = yesNoValue;
1169                 break;
1170
1171         case InPort:
1172                 inPort = uNumValue;
1173                 break;
1174
1175         case OutPort:
1176                 outPort = uNumValue;
1177                 break;
1178
1179         case Port:
1180                 inOutPort = uNumValue;
1181                 break;
1182
1183         case AliasAddress:
1184                 memcpy(&aliasAddr, &addrValue, sizeof(struct in_addr));
1185                 break;
1186
1187         case TargetAddress:
1188                 PacketAliasSetTarget(addrValue);
1189                 break;
1190
1191         case RedirectPort:
1192                 SetupPortRedirect(strValue);
1193                 break;
1194
1195         case RedirectProto:
1196                 SetupProtoRedirect(strValue);
1197                 break;
1198
1199         case RedirectAddress:
1200                 SetupAddressRedirect(strValue);
1201                 break;
1202
1203         case ProxyRule:
1204                 PacketAliasProxyRule(strValue);
1205                 break;
1206
1207         case InterfaceName:
1208                 if (ifName)
1209                         free(ifName);
1210
1211                 ifName = strdup(strValue);
1212                 break;
1213
1214         case ConfigFile:
1215                 ReadConfigFile(strValue);
1216                 break;
1217
1218         case LogDenied:
1219                 logDropped = yesNoValue;
1220                 break;
1221
1222         case LogFacility:
1223
1224                 fac_record = facilitynames;
1225                 while (fac_record->c_name != NULL) {
1226                         if (!strcmp(fac_record->c_name, strValue)) {
1227                                 logFacility = fac_record->c_val;
1228                                 break;
1229
1230                         } else
1231                                 fac_record++;
1232                 }
1233
1234                 if(fac_record->c_name == NULL)
1235                         errx(1, "Unknown log facility name: %s", strValue);
1236
1237                 break;
1238
1239         case PunchFW:
1240                 SetupPunchFW(strValue);
1241                 break;
1242
1243         case LogIpfwDenied:
1244                 logIpfwDenied = yesNoValue;
1245                 break;
1246         case ExitDelay:
1247                 if (numValue < 0 || numValue > MAX_EXIT_DELAY)
1248                         errx(1, "Incorrect exit delay: %d", numValue);
1249                 exitDelay = numValue;
1250                 break;
1251         }
1252 }
1253
1254 void
1255 ReadConfigFile(const char *fileName)
1256 {
1257         FILE*   file;
1258         char    *buf;
1259         size_t  len;
1260         char    *ptr, *p;
1261         char*   option;
1262
1263         file = fopen(fileName, "r");
1264         if (!file)
1265                 err(1, "cannot open config file %s", fileName);
1266
1267         while ((buf = fgetln(file, &len)) != NULL) {
1268                 if (buf[len - 1] == '\n')
1269                         buf[len - 1] = '\0';
1270                 else
1271                         errx(1, "config file format error: "
1272                                 "last line should end with newline");
1273
1274 /*
1275  * Check for comments, strip off trailing spaces.
1276  */
1277                 if ((ptr = strchr(buf, '#')))
1278                         *ptr = '\0';
1279                 for (ptr = buf; isspace(*ptr); ++ptr)
1280                         continue;
1281                 if (*ptr == '\0')
1282                         continue;
1283                 for (p = strchr(buf, '\0'); isspace(*--p);)
1284                         continue;
1285                 *++p = '\0';
1286
1287 /*
1288  * Extract option name.
1289  */
1290                 option = ptr;
1291                 while (*ptr && !isspace(*ptr))
1292                         ++ptr;
1293
1294                 if (*ptr != '\0') {
1295                         *ptr = '\0';
1296                         ++ptr;
1297                 }
1298 /*
1299  * Skip white space between name and parms.
1300  */
1301                 while (*ptr && isspace(*ptr))
1302                         ++ptr;
1303
1304                 ParseOption(option, *ptr ? ptr : NULL);
1305         }
1306
1307         fclose(file);
1308 }
1309
1310 static void
1311 Usage(void)
1312 {
1313         int                     i;
1314         int                     max;
1315         struct OptionInfo*      info;
1316
1317         fprintf(stderr, "Recognized options:\n\n");
1318
1319         max = sizeof(optionTable) / sizeof(struct OptionInfo);
1320         for (i = 0, info = optionTable; i < max; i++, info++) {
1321                 fprintf(stderr, "-%-20s %s\n", info->name,
1322                                                 info->parmDescription);
1323
1324                 if (info->shortName)
1325                         fprintf(stderr, "-%-20s %s\n", info->shortName,
1326                                                         info->parmDescription);
1327
1328                 fprintf(stderr, "      %s\n\n", info->description);
1329         }
1330
1331         exit(1);
1332 }
1333
1334 void
1335 SetupPortRedirect(const char *parms)
1336 {
1337         char            buf[128];
1338         char*           ptr;
1339         char*           serverPool;
1340         struct in_addr  localAddr;
1341         struct in_addr  publicAddr;
1342         struct in_addr  remoteAddr;
1343         port_range      portRange;
1344         u_short         localPort      = 0;
1345         u_short         publicPort     = 0;
1346         u_short         remotePort     = 0;
1347         u_short         numLocalPorts  = 0;
1348         u_short         numPublicPorts = 0;
1349         u_short         numRemotePorts = 0;
1350         int             proto;
1351         char*           protoName;
1352         char*           separator;
1353         int             i;
1354         struct alias_link *alink = NULL;
1355
1356         strcpy(buf, parms);
1357 /*
1358  * Extract protocol.
1359  */
1360         protoName = strtok(buf, " \t");
1361         if (!protoName)
1362                 errx(1, "redirect_port: missing protocol");
1363
1364         proto = StrToProto(protoName);
1365 /*
1366  * Extract local address.
1367  */
1368         ptr = strtok(NULL, " \t");
1369         if (!ptr)
1370                 errx(1, "redirect_port: missing local address");
1371
1372         separator = strchr(ptr, ',');
1373         if (separator) {                /* LSNAT redirection syntax. */
1374                 localAddr.s_addr = INADDR_NONE;
1375                 localPort = ~0;
1376                 numLocalPorts = 1;
1377                 serverPool = ptr;
1378         } else {
1379                 if (StrToAddrAndPortRange(ptr, &localAddr, protoName, &portRange) != 0 )
1380                         errx(1, "redirect_port: invalid local port range");
1381
1382                 localPort     = GETLOPORT(portRange);
1383                 numLocalPorts = GETNUMPORTS(portRange);
1384                 serverPool = NULL;
1385         }
1386
1387 /*
1388  * Extract public port and optionally address.
1389  */
1390         ptr = strtok(NULL, " \t");
1391         if (!ptr)
1392                 errx(1, "redirect_port: missing public port");
1393
1394         separator = strchr(ptr, ':');
1395         if (separator) {
1396                 if (StrToAddrAndPortRange(ptr, &publicAddr, protoName, &portRange) != 0 )
1397                         errx(1, "redirect_port: invalid public port range");
1398         } else {
1399                 publicAddr.s_addr = INADDR_ANY;
1400                 if (StrToPortRange(ptr, protoName, &portRange) != 0)
1401                         errx(1, "redirect_port: invalid public port range");
1402         }
1403
1404         publicPort     = GETLOPORT(portRange);
1405         numPublicPorts = GETNUMPORTS(portRange);
1406
1407 /*
1408  * Extract remote address and optionally port.
1409  */
1410         ptr = strtok(NULL, " \t");
1411         if (ptr) {
1412                 separator = strchr(ptr, ':');
1413                 if (separator) {
1414                         if (StrToAddrAndPortRange(ptr, &remoteAddr, protoName, &portRange) != 0)
1415                                 errx(1, "redirect_port: invalid remote port range");
1416                 } else {
1417                         SETLOPORT(portRange, 0);
1418                         SETNUMPORTS(portRange, 1);
1419                         StrToAddr(ptr, &remoteAddr);
1420                 }
1421         } else {
1422                 SETLOPORT(portRange, 0);
1423                 SETNUMPORTS(portRange, 1);
1424                 remoteAddr.s_addr = INADDR_ANY;
1425         }
1426
1427         remotePort     = GETLOPORT(portRange);
1428         numRemotePorts = GETNUMPORTS(portRange);
1429
1430 /*
1431  * Make sure port ranges match up, then add the redirect ports.
1432  */
1433         if (numLocalPorts != numPublicPorts)
1434                 errx(1, "redirect_port: port ranges must be equal in size");
1435
1436         /* Remote port range is allowed to be '0' which means all ports. */
1437         if (numRemotePorts != numLocalPorts && (numRemotePorts != 1 || remotePort != 0))
1438                 errx(1, "redirect_port: remote port must be 0 or equal to local port range in size");
1439
1440         for (i = 0 ; i < numPublicPorts ; ++i) {
1441                 /* If remotePort is all ports, set it to 0. */
1442                 u_short remotePortCopy = remotePort + i;
1443                 if (numRemotePorts == 1 && remotePort == 0)
1444                         remotePortCopy = 0;
1445
1446                 alink = PacketAliasRedirectPort(localAddr,
1447                                                 htons(localPort + i),
1448                                                 remoteAddr,
1449                                                 htons(remotePortCopy),
1450                                                 publicAddr,
1451                                                 htons(publicPort + i),
1452                                                 proto);
1453         }
1454
1455 /*
1456  * Setup LSNAT server pool.
1457  */
1458         if (serverPool != NULL && alink != NULL) {
1459                 ptr = strtok(serverPool, ",");
1460                 while (ptr != NULL) {
1461                         if (StrToAddrAndPortRange(ptr, &localAddr, protoName, &portRange) != 0)
1462                                 errx(1, "redirect_port: invalid local port range");
1463
1464                         localPort = GETLOPORT(portRange);
1465                         if (GETNUMPORTS(portRange) != 1)
1466                                 errx(1, "redirect_port: local port must be single in this context");
1467                         PacketAliasAddServer(alink, localAddr, htons(localPort));
1468                         ptr = strtok(NULL, ",");
1469                 }
1470         }
1471 }
1472
1473 void
1474 SetupProtoRedirect(const char *parms)
1475 {
1476         char            buf[128];
1477         char*           ptr;
1478         struct in_addr  localAddr;
1479         struct in_addr  publicAddr;
1480         struct in_addr  remoteAddr;
1481         int             proto;
1482         char*           protoName;
1483         struct protoent *protoent;
1484
1485         strcpy(buf, parms);
1486 /*
1487  * Extract protocol.
1488  */
1489         protoName = strtok(buf, " \t");
1490         if (!protoName)
1491                 errx(1, "redirect_proto: missing protocol");
1492
1493         protoent = getprotobyname(protoName);
1494         if (protoent == NULL)
1495                 errx(1, "redirect_proto: unknown protocol %s", protoName);
1496         else
1497                 proto = protoent->p_proto;
1498 /*
1499  * Extract local address.
1500  */
1501         ptr = strtok(NULL, " \t");
1502         if (!ptr)
1503                 errx(1, "redirect_proto: missing local address");
1504         else
1505                 StrToAddr(ptr, &localAddr);
1506 /*
1507  * Extract optional public address.
1508  */
1509         ptr = strtok(NULL, " \t");
1510         if (ptr)
1511                 StrToAddr(ptr, &publicAddr);
1512         else
1513                 publicAddr.s_addr = INADDR_ANY;
1514 /*
1515  * Extract optional remote address.
1516  */
1517         ptr = strtok(NULL, " \t");
1518         if (ptr)
1519                 StrToAddr(ptr, &remoteAddr);
1520         else
1521                 remoteAddr.s_addr = INADDR_ANY;
1522 /*
1523  * Create aliasing link.
1524  */
1525         PacketAliasRedirectProto(localAddr, remoteAddr, publicAddr, proto);
1526 }
1527
1528 void
1529 SetupAddressRedirect(const char *parms)
1530 {
1531         char            buf[128];
1532         char*           ptr;
1533         char*           separator;
1534         struct in_addr  localAddr;
1535         struct in_addr  publicAddr;
1536         char*           serverPool;
1537         struct alias_link *alink;
1538
1539         strcpy(buf, parms);
1540 /*
1541  * Extract local address.
1542  */
1543         ptr = strtok(buf, " \t");
1544         if (!ptr)
1545                 errx(1, "redirect_address: missing local address");
1546
1547         separator = strchr(ptr, ',');
1548         if (separator) {                /* LSNAT redirection syntax. */
1549                 localAddr.s_addr = INADDR_NONE;
1550                 serverPool = ptr;
1551         } else {
1552                 StrToAddr(ptr, &localAddr);
1553                 serverPool = NULL;
1554         }
1555 /*
1556  * Extract public address.
1557  */
1558         ptr = strtok(NULL, " \t");
1559         if (!ptr)
1560                 errx(1, "redirect_address: missing public address");
1561
1562         StrToAddr(ptr, &publicAddr);
1563         alink = PacketAliasRedirectAddr(localAddr, publicAddr);
1564
1565 /*
1566  * Setup LSNAT server pool.
1567  */
1568         if (serverPool != NULL && alink != NULL) {
1569                 ptr = strtok(serverPool, ",");
1570                 while (ptr != NULL) {
1571                         StrToAddr(ptr, &localAddr);
1572                         PacketAliasAddServer(alink, localAddr, htons(~0));
1573                         ptr = strtok(NULL, ",");
1574                 }
1575         }
1576 }
1577
1578 void
1579 StrToAddr(const char *str, struct in_addr *addr)
1580 {
1581         struct hostent *hp;
1582
1583         if (inet_aton(str, addr))
1584                 return;
1585
1586         hp = gethostbyname(str);
1587         if (!hp)
1588                 errx(1, "unknown host %s", str);
1589
1590         memcpy(addr, hp->h_addr, sizeof(struct in_addr));
1591 }
1592
1593 u_short
1594 StrToPort(const char *str, const char *proto)
1595 {
1596         u_short         port;
1597         struct servent* sp;
1598         char*           end;
1599
1600         port = strtol(str, &end, 10);
1601         if (end != str)
1602                 return htons(port);
1603
1604         sp = getservbyname(str, proto);
1605         if (!sp)
1606                 errx(1, "unknown service %s/%s", str, proto);
1607
1608         return sp->s_port;
1609 }
1610
1611 int
1612 StrToPortRange(const char *str, const char *proto, port_range *portRange)
1613 {
1614         char*           sep;
1615         struct servent* sp;
1616         char*           end;
1617         u_short         loPort;
1618         u_short         hiPort;
1619
1620         /* First see if this is a service, return corresponding port if so. */
1621         sp = getservbyname(str,proto);
1622         if (sp) {
1623                 SETLOPORT(*portRange, ntohs(sp->s_port));
1624                 SETNUMPORTS(*portRange, 1);
1625                 return 0;
1626         }
1627
1628         /* Not a service, see if it's a single port or port range. */
1629         sep = strchr(str, '-');
1630         if (sep == NULL) {
1631                 SETLOPORT(*portRange, strtol(str, &end, 10));
1632                 if (end != str) {
1633                         /* Single port. */
1634                         SETNUMPORTS(*portRange, 1);
1635                         return 0;
1636                 }
1637
1638                 /* Error in port range field. */
1639                 errx(1, "unknown service %s/%s", str, proto);
1640         }
1641
1642         /* Port range, get the values and sanity check. */
1643         sscanf(str, "%hu-%hu", &loPort, &hiPort);
1644         SETLOPORT(*portRange, loPort);
1645         SETNUMPORTS(*portRange, 0);     /* Error by default */
1646         if (loPort <= hiPort)
1647                 SETNUMPORTS(*portRange, hiPort - loPort + 1);
1648
1649         if (GETNUMPORTS(*portRange) == 0)
1650                 errx(1, "invalid port range %s", str);
1651
1652         return 0;
1653 }
1654
1655
1656 int
1657 StrToProto(const char *str)
1658 {
1659         if (!strcmp(str, "tcp"))
1660                 return IPPROTO_TCP;
1661
1662         if (!strcmp(str, "udp"))
1663                 return IPPROTO_UDP;
1664
1665         errx(1, "unknown protocol %s. Expected tcp or udp", str);
1666 }
1667
1668 int
1669 StrToAddrAndPortRange(const char *str, struct in_addr *addr, char *proto, port_range *portRange)
1670 {
1671         char*   ptr;
1672
1673         ptr = strchr(str, ':');
1674         if (!ptr)
1675                 errx(1, "%s is missing port number", str);
1676
1677         *ptr = '\0';
1678         ++ptr;
1679
1680         StrToAddr(str, addr);
1681         return StrToPortRange(ptr, proto, portRange);
1682 }
1683
1684 static void
1685 SetupPunchFW(const char *strValue)
1686 {
1687         unsigned int base, num;
1688
1689         if (sscanf(strValue, "%u:%u", &base, &num) != 2)
1690                 errx(1, "punch_fw: basenumber:count parameter required");
1691
1692         PacketAliasSetFWBase(base, num);
1693         PacketAliasSetMode(PKT_ALIAS_PUNCH_FW, PKT_ALIAS_PUNCH_FW);
1694 }