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