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