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