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