933120f9de3c387b8154b1a3f97c8f2d704ef82e
[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 #define ROUNDUP(a) \
746         ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
747 #define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len))
748
749                         for (i = 1; i < RTA_IFA; i <<= 1)
750                                 if (ifam->ifam_addrs & i)
751                                         ADVANCE(cp, (struct sockaddr *)cp);
752                         if (((struct sockaddr *)cp)->sa_family == AF_INET) {
753                                 s_in = (struct sockaddr_in *)cp;
754                                 break;
755                         }
756                 }
757         }
758         if (s_in == NULL)
759                 errx(1, "%s: cannot get interface address", ifn);
760
761         PacketAliasSetAddress(s_in->sin_addr);
762         syslog(LOG_INFO, "Aliasing to %s, mtu %d bytes",
763                inet_ntoa(s_in->sin_addr), ifMTU);
764
765         free(buf);
766 }
767
768 void
769 Quit(const char *msg)
770 {
771         Warn(msg);
772         exit(1);
773 }
774
775 void
776 Warn(const char *msg)
777 {
778         if (background)
779                 syslog(LOG_ALERT, "%s (%m)", msg);
780         else
781                 warn("%s", msg);
782 }
783
784 static void
785 RefreshAddr(int sig __unused)
786 {
787         if (ifName)
788                 assignAliasAddr = 1;
789 }
790
791 static void
792 InitiateShutdown(int sig __unused)
793 {
794         struct sigaction sa;
795 /*
796  * Start timer to allow kernel gracefully
797  * shutdown existing connections when system
798  * is shut down.
799  */
800         sa.sa_handler = Shutdown;
801         sa.sa_flags = 0;
802         sigemptyset(&sa.sa_mask);
803         sigaction(SIGALRM, &sa, NULL);
804         ualarm(exitDelay*1000, 1000);
805 }
806
807 static void
808 Shutdown(int sig __unused)
809 {
810         running = 0;
811 }
812
813 /*
814  * Different options recognized by this program.
815  */
816
817 enum Option {
818         PacketAliasOption,
819         Verbose,
820         InPort,
821         OutPort,
822         Port,
823         AliasAddress,
824         TargetAddress,
825         InterfaceName,
826         RedirectPort,
827         RedirectProto,
828         RedirectAddress,
829         ConfigFile,
830         DynamicMode,
831         ProxyRule,
832         LogDenied,
833         LogFacility,
834         PunchFW,
835         LogIpfwDenied,
836         ExitDelay
837 };
838
839 enum Param {
840         YesNo,
841         Numeric,
842         String,
843         None,
844         Address,
845         Service
846 };
847
848 /*
849  * Option information structure (used by ParseOption).
850  */
851
852 struct OptionInfo {
853         enum Option             type;
854         int                     packetAliasOpt;
855         enum Param              parm;
856         const char*             parmDescription;
857         const char*             description;
858         const char*             name;
859         const char*             shortName;
860 };
861
862 /*
863  * Table of known options.
864  */
865
866 static struct OptionInfo optionTable[] = {
867         { PacketAliasOption,
868                 PKT_ALIAS_UNREGISTERED_ONLY,
869                 YesNo,
870                 "[yes|no]",
871                 "alias only unregistered addresses",
872                 "unregistered_only",
873                 "u" },
874
875         { PacketAliasOption,
876                 PKT_ALIAS_LOG,
877                 YesNo,
878                 "[yes|no]",
879                 "enable logging",
880                 "log",
881                 "l" },
882
883         { PacketAliasOption,
884                 PKT_ALIAS_PROXY_ONLY,
885                 YesNo,
886                 "[yes|no]",
887                 "proxy only",
888                 "proxy_only",
889                 NULL },
890
891         { PacketAliasOption,
892                 PKT_ALIAS_REVERSE,
893                 YesNo,
894                 "[yes|no]",
895                 "operate in reverse mode",
896                 "reverse",
897                 NULL },
898
899         { PacketAliasOption,
900                 PKT_ALIAS_DENY_INCOMING,
901                 YesNo,
902                 "[yes|no]",
903                 "allow incoming connections",
904                 "deny_incoming",
905                 "d" },
906
907         { PacketAliasOption,
908                 PKT_ALIAS_USE_SOCKETS,
909                 YesNo,
910                 "[yes|no]",
911                 "use sockets to inhibit port conflict",
912                 "use_sockets",
913                 "s" },
914
915         { PacketAliasOption,
916                 PKT_ALIAS_SAME_PORTS,
917                 YesNo,
918                 "[yes|no]",
919                 "try to keep original port numbers for connections",
920                 "same_ports",
921                 "m" },
922
923         { Verbose,
924                 0,
925                 YesNo,
926                 "[yes|no]",
927                 "verbose mode, dump packet information",
928                 "verbose",
929                 "v" },
930
931         { DynamicMode,
932                 0,
933                 YesNo,
934                 "[yes|no]",
935                 "dynamic mode, automatically detect interface address changes",
936                 "dynamic",
937                 NULL },
938
939         { InPort,
940                 0,
941                 Service,
942                 "number|service_name",
943                 "set port for incoming packets",
944                 "in_port",
945                 "i" },
946
947         { OutPort,
948                 0,
949                 Service,
950                 "number|service_name",
951                 "set port for outgoing packets",
952                 "out_port",
953                 "o" },
954
955         { Port,
956                 0,
957                 Service,
958                 "number|service_name",
959                 "set port (defaults to natd/divert)",
960                 "port",
961                 "p" },
962
963         { AliasAddress,
964                 0,
965                 Address,
966                 "x.x.x.x",
967                 "address to use for aliasing",
968                 "alias_address",
969                 "a" },
970
971         { TargetAddress,
972                 0,
973                 Address,
974                 "x.x.x.x",
975                 "address to use for incoming sessions",
976                 "target_address",
977                 "t" },
978
979         { InterfaceName,
980                 0,
981                 String,
982                 "network_if_name",
983                 "take aliasing address from interface",
984                 "interface",
985                 "n" },
986
987         { ProxyRule,
988                 0,
989                 String,
990                 "[type encode_ip_hdr|encode_tcp_stream] port xxxx server "
991                 "a.b.c.d:yyyy",
992                 "add transparent proxying / destination NAT",
993                 "proxy_rule",
994                 NULL },
995
996         { RedirectPort,
997                 0,
998                 String,
999                 "tcp|udp local_addr:local_port_range[,...] [public_addr:]public_port_range"
1000                 " [remote_addr[:remote_port_range]]",
1001                 "redirect a port (or ports) for incoming traffic",
1002                 "redirect_port",
1003                 NULL },
1004
1005         { RedirectProto,
1006                 0,
1007                 String,
1008                 "proto local_addr [public_addr] [remote_addr]",
1009                 "redirect packets of a given proto",
1010                 "redirect_proto",
1011                 NULL },
1012
1013         { RedirectAddress,
1014                 0,
1015                 String,
1016                 "local_addr[,...] public_addr",
1017                 "define mapping between local and public addresses",
1018                 "redirect_address",
1019                 NULL },
1020
1021         { ConfigFile,
1022                 0,
1023                 String,
1024                 "file_name",
1025                 "read options from configuration file",
1026                 "config",
1027                 "f" },
1028
1029         { LogDenied,
1030                 0,
1031                 YesNo,
1032                 "[yes|no]",
1033                 "enable logging of denied incoming packets",
1034                 "log_denied",
1035                 NULL },
1036
1037         { LogFacility,
1038                 0,
1039                 String,
1040                 "facility",
1041                 "name of syslog facility to use for logging",
1042                 "log_facility",
1043                 NULL },
1044
1045         { PunchFW,
1046                 0,
1047                 String,
1048                 "basenumber:count",
1049                 "punch holes in the firewall for incoming FTP/IRC DCC connections",
1050                 "punch_fw",
1051                 NULL },
1052
1053         { LogIpfwDenied,
1054                 0,
1055                 YesNo,
1056                 "[yes|no]",
1057                 "log packets converted by natd, but denied by ipfw",
1058                 "log_ipfw_denied",
1059                 NULL },
1060         { ExitDelay,
1061                 0,
1062                 Numeric,
1063                 "ms",
1064                 "delay in ms before daemon exit after signal",
1065                 "exit_delay",
1066                 NULL },
1067 };
1068
1069 static void
1070 ParseOption(const char *option, const char *parms)
1071 {
1072         int                     i;
1073         struct OptionInfo*      info;
1074         int                     yesNoValue;
1075         int                     aliasValue;
1076         int                     numValue;
1077         u_short                 uNumValue;
1078         const char*             strValue;
1079         struct in_addr          addrValue;
1080         int                     max;
1081         char*                   end;
1082         CODE*                   fac_record = NULL;
1083 /*
1084  * Find option from table.
1085  */
1086         max = sizeof(optionTable) / sizeof(struct OptionInfo);
1087         for (i = 0, info = optionTable; i < max; i++, info++) {
1088                 if (!strcmp(info->name, option))
1089                         break;
1090
1091                 if (info->shortName)
1092                         if (!strcmp(info->shortName, option))
1093                                 break;
1094         }
1095
1096         if (i >= max) {
1097                 warnx("unknown option %s", option);
1098                 Usage();
1099         }
1100
1101         uNumValue       = 0;
1102         yesNoValue      = 0;
1103         numValue        = 0;
1104         strValue        = NULL;
1105 /*
1106  * Check parameters.
1107  */
1108         switch (info->parm) {
1109         case YesNo:
1110                 if (!parms)
1111                         parms = "yes";
1112
1113                 if (!strcmp(parms, "yes"))
1114                         yesNoValue = 1;
1115                 else
1116                         if (!strcmp(parms, "no"))
1117                                 yesNoValue = 0;
1118                         else
1119                                 errx(1, "%s needs yes/no parameter", option);
1120                 break;
1121
1122         case Service:
1123                 if (!parms)
1124                         errx(1, "%s needs service name or "
1125                                 "port number parameter",
1126                                 option);
1127
1128                 uNumValue = StrToPort(parms, "divert");
1129                 break;
1130
1131         case Numeric:
1132                 if (parms)
1133                         numValue = strtol(parms, &end, 10);
1134                 else
1135                         end = NULL;
1136
1137                 if (end == parms)
1138                         errx(1, "%s needs numeric parameter", option);
1139                 break;
1140
1141         case String:
1142                 strValue = parms;
1143                 if (!strValue)
1144                         errx(1, "%s needs parameter", option);
1145                 break;
1146
1147         case None:
1148                 if (parms)
1149                         errx(1, "%s does not take parameters", option);
1150                 break;
1151
1152         case Address:
1153                 if (!parms)
1154                         errx(1, "%s needs address/host parameter", option);
1155
1156                 StrToAddr(parms, &addrValue);
1157                 break;
1158         }
1159
1160         switch (info->type) {
1161         case PacketAliasOption:
1162
1163                 aliasValue = yesNoValue ? info->packetAliasOpt : 0;
1164                 PacketAliasSetMode(aliasValue, info->packetAliasOpt);
1165                 break;
1166
1167         case Verbose:
1168                 verbose = yesNoValue;
1169                 break;
1170
1171         case DynamicMode:
1172                 dynamicMode = yesNoValue;
1173                 break;
1174
1175         case InPort:
1176                 inPort = uNumValue;
1177                 break;
1178
1179         case OutPort:
1180                 outPort = uNumValue;
1181                 break;
1182
1183         case Port:
1184                 inOutPort = uNumValue;
1185                 break;
1186
1187         case AliasAddress:
1188                 memcpy(&aliasAddr, &addrValue, sizeof(struct in_addr));
1189                 break;
1190
1191         case TargetAddress:
1192                 PacketAliasSetTarget(addrValue);
1193                 break;
1194
1195         case RedirectPort:
1196                 SetupPortRedirect(strValue);
1197                 break;
1198
1199         case RedirectProto:
1200                 SetupProtoRedirect(strValue);
1201                 break;
1202
1203         case RedirectAddress:
1204                 SetupAddressRedirect(strValue);
1205                 break;
1206
1207         case ProxyRule:
1208                 PacketAliasProxyRule(strValue);
1209                 break;
1210
1211         case InterfaceName:
1212                 if (ifName)
1213                         free(ifName);
1214
1215                 ifName = strdup(strValue);
1216                 break;
1217
1218         case ConfigFile:
1219                 ReadConfigFile(strValue);
1220                 break;
1221
1222         case LogDenied:
1223                 logDropped = yesNoValue;
1224                 break;
1225
1226         case LogFacility:
1227
1228                 fac_record = facilitynames;
1229                 while (fac_record->c_name != NULL) {
1230                         if (!strcmp(fac_record->c_name, strValue)) {
1231                                 logFacility = fac_record->c_val;
1232                                 break;
1233
1234                         } else
1235                                 fac_record++;
1236                 }
1237
1238                 if(fac_record->c_name == NULL)
1239                         errx(1, "Unknown log facility name: %s", strValue);
1240
1241                 break;
1242
1243         case PunchFW:
1244                 SetupPunchFW(strValue);
1245                 break;
1246
1247         case LogIpfwDenied:
1248                 logIpfwDenied = yesNoValue;
1249                 break;
1250         case ExitDelay:
1251                 if (numValue < 0 || numValue > MAX_EXIT_DELAY)
1252                         errx(1, "Incorrect exit delay: %d", numValue);
1253                 exitDelay = numValue;
1254                 break;
1255         }
1256 }
1257
1258 void
1259 ReadConfigFile(const char *fileName)
1260 {
1261         FILE*   file;
1262         char    *buf;
1263         size_t  len;
1264         char    *ptr, *p;
1265         char*   option;
1266
1267         file = fopen(fileName, "r");
1268         if (!file)
1269                 err(1, "cannot open config file %s", fileName);
1270
1271         while ((buf = fgetln(file, &len)) != NULL) {
1272                 if (buf[len - 1] == '\n')
1273                         buf[len - 1] = '\0';
1274                 else
1275                         errx(1, "config file format error: "
1276                                 "last line should end with newline");
1277
1278 /*
1279  * Check for comments, strip off trailing spaces.
1280  */
1281                 if ((ptr = strchr(buf, '#')))
1282                         *ptr = '\0';
1283                 for (ptr = buf; isspace(*ptr); ++ptr)
1284                         continue;
1285                 if (*ptr == '\0')
1286                         continue;
1287                 for (p = strchr(buf, '\0'); isspace(*--p);)
1288                         continue;
1289                 *++p = '\0';
1290
1291 /*
1292  * Extract option name.
1293  */
1294                 option = ptr;
1295                 while (*ptr && !isspace(*ptr))
1296                         ++ptr;
1297
1298                 if (*ptr != '\0') {
1299                         *ptr = '\0';
1300                         ++ptr;
1301                 }
1302 /*
1303  * Skip white space between name and parms.
1304  */
1305                 while (*ptr && isspace(*ptr))
1306                         ++ptr;
1307
1308                 ParseOption(option, *ptr ? ptr : NULL);
1309         }
1310
1311         fclose(file);
1312 }
1313
1314 static void
1315 Usage(void)
1316 {
1317         int                     i;
1318         int                     max;
1319         struct OptionInfo*      info;
1320
1321         fprintf(stderr, "Recognized options:\n\n");
1322
1323         max = sizeof(optionTable) / sizeof(struct OptionInfo);
1324         for (i = 0, info = optionTable; i < max; i++, info++) {
1325                 fprintf(stderr, "-%-20s %s\n", info->name,
1326                                                 info->parmDescription);
1327
1328                 if (info->shortName)
1329                         fprintf(stderr, "-%-20s %s\n", info->shortName,
1330                                                         info->parmDescription);
1331
1332                 fprintf(stderr, "      %s\n\n", info->description);
1333         }
1334
1335         exit(1);
1336 }
1337
1338 void
1339 SetupPortRedirect(const char *parms)
1340 {
1341         char            buf[128];
1342         char*           ptr;
1343         char*           serverPool;
1344         struct in_addr  localAddr;
1345         struct in_addr  publicAddr;
1346         struct in_addr  remoteAddr;
1347         port_range      portRange;
1348         u_short         localPort      = 0;
1349         u_short         publicPort     = 0;
1350         u_short         remotePort     = 0;
1351         u_short         numLocalPorts  = 0;
1352         u_short         numPublicPorts = 0;
1353         u_short         numRemotePorts = 0;
1354         int             proto;
1355         char*           protoName;
1356         char*           separator;
1357         int             i;
1358         struct alias_link *alink = NULL;
1359
1360         strcpy(buf, parms);
1361 /*
1362  * Extract protocol.
1363  */
1364         protoName = strtok(buf, " \t");
1365         if (!protoName)
1366                 errx(1, "redirect_port: missing protocol");
1367
1368         proto = StrToProto(protoName);
1369 /*
1370  * Extract local address.
1371  */
1372         ptr = strtok(NULL, " \t");
1373         if (!ptr)
1374                 errx(1, "redirect_port: missing local address");
1375
1376         separator = strchr(ptr, ',');
1377         if (separator) {                /* LSNAT redirection syntax. */
1378                 localAddr.s_addr = INADDR_NONE;
1379                 localPort = ~0;
1380                 numLocalPorts = 1;
1381                 serverPool = ptr;
1382         } else {
1383                 if (StrToAddrAndPortRange(ptr, &localAddr, protoName, &portRange) != 0 )
1384                         errx(1, "redirect_port: invalid local port range");
1385
1386                 localPort     = GETLOPORT(portRange);
1387                 numLocalPorts = GETNUMPORTS(portRange);
1388                 serverPool = NULL;
1389         }
1390
1391 /*
1392  * Extract public port and optionally address.
1393  */
1394         ptr = strtok(NULL, " \t");
1395         if (!ptr)
1396                 errx(1, "redirect_port: missing public port");
1397
1398         separator = strchr(ptr, ':');
1399         if (separator) {
1400                 if (StrToAddrAndPortRange(ptr, &publicAddr, protoName, &portRange) != 0 )
1401                         errx(1, "redirect_port: invalid public port range");
1402         } else {
1403                 publicAddr.s_addr = INADDR_ANY;
1404                 if (StrToPortRange(ptr, protoName, &portRange) != 0)
1405                         errx(1, "redirect_port: invalid public port range");
1406         }
1407
1408         publicPort     = GETLOPORT(portRange);
1409         numPublicPorts = GETNUMPORTS(portRange);
1410
1411 /*
1412  * Extract remote address and optionally port.
1413  */
1414         ptr = strtok(NULL, " \t");
1415         if (ptr) {
1416                 separator = strchr(ptr, ':');
1417                 if (separator) {
1418                         if (StrToAddrAndPortRange(ptr, &remoteAddr, protoName, &portRange) != 0)
1419                                 errx(1, "redirect_port: invalid remote port range");
1420                 } else {
1421                         SETLOPORT(portRange, 0);
1422                         SETNUMPORTS(portRange, 1);
1423                         StrToAddr(ptr, &remoteAddr);
1424                 }
1425         } else {
1426                 SETLOPORT(portRange, 0);
1427                 SETNUMPORTS(portRange, 1);
1428                 remoteAddr.s_addr = INADDR_ANY;
1429         }
1430
1431         remotePort     = GETLOPORT(portRange);
1432         numRemotePorts = GETNUMPORTS(portRange);
1433
1434 /*
1435  * Make sure port ranges match up, then add the redirect ports.
1436  */
1437         if (numLocalPorts != numPublicPorts)
1438                 errx(1, "redirect_port: port ranges must be equal in size");
1439
1440         /* Remote port range is allowed to be '0' which means all ports. */
1441         if (numRemotePorts != numLocalPorts && (numRemotePorts != 1 || remotePort != 0))
1442                 errx(1, "redirect_port: remote port must be 0 or equal to local port range in size");
1443
1444         for (i = 0 ; i < numPublicPorts ; ++i) {
1445                 /* If remotePort is all ports, set it to 0. */
1446                 u_short remotePortCopy = remotePort + i;
1447                 if (numRemotePorts == 1 && remotePort == 0)
1448                         remotePortCopy = 0;
1449
1450                 alink = PacketAliasRedirectPort(localAddr,
1451                                                 htons(localPort + i),
1452                                                 remoteAddr,
1453                                                 htons(remotePortCopy),
1454                                                 publicAddr,
1455                                                 htons(publicPort + i),
1456                                                 proto);
1457         }
1458
1459 /*
1460  * Setup LSNAT server pool.
1461  */
1462         if (serverPool != NULL && alink != NULL) {
1463                 ptr = strtok(serverPool, ",");
1464                 while (ptr != NULL) {
1465                         if (StrToAddrAndPortRange(ptr, &localAddr, protoName, &portRange) != 0)
1466                                 errx(1, "redirect_port: invalid local port range");
1467
1468                         localPort = GETLOPORT(portRange);
1469                         if (GETNUMPORTS(portRange) != 1)
1470                                 errx(1, "redirect_port: local port must be single in this context");
1471                         PacketAliasAddServer(alink, localAddr, htons(localPort));
1472                         ptr = strtok(NULL, ",");
1473                 }
1474         }
1475 }
1476
1477 void
1478 SetupProtoRedirect(const char *parms)
1479 {
1480         char            buf[128];
1481         char*           ptr;
1482         struct in_addr  localAddr;
1483         struct in_addr  publicAddr;
1484         struct in_addr  remoteAddr;
1485         int             proto;
1486         char*           protoName;
1487         struct protoent *protoent;
1488
1489         strcpy(buf, parms);
1490 /*
1491  * Extract protocol.
1492  */
1493         protoName = strtok(buf, " \t");
1494         if (!protoName)
1495                 errx(1, "redirect_proto: missing protocol");
1496
1497         protoent = getprotobyname(protoName);
1498         if (protoent == NULL)
1499                 errx(1, "redirect_proto: unknown protocol %s", protoName);
1500         else
1501                 proto = protoent->p_proto;
1502 /*
1503  * Extract local address.
1504  */
1505         ptr = strtok(NULL, " \t");
1506         if (!ptr)
1507                 errx(1, "redirect_proto: missing local address");
1508         else
1509                 StrToAddr(ptr, &localAddr);
1510 /*
1511  * Extract optional public address.
1512  */
1513         ptr = strtok(NULL, " \t");
1514         if (ptr)
1515                 StrToAddr(ptr, &publicAddr);
1516         else
1517                 publicAddr.s_addr = INADDR_ANY;
1518 /*
1519  * Extract optional remote address.
1520  */
1521         ptr = strtok(NULL, " \t");
1522         if (ptr)
1523                 StrToAddr(ptr, &remoteAddr);
1524         else
1525                 remoteAddr.s_addr = INADDR_ANY;
1526 /*
1527  * Create aliasing link.
1528  */
1529         PacketAliasRedirectProto(localAddr, remoteAddr, publicAddr, proto);
1530 }
1531
1532 void
1533 SetupAddressRedirect(const char *parms)
1534 {
1535         char            buf[128];
1536         char*           ptr;
1537         char*           separator;
1538         struct in_addr  localAddr;
1539         struct in_addr  publicAddr;
1540         char*           serverPool;
1541         struct alias_link *alink;
1542
1543         strcpy(buf, parms);
1544 /*
1545  * Extract local address.
1546  */
1547         ptr = strtok(buf, " \t");
1548         if (!ptr)
1549                 errx(1, "redirect_address: missing local address");
1550
1551         separator = strchr(ptr, ',');
1552         if (separator) {                /* LSNAT redirection syntax. */
1553                 localAddr.s_addr = INADDR_NONE;
1554                 serverPool = ptr;
1555         } else {
1556                 StrToAddr(ptr, &localAddr);
1557                 serverPool = NULL;
1558         }
1559 /*
1560  * Extract public address.
1561  */
1562         ptr = strtok(NULL, " \t");
1563         if (!ptr)
1564                 errx(1, "redirect_address: missing public address");
1565
1566         StrToAddr(ptr, &publicAddr);
1567         alink = PacketAliasRedirectAddr(localAddr, publicAddr);
1568
1569 /*
1570  * Setup LSNAT server pool.
1571  */
1572         if (serverPool != NULL && alink != NULL) {
1573                 ptr = strtok(serverPool, ",");
1574                 while (ptr != NULL) {
1575                         StrToAddr(ptr, &localAddr);
1576                         PacketAliasAddServer(alink, localAddr, htons(~0));
1577                         ptr = strtok(NULL, ",");
1578                 }
1579         }
1580 }
1581
1582 void
1583 StrToAddr(const char *str, struct in_addr *addr)
1584 {
1585         struct hostent *hp;
1586
1587         if (inet_aton(str, addr))
1588                 return;
1589
1590         hp = gethostbyname(str);
1591         if (!hp)
1592                 errx(1, "unknown host %s", str);
1593
1594         memcpy(addr, hp->h_addr, sizeof(struct in_addr));
1595 }
1596
1597 u_short
1598 StrToPort(const char *str, const char *proto)
1599 {
1600         u_short         port;
1601         struct servent* sp;
1602         char*           end;
1603
1604         port = strtol(str, &end, 10);
1605         if (end != str)
1606                 return htons(port);
1607
1608         sp = getservbyname(str, proto);
1609         if (!sp)
1610                 errx(1, "unknown service %s/%s", str, proto);
1611
1612         return sp->s_port;
1613 }
1614
1615 int
1616 StrToPortRange(const char *str, const char *proto, port_range *portRange)
1617 {
1618         char*           sep;
1619         struct servent* sp;
1620         char*           end;
1621         u_short         loPort;
1622         u_short         hiPort;
1623
1624         /* First see if this is a service, return corresponding port if so. */
1625         sp = getservbyname(str,proto);
1626         if (sp) {
1627                 SETLOPORT(*portRange, ntohs(sp->s_port));
1628                 SETNUMPORTS(*portRange, 1);
1629                 return 0;
1630         }
1631
1632         /* Not a service, see if it's a single port or port range. */
1633         sep = strchr(str, '-');
1634         if (sep == NULL) {
1635                 SETLOPORT(*portRange, strtol(str, &end, 10));
1636                 if (end != str) {
1637                         /* Single port. */
1638                         SETNUMPORTS(*portRange, 1);
1639                         return 0;
1640                 }
1641
1642                 /* Error in port range field. */
1643                 errx(1, "unknown service %s/%s", str, proto);
1644         }
1645
1646         /* Port range, get the values and sanity check. */
1647         sscanf(str, "%hu-%hu", &loPort, &hiPort);
1648         SETLOPORT(*portRange, loPort);
1649         SETNUMPORTS(*portRange, 0);     /* Error by default */
1650         if (loPort <= hiPort)
1651                 SETNUMPORTS(*portRange, hiPort - loPort + 1);
1652
1653         if (GETNUMPORTS(*portRange) == 0)
1654                 errx(1, "invalid port range %s", str);
1655
1656         return 0;
1657 }
1658
1659
1660 int
1661 StrToProto(const char *str)
1662 {
1663         if (!strcmp(str, "tcp"))
1664                 return IPPROTO_TCP;
1665
1666         if (!strcmp(str, "udp"))
1667                 return IPPROTO_UDP;
1668
1669         errx(1, "unknown protocol %s. Expected tcp or udp", str);
1670 }
1671
1672 int
1673 StrToAddrAndPortRange(const char *str, struct in_addr *addr, char *proto, port_range *portRange)
1674 {
1675         char*   ptr;
1676
1677         ptr = strchr(str, ':');
1678         if (!ptr)
1679                 errx(1, "%s is missing port number", str);
1680
1681         *ptr = '\0';
1682         ++ptr;
1683
1684         StrToAddr(str, addr);
1685         return StrToPortRange(ptr, proto, portRange);
1686 }
1687
1688 static void
1689 SetupPunchFW(const char *strValue)
1690 {
1691         unsigned int base, num;
1692
1693         if (sscanf(strValue, "%u:%u", &base, &num) != 2)
1694                 errx(1, "punch_fw: basenumber:count parameter required");
1695
1696         PacketAliasSetFWBase(base, num);
1697         PacketAliasSetMode(PKT_ALIAS_PUNCH_FW, PKT_ALIAS_PUNCH_FW);
1698 }