xref: /freebsd/sbin/natd/natd.c (revision 2e7e7c71ef71706fb0e81c3c6e5e93d6797d4fb3)
124084f9bSBrian Somers /*
224084f9bSBrian Somers  * natd - Network Address Translation Daemon for FreeBSD.
324084f9bSBrian Somers  *
4f13f9fadSAlexander Langer  * This software is provided free of charge, with no
524084f9bSBrian Somers  * warranty of any kind, either expressed or implied.
624084f9bSBrian Somers  * Use at your own risk.
724084f9bSBrian Somers  *
824084f9bSBrian Somers  * You may copy, modify and distribute this software (natd.c) freely.
924084f9bSBrian Somers  *
10fb994b07SBrian Somers  * Ari Suutari <suutari@iki.fi>
1124084f9bSBrian Somers  *
127f3dea24SPeter Wemm  * $FreeBSD$
1324084f9bSBrian Somers  */
1424084f9bSBrian Somers 
1559a7c613SBrian Somers #define SYSLOG_NAMES
1659a7c613SBrian Somers 
1724084f9bSBrian Somers #include <sys/types.h>
1824084f9bSBrian Somers #include <sys/socket.h>
1924084f9bSBrian Somers #include <sys/time.h>
2024084f9bSBrian Somers 
2124084f9bSBrian Somers #include <netinet/in.h>
2224084f9bSBrian Somers #include <netinet/in_systm.h>
2324084f9bSBrian Somers #include <netinet/ip.h>
2424084f9bSBrian Somers #include <machine/in_cksum.h>
2524084f9bSBrian Somers #include <netinet/tcp.h>
2659a7c613SBrian Somers #include <netinet/udp.h>
2759a7c613SBrian Somers #include <netinet/ip_icmp.h>
2824084f9bSBrian Somers #include <sys/ioctl.h>
2924084f9bSBrian Somers #include <net/if.h>
3024084f9bSBrian Somers #include <net/route.h>
3124084f9bSBrian Somers #include <arpa/inet.h>
3224084f9bSBrian Somers 
3324084f9bSBrian Somers #include <alias.h>
340fc81af1SPhilippe Charnier #include <ctype.h>
350fc81af1SPhilippe Charnier #include <err.h>
360fc81af1SPhilippe Charnier #include <errno.h>
370fc81af1SPhilippe Charnier #include <netdb.h>
380fc81af1SPhilippe Charnier #include <signal.h>
390fc81af1SPhilippe Charnier #include <stdio.h>
400fc81af1SPhilippe Charnier #include <stdlib.h>
410fc81af1SPhilippe Charnier #include <string.h>
420fc81af1SPhilippe Charnier #include <syslog.h>
430fc81af1SPhilippe Charnier #include <unistd.h>
4467a886fbSBrian Somers 
4524084f9bSBrian Somers #include "natd.h"
4624084f9bSBrian Somers 
4724084f9bSBrian Somers /*
4824084f9bSBrian Somers  * Default values for input and output
4924084f9bSBrian Somers  * divert socket ports.
5024084f9bSBrian Somers  */
5124084f9bSBrian Somers 
5224084f9bSBrian Somers #define	DEFAULT_SERVICE	"natd"
5324084f9bSBrian Somers 
5424084f9bSBrian Somers /*
555d8ee958SBrian Somers  * Definition of a port range, and macros to deal with values.
565d8ee958SBrian Somers  * FORMAT:  HI 16-bits == first port in range, 0 == all ports.
575d8ee958SBrian Somers  *          LO 16-bits == number of ports in range
585d8ee958SBrian Somers  * NOTES:   - Port values are not stored in network byte order.
595d8ee958SBrian Somers  */
605d8ee958SBrian Somers 
615d8ee958SBrian Somers typedef u_long port_range;
625d8ee958SBrian Somers 
635d8ee958SBrian Somers #define GETLOPORT(x)     ((x) >> 0x10)
645d8ee958SBrian Somers #define GETNUMPORTS(x)   ((x) & 0x0000ffff)
655d8ee958SBrian Somers #define GETHIPORT(x)     (GETLOPORT((x)) + GETNUMPORTS((x)))
665d8ee958SBrian Somers 
675d8ee958SBrian Somers /* Set y to be the low-port value in port_range variable x. */
685d8ee958SBrian Somers #define SETLOPORT(x,y)   ((x) = ((x) & 0x0000ffff) | ((y) << 0x10))
695d8ee958SBrian Somers 
705d8ee958SBrian Somers /* Set y to be the number of ports in port_range variable x. */
715d8ee958SBrian Somers #define SETNUMPORTS(x,y) ((x) = ((x) & 0xffff0000) | (y))
725d8ee958SBrian Somers 
735d8ee958SBrian Somers /*
7424084f9bSBrian Somers  * Function prototypes.
7524084f9bSBrian Somers  */
7624084f9bSBrian Somers 
7759a7c613SBrian Somers static void	DoAliasing (int fd, int direction);
78902cb50aSBrian Somers static void	DaemonMode (void);
7924084f9bSBrian Somers static void	HandleRoutingInfo (int fd);
80902cb50aSBrian Somers static void	Usage (void);
8159a7c613SBrian Somers static char*	FormatPacket (struct ip*);
8224084f9bSBrian Somers static void	PrintPacket (struct ip*);
83902cb50aSBrian Somers static void	SyslogPacket (struct ip*, int priority, const char *label);
8424084f9bSBrian Somers static void	SetAliasAddressFromIfName (char* ifName);
85902cb50aSBrian Somers static void	InitiateShutdown (int);
86902cb50aSBrian Somers static void	Shutdown (int);
87902cb50aSBrian Somers static void	RefreshAddr (int);
88902cb50aSBrian Somers static void	ParseOption (const char* option, const char* parms, int cmdLine);
89902cb50aSBrian Somers static void	ReadConfigFile (const char* fileName);
90902cb50aSBrian Somers static void	SetupPortRedirect (const char* parms);
91902cb50aSBrian Somers static void	SetupAddressRedirect (const char* parms);
92902cb50aSBrian Somers static void	SetupPptpAlias (const char* parms);
93902cb50aSBrian Somers static void	StrToAddr (const char* str, struct in_addr* addr);
94902cb50aSBrian Somers static u_short  StrToPort (const char* str, const char* proto);
95902cb50aSBrian Somers static int      StrToPortRange (const char* str, const char* proto, port_range *portRange);
96902cb50aSBrian Somers static int 	StrToProto (const char* str);
97902cb50aSBrian Somers static int      StrToAddrAndPortRange (const char* str, struct in_addr* addr, char* proto, port_range *portRange);
9824084f9bSBrian Somers static void	ParseArgs (int argc, char** argv);
99fb994b07SBrian Somers static void	FlushPacketBuffer (int fd);
10024084f9bSBrian Somers 
10124084f9bSBrian Somers /*
10224084f9bSBrian Somers  * Globals.
10324084f9bSBrian Somers  */
10424084f9bSBrian Somers 
10524084f9bSBrian Somers static	int			verbose;
10624084f9bSBrian Somers static 	int			background;
10724084f9bSBrian Somers static	int			running;
10824084f9bSBrian Somers static	int			assignAliasAddr;
10924084f9bSBrian Somers static	char*			ifName;
11024084f9bSBrian Somers static  int			ifIndex;
11167a886fbSBrian Somers static	u_short			inPort;
11267a886fbSBrian Somers static	u_short			outPort;
11367a886fbSBrian Somers static	u_short			inOutPort;
11424084f9bSBrian Somers static	struct in_addr		aliasAddr;
11524084f9bSBrian Somers static 	int			dynamicMode;
11624084f9bSBrian Somers static  int			ifMTU;
11724084f9bSBrian Somers static	int			aliasOverhead;
11824084f9bSBrian Somers static 	int			icmpSock;
119fb994b07SBrian Somers static	char			packetBuf[IP_MAXPACKET];
120fb994b07SBrian Somers static 	int			packetLen;
121fb994b07SBrian Somers static	struct sockaddr_in	packetAddr;
122fb994b07SBrian Somers static 	int			packetSock;
12359a7c613SBrian Somers static 	int			packetDirection;
124f9b06d5cSBrian Somers static  int			dropIgnoredIncoming;
12559a7c613SBrian Somers static  int			logDropped;
12659a7c613SBrian Somers static	int			logFacility;
12724084f9bSBrian Somers 
12824084f9bSBrian Somers int main (int argc, char** argv)
12924084f9bSBrian Somers {
13024084f9bSBrian Somers 	int			divertIn;
13124084f9bSBrian Somers 	int			divertOut;
13224084f9bSBrian Somers 	int			divertInOut;
13324084f9bSBrian Somers 	int			routeSock;
13424084f9bSBrian Somers 	struct sockaddr_in	addr;
13524084f9bSBrian Somers 	fd_set			readMask;
136fb994b07SBrian Somers 	fd_set			writeMask;
13724084f9bSBrian Somers 	int			fdMax;
13824084f9bSBrian Somers /*
13924084f9bSBrian Somers  * Initialize packet aliasing software.
14024084f9bSBrian Somers  * Done already here to be able to alter option bits
14124084f9bSBrian Somers  * during command line and configuration file processing.
14224084f9bSBrian Somers  */
143fb994b07SBrian Somers 	PacketAliasInit ();
14424084f9bSBrian Somers /*
14524084f9bSBrian Somers  * Parse options.
14624084f9bSBrian Somers  */
14724084f9bSBrian Somers 	inPort			= 0;
14824084f9bSBrian Somers 	outPort			= 0;
14924084f9bSBrian Somers 	verbose 		= 0;
15024084f9bSBrian Somers 	inOutPort		= 0;
15124084f9bSBrian Somers 	ifName			= NULL;
15224084f9bSBrian Somers 	ifMTU			= -1;
15324084f9bSBrian Somers 	background		= 0;
15424084f9bSBrian Somers 	running			= 1;
15524084f9bSBrian Somers 	assignAliasAddr		= 0;
15624084f9bSBrian Somers 	aliasAddr.s_addr	= INADDR_NONE;
15724084f9bSBrian Somers 	aliasOverhead		= 12;
15824084f9bSBrian Somers 	dynamicMode		= 0;
15959a7c613SBrian Somers  	logDropped		= 0;
16059a7c613SBrian Somers  	logFacility		= LOG_DAEMON;
161fb994b07SBrian Somers /*
162fb994b07SBrian Somers  * Mark packet buffer empty.
163fb994b07SBrian Somers  */
164fb994b07SBrian Somers 	packetSock		= -1;
16559a7c613SBrian Somers 	packetDirection		= DONT_KNOW;
16624084f9bSBrian Somers 
16724084f9bSBrian Somers 	ParseArgs (argc, argv);
16824084f9bSBrian Somers /*
16959a7c613SBrian Somers  * Open syslog channel.
17059a7c613SBrian Somers  */
17159a7c613SBrian Somers 	openlog ("natd", LOG_CONS | LOG_PID, logFacility);
17259a7c613SBrian Somers /*
17324084f9bSBrian Somers  * Check that valid aliasing address has been given.
17424084f9bSBrian Somers  */
1750fc81af1SPhilippe Charnier 	if (aliasAddr.s_addr == INADDR_NONE && ifName == NULL)
1760fc81af1SPhilippe Charnier 		errx (1, "aliasing address not given");
17724084f9bSBrian Somers 
1780fc81af1SPhilippe Charnier 	if (aliasAddr.s_addr != INADDR_NONE && ifName != NULL)
17967a886fbSBrian Somers 		errx (1, "both alias address and interface "
18067a886fbSBrian Somers 			 "name are not allowed");
18124084f9bSBrian Somers /*
18224084f9bSBrian Somers  * Check that valid port number is known.
18324084f9bSBrian Somers  */
18424084f9bSBrian Somers 	if (inPort != 0 || outPort != 0)
1850fc81af1SPhilippe Charnier 		if (inPort == 0 || outPort == 0)
1860fc81af1SPhilippe Charnier 			errx (1, "both input and output ports are required");
18724084f9bSBrian Somers 
18824084f9bSBrian Somers 	if (inPort == 0 && outPort == 0 && inOutPort == 0)
18924084f9bSBrian Somers 		ParseOption ("port", DEFAULT_SERVICE, 0);
19024084f9bSBrian Somers 
19124084f9bSBrian Somers /*
192f9b06d5cSBrian Somers  * Check if ignored packets should be dropped.
193f9b06d5cSBrian Somers  */
194f9b06d5cSBrian Somers 	dropIgnoredIncoming = PacketAliasSetMode (0, 0);
195f9b06d5cSBrian Somers 	dropIgnoredIncoming &= PKT_ALIAS_DENY_INCOMING;
196f9b06d5cSBrian Somers /*
19724084f9bSBrian Somers  * Create divert sockets. Use only one socket if -p was specified
19824084f9bSBrian Somers  * on command line. Otherwise, create separate sockets for
19924084f9bSBrian Somers  * outgoing and incoming connnections.
20024084f9bSBrian Somers  */
20124084f9bSBrian Somers 	if (inOutPort) {
20224084f9bSBrian Somers 
20324084f9bSBrian Somers 		divertInOut = socket (PF_INET, SOCK_RAW, IPPROTO_DIVERT);
20424084f9bSBrian Somers 		if (divertInOut == -1)
20524084f9bSBrian Somers 			Quit ("Unable to create divert socket.");
20624084f9bSBrian Somers 
20724084f9bSBrian Somers 		divertIn  = -1;
20824084f9bSBrian Somers 		divertOut = -1;
20924084f9bSBrian Somers /*
21024084f9bSBrian Somers  * Bind socket.
21124084f9bSBrian Somers  */
21224084f9bSBrian Somers 
21324084f9bSBrian Somers 		addr.sin_family		= AF_INET;
21424084f9bSBrian Somers 		addr.sin_addr.s_addr	= INADDR_ANY;
21524084f9bSBrian Somers 		addr.sin_port		= inOutPort;
21624084f9bSBrian Somers 
21724084f9bSBrian Somers 		if (bind (divertInOut,
21824084f9bSBrian Somers 			  (struct sockaddr*) &addr,
21924084f9bSBrian Somers 			  sizeof addr) == -1)
22024084f9bSBrian Somers 			Quit ("Unable to bind divert socket.");
22124084f9bSBrian Somers 	}
22224084f9bSBrian Somers 	else {
22324084f9bSBrian Somers 
22424084f9bSBrian Somers 		divertIn = socket (PF_INET, SOCK_RAW, IPPROTO_DIVERT);
22524084f9bSBrian Somers 		if (divertIn == -1)
22624084f9bSBrian Somers 			Quit ("Unable to create incoming divert socket.");
22724084f9bSBrian Somers 
22824084f9bSBrian Somers 		divertOut = socket (PF_INET, SOCK_RAW, IPPROTO_DIVERT);
22924084f9bSBrian Somers 		if (divertOut == -1)
23024084f9bSBrian Somers 			Quit ("Unable to create outgoing divert socket.");
23124084f9bSBrian Somers 
23224084f9bSBrian Somers 		divertInOut = -1;
23324084f9bSBrian Somers 
23424084f9bSBrian Somers /*
23524084f9bSBrian Somers  * Bind divert sockets.
23624084f9bSBrian Somers  */
23724084f9bSBrian Somers 
23824084f9bSBrian Somers 		addr.sin_family		= AF_INET;
23924084f9bSBrian Somers 		addr.sin_addr.s_addr	= INADDR_ANY;
24024084f9bSBrian Somers 		addr.sin_port		= inPort;
24124084f9bSBrian Somers 
24224084f9bSBrian Somers 		if (bind (divertIn,
24324084f9bSBrian Somers 			  (struct sockaddr*) &addr,
24424084f9bSBrian Somers 			  sizeof addr) == -1)
24524084f9bSBrian Somers 			Quit ("Unable to bind incoming divert socket.");
24624084f9bSBrian Somers 
24724084f9bSBrian Somers 		addr.sin_family		= AF_INET;
24824084f9bSBrian Somers 		addr.sin_addr.s_addr	= INADDR_ANY;
24924084f9bSBrian Somers 		addr.sin_port		= outPort;
25024084f9bSBrian Somers 
25124084f9bSBrian Somers 		if (bind (divertOut,
25224084f9bSBrian Somers 			  (struct sockaddr*) &addr,
25324084f9bSBrian Somers 			  sizeof addr) == -1)
25424084f9bSBrian Somers 			Quit ("Unable to bind outgoing divert socket.");
25524084f9bSBrian Somers 	}
25624084f9bSBrian Somers /*
25724084f9bSBrian Somers  * Create routing socket if interface name specified.
25824084f9bSBrian Somers  */
25924084f9bSBrian Somers 	if (ifName && dynamicMode) {
26024084f9bSBrian Somers 
26124084f9bSBrian Somers 		routeSock = socket (PF_ROUTE, SOCK_RAW, 0);
26224084f9bSBrian Somers 		if (routeSock == -1)
26324084f9bSBrian Somers 			Quit ("Unable to create routing info socket.");
26424084f9bSBrian Somers 	}
26524084f9bSBrian Somers 	else
26624084f9bSBrian Somers 		routeSock = -1;
26724084f9bSBrian Somers /*
26824084f9bSBrian Somers  * Create socket for sending ICMP messages.
26924084f9bSBrian Somers  */
27024084f9bSBrian Somers 	icmpSock = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP);
27124084f9bSBrian Somers 	if (icmpSock == -1)
27224084f9bSBrian Somers 		Quit ("Unable to create ICMP socket.");
273f3d64024SBrian Somers 
274f3d64024SBrian Somers /*
275f3d64024SBrian Somers  * And disable reads for the socket, otherwise it slowly fills
276f3d64024SBrian Somers  * up with received icmps which we do not use.
277f3d64024SBrian Somers  */
278f3d64024SBrian Somers 	shutdown(icmpSock, SHUT_RD);
279f3d64024SBrian Somers 
28024084f9bSBrian Somers /*
28124084f9bSBrian Somers  * Become a daemon unless verbose mode was requested.
28224084f9bSBrian Somers  */
28324084f9bSBrian Somers 	if (!verbose)
28424084f9bSBrian Somers 		DaemonMode ();
28524084f9bSBrian Somers /*
28624084f9bSBrian Somers  * Catch signals to manage shutdown and
28724084f9bSBrian Somers  * refresh of interface address.
28824084f9bSBrian Somers  */
289cd45c931SRuslan Ermilov 	siginterrupt(SIGTERM, 1);
290cd45c931SRuslan Ermilov 	siginterrupt(SIGHUP, 1);
29124084f9bSBrian Somers 	signal (SIGTERM, InitiateShutdown);
29224084f9bSBrian Somers 	signal (SIGHUP, RefreshAddr);
29324084f9bSBrian Somers /*
29424084f9bSBrian Somers  * Set alias address if it has been given.
29524084f9bSBrian Somers  */
29624084f9bSBrian Somers 	if (aliasAddr.s_addr != INADDR_NONE)
297fb994b07SBrian Somers 		PacketAliasSetAddress (aliasAddr);
29824084f9bSBrian Somers /*
29924084f9bSBrian Somers  * We need largest descriptor number for select.
30024084f9bSBrian Somers  */
30124084f9bSBrian Somers 
30224084f9bSBrian Somers 	fdMax = -1;
30324084f9bSBrian Somers 
30424084f9bSBrian Somers 	if (divertIn > fdMax)
30524084f9bSBrian Somers 		fdMax = divertIn;
30624084f9bSBrian Somers 
30724084f9bSBrian Somers 	if (divertOut > fdMax)
30824084f9bSBrian Somers 		fdMax = divertOut;
30924084f9bSBrian Somers 
31024084f9bSBrian Somers 	if (divertInOut > fdMax)
31124084f9bSBrian Somers 		fdMax = divertInOut;
31224084f9bSBrian Somers 
31324084f9bSBrian Somers 	if (routeSock > fdMax)
31424084f9bSBrian Somers 		fdMax = routeSock;
31524084f9bSBrian Somers 
31624084f9bSBrian Somers 	while (running) {
317fb994b07SBrian Somers 
318fb994b07SBrian Somers 		if (divertInOut != -1 && !ifName && packetSock == -1) {
319fb994b07SBrian Somers /*
320fb994b07SBrian Somers  * When using only one socket, just call
321fb994b07SBrian Somers  * DoAliasing repeatedly to process packets.
322fb994b07SBrian Somers  */
32359a7c613SBrian Somers 			DoAliasing (divertInOut, DONT_KNOW);
324fb994b07SBrian Somers 			continue;
325fb994b07SBrian Somers 		}
32624084f9bSBrian Somers /*
32724084f9bSBrian Somers  * Build read mask from socket descriptors to select.
32824084f9bSBrian Somers  */
32924084f9bSBrian Somers 		FD_ZERO (&readMask);
330fb994b07SBrian Somers 		FD_ZERO (&writeMask);
33124084f9bSBrian Somers 
332fb994b07SBrian Somers /*
333fb994b07SBrian Somers  * If there is unsent packet in buffer, use select
334fb994b07SBrian Somers  * to check when socket comes writable again.
335fb994b07SBrian Somers  */
336fb994b07SBrian Somers 		if (packetSock != -1) {
337fb994b07SBrian Somers 
338fb994b07SBrian Somers 			FD_SET (packetSock, &writeMask);
339fb994b07SBrian Somers 		}
340fb994b07SBrian Somers 		else {
341fb994b07SBrian Somers /*
342fb994b07SBrian Somers  * No unsent packet exists - safe to check if
343fb994b07SBrian Somers  * new ones are available.
344fb994b07SBrian Somers  */
34524084f9bSBrian Somers 			if (divertIn != -1)
34624084f9bSBrian Somers 				FD_SET (divertIn, &readMask);
34724084f9bSBrian Somers 
34824084f9bSBrian Somers 			if (divertOut != -1)
34924084f9bSBrian Somers 				FD_SET (divertOut, &readMask);
35024084f9bSBrian Somers 
35124084f9bSBrian Somers 			if (divertInOut != -1)
35224084f9bSBrian Somers 				FD_SET (divertInOut, &readMask);
353fb994b07SBrian Somers 		}
354fb994b07SBrian Somers /*
355fb994b07SBrian Somers  * Routing info is processed always.
356fb994b07SBrian Somers  */
35724084f9bSBrian Somers 		if (routeSock != -1)
35824084f9bSBrian Somers 			FD_SET (routeSock, &readMask);
35924084f9bSBrian Somers 
36024084f9bSBrian Somers 		if (select (fdMax + 1,
36124084f9bSBrian Somers 			    &readMask,
362fb994b07SBrian Somers 			    &writeMask,
36324084f9bSBrian Somers 			    NULL,
36424084f9bSBrian Somers 			    NULL) == -1) {
36524084f9bSBrian Somers 
36624084f9bSBrian Somers 			if (errno == EINTR)
36724084f9bSBrian Somers 				continue;
36824084f9bSBrian Somers 
36924084f9bSBrian Somers 			Quit ("Select failed.");
37024084f9bSBrian Somers 		}
37124084f9bSBrian Somers 
372fb994b07SBrian Somers 		if (packetSock != -1)
373fb994b07SBrian Somers 			if (FD_ISSET (packetSock, &writeMask))
374fb994b07SBrian Somers 				FlushPacketBuffer (packetSock);
375fb994b07SBrian Somers 
37624084f9bSBrian Somers 		if (divertIn != -1)
37724084f9bSBrian Somers 			if (FD_ISSET (divertIn, &readMask))
37859a7c613SBrian Somers 				DoAliasing (divertIn, INPUT);
37924084f9bSBrian Somers 
38024084f9bSBrian Somers 		if (divertOut != -1)
38124084f9bSBrian Somers 			if (FD_ISSET (divertOut, &readMask))
38259a7c613SBrian Somers 				DoAliasing (divertOut, OUTPUT);
38324084f9bSBrian Somers 
38424084f9bSBrian Somers 		if (divertInOut != -1)
38524084f9bSBrian Somers 			if (FD_ISSET (divertInOut, &readMask))
38659a7c613SBrian Somers 				DoAliasing (divertInOut, DONT_KNOW);
38724084f9bSBrian Somers 
38824084f9bSBrian Somers 		if (routeSock != -1)
38924084f9bSBrian Somers 			if (FD_ISSET (routeSock, &readMask))
39024084f9bSBrian Somers 				HandleRoutingInfo (routeSock);
39124084f9bSBrian Somers 	}
39224084f9bSBrian Somers 
39324084f9bSBrian Somers 	if (background)
39424084f9bSBrian Somers 		unlink (PIDFILE);
39524084f9bSBrian Somers 
39624084f9bSBrian Somers 	return 0;
39724084f9bSBrian Somers }
39824084f9bSBrian Somers 
39924084f9bSBrian Somers static void DaemonMode ()
40024084f9bSBrian Somers {
40124084f9bSBrian Somers 	FILE*	pidFile;
40224084f9bSBrian Somers 
40324084f9bSBrian Somers 	daemon (0, 0);
40424084f9bSBrian Somers 	background = 1;
40524084f9bSBrian Somers 
40624084f9bSBrian Somers 	pidFile = fopen (PIDFILE, "w");
40724084f9bSBrian Somers 	if (pidFile) {
40824084f9bSBrian Somers 
40924084f9bSBrian Somers 		fprintf (pidFile, "%d\n", getpid ());
41024084f9bSBrian Somers 		fclose (pidFile);
41124084f9bSBrian Somers 	}
41224084f9bSBrian Somers }
41324084f9bSBrian Somers 
41424084f9bSBrian Somers static void ParseArgs (int argc, char** argv)
41524084f9bSBrian Somers {
41624084f9bSBrian Somers 	int		arg;
41724084f9bSBrian Somers 	char*		parm;
41824084f9bSBrian Somers 	char*		opt;
41924084f9bSBrian Somers 	char		parmBuf[256];
42024084f9bSBrian Somers 
42124084f9bSBrian Somers 	for (arg = 1; arg < argc; arg++) {
42224084f9bSBrian Somers 
42324084f9bSBrian Somers 		opt  = argv[arg];
42424084f9bSBrian Somers 		if (*opt != '-') {
42524084f9bSBrian Somers 
4260fc81af1SPhilippe Charnier 			warnx ("invalid option %s", opt);
42724084f9bSBrian Somers 			Usage ();
42824084f9bSBrian Somers 		}
42924084f9bSBrian Somers 
43024084f9bSBrian Somers 		parm = NULL;
43124084f9bSBrian Somers 		parmBuf[0] = '\0';
43224084f9bSBrian Somers 
43324084f9bSBrian Somers 		while (arg < argc - 1) {
43424084f9bSBrian Somers 
43524084f9bSBrian Somers 			if (argv[arg + 1][0] == '-')
43624084f9bSBrian Somers 				break;
43724084f9bSBrian Somers 
43824084f9bSBrian Somers 			if (parm)
43924084f9bSBrian Somers 				strcat (parmBuf, " ");
44024084f9bSBrian Somers 
44124084f9bSBrian Somers 			++arg;
44224084f9bSBrian Somers 			parm = parmBuf;
44324084f9bSBrian Somers 			strcat (parmBuf, argv[arg]);
44424084f9bSBrian Somers 		}
44524084f9bSBrian Somers 
44624084f9bSBrian Somers 		ParseOption (opt + 1, parm, 1);
44724084f9bSBrian Somers 	}
44824084f9bSBrian Somers }
44924084f9bSBrian Somers 
45059a7c613SBrian Somers static void DoAliasing (int fd, int direction)
45124084f9bSBrian Somers {
45224084f9bSBrian Somers 	int			bytes;
45324084f9bSBrian Somers 	int			origBytes;
454f9b06d5cSBrian Somers 	int			status;
45524084f9bSBrian Somers 	int			addrSize;
45624084f9bSBrian Somers 	struct ip*		ip;
45724084f9bSBrian Somers 
45824084f9bSBrian Somers 	if (assignAliasAddr) {
45924084f9bSBrian Somers 
46024084f9bSBrian Somers 		SetAliasAddressFromIfName (ifName);
46124084f9bSBrian Somers 		assignAliasAddr = 0;
46224084f9bSBrian Somers 	}
46324084f9bSBrian Somers /*
46424084f9bSBrian Somers  * Get packet from socket.
46524084f9bSBrian Somers  */
466fb994b07SBrian Somers 	addrSize  = sizeof packetAddr;
46724084f9bSBrian Somers 	origBytes = recvfrom (fd,
468fb994b07SBrian Somers 			      packetBuf,
469fb994b07SBrian Somers 			      sizeof packetBuf,
47024084f9bSBrian Somers 			      0,
471fb994b07SBrian Somers 			      (struct sockaddr*) &packetAddr,
47224084f9bSBrian Somers 			      &addrSize);
47324084f9bSBrian Somers 
47424084f9bSBrian Somers 	if (origBytes == -1) {
47524084f9bSBrian Somers 
47624084f9bSBrian Somers 		if (errno != EINTR)
4770fc81af1SPhilippe Charnier 			Warn ("read from divert socket failed");
47824084f9bSBrian Somers 
47924084f9bSBrian Somers 		return;
48024084f9bSBrian Somers 	}
48124084f9bSBrian Somers /*
48224084f9bSBrian Somers  * This is a IP packet.
48324084f9bSBrian Somers  */
484fb994b07SBrian Somers 	ip = (struct ip*) packetBuf;
485ebe70c8fSWarner Losh 	if (direction == DONT_KNOW) {
48659a7c613SBrian Somers 		if (packetAddr.sin_addr.s_addr == INADDR_ANY)
48759a7c613SBrian Somers 			direction = OUTPUT;
48859a7c613SBrian Somers 		else
48959a7c613SBrian Somers 			direction = INPUT;
490ebe70c8fSWarner Losh 	}
49124084f9bSBrian Somers 
49224084f9bSBrian Somers 	if (verbose) {
49324084f9bSBrian Somers /*
49424084f9bSBrian Somers  * Print packet direction and protocol type.
49524084f9bSBrian Somers  */
49659a7c613SBrian Somers 		printf (direction == OUTPUT ? "Out " : "In  ");
49724084f9bSBrian Somers 
49824084f9bSBrian Somers 		switch (ip->ip_p) {
49924084f9bSBrian Somers 		case IPPROTO_TCP:
50024084f9bSBrian Somers 			printf ("[TCP]  ");
50124084f9bSBrian Somers 			break;
50224084f9bSBrian Somers 
50324084f9bSBrian Somers 		case IPPROTO_UDP:
50424084f9bSBrian Somers 			printf ("[UDP]  ");
50524084f9bSBrian Somers 			break;
50624084f9bSBrian Somers 
50724084f9bSBrian Somers 		case IPPROTO_ICMP:
50824084f9bSBrian Somers 			printf ("[ICMP] ");
50924084f9bSBrian Somers 			break;
51024084f9bSBrian Somers 
51124084f9bSBrian Somers 		default:
51259a7c613SBrian Somers 			printf ("[%d]    ", ip->ip_p);
51324084f9bSBrian Somers 			break;
51424084f9bSBrian Somers 		}
51524084f9bSBrian Somers /*
51624084f9bSBrian Somers  * Print addresses.
51724084f9bSBrian Somers  */
51824084f9bSBrian Somers 		PrintPacket (ip);
51924084f9bSBrian Somers 	}
52024084f9bSBrian Somers 
52159a7c613SBrian Somers 	if (direction == OUTPUT) {
52224084f9bSBrian Somers /*
52324084f9bSBrian Somers  * Outgoing packets. Do aliasing.
52424084f9bSBrian Somers  */
525fb994b07SBrian Somers 		PacketAliasOut (packetBuf, IP_MAXPACKET);
52624084f9bSBrian Somers 	}
52724084f9bSBrian Somers 	else {
52859a7c613SBrian Somers 
52924084f9bSBrian Somers /*
53024084f9bSBrian Somers  * Do aliasing.
53124084f9bSBrian Somers  */
532f9b06d5cSBrian Somers 		status = PacketAliasIn (packetBuf, IP_MAXPACKET);
533f9b06d5cSBrian Somers 		if (status == PKT_ALIAS_IGNORED &&
534f9b06d5cSBrian Somers 		    dropIgnoredIncoming) {
535f9b06d5cSBrian Somers 
53659a7c613SBrian Somers 			if (verbose)
537f9b06d5cSBrian Somers 				printf (" dropped.\n");
53859a7c613SBrian Somers 
53959a7c613SBrian Somers 			if (logDropped)
54059a7c613SBrian Somers 				SyslogPacket (ip, LOG_WARNING, "denied");
54159a7c613SBrian Somers 
542f9b06d5cSBrian Somers 			return;
543f9b06d5cSBrian Somers 		}
54424084f9bSBrian Somers 	}
54524084f9bSBrian Somers /*
54624084f9bSBrian Somers  * Length might have changed during aliasing.
54724084f9bSBrian Somers  */
54824084f9bSBrian Somers 	bytes = ntohs (ip->ip_len);
54924084f9bSBrian Somers /*
55024084f9bSBrian Somers  * Update alias overhead size for outgoing packets.
55124084f9bSBrian Somers  */
55259a7c613SBrian Somers 	if (direction == OUTPUT &&
55324084f9bSBrian Somers 	    bytes - origBytes > aliasOverhead)
55424084f9bSBrian Somers 		aliasOverhead = bytes - origBytes;
55524084f9bSBrian Somers 
55624084f9bSBrian Somers 	if (verbose) {
55724084f9bSBrian Somers 
55824084f9bSBrian Somers /*
55924084f9bSBrian Somers  * Print addresses after aliasing.
56024084f9bSBrian Somers  */
56124084f9bSBrian Somers 		printf (" aliased to\n");
56224084f9bSBrian Somers 		printf ("           ");
56324084f9bSBrian Somers 		PrintPacket (ip);
56424084f9bSBrian Somers 		printf ("\n");
56524084f9bSBrian Somers 	}
566fb994b07SBrian Somers 
567fb994b07SBrian Somers 	packetLen  	= bytes;
568fb994b07SBrian Somers 	packetSock 	= fd;
56959a7c613SBrian Somers 	packetDirection = direction;
57059a7c613SBrian Somers 
571fb994b07SBrian Somers 	FlushPacketBuffer (fd);
572fb994b07SBrian Somers }
573fb994b07SBrian Somers 
574fb994b07SBrian Somers static void FlushPacketBuffer (int fd)
575fb994b07SBrian Somers {
576fb994b07SBrian Somers 	int			wrote;
577fb994b07SBrian Somers 	char			msgBuf[80];
57824084f9bSBrian Somers /*
57924084f9bSBrian Somers  * Put packet back for processing.
58024084f9bSBrian Somers  */
58124084f9bSBrian Somers 	wrote = sendto (fd,
582fb994b07SBrian Somers 		        packetBuf,
583fb994b07SBrian Somers 	    		packetLen,
58424084f9bSBrian Somers 	    		0,
585fb994b07SBrian Somers 	    		(struct sockaddr*) &packetAddr,
586fb994b07SBrian Somers 	    		sizeof packetAddr);
58724084f9bSBrian Somers 
588fb994b07SBrian Somers 	if (wrote != packetLen) {
589fb994b07SBrian Somers /*
590fb994b07SBrian Somers  * If buffer space is not available,
591fb994b07SBrian Somers  * just return. Main loop will take care of
592fb994b07SBrian Somers  * retrying send when space becomes available.
593fb994b07SBrian Somers  */
594fb994b07SBrian Somers 		if (errno == ENOBUFS)
595fb994b07SBrian Somers 			return;
59624084f9bSBrian Somers 
59724084f9bSBrian Somers 		if (errno == EMSGSIZE) {
59824084f9bSBrian Somers 
59959a7c613SBrian Somers 			if (packetDirection == OUTPUT &&
60024084f9bSBrian Somers 			    ifMTU != -1)
60124084f9bSBrian Somers 				SendNeedFragIcmp (icmpSock,
602fb994b07SBrian Somers 						  (struct ip*) packetBuf,
60324084f9bSBrian Somers 						  ifMTU - aliasOverhead);
60424084f9bSBrian Somers 		}
60524084f9bSBrian Somers 		else {
60624084f9bSBrian Somers 
6070fc81af1SPhilippe Charnier 			sprintf (msgBuf, "failed to write packet back");
60824084f9bSBrian Somers 			Warn (msgBuf);
60924084f9bSBrian Somers 		}
61024084f9bSBrian Somers 	}
611fb994b07SBrian Somers 
612fb994b07SBrian Somers 	packetSock = -1;
61324084f9bSBrian Somers }
61424084f9bSBrian Somers 
61524084f9bSBrian Somers static void HandleRoutingInfo (int fd)
61624084f9bSBrian Somers {
61724084f9bSBrian Somers 	int			bytes;
61824084f9bSBrian Somers 	struct if_msghdr	ifMsg;
61924084f9bSBrian Somers /*
62024084f9bSBrian Somers  * Get packet from socket.
62124084f9bSBrian Somers  */
62224084f9bSBrian Somers 	bytes = read (fd, &ifMsg, sizeof ifMsg);
62324084f9bSBrian Somers 	if (bytes == -1) {
62424084f9bSBrian Somers 
6250fc81af1SPhilippe Charnier 		Warn ("read from routing socket failed");
62624084f9bSBrian Somers 		return;
62724084f9bSBrian Somers 	}
62824084f9bSBrian Somers 
62924084f9bSBrian Somers 	if (ifMsg.ifm_version != RTM_VERSION) {
63024084f9bSBrian Somers 
6310fc81af1SPhilippe Charnier 		Warn ("unexpected packet read from routing socket");
63224084f9bSBrian Somers 		return;
63324084f9bSBrian Somers 	}
63424084f9bSBrian Somers 
63524084f9bSBrian Somers 	if (verbose)
63624084f9bSBrian Somers 		printf ("Routing message %X received.\n", ifMsg.ifm_type);
63724084f9bSBrian Somers 
63824084f9bSBrian Somers 	if (ifMsg.ifm_type != RTM_NEWADDR)
63924084f9bSBrian Somers 		return;
64024084f9bSBrian Somers 
64124084f9bSBrian Somers 	if (verbose && ifMsg.ifm_index == ifIndex)
64224084f9bSBrian Somers 		printf ("Interface address has changed.\n");
64324084f9bSBrian Somers 
64424084f9bSBrian Somers 	if (ifMsg.ifm_index == ifIndex)
64524084f9bSBrian Somers 		assignAliasAddr = 1;
64624084f9bSBrian Somers }
64724084f9bSBrian Somers 
64824084f9bSBrian Somers static void PrintPacket (struct ip* ip)
64924084f9bSBrian Somers {
65059a7c613SBrian Somers 	printf ("%s", FormatPacket (ip));
65159a7c613SBrian Somers }
65259a7c613SBrian Somers 
653902cb50aSBrian Somers static void SyslogPacket (struct ip* ip, int priority, const char *label)
65459a7c613SBrian Somers {
65559a7c613SBrian Somers 	syslog (priority, "%s %s", label, FormatPacket (ip));
65659a7c613SBrian Somers }
65759a7c613SBrian Somers 
65859a7c613SBrian Somers static char* FormatPacket (struct ip* ip)
65959a7c613SBrian Somers {
66059a7c613SBrian Somers 	static char	buf[256];
66124084f9bSBrian Somers 	struct tcphdr*	tcphdr;
66259a7c613SBrian Somers 	struct udphdr*	udphdr;
66359a7c613SBrian Somers 	struct icmp*	icmphdr;
66459a7c613SBrian Somers 	char		src[20];
66559a7c613SBrian Somers 	char		dst[20];
66624084f9bSBrian Somers 
66759a7c613SBrian Somers 	strcpy (src, inet_ntoa (ip->ip_src));
66859a7c613SBrian Somers 	strcpy (dst, inet_ntoa (ip->ip_dst));
66959a7c613SBrian Somers 
67059a7c613SBrian Somers 	switch (ip->ip_p) {
67159a7c613SBrian Somers 	case IPPROTO_TCP:
67224084f9bSBrian Somers 		tcphdr = (struct tcphdr*) ((char*) ip + (ip->ip_hl << 2));
67359a7c613SBrian Somers 		sprintf (buf, "[TCP] %s:%d -> %s:%d",
67459a7c613SBrian Somers 			      src,
67559a7c613SBrian Somers 			      ntohs (tcphdr->th_sport),
67659a7c613SBrian Somers 			      dst,
67759a7c613SBrian Somers 			      ntohs (tcphdr->th_dport));
67859a7c613SBrian Somers 		break;
67924084f9bSBrian Somers 
68059a7c613SBrian Somers 	case IPPROTO_UDP:
68159a7c613SBrian Somers 		udphdr = (struct udphdr*) ((char*) ip + (ip->ip_hl << 2));
68259a7c613SBrian Somers 		sprintf (buf, "[UDP] %s:%d -> %s:%d",
68359a7c613SBrian Somers 			      src,
68459a7c613SBrian Somers 			      ntohs (udphdr->uh_sport),
68559a7c613SBrian Somers 			      dst,
68659a7c613SBrian Somers 			      ntohs (udphdr->uh_dport));
68759a7c613SBrian Somers 		break;
68824084f9bSBrian Somers 
68959a7c613SBrian Somers 	case IPPROTO_ICMP:
69059a7c613SBrian Somers 		icmphdr = (struct icmp*) ((char*) ip + (ip->ip_hl << 2));
691b71e869dSBrian Somers 		sprintf (buf, "[ICMP] %s -> %s %u(%u)",
69259a7c613SBrian Somers 			      src,
69359a7c613SBrian Somers 			      dst,
694b71e869dSBrian Somers 			      icmphdr->icmp_type,
695b71e869dSBrian Somers 			      icmphdr->icmp_code);
69659a7c613SBrian Somers 		break;
69759a7c613SBrian Somers 
69859a7c613SBrian Somers 	default:
69959a7c613SBrian Somers 		sprintf (buf, "[%d] %s -> %s ", ip->ip_p, src, dst);
70059a7c613SBrian Somers 		break;
70159a7c613SBrian Somers 	}
70259a7c613SBrian Somers 
70359a7c613SBrian Somers 	return buf;
70424084f9bSBrian Somers }
70524084f9bSBrian Somers 
706902cb50aSBrian Somers static void SetAliasAddressFromIfName (char* ifn)
70724084f9bSBrian Somers {
70824084f9bSBrian Somers 	struct ifconf		cf;
70924084f9bSBrian Somers 	struct ifreq		buf[32];
71024084f9bSBrian Somers 	char			msg[80];
71124084f9bSBrian Somers 	struct ifreq*		ifPtr;
71224084f9bSBrian Somers 	int			extra;
71324084f9bSBrian Somers 	int			helperSock;
71424084f9bSBrian Somers 	int			bytes;
71524084f9bSBrian Somers 	struct sockaddr_in*	addr;
71624084f9bSBrian Somers 	int			found;
71724084f9bSBrian Somers 	struct ifreq		req;
71824084f9bSBrian Somers 	char			last[10];
71924084f9bSBrian Somers /*
72024084f9bSBrian Somers  * Create a dummy socket to access interface information.
72124084f9bSBrian Somers  */
72224084f9bSBrian Somers 	helperSock = socket (AF_INET, SOCK_DGRAM, 0);
72324084f9bSBrian Somers 	if (helperSock == -1) {
72424084f9bSBrian Somers 
72524084f9bSBrian Somers 		Quit ("Failed to create helper socket.");
72624084f9bSBrian Somers 		exit (1);
72724084f9bSBrian Somers 	}
72824084f9bSBrian Somers 
72924084f9bSBrian Somers 	cf.ifc_len = sizeof (buf);
73024084f9bSBrian Somers 	cf.ifc_req = buf;
73124084f9bSBrian Somers /*
73224084f9bSBrian Somers  * Get interface data.
73324084f9bSBrian Somers  */
73424084f9bSBrian Somers 	if (ioctl (helperSock, SIOCGIFCONF, &cf) == -1) {
73524084f9bSBrian Somers 
73624084f9bSBrian Somers 		Quit ("Ioctl SIOCGIFCONF failed.");
73724084f9bSBrian Somers 		exit (1);
73824084f9bSBrian Somers 	}
73924084f9bSBrian Somers 
74024084f9bSBrian Somers 	ifIndex	= 0;
74124084f9bSBrian Somers 	ifPtr	= buf;
74224084f9bSBrian Somers 	bytes	= cf.ifc_len;
74324084f9bSBrian Somers 	found   = 0;
74424084f9bSBrian Somers 	last[0] = '\0';
74524084f9bSBrian Somers /*
74624084f9bSBrian Somers  * Loop through interfaces until one with
74724084f9bSBrian Somers  * given name is found. This is done to
74824084f9bSBrian Somers  * find correct interface index for routing
74924084f9bSBrian Somers  * message processing.
75024084f9bSBrian Somers  */
75124084f9bSBrian Somers 	while (bytes) {
75224084f9bSBrian Somers 
75324084f9bSBrian Somers 		if (ifPtr->ifr_addr.sa_family == AF_INET &&
754902cb50aSBrian Somers                     !strcmp (ifPtr->ifr_name, ifn)) {
75524084f9bSBrian Somers 
75624084f9bSBrian Somers 			found = 1;
75724084f9bSBrian Somers 			break;
75824084f9bSBrian Somers 		}
75924084f9bSBrian Somers 
76024084f9bSBrian Somers 		if (strcmp (last, ifPtr->ifr_name)) {
76124084f9bSBrian Somers 
76224084f9bSBrian Somers 			strcpy (last, ifPtr->ifr_name);
76324084f9bSBrian Somers 			++ifIndex;
76424084f9bSBrian Somers 		}
76524084f9bSBrian Somers 
76624084f9bSBrian Somers 		extra = ifPtr->ifr_addr.sa_len - sizeof (struct sockaddr);
767eed2b804SArchie Cobbs 		if (extra < 0)
768eed2b804SArchie Cobbs 			extra = 0;
76924084f9bSBrian Somers 
77024084f9bSBrian Somers 		ifPtr++;
77124084f9bSBrian Somers 		ifPtr = (struct ifreq*) ((char*) ifPtr + extra);
77224084f9bSBrian Somers 		bytes -= sizeof (struct ifreq) + extra;
77324084f9bSBrian Somers 	}
77424084f9bSBrian Somers 
77524084f9bSBrian Somers 	if (!found) {
77624084f9bSBrian Somers 
77724084f9bSBrian Somers 		close (helperSock);
778902cb50aSBrian Somers 		sprintf (msg, "Unknown interface name %s.\n", ifn);
77924084f9bSBrian Somers 		Quit (msg);
78024084f9bSBrian Somers 	}
78124084f9bSBrian Somers /*
78224084f9bSBrian Somers  * Get MTU size.
78324084f9bSBrian Somers  */
784902cb50aSBrian Somers 	strcpy (req.ifr_name, ifn);
78524084f9bSBrian Somers 
78624084f9bSBrian Somers 	if (ioctl (helperSock, SIOCGIFMTU, &req) == -1)
78724084f9bSBrian Somers 		Quit ("Cannot get interface mtu size.");
78824084f9bSBrian Somers 
78924084f9bSBrian Somers 	ifMTU = req.ifr_mtu;
79024084f9bSBrian Somers /*
79124084f9bSBrian Somers  * Get interface address.
79224084f9bSBrian Somers  */
79324084f9bSBrian Somers 	if (ioctl (helperSock, SIOCGIFADDR, &req) == -1)
79424084f9bSBrian Somers 		Quit ("Cannot get interface address.");
79524084f9bSBrian Somers 
79624084f9bSBrian Somers 	addr = (struct sockaddr_in*) &req.ifr_addr;
797f627793dSJordan K. Hubbard 	PacketAliasSetAddress (addr->sin_addr);
79824084f9bSBrian Somers 	syslog (LOG_INFO, "Aliasing to %s, mtu %d bytes",
79924084f9bSBrian Somers 			  inet_ntoa (addr->sin_addr),
80024084f9bSBrian Somers 			  ifMTU);
80124084f9bSBrian Somers 
80224084f9bSBrian Somers 	close (helperSock);
80324084f9bSBrian Somers }
80424084f9bSBrian Somers 
805902cb50aSBrian Somers void Quit (const char* msg)
80624084f9bSBrian Somers {
80724084f9bSBrian Somers 	Warn (msg);
80824084f9bSBrian Somers 	exit (1);
80924084f9bSBrian Somers }
81024084f9bSBrian Somers 
811902cb50aSBrian Somers void Warn (const char* msg)
81224084f9bSBrian Somers {
81324084f9bSBrian Somers 	if (background)
81424084f9bSBrian Somers 		syslog (LOG_ALERT, "%s (%m)", msg);
81524084f9bSBrian Somers 	else
8160fc81af1SPhilippe Charnier 		warn (msg);
81724084f9bSBrian Somers }
81824084f9bSBrian Somers 
819902cb50aSBrian Somers static void RefreshAddr (int sig)
82024084f9bSBrian Somers {
82124084f9bSBrian Somers 	if (ifName)
82224084f9bSBrian Somers 		assignAliasAddr = 1;
82324084f9bSBrian Somers }
82424084f9bSBrian Somers 
825902cb50aSBrian Somers static void InitiateShutdown (int sig)
82624084f9bSBrian Somers {
82724084f9bSBrian Somers /*
82824084f9bSBrian Somers  * Start timer to allow kernel gracefully
82924084f9bSBrian Somers  * shutdown existing connections when system
83024084f9bSBrian Somers  * is shut down.
83124084f9bSBrian Somers  */
832cd45c931SRuslan Ermilov 	siginterrupt(SIGALRM, 1);
83324084f9bSBrian Somers 	signal (SIGALRM, Shutdown);
83424084f9bSBrian Somers 	alarm (10);
83524084f9bSBrian Somers }
83624084f9bSBrian Somers 
837902cb50aSBrian Somers static void Shutdown (int sig)
83824084f9bSBrian Somers {
83924084f9bSBrian Somers 	running = 0;
84024084f9bSBrian Somers }
84124084f9bSBrian Somers 
84224084f9bSBrian Somers /*
84324084f9bSBrian Somers  * Different options recognized by this program.
84424084f9bSBrian Somers  */
84524084f9bSBrian Somers 
84624084f9bSBrian Somers enum Option {
84724084f9bSBrian Somers 
84824084f9bSBrian Somers 	PacketAliasOption,
84924084f9bSBrian Somers 	Verbose,
85024084f9bSBrian Somers 	InPort,
85124084f9bSBrian Somers 	OutPort,
85224084f9bSBrian Somers 	Port,
85324084f9bSBrian Somers 	AliasAddress,
85424084f9bSBrian Somers 	InterfaceName,
85524084f9bSBrian Somers 	RedirectPort,
85624084f9bSBrian Somers 	RedirectAddress,
85724084f9bSBrian Somers 	ConfigFile,
85859a7c613SBrian Somers 	DynamicMode,
85959a7c613SBrian Somers 	PptpAlias,
86059a7c613SBrian Somers 	ProxyRule,
86159a7c613SBrian Somers  	LogDenied,
86259a7c613SBrian Somers  	LogFacility
86324084f9bSBrian Somers };
86424084f9bSBrian Somers 
86524084f9bSBrian Somers enum Param {
86624084f9bSBrian Somers 
86724084f9bSBrian Somers 	YesNo,
86824084f9bSBrian Somers 	Numeric,
86924084f9bSBrian Somers 	String,
87024084f9bSBrian Somers 	None,
87124084f9bSBrian Somers 	Address,
87224084f9bSBrian Somers 	Service
87324084f9bSBrian Somers };
87424084f9bSBrian Somers 
87524084f9bSBrian Somers /*
87624084f9bSBrian Somers  * Option information structure (used by ParseOption).
87724084f9bSBrian Somers  */
87824084f9bSBrian Somers 
87924084f9bSBrian Somers struct OptionInfo {
88024084f9bSBrian Somers 
88124084f9bSBrian Somers 	enum Option		type;
88224084f9bSBrian Somers 	int			packetAliasOpt;
88324084f9bSBrian Somers 	enum Param		parm;
884902cb50aSBrian Somers 	const char*		parmDescription;
885902cb50aSBrian Somers 	const char*		description;
886902cb50aSBrian Somers 	const char*		name;
887902cb50aSBrian Somers 	const char*		shortName;
88824084f9bSBrian Somers };
88924084f9bSBrian Somers 
89024084f9bSBrian Somers /*
89124084f9bSBrian Somers  * Table of known options.
89224084f9bSBrian Somers  */
89324084f9bSBrian Somers 
89424084f9bSBrian Somers static struct OptionInfo optionTable[] = {
89524084f9bSBrian Somers 
89624084f9bSBrian Somers 	{ PacketAliasOption,
89724084f9bSBrian Somers 		PKT_ALIAS_UNREGISTERED_ONLY,
89824084f9bSBrian Somers 		YesNo,
89924084f9bSBrian Somers 		"[yes|no]",
90024084f9bSBrian Somers 		"alias only unregistered addresses",
90124084f9bSBrian Somers 		"unregistered_only",
90224084f9bSBrian Somers 		"u" },
90324084f9bSBrian Somers 
90424084f9bSBrian Somers 	{ PacketAliasOption,
90524084f9bSBrian Somers 		PKT_ALIAS_LOG,
90624084f9bSBrian Somers 		YesNo,
90724084f9bSBrian Somers 		"[yes|no]",
90824084f9bSBrian Somers 		"enable logging",
90924084f9bSBrian Somers 		"log",
91024084f9bSBrian Somers 		"l" },
91124084f9bSBrian Somers 
91224084f9bSBrian Somers 	{ PacketAliasOption,
91359a7c613SBrian Somers 		PKT_ALIAS_PROXY_ONLY,
91459a7c613SBrian Somers 		YesNo,
91559a7c613SBrian Somers 		"[yes|no]",
91659a7c613SBrian Somers 		"proxy only",
91759a7c613SBrian Somers 		"proxy_only",
91859a7c613SBrian Somers 		NULL },
91959a7c613SBrian Somers 
92059a7c613SBrian Somers 	{ PacketAliasOption,
92159a7c613SBrian Somers 		PKT_ALIAS_REVERSE,
92259a7c613SBrian Somers 		YesNo,
92359a7c613SBrian Somers 		"[yes|no]",
92459a7c613SBrian Somers 		"operate in reverse mode",
92559a7c613SBrian Somers 		"reverse",
92659a7c613SBrian Somers 		NULL },
92759a7c613SBrian Somers 
92859a7c613SBrian Somers 	{ PacketAliasOption,
92924084f9bSBrian Somers 		PKT_ALIAS_DENY_INCOMING,
93024084f9bSBrian Somers 		YesNo,
93124084f9bSBrian Somers 		"[yes|no]",
93224084f9bSBrian Somers 		"allow incoming connections",
93324084f9bSBrian Somers 		"deny_incoming",
93424084f9bSBrian Somers 		"d" },
93524084f9bSBrian Somers 
93624084f9bSBrian Somers 	{ PacketAliasOption,
93724084f9bSBrian Somers 		PKT_ALIAS_USE_SOCKETS,
93824084f9bSBrian Somers 		YesNo,
93924084f9bSBrian Somers 		"[yes|no]",
94024084f9bSBrian Somers 		"use sockets to inhibit port conflict",
94124084f9bSBrian Somers 		"use_sockets",
94224084f9bSBrian Somers 		"s" },
94324084f9bSBrian Somers 
94424084f9bSBrian Somers 	{ PacketAliasOption,
94524084f9bSBrian Somers 		PKT_ALIAS_SAME_PORTS,
94624084f9bSBrian Somers 		YesNo,
94724084f9bSBrian Somers 		"[yes|no]",
94824084f9bSBrian Somers 		"try to keep original port numbers for connections",
94924084f9bSBrian Somers 		"same_ports",
95024084f9bSBrian Somers 		"m" },
95124084f9bSBrian Somers 
95224084f9bSBrian Somers 	{ Verbose,
95324084f9bSBrian Somers 		0,
95424084f9bSBrian Somers 		YesNo,
95524084f9bSBrian Somers 		"[yes|no]",
95624084f9bSBrian Somers 		"verbose mode, dump packet information",
95724084f9bSBrian Somers 		"verbose",
95824084f9bSBrian Somers 		"v" },
95924084f9bSBrian Somers 
96024084f9bSBrian Somers 	{ DynamicMode,
96124084f9bSBrian Somers 		0,
96224084f9bSBrian Somers 		YesNo,
96324084f9bSBrian Somers 		"[yes|no]",
96424084f9bSBrian Somers 		"dynamic mode, automatically detect interface address changes",
96524084f9bSBrian Somers 		"dynamic",
96624084f9bSBrian Somers 		NULL },
96724084f9bSBrian Somers 
96824084f9bSBrian Somers 	{ InPort,
96924084f9bSBrian Somers 		0,
97024084f9bSBrian Somers 		Service,
97124084f9bSBrian Somers 		"number|service_name",
97224084f9bSBrian Somers 		"set port for incoming packets",
97324084f9bSBrian Somers 		"in_port",
97424084f9bSBrian Somers 		"i" },
97524084f9bSBrian Somers 
97624084f9bSBrian Somers 	{ OutPort,
97724084f9bSBrian Somers 		0,
97824084f9bSBrian Somers 		Service,
97924084f9bSBrian Somers 		"number|service_name",
98024084f9bSBrian Somers 		"set port for outgoing packets",
98124084f9bSBrian Somers 		"out_port",
98224084f9bSBrian Somers 		"o" },
98324084f9bSBrian Somers 
98424084f9bSBrian Somers 	{ Port,
98524084f9bSBrian Somers 		0,
98624084f9bSBrian Somers 		Service,
98724084f9bSBrian Somers 		"number|service_name",
98824084f9bSBrian Somers 		"set port (defaults to natd/divert)",
98924084f9bSBrian Somers 		"port",
99024084f9bSBrian Somers 		"p" },
99124084f9bSBrian Somers 
99224084f9bSBrian Somers 	{ AliasAddress,
99324084f9bSBrian Somers 		0,
99424084f9bSBrian Somers 		Address,
99524084f9bSBrian Somers 		"x.x.x.x",
99624084f9bSBrian Somers 		"address to use for aliasing",
99724084f9bSBrian Somers 		"alias_address",
99824084f9bSBrian Somers 		"a" },
99924084f9bSBrian Somers 
100024084f9bSBrian Somers 	{ InterfaceName,
100124084f9bSBrian Somers 		0,
100224084f9bSBrian Somers 		String,
100324084f9bSBrian Somers 	        "network_if_name",
100424084f9bSBrian Somers 		"take aliasing address from interface",
100524084f9bSBrian Somers 		"interface",
100624084f9bSBrian Somers 		"n" },
100724084f9bSBrian Somers 
100859a7c613SBrian Somers 	{ ProxyRule,
100924084f9bSBrian Somers 		0,
101024084f9bSBrian Somers 		String,
101159a7c613SBrian Somers 	        "[type encode_ip_hdr|encode_tcp_stream] port xxxx server "
101259a7c613SBrian Somers 		"a.b.c.d:yyyy",
101359a7c613SBrian Somers 		"add transparent proxying / destination NAT",
101459a7c613SBrian Somers 		"proxy_rule",
101524084f9bSBrian Somers 		NULL },
101624084f9bSBrian Somers 
101724084f9bSBrian Somers 	{ RedirectPort,
101824084f9bSBrian Somers 		0,
101924084f9bSBrian Somers 		String,
10205d8ee958SBrian Somers 	        "tcp|udp local_addr:local_port_range [public_addr:]public_port_range"
10215d8ee958SBrian Somers 	 	" [remote_addr[:remote_port_range]]",
10225d8ee958SBrian Somers 		"redirect a port (or ports) for incoming traffic",
102324084f9bSBrian Somers 		"redirect_port",
102424084f9bSBrian Somers 		NULL },
102524084f9bSBrian Somers 
102624084f9bSBrian Somers 	{ RedirectAddress,
102724084f9bSBrian Somers 		0,
102824084f9bSBrian Somers 		String,
102924084f9bSBrian Somers 	        "local_addr public_addr",
103024084f9bSBrian Somers 		"define mapping between local and public addresses",
103124084f9bSBrian Somers 		"redirect_address",
103224084f9bSBrian Somers 		NULL },
103324084f9bSBrian Somers 
103459a7c613SBrian Somers        { PptpAlias,
103559a7c613SBrian Somers 		0,
103659a7c613SBrian Somers 		String,
103759a7c613SBrian Somers 		"src",
103859a7c613SBrian Somers 		"define inside machine for PPTP traffic",
103959a7c613SBrian Somers 		"pptpalias",
104059a7c613SBrian Somers 		NULL },
104159a7c613SBrian Somers 
104224084f9bSBrian Somers 	{ ConfigFile,
104324084f9bSBrian Somers 		0,
104424084f9bSBrian Somers 		String,
104524084f9bSBrian Somers 		"file_name",
104624084f9bSBrian Somers 		"read options from configuration file",
104724084f9bSBrian Somers 		"config",
104859a7c613SBrian Somers 		"f" },
104959a7c613SBrian Somers 
105059a7c613SBrian Somers 	{ LogDenied,
105159a7c613SBrian Somers 		0,
105259a7c613SBrian Somers 		YesNo,
105359a7c613SBrian Somers 	        "[yes|no]",
105459a7c613SBrian Somers 		"enable logging of denied incoming packets",
105559a7c613SBrian Somers 		"log_denied",
105659a7c613SBrian Somers 		NULL },
105759a7c613SBrian Somers 
105859a7c613SBrian Somers 	{ LogFacility,
105959a7c613SBrian Somers 		0,
106059a7c613SBrian Somers 		String,
106159a7c613SBrian Somers 	        "facility",
106259a7c613SBrian Somers 		"name of syslog facility to use for logging",
106359a7c613SBrian Somers 		"log_facility",
106459a7c613SBrian Somers 		NULL }
106559a7c613SBrian Somers 
106624084f9bSBrian Somers };
106724084f9bSBrian Somers 
1068902cb50aSBrian Somers static void ParseOption (const char* option, const char* parms, int cmdLine)
106924084f9bSBrian Somers {
107024084f9bSBrian Somers 	int			i;
107124084f9bSBrian Somers 	struct OptionInfo*	info;
107224084f9bSBrian Somers 	int			yesNoValue;
107324084f9bSBrian Somers 	int			aliasValue;
107424084f9bSBrian Somers 	int			numValue;
107567a886fbSBrian Somers 	u_short			uNumValue;
1076902cb50aSBrian Somers 	const char*		strValue;
107724084f9bSBrian Somers 	struct in_addr		addrValue;
107824084f9bSBrian Somers 	int			max;
107924084f9bSBrian Somers 	char*			end;
108059a7c613SBrian Somers 	CODE* 			fac_record = NULL;
108124084f9bSBrian Somers /*
108224084f9bSBrian Somers  * Find option from table.
108324084f9bSBrian Somers  */
108424084f9bSBrian Somers 	max = sizeof (optionTable) / sizeof (struct OptionInfo);
108524084f9bSBrian Somers 	for (i = 0, info = optionTable; i < max; i++, info++) {
108624084f9bSBrian Somers 
108724084f9bSBrian Somers 		if (!strcmp (info->name, option))
108824084f9bSBrian Somers 			break;
108924084f9bSBrian Somers 
109024084f9bSBrian Somers 		if (info->shortName)
109124084f9bSBrian Somers 			if (!strcmp (info->shortName, option))
109224084f9bSBrian Somers 				break;
109324084f9bSBrian Somers 	}
109424084f9bSBrian Somers 
109524084f9bSBrian Somers 	if (i >= max) {
109624084f9bSBrian Somers 
10970fc81af1SPhilippe Charnier 		warnx ("unknown option %s", option);
109824084f9bSBrian Somers 		Usage ();
109924084f9bSBrian Somers 	}
110024084f9bSBrian Somers 
110167a886fbSBrian Somers 	uNumValue	= 0;
110224084f9bSBrian Somers 	yesNoValue	= 0;
110324084f9bSBrian Somers 	numValue	= 0;
110424084f9bSBrian Somers 	strValue	= NULL;
110524084f9bSBrian Somers /*
110624084f9bSBrian Somers  * Check parameters.
110724084f9bSBrian Somers  */
110824084f9bSBrian Somers 	switch (info->parm) {
110924084f9bSBrian Somers 	case YesNo:
111024084f9bSBrian Somers 		if (!parms)
111124084f9bSBrian Somers 			parms = "yes";
111224084f9bSBrian Somers 
111324084f9bSBrian Somers 		if (!strcmp (parms, "yes"))
111424084f9bSBrian Somers 			yesNoValue = 1;
111524084f9bSBrian Somers 		else
111624084f9bSBrian Somers 			if (!strcmp (parms, "no"))
111724084f9bSBrian Somers 				yesNoValue = 0;
11180fc81af1SPhilippe Charnier 			else
11190fc81af1SPhilippe Charnier 				errx (1, "%s needs yes/no parameter", option);
112024084f9bSBrian Somers 		break;
112124084f9bSBrian Somers 
112224084f9bSBrian Somers 	case Service:
11230fc81af1SPhilippe Charnier 		if (!parms)
112467a886fbSBrian Somers 			errx (1, "%s needs service name or "
112567a886fbSBrian Somers 				 "port number parameter",
112667a886fbSBrian Somers 				 option);
112724084f9bSBrian Somers 
112867a886fbSBrian Somers 		uNumValue = StrToPort (parms, "divert");
112924084f9bSBrian Somers 		break;
113024084f9bSBrian Somers 
113124084f9bSBrian Somers 	case Numeric:
113224084f9bSBrian Somers 		if (parms)
113324084f9bSBrian Somers 			numValue = strtol (parms, &end, 10);
113424084f9bSBrian Somers 		else
1135902cb50aSBrian Somers 			end = NULL;
113624084f9bSBrian Somers 
11370fc81af1SPhilippe Charnier 		if (end == parms)
11380fc81af1SPhilippe Charnier 			errx (1, "%s needs numeric parameter", option);
113924084f9bSBrian Somers 		break;
114024084f9bSBrian Somers 
114124084f9bSBrian Somers 	case String:
114224084f9bSBrian Somers 		strValue = parms;
11430fc81af1SPhilippe Charnier 		if (!strValue)
11440fc81af1SPhilippe Charnier 			errx (1, "%s needs parameter", option);
114524084f9bSBrian Somers 		break;
114624084f9bSBrian Somers 
114724084f9bSBrian Somers 	case None:
11480fc81af1SPhilippe Charnier 		if (parms)
11490fc81af1SPhilippe Charnier 			errx (1, "%s does not take parameters", option);
115024084f9bSBrian Somers 		break;
115124084f9bSBrian Somers 
115224084f9bSBrian Somers 	case Address:
11530fc81af1SPhilippe Charnier 		if (!parms)
11540fc81af1SPhilippe Charnier 			errx (1, "%s needs address/host parameter", option);
115524084f9bSBrian Somers 
115624084f9bSBrian Somers 		StrToAddr (parms, &addrValue);
115724084f9bSBrian Somers 		break;
115824084f9bSBrian Somers 	}
115924084f9bSBrian Somers 
116024084f9bSBrian Somers 	switch (info->type) {
116124084f9bSBrian Somers 	case PacketAliasOption:
116224084f9bSBrian Somers 
116324084f9bSBrian Somers 		aliasValue = yesNoValue ? info->packetAliasOpt : 0;
1164fb994b07SBrian Somers 		PacketAliasSetMode (aliasValue, info->packetAliasOpt);
116524084f9bSBrian Somers 		break;
116624084f9bSBrian Somers 
116724084f9bSBrian Somers 	case Verbose:
116824084f9bSBrian Somers 		verbose = yesNoValue;
116924084f9bSBrian Somers 		break;
117024084f9bSBrian Somers 
117124084f9bSBrian Somers 	case DynamicMode:
117224084f9bSBrian Somers 		dynamicMode = yesNoValue;
117324084f9bSBrian Somers 		break;
117424084f9bSBrian Somers 
117524084f9bSBrian Somers 	case InPort:
117667a886fbSBrian Somers 		inPort = uNumValue;
117724084f9bSBrian Somers 		break;
117824084f9bSBrian Somers 
117924084f9bSBrian Somers 	case OutPort:
118067a886fbSBrian Somers 		outPort = uNumValue;
118124084f9bSBrian Somers 		break;
118224084f9bSBrian Somers 
118324084f9bSBrian Somers 	case Port:
118467a886fbSBrian Somers 		inOutPort = uNumValue;
118524084f9bSBrian Somers 		break;
118624084f9bSBrian Somers 
118724084f9bSBrian Somers 	case AliasAddress:
118824084f9bSBrian Somers 		memcpy (&aliasAddr, &addrValue, sizeof (struct in_addr));
118924084f9bSBrian Somers 		break;
119024084f9bSBrian Somers 
119124084f9bSBrian Somers 	case RedirectPort:
119224084f9bSBrian Somers 		SetupPortRedirect (strValue);
119324084f9bSBrian Somers 		break;
119424084f9bSBrian Somers 
119524084f9bSBrian Somers 	case RedirectAddress:
119624084f9bSBrian Somers 		SetupAddressRedirect (strValue);
119724084f9bSBrian Somers 		break;
119824084f9bSBrian Somers 
119959a7c613SBrian Somers 	case PptpAlias:
120059a7c613SBrian Somers 		SetupPptpAlias (strValue);
120159a7c613SBrian Somers 		break;
120259a7c613SBrian Somers 
120359a7c613SBrian Somers 	case ProxyRule:
120459a7c613SBrian Somers 		PacketAliasProxyRule (strValue);
120559a7c613SBrian Somers 		break;
120659a7c613SBrian Somers 
120724084f9bSBrian Somers 	case InterfaceName:
120824084f9bSBrian Somers 		if (ifName)
120924084f9bSBrian Somers 			free (ifName);
121024084f9bSBrian Somers 
121124084f9bSBrian Somers 		ifName = strdup (strValue);
121224084f9bSBrian Somers 		assignAliasAddr = 1;
121324084f9bSBrian Somers 		break;
121424084f9bSBrian Somers 
121524084f9bSBrian Somers 	case ConfigFile:
121624084f9bSBrian Somers 		ReadConfigFile (strValue);
121724084f9bSBrian Somers 		break;
121859a7c613SBrian Somers 
121959a7c613SBrian Somers 	case LogDenied:
122059a7c613SBrian Somers 		logDropped = 1;
122159a7c613SBrian Somers 		break;
122259a7c613SBrian Somers 
122359a7c613SBrian Somers 	case LogFacility:
122459a7c613SBrian Somers 
122559a7c613SBrian Somers 		fac_record = facilitynames;
122659a7c613SBrian Somers 		while (fac_record->c_name != NULL) {
122759a7c613SBrian Somers 
122859a7c613SBrian Somers 			if (!strcmp (fac_record->c_name, strValue)) {
122959a7c613SBrian Somers 
123059a7c613SBrian Somers 				logFacility = fac_record->c_val;
123159a7c613SBrian Somers 				break;
123259a7c613SBrian Somers 
123359a7c613SBrian Somers 			}
123459a7c613SBrian Somers 			else
123559a7c613SBrian Somers 				fac_record++;
123659a7c613SBrian Somers 		}
123759a7c613SBrian Somers 
123859a7c613SBrian Somers 		if(fac_record->c_name == NULL)
123959a7c613SBrian Somers 			errx(1, "Unknown log facility name: %s", strValue);
124059a7c613SBrian Somers 
124159a7c613SBrian Somers 		break;
124224084f9bSBrian Somers 	}
124324084f9bSBrian Somers }
124424084f9bSBrian Somers 
1245902cb50aSBrian Somers void ReadConfigFile (const char* fileName)
124624084f9bSBrian Somers {
124724084f9bSBrian Somers 	FILE*	file;
124824084f9bSBrian Somers 	char	buf[128];
12492e7e7c71SRuslan Ermilov 	char	*ptr, *p;
125024084f9bSBrian Somers 	char*	option;
125124084f9bSBrian Somers 
125224084f9bSBrian Somers 	file = fopen (fileName, "r");
125324084f9bSBrian Somers 	if (!file) {
125424084f9bSBrian Somers 
125524084f9bSBrian Somers 		sprintf (buf, "Cannot open config file %s.\n", fileName);
125624084f9bSBrian Somers 		Quit (buf);
125724084f9bSBrian Somers 	}
125824084f9bSBrian Somers 
125924084f9bSBrian Somers 	while (fgets (buf, sizeof (buf), file)) {
126024084f9bSBrian Somers 
126124084f9bSBrian Somers 		ptr = strchr (buf, '\n');
12620fc81af1SPhilippe Charnier 		if (!ptr)
126367a886fbSBrian Somers 			errx (1, "config line too long: %s", buf);
126424084f9bSBrian Somers 
126524084f9bSBrian Somers 		*ptr = '\0';
126624084f9bSBrian Somers 
126724084f9bSBrian Somers /*
12682e7e7c71SRuslan Ermilov  * Check for comments, strip off trailing spaces.
126924084f9bSBrian Somers  */
12702e7e7c71SRuslan Ermilov 		if ((ptr = strchr(buf, '#')))
12712e7e7c71SRuslan Ermilov 			*ptr = '\0';
12722e7e7c71SRuslan Ermilov 		for (ptr = buf; isspace(*ptr); ++ptr)
12732e7e7c71SRuslan Ermilov 			continue;
127424084f9bSBrian Somers 		if (*ptr == '\0')
127524084f9bSBrian Somers 			continue;
12762e7e7c71SRuslan Ermilov 		for (p = strchr(buf, '\0'); isspace(*--p);)
12772e7e7c71SRuslan Ermilov 			continue;
12782e7e7c71SRuslan Ermilov 		*++p = '\0';
12792e7e7c71SRuslan Ermilov 
128024084f9bSBrian Somers /*
128124084f9bSBrian Somers  * Extract option name.
128224084f9bSBrian Somers  */
128324084f9bSBrian Somers 		option = ptr;
128424084f9bSBrian Somers 		while (*ptr && !isspace (*ptr))
128524084f9bSBrian Somers 			++ptr;
128624084f9bSBrian Somers 
128724084f9bSBrian Somers 		if (*ptr != '\0') {
128824084f9bSBrian Somers 
128924084f9bSBrian Somers 			*ptr = '\0';
129024084f9bSBrian Somers 			++ptr;
129124084f9bSBrian Somers 		}
129224084f9bSBrian Somers /*
129324084f9bSBrian Somers  * Skip white space between name and parms.
129424084f9bSBrian Somers  */
129524084f9bSBrian Somers 		while (*ptr && isspace (*ptr))
129624084f9bSBrian Somers 			++ptr;
129724084f9bSBrian Somers 
129824084f9bSBrian Somers 		ParseOption (option, *ptr ? ptr : NULL, 0);
129924084f9bSBrian Somers 	}
130024084f9bSBrian Somers 
130124084f9bSBrian Somers 	fclose (file);
130224084f9bSBrian Somers }
130324084f9bSBrian Somers 
130424084f9bSBrian Somers static void Usage ()
130524084f9bSBrian Somers {
130624084f9bSBrian Somers 	int			i;
130724084f9bSBrian Somers 	int			max;
130824084f9bSBrian Somers 	struct OptionInfo*	info;
130924084f9bSBrian Somers 
131024084f9bSBrian Somers 	fprintf (stderr, "Recognized options:\n\n");
131124084f9bSBrian Somers 
131224084f9bSBrian Somers 	max = sizeof (optionTable) / sizeof (struct OptionInfo);
131324084f9bSBrian Somers 	for (i = 0, info = optionTable; i < max; i++, info++) {
131424084f9bSBrian Somers 
131524084f9bSBrian Somers 		fprintf (stderr, "-%-20s %s\n", info->name,
131624084f9bSBrian Somers 						info->parmDescription);
131724084f9bSBrian Somers 
131824084f9bSBrian Somers 		if (info->shortName)
131924084f9bSBrian Somers 			fprintf (stderr, "-%-20s %s\n", info->shortName,
132024084f9bSBrian Somers 							info->parmDescription);
132124084f9bSBrian Somers 
132224084f9bSBrian Somers 		fprintf (stderr, "      %s\n\n", info->description);
132324084f9bSBrian Somers 	}
132424084f9bSBrian Somers 
132524084f9bSBrian Somers 	exit (1);
132624084f9bSBrian Somers }
132724084f9bSBrian Somers 
1328902cb50aSBrian Somers void SetupPptpAlias (const char* parms)
132924084f9bSBrian Somers {
133024084f9bSBrian Somers 	char		buf[128];
133124084f9bSBrian Somers 	char*		ptr;
133224084f9bSBrian Somers 	struct in_addr	srcAddr;
133324084f9bSBrian Somers 
133424084f9bSBrian Somers 	strcpy (buf, parms);
133524084f9bSBrian Somers 
133624084f9bSBrian Somers /*
133724084f9bSBrian Somers  * Extract source address.
133824084f9bSBrian Somers  */
133959a7c613SBrian Somers 	ptr = strtok (buf, " \t");
13400fc81af1SPhilippe Charnier 	if (!ptr)
134159a7c613SBrian Somers 		errx(1, "pptpalias: missing src address");
134224084f9bSBrian Somers 
134359a7c613SBrian Somers 	StrToAddr (ptr, &srcAddr);
134459a7c613SBrian Somers 	PacketAliasPptp (srcAddr);
134524084f9bSBrian Somers }
134624084f9bSBrian Somers 
1347902cb50aSBrian Somers void SetupPortRedirect (const char* parms)
134824084f9bSBrian Somers {
134924084f9bSBrian Somers 	char		buf[128];
135024084f9bSBrian Somers 	char*		ptr;
135124084f9bSBrian Somers 	struct in_addr	localAddr;
135224084f9bSBrian Somers 	struct in_addr	publicAddr;
135324084f9bSBrian Somers 	struct in_addr	remoteAddr;
13545d8ee958SBrian Somers 	port_range      portRange;
13555d8ee958SBrian Somers 	u_short         localPort      = 0;
13565d8ee958SBrian Somers 	u_short         publicPort     = 0;
13575d8ee958SBrian Somers 	u_short         remotePort     = 0;
13585d8ee958SBrian Somers 	u_short         numLocalPorts  = 0;
13595d8ee958SBrian Somers 	u_short         numPublicPorts = 0;
13605d8ee958SBrian Somers 	u_short         numRemotePorts = 0;
136124084f9bSBrian Somers 	int		proto;
136224084f9bSBrian Somers 	char*		protoName;
136324084f9bSBrian Somers 	char*		separator;
13645d8ee958SBrian Somers 	int             i;
136524084f9bSBrian Somers 
136624084f9bSBrian Somers 	strcpy (buf, parms);
136724084f9bSBrian Somers /*
136824084f9bSBrian Somers  * Extract protocol.
136924084f9bSBrian Somers  */
137024084f9bSBrian Somers 	protoName = strtok (buf, " \t");
13710fc81af1SPhilippe Charnier 	if (!protoName)
13720fc81af1SPhilippe Charnier 		errx (1, "redirect_port: missing protocol");
137324084f9bSBrian Somers 
137424084f9bSBrian Somers 	proto = StrToProto (protoName);
137524084f9bSBrian Somers /*
137624084f9bSBrian Somers  * Extract local address.
137724084f9bSBrian Somers  */
137824084f9bSBrian Somers 	ptr = strtok (NULL, " \t");
13790fc81af1SPhilippe Charnier 	if (!ptr)
13800fc81af1SPhilippe Charnier 		errx (1, "redirect_port: missing local address");
138124084f9bSBrian Somers 
13825d8ee958SBrian Somers 	if ( StrToAddrAndPortRange (ptr, &localAddr, protoName, &portRange) != 0 )
13835d8ee958SBrian Somers 	        errx (1, "redirect_port: invalid local port range");
13845d8ee958SBrian Somers 
13855d8ee958SBrian Somers 	localPort     = GETLOPORT(portRange);
13865d8ee958SBrian Somers 	numLocalPorts = GETNUMPORTS(portRange);
13875d8ee958SBrian Somers 
138824084f9bSBrian Somers /*
13899c501140SBrian Somers  * Extract public port and optionally address.
139024084f9bSBrian Somers  */
139124084f9bSBrian Somers 	ptr = strtok (NULL, " \t");
13920fc81af1SPhilippe Charnier 	if (!ptr)
13930fc81af1SPhilippe Charnier 		errx (1, "redirect_port: missing public port");
139424084f9bSBrian Somers 
139524084f9bSBrian Somers 	separator = strchr (ptr, ':');
13965d8ee958SBrian Somers 	if (separator) {
13975d8ee958SBrian Somers 	        if (StrToAddrAndPortRange (ptr, &publicAddr, protoName, &portRange) != 0 )
13985d8ee958SBrian Somers 		        errx (1, "redirect_port: invalid public port range");
139924084f9bSBrian Somers 	}
14005d8ee958SBrian Somers 	else {
14015d8ee958SBrian Somers 		publicAddr.s_addr = INADDR_ANY;
14025d8ee958SBrian Somers 		if (StrToPortRange (ptr, protoName, &portRange) != 0)
14035d8ee958SBrian Somers 		        errx (1, "redirect_port: invalid public port range");
14045d8ee958SBrian Somers 	}
14055d8ee958SBrian Somers 
14065d8ee958SBrian Somers 	publicPort     = GETLOPORT(portRange);
14075d8ee958SBrian Somers 	numPublicPorts = GETNUMPORTS(portRange);
140824084f9bSBrian Somers 
140924084f9bSBrian Somers /*
141024084f9bSBrian Somers  * Extract remote address and optionally port.
141124084f9bSBrian Somers  */
141224084f9bSBrian Somers 	ptr = strtok (NULL, " \t");
141324084f9bSBrian Somers 	if (ptr) {
141424084f9bSBrian Somers 		separator = strchr (ptr, ':');
1415ebe70c8fSWarner Losh 		if (separator) {
14165d8ee958SBrian Somers 		        if (StrToAddrAndPortRange (ptr, &remoteAddr, protoName, &portRange) != 0)
14175d8ee958SBrian Somers 			        errx (1, "redirect_port: invalid remote port range");
1418ebe70c8fSWarner Losh 		} else {
14195d8ee958SBrian Somers 		        SETLOPORT(portRange, 0);
14205d8ee958SBrian Somers 			SETNUMPORTS(portRange, 1);
142124084f9bSBrian Somers 			StrToAddr (ptr, &remoteAddr);
142224084f9bSBrian Somers 		}
142324084f9bSBrian Somers 	}
142424084f9bSBrian Somers 	else {
14255d8ee958SBrian Somers 	        SETLOPORT(portRange, 0);
14265d8ee958SBrian Somers 		SETNUMPORTS(portRange, 1);
142724084f9bSBrian Somers 		remoteAddr.s_addr = INADDR_ANY;
142824084f9bSBrian Somers 	}
142924084f9bSBrian Somers 
14305d8ee958SBrian Somers 	remotePort     = GETLOPORT(portRange);
14315d8ee958SBrian Somers 	numRemotePorts = GETNUMPORTS(portRange);
14325d8ee958SBrian Somers 
14335d8ee958SBrian Somers /*
14345d8ee958SBrian Somers  * Make sure port ranges match up, then add the redirect ports.
14355d8ee958SBrian Somers  */
14365d8ee958SBrian Somers 	if (numLocalPorts != numPublicPorts)
14375d8ee958SBrian Somers 	        errx (1, "redirect_port: port ranges must be equal in size");
14385d8ee958SBrian Somers 
14395d8ee958SBrian Somers 	/* Remote port range is allowed to be '0' which means all ports. */
144029d97436SBrian Somers 	if (numRemotePorts != numLocalPorts && (numRemotePorts != 1 || remotePort != 0))
14415d8ee958SBrian Somers 	        errx (1, "redirect_port: remote port must be 0 or equal to local port range in size");
14425d8ee958SBrian Somers 
14435d8ee958SBrian Somers 	for (i = 0 ; i < numPublicPorts ; ++i) {
14445d8ee958SBrian Somers 	        /* If remotePort is all ports, set it to 0. */
14455d8ee958SBrian Somers 	        u_short remotePortCopy = remotePort + i;
14465d8ee958SBrian Somers 	        if (numRemotePorts == 1 && remotePort == 0)
14475d8ee958SBrian Somers 		        remotePortCopy = 0;
14485d8ee958SBrian Somers 
144924084f9bSBrian Somers 	        PacketAliasRedirectPort (localAddr,
14505d8ee958SBrian Somers 					 htons(localPort + i),
145124084f9bSBrian Somers 					 remoteAddr,
14525d8ee958SBrian Somers 					 htons(remotePortCopy),
145324084f9bSBrian Somers 					 publicAddr,
14545d8ee958SBrian Somers 					 htons(publicPort + i),
145524084f9bSBrian Somers 					 proto);
145624084f9bSBrian Somers 	}
14575d8ee958SBrian Somers }
145824084f9bSBrian Somers 
1459902cb50aSBrian Somers void SetupAddressRedirect (const char* parms)
146024084f9bSBrian Somers {
146124084f9bSBrian Somers 	char		buf[128];
146224084f9bSBrian Somers 	char*		ptr;
146324084f9bSBrian Somers 	struct in_addr	localAddr;
146424084f9bSBrian Somers 	struct in_addr	publicAddr;
146524084f9bSBrian Somers 
146624084f9bSBrian Somers 	strcpy (buf, parms);
146724084f9bSBrian Somers /*
146824084f9bSBrian Somers  * Extract local address.
146924084f9bSBrian Somers  */
147024084f9bSBrian Somers 	ptr = strtok (buf, " \t");
14710fc81af1SPhilippe Charnier 	if (!ptr)
14720fc81af1SPhilippe Charnier 		errx (1, "redirect_address: missing local address");
147324084f9bSBrian Somers 
147424084f9bSBrian Somers 	StrToAddr (ptr, &localAddr);
147524084f9bSBrian Somers /*
147624084f9bSBrian Somers  * Extract public address.
147724084f9bSBrian Somers  */
147824084f9bSBrian Somers 	ptr = strtok (NULL, " \t");
14790fc81af1SPhilippe Charnier 	if (!ptr)
14800fc81af1SPhilippe Charnier 		errx (1, "redirect_address: missing public address");
148124084f9bSBrian Somers 
148224084f9bSBrian Somers 	StrToAddr (ptr, &publicAddr);
148324084f9bSBrian Somers 	PacketAliasRedirectAddr (localAddr, publicAddr);
148424084f9bSBrian Somers }
148524084f9bSBrian Somers 
1486902cb50aSBrian Somers void StrToAddr (const char* str, struct in_addr* addr)
148724084f9bSBrian Somers {
148824084f9bSBrian Somers 	struct hostent* hp;
148924084f9bSBrian Somers 
149024084f9bSBrian Somers 	if (inet_aton (str, addr))
149124084f9bSBrian Somers 		return;
149224084f9bSBrian Somers 
149324084f9bSBrian Somers 	hp = gethostbyname (str);
14940fc81af1SPhilippe Charnier 	if (!hp)
14950fc81af1SPhilippe Charnier 		errx (1, "unknown host %s", str);
149624084f9bSBrian Somers 
149724084f9bSBrian Somers 	memcpy (addr, hp->h_addr, sizeof (struct in_addr));
149824084f9bSBrian Somers }
149924084f9bSBrian Somers 
1500902cb50aSBrian Somers u_short StrToPort (const char* str, const char* proto)
150124084f9bSBrian Somers {
150267a886fbSBrian Somers 	u_short		port;
150324084f9bSBrian Somers 	struct servent*	sp;
150424084f9bSBrian Somers 	char*		end;
150524084f9bSBrian Somers 
150624084f9bSBrian Somers 	port = strtol (str, &end, 10);
150724084f9bSBrian Somers 	if (end != str)
150827c20503SBrian Somers 		return htons (port);
150924084f9bSBrian Somers 
151024084f9bSBrian Somers 	sp = getservbyname (str, proto);
15110fc81af1SPhilippe Charnier 	if (!sp)
15120fc81af1SPhilippe Charnier 		errx (1, "unknown service %s/%s", str, proto);
151324084f9bSBrian Somers 
151424084f9bSBrian Somers 	return sp->s_port;
151524084f9bSBrian Somers }
151624084f9bSBrian Somers 
1517902cb50aSBrian Somers int StrToPortRange (const char* str, const char* proto, port_range *portRange)
15185d8ee958SBrian Somers {
15195d8ee958SBrian Somers 	char*           sep;
15205d8ee958SBrian Somers 	struct servent*	sp;
15215d8ee958SBrian Somers 	char*		end;
15225d8ee958SBrian Somers 	u_short         loPort;
15235d8ee958SBrian Somers 	u_short         hiPort;
15245d8ee958SBrian Somers 
15255d8ee958SBrian Somers 	/* First see if this is a service, return corresponding port if so. */
15265d8ee958SBrian Somers 	sp = getservbyname (str,proto);
15275d8ee958SBrian Somers 	if (sp) {
15285d8ee958SBrian Somers 	        SETLOPORT(*portRange, ntohs(sp->s_port));
15295d8ee958SBrian Somers 		SETNUMPORTS(*portRange, 1);
15305d8ee958SBrian Somers 		return 0;
15315d8ee958SBrian Somers 	}
15325d8ee958SBrian Somers 
15335d8ee958SBrian Somers 	/* Not a service, see if it's a single port or port range. */
15345d8ee958SBrian Somers 	sep = strchr (str, '-');
15355d8ee958SBrian Somers 	if (sep == NULL) {
15365d8ee958SBrian Somers 	        SETLOPORT(*portRange, strtol(str, &end, 10));
15375d8ee958SBrian Somers 		if (end != str) {
15385d8ee958SBrian Somers 		        /* Single port. */
15395d8ee958SBrian Somers 		        SETNUMPORTS(*portRange, 1);
15405d8ee958SBrian Somers 			return 0;
15415d8ee958SBrian Somers 		}
15425d8ee958SBrian Somers 
15435d8ee958SBrian Somers 		/* Error in port range field. */
15445d8ee958SBrian Somers 		errx (1, "unknown service %s/%s", str, proto);
15455d8ee958SBrian Somers 	}
15465d8ee958SBrian Somers 
15475d8ee958SBrian Somers 	/* Port range, get the values and sanity check. */
15485d8ee958SBrian Somers 	sscanf (str, "%hu-%hu", &loPort, &hiPort);
15495d8ee958SBrian Somers 	SETLOPORT(*portRange, loPort);
15505d8ee958SBrian Somers 	SETNUMPORTS(*portRange, 0);	/* Error by default */
15515d8ee958SBrian Somers 	if (loPort <= hiPort)
15525d8ee958SBrian Somers 	        SETNUMPORTS(*portRange, hiPort - loPort + 1);
15535d8ee958SBrian Somers 
15545d8ee958SBrian Somers 	if (GETNUMPORTS(*portRange) == 0)
15555d8ee958SBrian Somers 	        errx (1, "invalid port range %s", str);
15565d8ee958SBrian Somers 
15575d8ee958SBrian Somers 	return 0;
15585d8ee958SBrian Somers }
15595d8ee958SBrian Somers 
15605d8ee958SBrian Somers 
1561902cb50aSBrian Somers int StrToProto (const char* str)
156224084f9bSBrian Somers {
156324084f9bSBrian Somers 	if (!strcmp (str, "tcp"))
156424084f9bSBrian Somers 		return IPPROTO_TCP;
156524084f9bSBrian Somers 
156624084f9bSBrian Somers 	if (!strcmp (str, "udp"))
156724084f9bSBrian Somers 		return IPPROTO_UDP;
156824084f9bSBrian Somers 
15690fc81af1SPhilippe Charnier 	errx (1, "unknown protocol %s. Expected tcp or udp", str);
157024084f9bSBrian Somers }
157124084f9bSBrian Somers 
1572902cb50aSBrian Somers int StrToAddrAndPortRange (const char* str, struct in_addr* addr, char* proto, port_range *portRange)
157324084f9bSBrian Somers {
157424084f9bSBrian Somers 	char*	ptr;
157524084f9bSBrian Somers 
157624084f9bSBrian Somers 	ptr = strchr (str, ':');
15770fc81af1SPhilippe Charnier 	if (!ptr)
15780fc81af1SPhilippe Charnier 		errx (1, "%s is missing port number", str);
157924084f9bSBrian Somers 
158024084f9bSBrian Somers 	*ptr = '\0';
158124084f9bSBrian Somers 	++ptr;
158224084f9bSBrian Somers 
158324084f9bSBrian Somers 	StrToAddr (str, addr);
15845d8ee958SBrian Somers 	return StrToPortRange (ptr, proto, portRange);
158524084f9bSBrian Somers }
1586