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