xref: /freebsd/sbin/natd/natd.c (revision 4c04fa4c1a9e16ff8f66b8aabfb7687de812bf35)
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>
194c04fa4cSRuslan Ermilov #include <sys/sysctl.h>
2024084f9bSBrian Somers #include <sys/time.h>
2124084f9bSBrian Somers 
2224084f9bSBrian Somers #include <netinet/in.h>
2324084f9bSBrian Somers #include <netinet/in_systm.h>
2424084f9bSBrian Somers #include <netinet/ip.h>
2524084f9bSBrian Somers #include <machine/in_cksum.h>
2624084f9bSBrian Somers #include <netinet/tcp.h>
2759a7c613SBrian Somers #include <netinet/udp.h>
2859a7c613SBrian Somers #include <netinet/ip_icmp.h>
2924084f9bSBrian Somers #include <net/if.h>
304c04fa4cSRuslan Ermilov #include <net/if_dl.h>
3124084f9bSBrian Somers #include <net/route.h>
3224084f9bSBrian Somers #include <arpa/inet.h>
3324084f9bSBrian Somers 
3424084f9bSBrian Somers #include <alias.h>
350fc81af1SPhilippe Charnier #include <ctype.h>
360fc81af1SPhilippe Charnier #include <err.h>
370fc81af1SPhilippe Charnier #include <errno.h>
380fc81af1SPhilippe Charnier #include <netdb.h>
390fc81af1SPhilippe Charnier #include <signal.h>
400fc81af1SPhilippe Charnier #include <stdio.h>
410fc81af1SPhilippe Charnier #include <stdlib.h>
420fc81af1SPhilippe Charnier #include <string.h>
430fc81af1SPhilippe Charnier #include <syslog.h>
440fc81af1SPhilippe Charnier #include <unistd.h>
4567a886fbSBrian Somers 
4624084f9bSBrian Somers #include "natd.h"
4724084f9bSBrian Somers 
4824084f9bSBrian Somers /*
4924084f9bSBrian Somers  * Default values for input and output
5024084f9bSBrian Somers  * divert socket ports.
5124084f9bSBrian Somers  */
5224084f9bSBrian Somers 
5324084f9bSBrian Somers #define	DEFAULT_SERVICE	"natd"
5424084f9bSBrian Somers 
5524084f9bSBrian Somers /*
565d8ee958SBrian Somers  * Definition of a port range, and macros to deal with values.
575d8ee958SBrian Somers  * FORMAT:  HI 16-bits == first port in range, 0 == all ports.
585d8ee958SBrian Somers  *          LO 16-bits == number of ports in range
595d8ee958SBrian Somers  * NOTES:   - Port values are not stored in network byte order.
605d8ee958SBrian Somers  */
615d8ee958SBrian Somers 
625d8ee958SBrian Somers typedef u_long port_range;
635d8ee958SBrian Somers 
645d8ee958SBrian Somers #define GETLOPORT(x)     ((x) >> 0x10)
655d8ee958SBrian Somers #define GETNUMPORTS(x)   ((x) & 0x0000ffff)
665d8ee958SBrian Somers #define GETHIPORT(x)     (GETLOPORT((x)) + GETNUMPORTS((x)))
675d8ee958SBrian Somers 
685d8ee958SBrian Somers /* Set y to be the low-port value in port_range variable x. */
695d8ee958SBrian Somers #define SETLOPORT(x,y)   ((x) = ((x) & 0x0000ffff) | ((y) << 0x10))
705d8ee958SBrian Somers 
715d8ee958SBrian Somers /* Set y to be the number of ports in port_range variable x. */
725d8ee958SBrian Somers #define SETNUMPORTS(x,y) ((x) = ((x) & 0xffff0000) | (y))
735d8ee958SBrian Somers 
745d8ee958SBrian Somers /*
7524084f9bSBrian Somers  * Function prototypes.
7624084f9bSBrian Somers  */
7724084f9bSBrian Somers 
7859a7c613SBrian Somers static void	DoAliasing (int fd, int direction);
79902cb50aSBrian Somers static void	DaemonMode (void);
8024084f9bSBrian Somers static void	HandleRoutingInfo (int fd);
81902cb50aSBrian Somers static void	Usage (void);
8259a7c613SBrian Somers static char*	FormatPacket (struct ip*);
8324084f9bSBrian Somers static void	PrintPacket (struct ip*);
84902cb50aSBrian Somers static void	SyslogPacket (struct ip*, int priority, const char *label);
854c04fa4cSRuslan Ermilov static void	SetAliasAddressFromIfName (const char *ifName);
86902cb50aSBrian Somers static void	InitiateShutdown (int);
87902cb50aSBrian Somers static void	Shutdown (int);
88902cb50aSBrian Somers static void	RefreshAddr (int);
89902cb50aSBrian Somers static void	ParseOption (const char* option, const char* parms, int cmdLine);
90902cb50aSBrian Somers static void	ReadConfigFile (const char* fileName);
91902cb50aSBrian Somers static void	SetupPortRedirect (const char* parms);
92902cb50aSBrian Somers static void	SetupAddressRedirect (const char* parms);
93902cb50aSBrian Somers static void	SetupPptpAlias (const char* parms);
94902cb50aSBrian Somers static void	StrToAddr (const char* str, struct in_addr* addr);
95902cb50aSBrian Somers static u_short  StrToPort (const char* str, const char* proto);
96902cb50aSBrian Somers static int      StrToPortRange (const char* str, const char* proto, port_range *portRange);
97902cb50aSBrian Somers static int 	StrToProto (const char* str);
98902cb50aSBrian Somers static int      StrToAddrAndPortRange (const char* str, struct in_addr* addr, char* proto, port_range *portRange);
9924084f9bSBrian Somers static void	ParseArgs (int argc, char** argv);
100fb994b07SBrian Somers static void	FlushPacketBuffer (int fd);
10124084f9bSBrian Somers 
10224084f9bSBrian Somers /*
10324084f9bSBrian Somers  * Globals.
10424084f9bSBrian Somers  */
10524084f9bSBrian Somers 
10624084f9bSBrian Somers static	int			verbose;
10724084f9bSBrian Somers static 	int			background;
10824084f9bSBrian Somers static	int			running;
10924084f9bSBrian Somers static	int			assignAliasAddr;
11024084f9bSBrian Somers static	char*			ifName;
11124084f9bSBrian Somers static  int			ifIndex;
11267a886fbSBrian Somers static	u_short			inPort;
11367a886fbSBrian Somers static	u_short			outPort;
11467a886fbSBrian Somers static	u_short			inOutPort;
11524084f9bSBrian Somers static	struct in_addr		aliasAddr;
11624084f9bSBrian Somers static 	int			dynamicMode;
11724084f9bSBrian Somers static  int			ifMTU;
11824084f9bSBrian Somers static	int			aliasOverhead;
11924084f9bSBrian Somers static 	int			icmpSock;
120fb994b07SBrian Somers static	char			packetBuf[IP_MAXPACKET];
121fb994b07SBrian Somers static 	int			packetLen;
122fb994b07SBrian Somers static	struct sockaddr_in	packetAddr;
123fb994b07SBrian Somers static 	int			packetSock;
12459a7c613SBrian Somers static 	int			packetDirection;
125f9b06d5cSBrian Somers static  int			dropIgnoredIncoming;
12659a7c613SBrian Somers static  int			logDropped;
12759a7c613SBrian Somers static	int			logFacility;
12824084f9bSBrian Somers 
12924084f9bSBrian Somers int main (int argc, char** argv)
13024084f9bSBrian Somers {
13124084f9bSBrian Somers 	int			divertIn;
13224084f9bSBrian Somers 	int			divertOut;
13324084f9bSBrian Somers 	int			divertInOut;
13424084f9bSBrian Somers 	int			routeSock;
13524084f9bSBrian Somers 	struct sockaddr_in	addr;
13624084f9bSBrian Somers 	fd_set			readMask;
137fb994b07SBrian Somers 	fd_set			writeMask;
13824084f9bSBrian Somers 	int			fdMax;
13924084f9bSBrian Somers /*
14024084f9bSBrian Somers  * Initialize packet aliasing software.
14124084f9bSBrian Somers  * Done already here to be able to alter option bits
14224084f9bSBrian Somers  * during command line and configuration file processing.
14324084f9bSBrian Somers  */
144fb994b07SBrian Somers 	PacketAliasInit ();
14524084f9bSBrian Somers /*
14624084f9bSBrian Somers  * Parse options.
14724084f9bSBrian Somers  */
14824084f9bSBrian Somers 	inPort			= 0;
14924084f9bSBrian Somers 	outPort			= 0;
15024084f9bSBrian Somers 	verbose 		= 0;
15124084f9bSBrian Somers 	inOutPort		= 0;
15224084f9bSBrian Somers 	ifName			= NULL;
15324084f9bSBrian Somers 	ifMTU			= -1;
15424084f9bSBrian Somers 	background		= 0;
15524084f9bSBrian Somers 	running			= 1;
15624084f9bSBrian Somers 	assignAliasAddr		= 0;
15724084f9bSBrian Somers 	aliasAddr.s_addr	= INADDR_NONE;
15824084f9bSBrian Somers 	aliasOverhead		= 12;
15924084f9bSBrian Somers 	dynamicMode		= 0;
16059a7c613SBrian Somers  	logDropped		= 0;
16159a7c613SBrian Somers  	logFacility		= LOG_DAEMON;
162fb994b07SBrian Somers /*
163fb994b07SBrian Somers  * Mark packet buffer empty.
164fb994b07SBrian Somers  */
165fb994b07SBrian Somers 	packetSock		= -1;
16659a7c613SBrian Somers 	packetDirection		= DONT_KNOW;
16724084f9bSBrian Somers 
16824084f9bSBrian Somers 	ParseArgs (argc, argv);
16924084f9bSBrian Somers /*
17059a7c613SBrian Somers  * Open syslog channel.
17159a7c613SBrian Somers  */
1724c04fa4cSRuslan Ermilov 	openlog ("natd", LOG_CONS | LOG_PID | (verbose ? LOG_PERROR : 0),
1734c04fa4cSRuslan Ermilov 		 logFacility);
17459a7c613SBrian Somers /*
17524084f9bSBrian Somers  * Check that valid aliasing address has been given.
17624084f9bSBrian Somers  */
1770fc81af1SPhilippe Charnier 	if (aliasAddr.s_addr == INADDR_NONE && ifName == NULL)
1780fc81af1SPhilippe Charnier 		errx (1, "aliasing address not given");
17924084f9bSBrian Somers 
1800fc81af1SPhilippe Charnier 	if (aliasAddr.s_addr != INADDR_NONE && ifName != NULL)
18167a886fbSBrian Somers 		errx (1, "both alias address and interface "
18267a886fbSBrian Somers 			 "name are not allowed");
18324084f9bSBrian Somers /*
18424084f9bSBrian Somers  * Check that valid port number is known.
18524084f9bSBrian Somers  */
18624084f9bSBrian Somers 	if (inPort != 0 || outPort != 0)
1870fc81af1SPhilippe Charnier 		if (inPort == 0 || outPort == 0)
1880fc81af1SPhilippe Charnier 			errx (1, "both input and output ports are required");
18924084f9bSBrian Somers 
19024084f9bSBrian Somers 	if (inPort == 0 && outPort == 0 && inOutPort == 0)
19124084f9bSBrian Somers 		ParseOption ("port", DEFAULT_SERVICE, 0);
19224084f9bSBrian Somers 
19324084f9bSBrian Somers /*
194f9b06d5cSBrian Somers  * Check if ignored packets should be dropped.
195f9b06d5cSBrian Somers  */
196f9b06d5cSBrian Somers 	dropIgnoredIncoming = PacketAliasSetMode (0, 0);
197f9b06d5cSBrian Somers 	dropIgnoredIncoming &= PKT_ALIAS_DENY_INCOMING;
198f9b06d5cSBrian Somers /*
19924084f9bSBrian Somers  * Create divert sockets. Use only one socket if -p was specified
20024084f9bSBrian Somers  * on command line. Otherwise, create separate sockets for
20124084f9bSBrian Somers  * outgoing and incoming connnections.
20224084f9bSBrian Somers  */
20324084f9bSBrian Somers 	if (inOutPort) {
20424084f9bSBrian Somers 
20524084f9bSBrian Somers 		divertInOut = socket (PF_INET, SOCK_RAW, IPPROTO_DIVERT);
20624084f9bSBrian Somers 		if (divertInOut == -1)
20724084f9bSBrian Somers 			Quit ("Unable to create divert socket.");
20824084f9bSBrian Somers 
20924084f9bSBrian Somers 		divertIn  = -1;
21024084f9bSBrian Somers 		divertOut = -1;
21124084f9bSBrian Somers /*
21224084f9bSBrian Somers  * Bind socket.
21324084f9bSBrian Somers  */
21424084f9bSBrian Somers 
21524084f9bSBrian Somers 		addr.sin_family		= AF_INET;
21624084f9bSBrian Somers 		addr.sin_addr.s_addr	= INADDR_ANY;
21724084f9bSBrian Somers 		addr.sin_port		= inOutPort;
21824084f9bSBrian Somers 
21924084f9bSBrian Somers 		if (bind (divertInOut,
22024084f9bSBrian Somers 			  (struct sockaddr*) &addr,
22124084f9bSBrian Somers 			  sizeof addr) == -1)
22224084f9bSBrian Somers 			Quit ("Unable to bind divert socket.");
22324084f9bSBrian Somers 	}
22424084f9bSBrian Somers 	else {
22524084f9bSBrian Somers 
22624084f9bSBrian Somers 		divertIn = socket (PF_INET, SOCK_RAW, IPPROTO_DIVERT);
22724084f9bSBrian Somers 		if (divertIn == -1)
22824084f9bSBrian Somers 			Quit ("Unable to create incoming divert socket.");
22924084f9bSBrian Somers 
23024084f9bSBrian Somers 		divertOut = socket (PF_INET, SOCK_RAW, IPPROTO_DIVERT);
23124084f9bSBrian Somers 		if (divertOut == -1)
23224084f9bSBrian Somers 			Quit ("Unable to create outgoing divert socket.");
23324084f9bSBrian Somers 
23424084f9bSBrian Somers 		divertInOut = -1;
23524084f9bSBrian Somers 
23624084f9bSBrian Somers /*
23724084f9bSBrian Somers  * Bind divert sockets.
23824084f9bSBrian Somers  */
23924084f9bSBrian Somers 
24024084f9bSBrian Somers 		addr.sin_family		= AF_INET;
24124084f9bSBrian Somers 		addr.sin_addr.s_addr	= INADDR_ANY;
24224084f9bSBrian Somers 		addr.sin_port		= inPort;
24324084f9bSBrian Somers 
24424084f9bSBrian Somers 		if (bind (divertIn,
24524084f9bSBrian Somers 			  (struct sockaddr*) &addr,
24624084f9bSBrian Somers 			  sizeof addr) == -1)
24724084f9bSBrian Somers 			Quit ("Unable to bind incoming divert socket.");
24824084f9bSBrian Somers 
24924084f9bSBrian Somers 		addr.sin_family		= AF_INET;
25024084f9bSBrian Somers 		addr.sin_addr.s_addr	= INADDR_ANY;
25124084f9bSBrian Somers 		addr.sin_port		= outPort;
25224084f9bSBrian Somers 
25324084f9bSBrian Somers 		if (bind (divertOut,
25424084f9bSBrian Somers 			  (struct sockaddr*) &addr,
25524084f9bSBrian Somers 			  sizeof addr) == -1)
25624084f9bSBrian Somers 			Quit ("Unable to bind outgoing divert socket.");
25724084f9bSBrian Somers 	}
25824084f9bSBrian Somers /*
259f2da55a2SRuslan Ermilov  * Create routing socket if interface name specified and in dynamic mode.
26024084f9bSBrian Somers  */
261f2da55a2SRuslan Ermilov 	routeSock = -1;
262f2da55a2SRuslan Ermilov 	if (ifName) {
263f2da55a2SRuslan Ermilov 		if (dynamicMode) {
26424084f9bSBrian Somers 
26524084f9bSBrian Somers 			routeSock = socket (PF_ROUTE, SOCK_RAW, 0);
26624084f9bSBrian Somers 			if (routeSock == -1)
26724084f9bSBrian Somers 				Quit ("Unable to create routing info socket.");
268f2da55a2SRuslan Ermilov 
269f2da55a2SRuslan Ermilov 			assignAliasAddr = 1;
27024084f9bSBrian Somers 		}
27124084f9bSBrian Somers 		else
272f2da55a2SRuslan Ermilov 			SetAliasAddressFromIfName (ifName);
273f2da55a2SRuslan Ermilov 	}
27424084f9bSBrian Somers /*
27524084f9bSBrian Somers  * Create socket for sending ICMP messages.
27624084f9bSBrian Somers  */
27724084f9bSBrian Somers 	icmpSock = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP);
27824084f9bSBrian Somers 	if (icmpSock == -1)
27924084f9bSBrian Somers 		Quit ("Unable to create ICMP socket.");
280f3d64024SBrian Somers 
281f3d64024SBrian Somers /*
282f3d64024SBrian Somers  * And disable reads for the socket, otherwise it slowly fills
283f3d64024SBrian Somers  * up with received icmps which we do not use.
284f3d64024SBrian Somers  */
285f3d64024SBrian Somers 	shutdown(icmpSock, SHUT_RD);
286f3d64024SBrian Somers 
28724084f9bSBrian Somers /*
28824084f9bSBrian Somers  * Become a daemon unless verbose mode was requested.
28924084f9bSBrian Somers  */
29024084f9bSBrian Somers 	if (!verbose)
29124084f9bSBrian Somers 		DaemonMode ();
29224084f9bSBrian Somers /*
29324084f9bSBrian Somers  * Catch signals to manage shutdown and
29424084f9bSBrian Somers  * refresh of interface address.
29524084f9bSBrian Somers  */
296cd45c931SRuslan Ermilov 	siginterrupt(SIGTERM, 1);
297cd45c931SRuslan Ermilov 	siginterrupt(SIGHUP, 1);
29824084f9bSBrian Somers 	signal (SIGTERM, InitiateShutdown);
29924084f9bSBrian Somers 	signal (SIGHUP, RefreshAddr);
30024084f9bSBrian Somers /*
30124084f9bSBrian Somers  * Set alias address if it has been given.
30224084f9bSBrian Somers  */
30324084f9bSBrian Somers 	if (aliasAddr.s_addr != INADDR_NONE)
304fb994b07SBrian Somers 		PacketAliasSetAddress (aliasAddr);
30524084f9bSBrian Somers /*
30624084f9bSBrian Somers  * We need largest descriptor number for select.
30724084f9bSBrian Somers  */
30824084f9bSBrian Somers 
30924084f9bSBrian Somers 	fdMax = -1;
31024084f9bSBrian Somers 
31124084f9bSBrian Somers 	if (divertIn > fdMax)
31224084f9bSBrian Somers 		fdMax = divertIn;
31324084f9bSBrian Somers 
31424084f9bSBrian Somers 	if (divertOut > fdMax)
31524084f9bSBrian Somers 		fdMax = divertOut;
31624084f9bSBrian Somers 
31724084f9bSBrian Somers 	if (divertInOut > fdMax)
31824084f9bSBrian Somers 		fdMax = divertInOut;
31924084f9bSBrian Somers 
32024084f9bSBrian Somers 	if (routeSock > fdMax)
32124084f9bSBrian Somers 		fdMax = routeSock;
32224084f9bSBrian Somers 
32324084f9bSBrian Somers 	while (running) {
324fb994b07SBrian Somers 
325fb994b07SBrian Somers 		if (divertInOut != -1 && !ifName && packetSock == -1) {
326fb994b07SBrian Somers /*
327fb994b07SBrian Somers  * When using only one socket, just call
328fb994b07SBrian Somers  * DoAliasing repeatedly to process packets.
329fb994b07SBrian Somers  */
33059a7c613SBrian Somers 			DoAliasing (divertInOut, DONT_KNOW);
331fb994b07SBrian Somers 			continue;
332fb994b07SBrian Somers 		}
33324084f9bSBrian Somers /*
33424084f9bSBrian Somers  * Build read mask from socket descriptors to select.
33524084f9bSBrian Somers  */
33624084f9bSBrian Somers 		FD_ZERO (&readMask);
337fb994b07SBrian Somers 		FD_ZERO (&writeMask);
33824084f9bSBrian Somers 
339fb994b07SBrian Somers /*
340fb994b07SBrian Somers  * If there is unsent packet in buffer, use select
341fb994b07SBrian Somers  * to check when socket comes writable again.
342fb994b07SBrian Somers  */
343fb994b07SBrian Somers 		if (packetSock != -1) {
344fb994b07SBrian Somers 
345fb994b07SBrian Somers 			FD_SET (packetSock, &writeMask);
346fb994b07SBrian Somers 		}
347fb994b07SBrian Somers 		else {
348fb994b07SBrian Somers /*
349fb994b07SBrian Somers  * No unsent packet exists - safe to check if
350fb994b07SBrian Somers  * new ones are available.
351fb994b07SBrian Somers  */
35224084f9bSBrian Somers 			if (divertIn != -1)
35324084f9bSBrian Somers 				FD_SET (divertIn, &readMask);
35424084f9bSBrian Somers 
35524084f9bSBrian Somers 			if (divertOut != -1)
35624084f9bSBrian Somers 				FD_SET (divertOut, &readMask);
35724084f9bSBrian Somers 
35824084f9bSBrian Somers 			if (divertInOut != -1)
35924084f9bSBrian Somers 				FD_SET (divertInOut, &readMask);
360fb994b07SBrian Somers 		}
361fb994b07SBrian Somers /*
362fb994b07SBrian Somers  * Routing info is processed always.
363fb994b07SBrian Somers  */
36424084f9bSBrian Somers 		if (routeSock != -1)
36524084f9bSBrian Somers 			FD_SET (routeSock, &readMask);
36624084f9bSBrian Somers 
36724084f9bSBrian Somers 		if (select (fdMax + 1,
36824084f9bSBrian Somers 			    &readMask,
369fb994b07SBrian Somers 			    &writeMask,
37024084f9bSBrian Somers 			    NULL,
37124084f9bSBrian Somers 			    NULL) == -1) {
37224084f9bSBrian Somers 
37324084f9bSBrian Somers 			if (errno == EINTR)
37424084f9bSBrian Somers 				continue;
37524084f9bSBrian Somers 
37624084f9bSBrian Somers 			Quit ("Select failed.");
37724084f9bSBrian Somers 		}
37824084f9bSBrian Somers 
379fb994b07SBrian Somers 		if (packetSock != -1)
380fb994b07SBrian Somers 			if (FD_ISSET (packetSock, &writeMask))
381fb994b07SBrian Somers 				FlushPacketBuffer (packetSock);
382fb994b07SBrian Somers 
38324084f9bSBrian Somers 		if (divertIn != -1)
38424084f9bSBrian Somers 			if (FD_ISSET (divertIn, &readMask))
38559a7c613SBrian Somers 				DoAliasing (divertIn, INPUT);
38624084f9bSBrian Somers 
38724084f9bSBrian Somers 		if (divertOut != -1)
38824084f9bSBrian Somers 			if (FD_ISSET (divertOut, &readMask))
38959a7c613SBrian Somers 				DoAliasing (divertOut, OUTPUT);
39024084f9bSBrian Somers 
39124084f9bSBrian Somers 		if (divertInOut != -1)
39224084f9bSBrian Somers 			if (FD_ISSET (divertInOut, &readMask))
39359a7c613SBrian Somers 				DoAliasing (divertInOut, DONT_KNOW);
39424084f9bSBrian Somers 
39524084f9bSBrian Somers 		if (routeSock != -1)
39624084f9bSBrian Somers 			if (FD_ISSET (routeSock, &readMask))
39724084f9bSBrian Somers 				HandleRoutingInfo (routeSock);
39824084f9bSBrian Somers 	}
39924084f9bSBrian Somers 
40024084f9bSBrian Somers 	if (background)
40124084f9bSBrian Somers 		unlink (PIDFILE);
40224084f9bSBrian Somers 
40324084f9bSBrian Somers 	return 0;
40424084f9bSBrian Somers }
40524084f9bSBrian Somers 
40624084f9bSBrian Somers static void DaemonMode ()
40724084f9bSBrian Somers {
40824084f9bSBrian Somers 	FILE*	pidFile;
40924084f9bSBrian Somers 
41024084f9bSBrian Somers 	daemon (0, 0);
41124084f9bSBrian Somers 	background = 1;
41224084f9bSBrian Somers 
41324084f9bSBrian Somers 	pidFile = fopen (PIDFILE, "w");
41424084f9bSBrian Somers 	if (pidFile) {
41524084f9bSBrian Somers 
41624084f9bSBrian Somers 		fprintf (pidFile, "%d\n", getpid ());
41724084f9bSBrian Somers 		fclose (pidFile);
41824084f9bSBrian Somers 	}
41924084f9bSBrian Somers }
42024084f9bSBrian Somers 
42124084f9bSBrian Somers static void ParseArgs (int argc, char** argv)
42224084f9bSBrian Somers {
42324084f9bSBrian Somers 	int		arg;
42424084f9bSBrian Somers 	char*		parm;
42524084f9bSBrian Somers 	char*		opt;
42624084f9bSBrian Somers 	char		parmBuf[256];
42724084f9bSBrian Somers 
42824084f9bSBrian Somers 	for (arg = 1; arg < argc; arg++) {
42924084f9bSBrian Somers 
43024084f9bSBrian Somers 		opt  = argv[arg];
43124084f9bSBrian Somers 		if (*opt != '-') {
43224084f9bSBrian Somers 
4330fc81af1SPhilippe Charnier 			warnx ("invalid option %s", opt);
43424084f9bSBrian Somers 			Usage ();
43524084f9bSBrian Somers 		}
43624084f9bSBrian Somers 
43724084f9bSBrian Somers 		parm = NULL;
43824084f9bSBrian Somers 		parmBuf[0] = '\0';
43924084f9bSBrian Somers 
44024084f9bSBrian Somers 		while (arg < argc - 1) {
44124084f9bSBrian Somers 
44224084f9bSBrian Somers 			if (argv[arg + 1][0] == '-')
44324084f9bSBrian Somers 				break;
44424084f9bSBrian Somers 
44524084f9bSBrian Somers 			if (parm)
44624084f9bSBrian Somers 				strcat (parmBuf, " ");
44724084f9bSBrian Somers 
44824084f9bSBrian Somers 			++arg;
44924084f9bSBrian Somers 			parm = parmBuf;
45024084f9bSBrian Somers 			strcat (parmBuf, argv[arg]);
45124084f9bSBrian Somers 		}
45224084f9bSBrian Somers 
45324084f9bSBrian Somers 		ParseOption (opt + 1, parm, 1);
45424084f9bSBrian Somers 	}
45524084f9bSBrian Somers }
45624084f9bSBrian Somers 
45759a7c613SBrian Somers static void DoAliasing (int fd, int direction)
45824084f9bSBrian Somers {
45924084f9bSBrian Somers 	int			bytes;
46024084f9bSBrian Somers 	int			origBytes;
461f9b06d5cSBrian Somers 	int			status;
46224084f9bSBrian Somers 	int			addrSize;
46324084f9bSBrian Somers 	struct ip*		ip;
46424084f9bSBrian Somers 
46524084f9bSBrian Somers 	if (assignAliasAddr) {
46624084f9bSBrian Somers 
46724084f9bSBrian Somers 		SetAliasAddressFromIfName (ifName);
46824084f9bSBrian Somers 		assignAliasAddr = 0;
46924084f9bSBrian Somers 	}
47024084f9bSBrian Somers /*
47124084f9bSBrian Somers  * Get packet from socket.
47224084f9bSBrian Somers  */
473fb994b07SBrian Somers 	addrSize  = sizeof packetAddr;
47424084f9bSBrian Somers 	origBytes = recvfrom (fd,
475fb994b07SBrian Somers 			      packetBuf,
476fb994b07SBrian Somers 			      sizeof packetBuf,
47724084f9bSBrian Somers 			      0,
478fb994b07SBrian Somers 			      (struct sockaddr*) &packetAddr,
47924084f9bSBrian Somers 			      &addrSize);
48024084f9bSBrian Somers 
48124084f9bSBrian Somers 	if (origBytes == -1) {
48224084f9bSBrian Somers 
48324084f9bSBrian Somers 		if (errno != EINTR)
4840fc81af1SPhilippe Charnier 			Warn ("read from divert socket failed");
48524084f9bSBrian Somers 
48624084f9bSBrian Somers 		return;
48724084f9bSBrian Somers 	}
48824084f9bSBrian Somers /*
48924084f9bSBrian Somers  * This is a IP packet.
49024084f9bSBrian Somers  */
491fb994b07SBrian Somers 	ip = (struct ip*) packetBuf;
492ebe70c8fSWarner Losh 	if (direction == DONT_KNOW) {
49359a7c613SBrian Somers 		if (packetAddr.sin_addr.s_addr == INADDR_ANY)
49459a7c613SBrian Somers 			direction = OUTPUT;
49559a7c613SBrian Somers 		else
49659a7c613SBrian Somers 			direction = INPUT;
497ebe70c8fSWarner Losh 	}
49824084f9bSBrian Somers 
49924084f9bSBrian Somers 	if (verbose) {
50024084f9bSBrian Somers /*
50124084f9bSBrian Somers  * Print packet direction and protocol type.
50224084f9bSBrian Somers  */
50359a7c613SBrian Somers 		printf (direction == OUTPUT ? "Out " : "In  ");
50424084f9bSBrian Somers 
50524084f9bSBrian Somers 		switch (ip->ip_p) {
50624084f9bSBrian Somers 		case IPPROTO_TCP:
50724084f9bSBrian Somers 			printf ("[TCP]  ");
50824084f9bSBrian Somers 			break;
50924084f9bSBrian Somers 
51024084f9bSBrian Somers 		case IPPROTO_UDP:
51124084f9bSBrian Somers 			printf ("[UDP]  ");
51224084f9bSBrian Somers 			break;
51324084f9bSBrian Somers 
51424084f9bSBrian Somers 		case IPPROTO_ICMP:
51524084f9bSBrian Somers 			printf ("[ICMP] ");
51624084f9bSBrian Somers 			break;
51724084f9bSBrian Somers 
51824084f9bSBrian Somers 		default:
51959a7c613SBrian Somers 			printf ("[%d]    ", ip->ip_p);
52024084f9bSBrian Somers 			break;
52124084f9bSBrian Somers 		}
52224084f9bSBrian Somers /*
52324084f9bSBrian Somers  * Print addresses.
52424084f9bSBrian Somers  */
52524084f9bSBrian Somers 		PrintPacket (ip);
52624084f9bSBrian Somers 	}
52724084f9bSBrian Somers 
52859a7c613SBrian Somers 	if (direction == OUTPUT) {
52924084f9bSBrian Somers /*
53024084f9bSBrian Somers  * Outgoing packets. Do aliasing.
53124084f9bSBrian Somers  */
532fb994b07SBrian Somers 		PacketAliasOut (packetBuf, IP_MAXPACKET);
53324084f9bSBrian Somers 	}
53424084f9bSBrian Somers 	else {
53559a7c613SBrian Somers 
53624084f9bSBrian Somers /*
53724084f9bSBrian Somers  * Do aliasing.
53824084f9bSBrian Somers  */
539f9b06d5cSBrian Somers 		status = PacketAliasIn (packetBuf, IP_MAXPACKET);
540f9b06d5cSBrian Somers 		if (status == PKT_ALIAS_IGNORED &&
541f9b06d5cSBrian Somers 		    dropIgnoredIncoming) {
542f9b06d5cSBrian Somers 
54359a7c613SBrian Somers 			if (verbose)
544f9b06d5cSBrian Somers 				printf (" dropped.\n");
54559a7c613SBrian Somers 
54659a7c613SBrian Somers 			if (logDropped)
54759a7c613SBrian Somers 				SyslogPacket (ip, LOG_WARNING, "denied");
54859a7c613SBrian Somers 
549f9b06d5cSBrian Somers 			return;
550f9b06d5cSBrian Somers 		}
55124084f9bSBrian Somers 	}
55224084f9bSBrian Somers /*
55324084f9bSBrian Somers  * Length might have changed during aliasing.
55424084f9bSBrian Somers  */
55524084f9bSBrian Somers 	bytes = ntohs (ip->ip_len);
55624084f9bSBrian Somers /*
55724084f9bSBrian Somers  * Update alias overhead size for outgoing packets.
55824084f9bSBrian Somers  */
55959a7c613SBrian Somers 	if (direction == OUTPUT &&
56024084f9bSBrian Somers 	    bytes - origBytes > aliasOverhead)
56124084f9bSBrian Somers 		aliasOverhead = bytes - origBytes;
56224084f9bSBrian Somers 
56324084f9bSBrian Somers 	if (verbose) {
56424084f9bSBrian Somers 
56524084f9bSBrian Somers /*
56624084f9bSBrian Somers  * Print addresses after aliasing.
56724084f9bSBrian Somers  */
56824084f9bSBrian Somers 		printf (" aliased to\n");
56924084f9bSBrian Somers 		printf ("           ");
57024084f9bSBrian Somers 		PrintPacket (ip);
57124084f9bSBrian Somers 		printf ("\n");
57224084f9bSBrian Somers 	}
573fb994b07SBrian Somers 
574fb994b07SBrian Somers 	packetLen  	= bytes;
575fb994b07SBrian Somers 	packetSock 	= fd;
57659a7c613SBrian Somers 	packetDirection = direction;
57759a7c613SBrian Somers 
578fb994b07SBrian Somers 	FlushPacketBuffer (fd);
579fb994b07SBrian Somers }
580fb994b07SBrian Somers 
581fb994b07SBrian Somers static void FlushPacketBuffer (int fd)
582fb994b07SBrian Somers {
583fb994b07SBrian Somers 	int			wrote;
584fb994b07SBrian Somers 	char			msgBuf[80];
58524084f9bSBrian Somers /*
58624084f9bSBrian Somers  * Put packet back for processing.
58724084f9bSBrian Somers  */
58824084f9bSBrian Somers 	wrote = sendto (fd,
589fb994b07SBrian Somers 		        packetBuf,
590fb994b07SBrian Somers 	    		packetLen,
59124084f9bSBrian Somers 	    		0,
592fb994b07SBrian Somers 	    		(struct sockaddr*) &packetAddr,
593fb994b07SBrian Somers 	    		sizeof packetAddr);
59424084f9bSBrian Somers 
595fb994b07SBrian Somers 	if (wrote != packetLen) {
596fb994b07SBrian Somers /*
597fb994b07SBrian Somers  * If buffer space is not available,
598fb994b07SBrian Somers  * just return. Main loop will take care of
599fb994b07SBrian Somers  * retrying send when space becomes available.
600fb994b07SBrian Somers  */
601fb994b07SBrian Somers 		if (errno == ENOBUFS)
602fb994b07SBrian Somers 			return;
60324084f9bSBrian Somers 
60424084f9bSBrian Somers 		if (errno == EMSGSIZE) {
60524084f9bSBrian Somers 
60659a7c613SBrian Somers 			if (packetDirection == OUTPUT &&
60724084f9bSBrian Somers 			    ifMTU != -1)
60824084f9bSBrian Somers 				SendNeedFragIcmp (icmpSock,
609fb994b07SBrian Somers 						  (struct ip*) packetBuf,
61024084f9bSBrian Somers 						  ifMTU - aliasOverhead);
61124084f9bSBrian Somers 		}
61224084f9bSBrian Somers 		else {
61324084f9bSBrian Somers 
6140fc81af1SPhilippe Charnier 			sprintf (msgBuf, "failed to write packet back");
61524084f9bSBrian Somers 			Warn (msgBuf);
61624084f9bSBrian Somers 		}
61724084f9bSBrian Somers 	}
618fb994b07SBrian Somers 
619fb994b07SBrian Somers 	packetSock = -1;
62024084f9bSBrian Somers }
62124084f9bSBrian Somers 
62224084f9bSBrian Somers static void HandleRoutingInfo (int fd)
62324084f9bSBrian Somers {
62424084f9bSBrian Somers 	int			bytes;
62524084f9bSBrian Somers 	struct if_msghdr	ifMsg;
62624084f9bSBrian Somers /*
62724084f9bSBrian Somers  * Get packet from socket.
62824084f9bSBrian Somers  */
62924084f9bSBrian Somers 	bytes = read (fd, &ifMsg, sizeof ifMsg);
63024084f9bSBrian Somers 	if (bytes == -1) {
63124084f9bSBrian Somers 
6320fc81af1SPhilippe Charnier 		Warn ("read from routing socket failed");
63324084f9bSBrian Somers 		return;
63424084f9bSBrian Somers 	}
63524084f9bSBrian Somers 
63624084f9bSBrian Somers 	if (ifMsg.ifm_version != RTM_VERSION) {
63724084f9bSBrian Somers 
6380fc81af1SPhilippe Charnier 		Warn ("unexpected packet read from routing socket");
63924084f9bSBrian Somers 		return;
64024084f9bSBrian Somers 	}
64124084f9bSBrian Somers 
64224084f9bSBrian Somers 	if (verbose)
64324084f9bSBrian Somers 		printf ("Routing message %X received.\n", ifMsg.ifm_type);
64424084f9bSBrian Somers 
64524084f9bSBrian Somers 	if (ifMsg.ifm_type != RTM_NEWADDR)
64624084f9bSBrian Somers 		return;
64724084f9bSBrian Somers 
64824084f9bSBrian Somers 	if (verbose && ifMsg.ifm_index == ifIndex)
64924084f9bSBrian Somers 		printf ("Interface address has changed.\n");
65024084f9bSBrian Somers 
65124084f9bSBrian Somers 	if (ifMsg.ifm_index == ifIndex)
65224084f9bSBrian Somers 		assignAliasAddr = 1;
65324084f9bSBrian Somers }
65424084f9bSBrian Somers 
65524084f9bSBrian Somers static void PrintPacket (struct ip* ip)
65624084f9bSBrian Somers {
65759a7c613SBrian Somers 	printf ("%s", FormatPacket (ip));
65859a7c613SBrian Somers }
65959a7c613SBrian Somers 
660902cb50aSBrian Somers static void SyslogPacket (struct ip* ip, int priority, const char *label)
66159a7c613SBrian Somers {
66259a7c613SBrian Somers 	syslog (priority, "%s %s", label, FormatPacket (ip));
66359a7c613SBrian Somers }
66459a7c613SBrian Somers 
66559a7c613SBrian Somers static char* FormatPacket (struct ip* ip)
66659a7c613SBrian Somers {
66759a7c613SBrian Somers 	static char	buf[256];
66824084f9bSBrian Somers 	struct tcphdr*	tcphdr;
66959a7c613SBrian Somers 	struct udphdr*	udphdr;
67059a7c613SBrian Somers 	struct icmp*	icmphdr;
67159a7c613SBrian Somers 	char		src[20];
67259a7c613SBrian Somers 	char		dst[20];
67324084f9bSBrian Somers 
67459a7c613SBrian Somers 	strcpy (src, inet_ntoa (ip->ip_src));
67559a7c613SBrian Somers 	strcpy (dst, inet_ntoa (ip->ip_dst));
67659a7c613SBrian Somers 
67759a7c613SBrian Somers 	switch (ip->ip_p) {
67859a7c613SBrian Somers 	case IPPROTO_TCP:
67924084f9bSBrian Somers 		tcphdr = (struct tcphdr*) ((char*) ip + (ip->ip_hl << 2));
68059a7c613SBrian Somers 		sprintf (buf, "[TCP] %s:%d -> %s:%d",
68159a7c613SBrian Somers 			      src,
68259a7c613SBrian Somers 			      ntohs (tcphdr->th_sport),
68359a7c613SBrian Somers 			      dst,
68459a7c613SBrian Somers 			      ntohs (tcphdr->th_dport));
68559a7c613SBrian Somers 		break;
68624084f9bSBrian Somers 
68759a7c613SBrian Somers 	case IPPROTO_UDP:
68859a7c613SBrian Somers 		udphdr = (struct udphdr*) ((char*) ip + (ip->ip_hl << 2));
68959a7c613SBrian Somers 		sprintf (buf, "[UDP] %s:%d -> %s:%d",
69059a7c613SBrian Somers 			      src,
69159a7c613SBrian Somers 			      ntohs (udphdr->uh_sport),
69259a7c613SBrian Somers 			      dst,
69359a7c613SBrian Somers 			      ntohs (udphdr->uh_dport));
69459a7c613SBrian Somers 		break;
69524084f9bSBrian Somers 
69659a7c613SBrian Somers 	case IPPROTO_ICMP:
69759a7c613SBrian Somers 		icmphdr = (struct icmp*) ((char*) ip + (ip->ip_hl << 2));
698b71e869dSBrian Somers 		sprintf (buf, "[ICMP] %s -> %s %u(%u)",
69959a7c613SBrian Somers 			      src,
70059a7c613SBrian Somers 			      dst,
701b71e869dSBrian Somers 			      icmphdr->icmp_type,
702b71e869dSBrian Somers 			      icmphdr->icmp_code);
70359a7c613SBrian Somers 		break;
70459a7c613SBrian Somers 
70559a7c613SBrian Somers 	default:
70659a7c613SBrian Somers 		sprintf (buf, "[%d] %s -> %s ", ip->ip_p, src, dst);
70759a7c613SBrian Somers 		break;
70859a7c613SBrian Somers 	}
70959a7c613SBrian Somers 
71059a7c613SBrian Somers 	return buf;
71124084f9bSBrian Somers }
71224084f9bSBrian Somers 
7134c04fa4cSRuslan Ermilov static void
7144c04fa4cSRuslan Ermilov SetAliasAddressFromIfName(const char *ifn)
71524084f9bSBrian Somers {
7164c04fa4cSRuslan Ermilov 	size_t needed;
7174c04fa4cSRuslan Ermilov 	int mib[6];
7184c04fa4cSRuslan Ermilov 	char *buf, *lim, *next;
7194c04fa4cSRuslan Ermilov 	struct if_msghdr *ifm;
7204c04fa4cSRuslan Ermilov 	struct ifa_msghdr *ifam;
7214c04fa4cSRuslan Ermilov 	struct sockaddr_dl *sdl;
7224c04fa4cSRuslan Ermilov 	struct sockaddr_in *sin;
72324084f9bSBrian Somers 
7244c04fa4cSRuslan Ermilov 	mib[0] = CTL_NET;
7254c04fa4cSRuslan Ermilov 	mib[1] = PF_ROUTE;
7264c04fa4cSRuslan Ermilov 	mib[2] = 0;
7274c04fa4cSRuslan Ermilov 	mib[3] = AF_INET;	/* Only IP addresses please */
7284c04fa4cSRuslan Ermilov 	mib[4] = NET_RT_IFLIST;
7294c04fa4cSRuslan Ermilov 	mib[5] = 0;		/* ifIndex??? */
73024084f9bSBrian Somers /*
73124084f9bSBrian Somers  * Get interface data.
73224084f9bSBrian Somers  */
7334c04fa4cSRuslan Ermilov 	if (sysctl(mib, 6, NULL, &needed, NULL, 0) == -1)
7344c04fa4cSRuslan Ermilov 		err(1, "iflist-sysctl-estimate");
7354c04fa4cSRuslan Ermilov 	if ((buf = malloc(needed)) == NULL)
7364c04fa4cSRuslan Ermilov 		errx(1, "malloc failed");
7374c04fa4cSRuslan Ermilov 	if (sysctl(mib, 6, buf, &needed, NULL, 0) == -1)
7384c04fa4cSRuslan Ermilov 		err(1, "iflist-sysctl-get");
7394c04fa4cSRuslan Ermilov 	lim = buf + needed;
74024084f9bSBrian Somers /*
74124084f9bSBrian Somers  * Loop through interfaces until one with
74224084f9bSBrian Somers  * given name is found. This is done to
74324084f9bSBrian Somers  * find correct interface index for routing
74424084f9bSBrian Somers  * message processing.
74524084f9bSBrian Somers  */
7464c04fa4cSRuslan Ermilov 	ifIndex	= 0;
7474c04fa4cSRuslan Ermilov 	next = buf;
7484c04fa4cSRuslan Ermilov 	while (next < lim) {
7494c04fa4cSRuslan Ermilov 		ifm = (struct if_msghdr *)next;
7504c04fa4cSRuslan Ermilov 		next += ifm->ifm_msglen;
7514c04fa4cSRuslan Ermilov 		if (ifm->ifm_version != RTM_VERSION) {
7524c04fa4cSRuslan Ermilov 			if (verbose)
7534c04fa4cSRuslan Ermilov 				warnx("routing message version %d "
7544c04fa4cSRuslan Ermilov 				      "not understood", ifm->ifm_version);
7554c04fa4cSRuslan Ermilov 			continue;
7564c04fa4cSRuslan Ermilov 		}
7574c04fa4cSRuslan Ermilov 		if (ifm->ifm_type == RTM_IFINFO) {
7584c04fa4cSRuslan Ermilov 			sdl = (struct sockaddr_dl *)(ifm + 1);
7594c04fa4cSRuslan Ermilov 			if (strlen(ifn) == sdl->sdl_nlen &&
7604c04fa4cSRuslan Ermilov 			    strncmp(ifn, sdl->sdl_data, sdl->sdl_nlen) == 0) {
7614c04fa4cSRuslan Ermilov 				ifIndex = ifm->ifm_index;
7624c04fa4cSRuslan Ermilov 				ifMTU = ifm->ifm_data.ifi_mtu;
76324084f9bSBrian Somers 				break;
76424084f9bSBrian Somers 			}
76524084f9bSBrian Somers 		}
76624084f9bSBrian Somers 	}
7674c04fa4cSRuslan Ermilov 	if (!ifIndex)
7684c04fa4cSRuslan Ermilov 		errx(1, "unknown interface name %s", ifn);
76924084f9bSBrian Somers /*
77024084f9bSBrian Somers  * Get interface address.
77124084f9bSBrian Somers  */
7724c04fa4cSRuslan Ermilov 	sin = NULL;
7734c04fa4cSRuslan Ermilov 	while (next < lim) {
7744c04fa4cSRuslan Ermilov 		ifam = (struct ifa_msghdr *)next;
7754c04fa4cSRuslan Ermilov 		next += ifam->ifam_msglen;
7764c04fa4cSRuslan Ermilov 		if (ifam->ifam_version != RTM_VERSION) {
7774c04fa4cSRuslan Ermilov 			if (verbose)
7784c04fa4cSRuslan Ermilov 				warnx("routing message version %d "
7794c04fa4cSRuslan Ermilov 				      "not understood", ifam->ifam_version);
7804c04fa4cSRuslan Ermilov 			continue;
7814c04fa4cSRuslan Ermilov 		}
7824c04fa4cSRuslan Ermilov 		if (ifam->ifam_type != RTM_NEWADDR)
7834c04fa4cSRuslan Ermilov 			break;
7844c04fa4cSRuslan Ermilov 		if (ifam->ifam_addrs & RTA_IFA) {
7854c04fa4cSRuslan Ermilov 			int i;
7864c04fa4cSRuslan Ermilov 			char *cp = (char *)(ifam + 1);
78724084f9bSBrian Somers 
7884c04fa4cSRuslan Ermilov #define ROUNDUP(a) \
7894c04fa4cSRuslan Ermilov 	((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
7904c04fa4cSRuslan Ermilov #define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len))
7914c04fa4cSRuslan Ermilov 
7924c04fa4cSRuslan Ermilov 			for (i = 1; i < RTA_IFA; i <<= 1)
7934c04fa4cSRuslan Ermilov 				if (ifam->ifam_addrs & i)
7944c04fa4cSRuslan Ermilov 					ADVANCE(cp, (struct sockaddr *)cp);
7954c04fa4cSRuslan Ermilov 			if (((struct sockaddr *)cp)->sa_family == AF_INET) {
7964c04fa4cSRuslan Ermilov 				sin = (struct sockaddr_in *)cp;
7974c04fa4cSRuslan Ermilov 				break;
7984c04fa4cSRuslan Ermilov 			}
7994c04fa4cSRuslan Ermilov 		}
8004c04fa4cSRuslan Ermilov 	}
8014c04fa4cSRuslan Ermilov 	if (sin == NULL)
8024c04fa4cSRuslan Ermilov 		errx(1, "%s: cannot get interface address", ifn);
8034c04fa4cSRuslan Ermilov 
8044c04fa4cSRuslan Ermilov 	PacketAliasSetAddress(sin->sin_addr);
80524084f9bSBrian Somers 	syslog(LOG_INFO, "Aliasing to %s, mtu %d bytes",
8064c04fa4cSRuslan Ermilov 	       inet_ntoa(sin->sin_addr), ifMTU);
80724084f9bSBrian Somers 
8084c04fa4cSRuslan Ermilov 	free(buf);
80924084f9bSBrian Somers }
81024084f9bSBrian Somers 
811902cb50aSBrian Somers void Quit (const char* msg)
81224084f9bSBrian Somers {
81324084f9bSBrian Somers 	Warn (msg);
81424084f9bSBrian Somers 	exit (1);
81524084f9bSBrian Somers }
81624084f9bSBrian Somers 
817902cb50aSBrian Somers void Warn (const char* msg)
81824084f9bSBrian Somers {
81924084f9bSBrian Somers 	if (background)
82024084f9bSBrian Somers 		syslog (LOG_ALERT, "%s (%m)", msg);
82124084f9bSBrian Somers 	else
8220fc81af1SPhilippe Charnier 		warn (msg);
82324084f9bSBrian Somers }
82424084f9bSBrian Somers 
825902cb50aSBrian Somers static void RefreshAddr (int sig)
82624084f9bSBrian Somers {
82724084f9bSBrian Somers 	if (ifName)
82824084f9bSBrian Somers 		assignAliasAddr = 1;
82924084f9bSBrian Somers }
83024084f9bSBrian Somers 
831902cb50aSBrian Somers static void InitiateShutdown (int sig)
83224084f9bSBrian Somers {
83324084f9bSBrian Somers /*
83424084f9bSBrian Somers  * Start timer to allow kernel gracefully
83524084f9bSBrian Somers  * shutdown existing connections when system
83624084f9bSBrian Somers  * is shut down.
83724084f9bSBrian Somers  */
838cd45c931SRuslan Ermilov 	siginterrupt(SIGALRM, 1);
83924084f9bSBrian Somers 	signal (SIGALRM, Shutdown);
84024084f9bSBrian Somers 	alarm (10);
84124084f9bSBrian Somers }
84224084f9bSBrian Somers 
843902cb50aSBrian Somers static void Shutdown (int sig)
84424084f9bSBrian Somers {
84524084f9bSBrian Somers 	running = 0;
84624084f9bSBrian Somers }
84724084f9bSBrian Somers 
84824084f9bSBrian Somers /*
84924084f9bSBrian Somers  * Different options recognized by this program.
85024084f9bSBrian Somers  */
85124084f9bSBrian Somers 
85224084f9bSBrian Somers enum Option {
85324084f9bSBrian Somers 
85424084f9bSBrian Somers 	PacketAliasOption,
85524084f9bSBrian Somers 	Verbose,
85624084f9bSBrian Somers 	InPort,
85724084f9bSBrian Somers 	OutPort,
85824084f9bSBrian Somers 	Port,
85924084f9bSBrian Somers 	AliasAddress,
86024084f9bSBrian Somers 	InterfaceName,
86124084f9bSBrian Somers 	RedirectPort,
86224084f9bSBrian Somers 	RedirectAddress,
86324084f9bSBrian Somers 	ConfigFile,
86459a7c613SBrian Somers 	DynamicMode,
86559a7c613SBrian Somers 	PptpAlias,
86659a7c613SBrian Somers 	ProxyRule,
86759a7c613SBrian Somers  	LogDenied,
86859a7c613SBrian Somers  	LogFacility
86924084f9bSBrian Somers };
87024084f9bSBrian Somers 
87124084f9bSBrian Somers enum Param {
87224084f9bSBrian Somers 
87324084f9bSBrian Somers 	YesNo,
87424084f9bSBrian Somers 	Numeric,
87524084f9bSBrian Somers 	String,
87624084f9bSBrian Somers 	None,
87724084f9bSBrian Somers 	Address,
87824084f9bSBrian Somers 	Service
87924084f9bSBrian Somers };
88024084f9bSBrian Somers 
88124084f9bSBrian Somers /*
88224084f9bSBrian Somers  * Option information structure (used by ParseOption).
88324084f9bSBrian Somers  */
88424084f9bSBrian Somers 
88524084f9bSBrian Somers struct OptionInfo {
88624084f9bSBrian Somers 
88724084f9bSBrian Somers 	enum Option		type;
88824084f9bSBrian Somers 	int			packetAliasOpt;
88924084f9bSBrian Somers 	enum Param		parm;
890902cb50aSBrian Somers 	const char*		parmDescription;
891902cb50aSBrian Somers 	const char*		description;
892902cb50aSBrian Somers 	const char*		name;
893902cb50aSBrian Somers 	const char*		shortName;
89424084f9bSBrian Somers };
89524084f9bSBrian Somers 
89624084f9bSBrian Somers /*
89724084f9bSBrian Somers  * Table of known options.
89824084f9bSBrian Somers  */
89924084f9bSBrian Somers 
90024084f9bSBrian Somers static struct OptionInfo optionTable[] = {
90124084f9bSBrian Somers 
90224084f9bSBrian Somers 	{ PacketAliasOption,
90324084f9bSBrian Somers 		PKT_ALIAS_UNREGISTERED_ONLY,
90424084f9bSBrian Somers 		YesNo,
90524084f9bSBrian Somers 		"[yes|no]",
90624084f9bSBrian Somers 		"alias only unregistered addresses",
90724084f9bSBrian Somers 		"unregistered_only",
90824084f9bSBrian Somers 		"u" },
90924084f9bSBrian Somers 
91024084f9bSBrian Somers 	{ PacketAliasOption,
91124084f9bSBrian Somers 		PKT_ALIAS_LOG,
91224084f9bSBrian Somers 		YesNo,
91324084f9bSBrian Somers 		"[yes|no]",
91424084f9bSBrian Somers 		"enable logging",
91524084f9bSBrian Somers 		"log",
91624084f9bSBrian Somers 		"l" },
91724084f9bSBrian Somers 
91824084f9bSBrian Somers 	{ PacketAliasOption,
91959a7c613SBrian Somers 		PKT_ALIAS_PROXY_ONLY,
92059a7c613SBrian Somers 		YesNo,
92159a7c613SBrian Somers 		"[yes|no]",
92259a7c613SBrian Somers 		"proxy only",
92359a7c613SBrian Somers 		"proxy_only",
92459a7c613SBrian Somers 		NULL },
92559a7c613SBrian Somers 
92659a7c613SBrian Somers 	{ PacketAliasOption,
92759a7c613SBrian Somers 		PKT_ALIAS_REVERSE,
92859a7c613SBrian Somers 		YesNo,
92959a7c613SBrian Somers 		"[yes|no]",
93059a7c613SBrian Somers 		"operate in reverse mode",
93159a7c613SBrian Somers 		"reverse",
93259a7c613SBrian Somers 		NULL },
93359a7c613SBrian Somers 
93459a7c613SBrian Somers 	{ PacketAliasOption,
93524084f9bSBrian Somers 		PKT_ALIAS_DENY_INCOMING,
93624084f9bSBrian Somers 		YesNo,
93724084f9bSBrian Somers 		"[yes|no]",
93824084f9bSBrian Somers 		"allow incoming connections",
93924084f9bSBrian Somers 		"deny_incoming",
94024084f9bSBrian Somers 		"d" },
94124084f9bSBrian Somers 
94224084f9bSBrian Somers 	{ PacketAliasOption,
94324084f9bSBrian Somers 		PKT_ALIAS_USE_SOCKETS,
94424084f9bSBrian Somers 		YesNo,
94524084f9bSBrian Somers 		"[yes|no]",
94624084f9bSBrian Somers 		"use sockets to inhibit port conflict",
94724084f9bSBrian Somers 		"use_sockets",
94824084f9bSBrian Somers 		"s" },
94924084f9bSBrian Somers 
95024084f9bSBrian Somers 	{ PacketAliasOption,
95124084f9bSBrian Somers 		PKT_ALIAS_SAME_PORTS,
95224084f9bSBrian Somers 		YesNo,
95324084f9bSBrian Somers 		"[yes|no]",
95424084f9bSBrian Somers 		"try to keep original port numbers for connections",
95524084f9bSBrian Somers 		"same_ports",
95624084f9bSBrian Somers 		"m" },
95724084f9bSBrian Somers 
95824084f9bSBrian Somers 	{ Verbose,
95924084f9bSBrian Somers 		0,
96024084f9bSBrian Somers 		YesNo,
96124084f9bSBrian Somers 		"[yes|no]",
96224084f9bSBrian Somers 		"verbose mode, dump packet information",
96324084f9bSBrian Somers 		"verbose",
96424084f9bSBrian Somers 		"v" },
96524084f9bSBrian Somers 
96624084f9bSBrian Somers 	{ DynamicMode,
96724084f9bSBrian Somers 		0,
96824084f9bSBrian Somers 		YesNo,
96924084f9bSBrian Somers 		"[yes|no]",
97024084f9bSBrian Somers 		"dynamic mode, automatically detect interface address changes",
97124084f9bSBrian Somers 		"dynamic",
97224084f9bSBrian Somers 		NULL },
97324084f9bSBrian Somers 
97424084f9bSBrian Somers 	{ InPort,
97524084f9bSBrian Somers 		0,
97624084f9bSBrian Somers 		Service,
97724084f9bSBrian Somers 		"number|service_name",
97824084f9bSBrian Somers 		"set port for incoming packets",
97924084f9bSBrian Somers 		"in_port",
98024084f9bSBrian Somers 		"i" },
98124084f9bSBrian Somers 
98224084f9bSBrian Somers 	{ OutPort,
98324084f9bSBrian Somers 		0,
98424084f9bSBrian Somers 		Service,
98524084f9bSBrian Somers 		"number|service_name",
98624084f9bSBrian Somers 		"set port for outgoing packets",
98724084f9bSBrian Somers 		"out_port",
98824084f9bSBrian Somers 		"o" },
98924084f9bSBrian Somers 
99024084f9bSBrian Somers 	{ Port,
99124084f9bSBrian Somers 		0,
99224084f9bSBrian Somers 		Service,
99324084f9bSBrian Somers 		"number|service_name",
99424084f9bSBrian Somers 		"set port (defaults to natd/divert)",
99524084f9bSBrian Somers 		"port",
99624084f9bSBrian Somers 		"p" },
99724084f9bSBrian Somers 
99824084f9bSBrian Somers 	{ AliasAddress,
99924084f9bSBrian Somers 		0,
100024084f9bSBrian Somers 		Address,
100124084f9bSBrian Somers 		"x.x.x.x",
100224084f9bSBrian Somers 		"address to use for aliasing",
100324084f9bSBrian Somers 		"alias_address",
100424084f9bSBrian Somers 		"a" },
100524084f9bSBrian Somers 
100624084f9bSBrian Somers 	{ InterfaceName,
100724084f9bSBrian Somers 		0,
100824084f9bSBrian Somers 		String,
100924084f9bSBrian Somers 	        "network_if_name",
101024084f9bSBrian Somers 		"take aliasing address from interface",
101124084f9bSBrian Somers 		"interface",
101224084f9bSBrian Somers 		"n" },
101324084f9bSBrian Somers 
101459a7c613SBrian Somers 	{ ProxyRule,
101524084f9bSBrian Somers 		0,
101624084f9bSBrian Somers 		String,
101759a7c613SBrian Somers 	        "[type encode_ip_hdr|encode_tcp_stream] port xxxx server "
101859a7c613SBrian Somers 		"a.b.c.d:yyyy",
101959a7c613SBrian Somers 		"add transparent proxying / destination NAT",
102059a7c613SBrian Somers 		"proxy_rule",
102124084f9bSBrian Somers 		NULL },
102224084f9bSBrian Somers 
102324084f9bSBrian Somers 	{ RedirectPort,
102424084f9bSBrian Somers 		0,
102524084f9bSBrian Somers 		String,
10265d8ee958SBrian Somers 	        "tcp|udp local_addr:local_port_range [public_addr:]public_port_range"
10275d8ee958SBrian Somers 	 	" [remote_addr[:remote_port_range]]",
10285d8ee958SBrian Somers 		"redirect a port (or ports) for incoming traffic",
102924084f9bSBrian Somers 		"redirect_port",
103024084f9bSBrian Somers 		NULL },
103124084f9bSBrian Somers 
103224084f9bSBrian Somers 	{ RedirectAddress,
103324084f9bSBrian Somers 		0,
103424084f9bSBrian Somers 		String,
103524084f9bSBrian Somers 	        "local_addr public_addr",
103624084f9bSBrian Somers 		"define mapping between local and public addresses",
103724084f9bSBrian Somers 		"redirect_address",
103824084f9bSBrian Somers 		NULL },
103924084f9bSBrian Somers 
104059a7c613SBrian Somers        { PptpAlias,
104159a7c613SBrian Somers 		0,
104259a7c613SBrian Somers 		String,
104359a7c613SBrian Somers 		"src",
104459a7c613SBrian Somers 		"define inside machine for PPTP traffic",
104559a7c613SBrian Somers 		"pptpalias",
104659a7c613SBrian Somers 		NULL },
104759a7c613SBrian Somers 
104824084f9bSBrian Somers 	{ ConfigFile,
104924084f9bSBrian Somers 		0,
105024084f9bSBrian Somers 		String,
105124084f9bSBrian Somers 		"file_name",
105224084f9bSBrian Somers 		"read options from configuration file",
105324084f9bSBrian Somers 		"config",
105459a7c613SBrian Somers 		"f" },
105559a7c613SBrian Somers 
105659a7c613SBrian Somers 	{ LogDenied,
105759a7c613SBrian Somers 		0,
105859a7c613SBrian Somers 		YesNo,
105959a7c613SBrian Somers 	        "[yes|no]",
106059a7c613SBrian Somers 		"enable logging of denied incoming packets",
106159a7c613SBrian Somers 		"log_denied",
106259a7c613SBrian Somers 		NULL },
106359a7c613SBrian Somers 
106459a7c613SBrian Somers 	{ LogFacility,
106559a7c613SBrian Somers 		0,
106659a7c613SBrian Somers 		String,
106759a7c613SBrian Somers 	        "facility",
106859a7c613SBrian Somers 		"name of syslog facility to use for logging",
106959a7c613SBrian Somers 		"log_facility",
107059a7c613SBrian Somers 		NULL }
107159a7c613SBrian Somers 
107224084f9bSBrian Somers };
107324084f9bSBrian Somers 
1074902cb50aSBrian Somers static void ParseOption (const char* option, const char* parms, int cmdLine)
107524084f9bSBrian Somers {
107624084f9bSBrian Somers 	int			i;
107724084f9bSBrian Somers 	struct OptionInfo*	info;
107824084f9bSBrian Somers 	int			yesNoValue;
107924084f9bSBrian Somers 	int			aliasValue;
108024084f9bSBrian Somers 	int			numValue;
108167a886fbSBrian Somers 	u_short			uNumValue;
1082902cb50aSBrian Somers 	const char*		strValue;
108324084f9bSBrian Somers 	struct in_addr		addrValue;
108424084f9bSBrian Somers 	int			max;
108524084f9bSBrian Somers 	char*			end;
108659a7c613SBrian Somers 	CODE* 			fac_record = NULL;
108724084f9bSBrian Somers /*
108824084f9bSBrian Somers  * Find option from table.
108924084f9bSBrian Somers  */
109024084f9bSBrian Somers 	max = sizeof (optionTable) / sizeof (struct OptionInfo);
109124084f9bSBrian Somers 	for (i = 0, info = optionTable; i < max; i++, info++) {
109224084f9bSBrian Somers 
109324084f9bSBrian Somers 		if (!strcmp (info->name, option))
109424084f9bSBrian Somers 			break;
109524084f9bSBrian Somers 
109624084f9bSBrian Somers 		if (info->shortName)
109724084f9bSBrian Somers 			if (!strcmp (info->shortName, option))
109824084f9bSBrian Somers 				break;
109924084f9bSBrian Somers 	}
110024084f9bSBrian Somers 
110124084f9bSBrian Somers 	if (i >= max) {
110224084f9bSBrian Somers 
11030fc81af1SPhilippe Charnier 		warnx ("unknown option %s", option);
110424084f9bSBrian Somers 		Usage ();
110524084f9bSBrian Somers 	}
110624084f9bSBrian Somers 
110767a886fbSBrian Somers 	uNumValue	= 0;
110824084f9bSBrian Somers 	yesNoValue	= 0;
110924084f9bSBrian Somers 	numValue	= 0;
111024084f9bSBrian Somers 	strValue	= NULL;
111124084f9bSBrian Somers /*
111224084f9bSBrian Somers  * Check parameters.
111324084f9bSBrian Somers  */
111424084f9bSBrian Somers 	switch (info->parm) {
111524084f9bSBrian Somers 	case YesNo:
111624084f9bSBrian Somers 		if (!parms)
111724084f9bSBrian Somers 			parms = "yes";
111824084f9bSBrian Somers 
111924084f9bSBrian Somers 		if (!strcmp (parms, "yes"))
112024084f9bSBrian Somers 			yesNoValue = 1;
112124084f9bSBrian Somers 		else
112224084f9bSBrian Somers 			if (!strcmp (parms, "no"))
112324084f9bSBrian Somers 				yesNoValue = 0;
11240fc81af1SPhilippe Charnier 			else
11250fc81af1SPhilippe Charnier 				errx (1, "%s needs yes/no parameter", option);
112624084f9bSBrian Somers 		break;
112724084f9bSBrian Somers 
112824084f9bSBrian Somers 	case Service:
11290fc81af1SPhilippe Charnier 		if (!parms)
113067a886fbSBrian Somers 			errx (1, "%s needs service name or "
113167a886fbSBrian Somers 				 "port number parameter",
113267a886fbSBrian Somers 				 option);
113324084f9bSBrian Somers 
113467a886fbSBrian Somers 		uNumValue = StrToPort (parms, "divert");
113524084f9bSBrian Somers 		break;
113624084f9bSBrian Somers 
113724084f9bSBrian Somers 	case Numeric:
113824084f9bSBrian Somers 		if (parms)
113924084f9bSBrian Somers 			numValue = strtol (parms, &end, 10);
114024084f9bSBrian Somers 		else
1141902cb50aSBrian Somers 			end = NULL;
114224084f9bSBrian Somers 
11430fc81af1SPhilippe Charnier 		if (end == parms)
11440fc81af1SPhilippe Charnier 			errx (1, "%s needs numeric parameter", option);
114524084f9bSBrian Somers 		break;
114624084f9bSBrian Somers 
114724084f9bSBrian Somers 	case String:
114824084f9bSBrian Somers 		strValue = parms;
11490fc81af1SPhilippe Charnier 		if (!strValue)
11500fc81af1SPhilippe Charnier 			errx (1, "%s needs parameter", option);
115124084f9bSBrian Somers 		break;
115224084f9bSBrian Somers 
115324084f9bSBrian Somers 	case None:
11540fc81af1SPhilippe Charnier 		if (parms)
11550fc81af1SPhilippe Charnier 			errx (1, "%s does not take parameters", option);
115624084f9bSBrian Somers 		break;
115724084f9bSBrian Somers 
115824084f9bSBrian Somers 	case Address:
11590fc81af1SPhilippe Charnier 		if (!parms)
11600fc81af1SPhilippe Charnier 			errx (1, "%s needs address/host parameter", option);
116124084f9bSBrian Somers 
116224084f9bSBrian Somers 		StrToAddr (parms, &addrValue);
116324084f9bSBrian Somers 		break;
116424084f9bSBrian Somers 	}
116524084f9bSBrian Somers 
116624084f9bSBrian Somers 	switch (info->type) {
116724084f9bSBrian Somers 	case PacketAliasOption:
116824084f9bSBrian Somers 
116924084f9bSBrian Somers 		aliasValue = yesNoValue ? info->packetAliasOpt : 0;
1170fb994b07SBrian Somers 		PacketAliasSetMode (aliasValue, info->packetAliasOpt);
117124084f9bSBrian Somers 		break;
117224084f9bSBrian Somers 
117324084f9bSBrian Somers 	case Verbose:
117424084f9bSBrian Somers 		verbose = yesNoValue;
117524084f9bSBrian Somers 		break;
117624084f9bSBrian Somers 
117724084f9bSBrian Somers 	case DynamicMode:
117824084f9bSBrian Somers 		dynamicMode = yesNoValue;
117924084f9bSBrian Somers 		break;
118024084f9bSBrian Somers 
118124084f9bSBrian Somers 	case InPort:
118267a886fbSBrian Somers 		inPort = uNumValue;
118324084f9bSBrian Somers 		break;
118424084f9bSBrian Somers 
118524084f9bSBrian Somers 	case OutPort:
118667a886fbSBrian Somers 		outPort = uNumValue;
118724084f9bSBrian Somers 		break;
118824084f9bSBrian Somers 
118924084f9bSBrian Somers 	case Port:
119067a886fbSBrian Somers 		inOutPort = uNumValue;
119124084f9bSBrian Somers 		break;
119224084f9bSBrian Somers 
119324084f9bSBrian Somers 	case AliasAddress:
119424084f9bSBrian Somers 		memcpy (&aliasAddr, &addrValue, sizeof (struct in_addr));
119524084f9bSBrian Somers 		break;
119624084f9bSBrian Somers 
119724084f9bSBrian Somers 	case RedirectPort:
119824084f9bSBrian Somers 		SetupPortRedirect (strValue);
119924084f9bSBrian Somers 		break;
120024084f9bSBrian Somers 
120124084f9bSBrian Somers 	case RedirectAddress:
120224084f9bSBrian Somers 		SetupAddressRedirect (strValue);
120324084f9bSBrian Somers 		break;
120424084f9bSBrian Somers 
120559a7c613SBrian Somers 	case PptpAlias:
120659a7c613SBrian Somers 		SetupPptpAlias (strValue);
120759a7c613SBrian Somers 		break;
120859a7c613SBrian Somers 
120959a7c613SBrian Somers 	case ProxyRule:
121059a7c613SBrian Somers 		PacketAliasProxyRule (strValue);
121159a7c613SBrian Somers 		break;
121259a7c613SBrian Somers 
121324084f9bSBrian Somers 	case InterfaceName:
121424084f9bSBrian Somers 		if (ifName)
121524084f9bSBrian Somers 			free (ifName);
121624084f9bSBrian Somers 
121724084f9bSBrian Somers 		ifName = strdup (strValue);
121824084f9bSBrian Somers 		break;
121924084f9bSBrian Somers 
122024084f9bSBrian Somers 	case ConfigFile:
122124084f9bSBrian Somers 		ReadConfigFile (strValue);
122224084f9bSBrian Somers 		break;
122359a7c613SBrian Somers 
122459a7c613SBrian Somers 	case LogDenied:
122559a7c613SBrian Somers 		logDropped = 1;
122659a7c613SBrian Somers 		break;
122759a7c613SBrian Somers 
122859a7c613SBrian Somers 	case LogFacility:
122959a7c613SBrian Somers 
123059a7c613SBrian Somers 		fac_record = facilitynames;
123159a7c613SBrian Somers 		while (fac_record->c_name != NULL) {
123259a7c613SBrian Somers 
123359a7c613SBrian Somers 			if (!strcmp (fac_record->c_name, strValue)) {
123459a7c613SBrian Somers 
123559a7c613SBrian Somers 				logFacility = fac_record->c_val;
123659a7c613SBrian Somers 				break;
123759a7c613SBrian Somers 
123859a7c613SBrian Somers 			}
123959a7c613SBrian Somers 			else
124059a7c613SBrian Somers 				fac_record++;
124159a7c613SBrian Somers 		}
124259a7c613SBrian Somers 
124359a7c613SBrian Somers 		if(fac_record->c_name == NULL)
124459a7c613SBrian Somers 			errx(1, "Unknown log facility name: %s", strValue);
124559a7c613SBrian Somers 
124659a7c613SBrian Somers 		break;
124724084f9bSBrian Somers 	}
124824084f9bSBrian Somers }
124924084f9bSBrian Somers 
1250902cb50aSBrian Somers void ReadConfigFile (const char* fileName)
125124084f9bSBrian Somers {
125224084f9bSBrian Somers 	FILE*	file;
125324084f9bSBrian Somers 	char	buf[128];
12542e7e7c71SRuslan Ermilov 	char	*ptr, *p;
125524084f9bSBrian Somers 	char*	option;
125624084f9bSBrian Somers 
125724084f9bSBrian Somers 	file = fopen (fileName, "r");
125824084f9bSBrian Somers 	if (!file) {
125924084f9bSBrian Somers 
126024084f9bSBrian Somers 		sprintf (buf, "Cannot open config file %s.\n", fileName);
126124084f9bSBrian Somers 		Quit (buf);
126224084f9bSBrian Somers 	}
126324084f9bSBrian Somers 
126424084f9bSBrian Somers 	while (fgets (buf, sizeof (buf), file)) {
126524084f9bSBrian Somers 
126624084f9bSBrian Somers 		ptr = strchr (buf, '\n');
12670fc81af1SPhilippe Charnier 		if (!ptr)
126867a886fbSBrian Somers 			errx (1, "config line too long: %s", buf);
126924084f9bSBrian Somers 
127024084f9bSBrian Somers 		*ptr = '\0';
127124084f9bSBrian Somers 
127224084f9bSBrian Somers /*
12732e7e7c71SRuslan Ermilov  * Check for comments, strip off trailing spaces.
127424084f9bSBrian Somers  */
12752e7e7c71SRuslan Ermilov 		if ((ptr = strchr(buf, '#')))
12762e7e7c71SRuslan Ermilov 			*ptr = '\0';
12772e7e7c71SRuslan Ermilov 		for (ptr = buf; isspace(*ptr); ++ptr)
12782e7e7c71SRuslan Ermilov 			continue;
127924084f9bSBrian Somers 		if (*ptr == '\0')
128024084f9bSBrian Somers 			continue;
12812e7e7c71SRuslan Ermilov 		for (p = strchr(buf, '\0'); isspace(*--p);)
12822e7e7c71SRuslan Ermilov 			continue;
12832e7e7c71SRuslan Ermilov 		*++p = '\0';
12842e7e7c71SRuslan Ermilov 
128524084f9bSBrian Somers /*
128624084f9bSBrian Somers  * Extract option name.
128724084f9bSBrian Somers  */
128824084f9bSBrian Somers 		option = ptr;
128924084f9bSBrian Somers 		while (*ptr && !isspace (*ptr))
129024084f9bSBrian Somers 			++ptr;
129124084f9bSBrian Somers 
129224084f9bSBrian Somers 		if (*ptr != '\0') {
129324084f9bSBrian Somers 
129424084f9bSBrian Somers 			*ptr = '\0';
129524084f9bSBrian Somers 			++ptr;
129624084f9bSBrian Somers 		}
129724084f9bSBrian Somers /*
129824084f9bSBrian Somers  * Skip white space between name and parms.
129924084f9bSBrian Somers  */
130024084f9bSBrian Somers 		while (*ptr && isspace (*ptr))
130124084f9bSBrian Somers 			++ptr;
130224084f9bSBrian Somers 
130324084f9bSBrian Somers 		ParseOption (option, *ptr ? ptr : NULL, 0);
130424084f9bSBrian Somers 	}
130524084f9bSBrian Somers 
130624084f9bSBrian Somers 	fclose (file);
130724084f9bSBrian Somers }
130824084f9bSBrian Somers 
130924084f9bSBrian Somers static void Usage ()
131024084f9bSBrian Somers {
131124084f9bSBrian Somers 	int			i;
131224084f9bSBrian Somers 	int			max;
131324084f9bSBrian Somers 	struct OptionInfo*	info;
131424084f9bSBrian Somers 
131524084f9bSBrian Somers 	fprintf (stderr, "Recognized options:\n\n");
131624084f9bSBrian Somers 
131724084f9bSBrian Somers 	max = sizeof (optionTable) / sizeof (struct OptionInfo);
131824084f9bSBrian Somers 	for (i = 0, info = optionTable; i < max; i++, info++) {
131924084f9bSBrian Somers 
132024084f9bSBrian Somers 		fprintf (stderr, "-%-20s %s\n", info->name,
132124084f9bSBrian Somers 						info->parmDescription);
132224084f9bSBrian Somers 
132324084f9bSBrian Somers 		if (info->shortName)
132424084f9bSBrian Somers 			fprintf (stderr, "-%-20s %s\n", info->shortName,
132524084f9bSBrian Somers 							info->parmDescription);
132624084f9bSBrian Somers 
132724084f9bSBrian Somers 		fprintf (stderr, "      %s\n\n", info->description);
132824084f9bSBrian Somers 	}
132924084f9bSBrian Somers 
133024084f9bSBrian Somers 	exit (1);
133124084f9bSBrian Somers }
133224084f9bSBrian Somers 
1333902cb50aSBrian Somers void SetupPptpAlias (const char* parms)
133424084f9bSBrian Somers {
133524084f9bSBrian Somers 	char		buf[128];
133624084f9bSBrian Somers 	char*		ptr;
133724084f9bSBrian Somers 	struct in_addr	srcAddr;
133824084f9bSBrian Somers 
133924084f9bSBrian Somers 	strcpy (buf, parms);
134024084f9bSBrian Somers 
134124084f9bSBrian Somers /*
134224084f9bSBrian Somers  * Extract source address.
134324084f9bSBrian Somers  */
134459a7c613SBrian Somers 	ptr = strtok (buf, " \t");
13450fc81af1SPhilippe Charnier 	if (!ptr)
134659a7c613SBrian Somers 		errx(1, "pptpalias: missing src address");
134724084f9bSBrian Somers 
134859a7c613SBrian Somers 	StrToAddr (ptr, &srcAddr);
134959a7c613SBrian Somers 	PacketAliasPptp (srcAddr);
135024084f9bSBrian Somers }
135124084f9bSBrian Somers 
1352902cb50aSBrian Somers void SetupPortRedirect (const char* parms)
135324084f9bSBrian Somers {
135424084f9bSBrian Somers 	char		buf[128];
135524084f9bSBrian Somers 	char*		ptr;
135624084f9bSBrian Somers 	struct in_addr	localAddr;
135724084f9bSBrian Somers 	struct in_addr	publicAddr;
135824084f9bSBrian Somers 	struct in_addr	remoteAddr;
13595d8ee958SBrian Somers 	port_range      portRange;
13605d8ee958SBrian Somers 	u_short         localPort      = 0;
13615d8ee958SBrian Somers 	u_short         publicPort     = 0;
13625d8ee958SBrian Somers 	u_short         remotePort     = 0;
13635d8ee958SBrian Somers 	u_short         numLocalPorts  = 0;
13645d8ee958SBrian Somers 	u_short         numPublicPorts = 0;
13655d8ee958SBrian Somers 	u_short         numRemotePorts = 0;
136624084f9bSBrian Somers 	int		proto;
136724084f9bSBrian Somers 	char*		protoName;
136824084f9bSBrian Somers 	char*		separator;
13695d8ee958SBrian Somers 	int             i;
137024084f9bSBrian Somers 
137124084f9bSBrian Somers 	strcpy (buf, parms);
137224084f9bSBrian Somers /*
137324084f9bSBrian Somers  * Extract protocol.
137424084f9bSBrian Somers  */
137524084f9bSBrian Somers 	protoName = strtok (buf, " \t");
13760fc81af1SPhilippe Charnier 	if (!protoName)
13770fc81af1SPhilippe Charnier 		errx (1, "redirect_port: missing protocol");
137824084f9bSBrian Somers 
137924084f9bSBrian Somers 	proto = StrToProto (protoName);
138024084f9bSBrian Somers /*
138124084f9bSBrian Somers  * Extract local address.
138224084f9bSBrian Somers  */
138324084f9bSBrian Somers 	ptr = strtok (NULL, " \t");
13840fc81af1SPhilippe Charnier 	if (!ptr)
13850fc81af1SPhilippe Charnier 		errx (1, "redirect_port: missing local address");
138624084f9bSBrian Somers 
13875d8ee958SBrian Somers 	if ( StrToAddrAndPortRange (ptr, &localAddr, protoName, &portRange) != 0 )
13885d8ee958SBrian Somers 	        errx (1, "redirect_port: invalid local port range");
13895d8ee958SBrian Somers 
13905d8ee958SBrian Somers 	localPort     = GETLOPORT(portRange);
13915d8ee958SBrian Somers 	numLocalPorts = GETNUMPORTS(portRange);
13925d8ee958SBrian Somers 
139324084f9bSBrian Somers /*
13949c501140SBrian Somers  * Extract public port and optionally address.
139524084f9bSBrian Somers  */
139624084f9bSBrian Somers 	ptr = strtok (NULL, " \t");
13970fc81af1SPhilippe Charnier 	if (!ptr)
13980fc81af1SPhilippe Charnier 		errx (1, "redirect_port: missing public port");
139924084f9bSBrian Somers 
140024084f9bSBrian Somers 	separator = strchr (ptr, ':');
14015d8ee958SBrian Somers 	if (separator) {
14025d8ee958SBrian Somers 	        if (StrToAddrAndPortRange (ptr, &publicAddr, protoName, &portRange) != 0 )
14035d8ee958SBrian Somers 		        errx (1, "redirect_port: invalid public port range");
140424084f9bSBrian Somers 	}
14055d8ee958SBrian Somers 	else {
14065d8ee958SBrian Somers 		publicAddr.s_addr = INADDR_ANY;
14075d8ee958SBrian Somers 		if (StrToPortRange (ptr, protoName, &portRange) != 0)
14085d8ee958SBrian Somers 		        errx (1, "redirect_port: invalid public port range");
14095d8ee958SBrian Somers 	}
14105d8ee958SBrian Somers 
14115d8ee958SBrian Somers 	publicPort     = GETLOPORT(portRange);
14125d8ee958SBrian Somers 	numPublicPorts = GETNUMPORTS(portRange);
141324084f9bSBrian Somers 
141424084f9bSBrian Somers /*
141524084f9bSBrian Somers  * Extract remote address and optionally port.
141624084f9bSBrian Somers  */
141724084f9bSBrian Somers 	ptr = strtok (NULL, " \t");
141824084f9bSBrian Somers 	if (ptr) {
141924084f9bSBrian Somers 		separator = strchr (ptr, ':');
1420ebe70c8fSWarner Losh 		if (separator) {
14215d8ee958SBrian Somers 		        if (StrToAddrAndPortRange (ptr, &remoteAddr, protoName, &portRange) != 0)
14225d8ee958SBrian Somers 			        errx (1, "redirect_port: invalid remote port range");
1423ebe70c8fSWarner Losh 		} else {
14245d8ee958SBrian Somers 		        SETLOPORT(portRange, 0);
14255d8ee958SBrian Somers 			SETNUMPORTS(portRange, 1);
142624084f9bSBrian Somers 			StrToAddr (ptr, &remoteAddr);
142724084f9bSBrian Somers 		}
142824084f9bSBrian Somers 	}
142924084f9bSBrian Somers 	else {
14305d8ee958SBrian Somers 	        SETLOPORT(portRange, 0);
14315d8ee958SBrian Somers 		SETNUMPORTS(portRange, 1);
143224084f9bSBrian Somers 		remoteAddr.s_addr = INADDR_ANY;
143324084f9bSBrian Somers 	}
143424084f9bSBrian Somers 
14355d8ee958SBrian Somers 	remotePort     = GETLOPORT(portRange);
14365d8ee958SBrian Somers 	numRemotePorts = GETNUMPORTS(portRange);
14375d8ee958SBrian Somers 
14385d8ee958SBrian Somers /*
14395d8ee958SBrian Somers  * Make sure port ranges match up, then add the redirect ports.
14405d8ee958SBrian Somers  */
14415d8ee958SBrian Somers 	if (numLocalPorts != numPublicPorts)
14425d8ee958SBrian Somers 	        errx (1, "redirect_port: port ranges must be equal in size");
14435d8ee958SBrian Somers 
14445d8ee958SBrian Somers 	/* Remote port range is allowed to be '0' which means all ports. */
144529d97436SBrian Somers 	if (numRemotePorts != numLocalPorts && (numRemotePorts != 1 || remotePort != 0))
14465d8ee958SBrian Somers 	        errx (1, "redirect_port: remote port must be 0 or equal to local port range in size");
14475d8ee958SBrian Somers 
14485d8ee958SBrian Somers 	for (i = 0 ; i < numPublicPorts ; ++i) {
14495d8ee958SBrian Somers 	        /* If remotePort is all ports, set it to 0. */
14505d8ee958SBrian Somers 	        u_short remotePortCopy = remotePort + i;
14515d8ee958SBrian Somers 	        if (numRemotePorts == 1 && remotePort == 0)
14525d8ee958SBrian Somers 		        remotePortCopy = 0;
14535d8ee958SBrian Somers 
145424084f9bSBrian Somers 	        PacketAliasRedirectPort (localAddr,
14555d8ee958SBrian Somers 					 htons(localPort + i),
145624084f9bSBrian Somers 					 remoteAddr,
14575d8ee958SBrian Somers 					 htons(remotePortCopy),
145824084f9bSBrian Somers 					 publicAddr,
14595d8ee958SBrian Somers 					 htons(publicPort + i),
146024084f9bSBrian Somers 					 proto);
146124084f9bSBrian Somers 	}
14625d8ee958SBrian Somers }
146324084f9bSBrian Somers 
1464902cb50aSBrian Somers void SetupAddressRedirect (const char* parms)
146524084f9bSBrian Somers {
146624084f9bSBrian Somers 	char		buf[128];
146724084f9bSBrian Somers 	char*		ptr;
146824084f9bSBrian Somers 	struct in_addr	localAddr;
146924084f9bSBrian Somers 	struct in_addr	publicAddr;
147024084f9bSBrian Somers 
147124084f9bSBrian Somers 	strcpy (buf, parms);
147224084f9bSBrian Somers /*
147324084f9bSBrian Somers  * Extract local address.
147424084f9bSBrian Somers  */
147524084f9bSBrian Somers 	ptr = strtok (buf, " \t");
14760fc81af1SPhilippe Charnier 	if (!ptr)
14770fc81af1SPhilippe Charnier 		errx (1, "redirect_address: missing local address");
147824084f9bSBrian Somers 
147924084f9bSBrian Somers 	StrToAddr (ptr, &localAddr);
148024084f9bSBrian Somers /*
148124084f9bSBrian Somers  * Extract public address.
148224084f9bSBrian Somers  */
148324084f9bSBrian Somers 	ptr = strtok (NULL, " \t");
14840fc81af1SPhilippe Charnier 	if (!ptr)
14850fc81af1SPhilippe Charnier 		errx (1, "redirect_address: missing public address");
148624084f9bSBrian Somers 
148724084f9bSBrian Somers 	StrToAddr (ptr, &publicAddr);
148824084f9bSBrian Somers 	PacketAliasRedirectAddr (localAddr, publicAddr);
148924084f9bSBrian Somers }
149024084f9bSBrian Somers 
1491902cb50aSBrian Somers void StrToAddr (const char* str, struct in_addr* addr)
149224084f9bSBrian Somers {
149324084f9bSBrian Somers 	struct hostent* hp;
149424084f9bSBrian Somers 
149524084f9bSBrian Somers 	if (inet_aton (str, addr))
149624084f9bSBrian Somers 		return;
149724084f9bSBrian Somers 
149824084f9bSBrian Somers 	hp = gethostbyname (str);
14990fc81af1SPhilippe Charnier 	if (!hp)
15000fc81af1SPhilippe Charnier 		errx (1, "unknown host %s", str);
150124084f9bSBrian Somers 
150224084f9bSBrian Somers 	memcpy (addr, hp->h_addr, sizeof (struct in_addr));
150324084f9bSBrian Somers }
150424084f9bSBrian Somers 
1505902cb50aSBrian Somers u_short StrToPort (const char* str, const char* proto)
150624084f9bSBrian Somers {
150767a886fbSBrian Somers 	u_short		port;
150824084f9bSBrian Somers 	struct servent*	sp;
150924084f9bSBrian Somers 	char*		end;
151024084f9bSBrian Somers 
151124084f9bSBrian Somers 	port = strtol (str, &end, 10);
151224084f9bSBrian Somers 	if (end != str)
151327c20503SBrian Somers 		return htons (port);
151424084f9bSBrian Somers 
151524084f9bSBrian Somers 	sp = getservbyname (str, proto);
15160fc81af1SPhilippe Charnier 	if (!sp)
15170fc81af1SPhilippe Charnier 		errx (1, "unknown service %s/%s", str, proto);
151824084f9bSBrian Somers 
151924084f9bSBrian Somers 	return sp->s_port;
152024084f9bSBrian Somers }
152124084f9bSBrian Somers 
1522902cb50aSBrian Somers int StrToPortRange (const char* str, const char* proto, port_range *portRange)
15235d8ee958SBrian Somers {
15245d8ee958SBrian Somers 	char*           sep;
15255d8ee958SBrian Somers 	struct servent*	sp;
15265d8ee958SBrian Somers 	char*		end;
15275d8ee958SBrian Somers 	u_short         loPort;
15285d8ee958SBrian Somers 	u_short         hiPort;
15295d8ee958SBrian Somers 
15305d8ee958SBrian Somers 	/* First see if this is a service, return corresponding port if so. */
15315d8ee958SBrian Somers 	sp = getservbyname (str,proto);
15325d8ee958SBrian Somers 	if (sp) {
15335d8ee958SBrian Somers 	        SETLOPORT(*portRange, ntohs(sp->s_port));
15345d8ee958SBrian Somers 		SETNUMPORTS(*portRange, 1);
15355d8ee958SBrian Somers 		return 0;
15365d8ee958SBrian Somers 	}
15375d8ee958SBrian Somers 
15385d8ee958SBrian Somers 	/* Not a service, see if it's a single port or port range. */
15395d8ee958SBrian Somers 	sep = strchr (str, '-');
15405d8ee958SBrian Somers 	if (sep == NULL) {
15415d8ee958SBrian Somers 	        SETLOPORT(*portRange, strtol(str, &end, 10));
15425d8ee958SBrian Somers 		if (end != str) {
15435d8ee958SBrian Somers 		        /* Single port. */
15445d8ee958SBrian Somers 		        SETNUMPORTS(*portRange, 1);
15455d8ee958SBrian Somers 			return 0;
15465d8ee958SBrian Somers 		}
15475d8ee958SBrian Somers 
15485d8ee958SBrian Somers 		/* Error in port range field. */
15495d8ee958SBrian Somers 		errx (1, "unknown service %s/%s", str, proto);
15505d8ee958SBrian Somers 	}
15515d8ee958SBrian Somers 
15525d8ee958SBrian Somers 	/* Port range, get the values and sanity check. */
15535d8ee958SBrian Somers 	sscanf (str, "%hu-%hu", &loPort, &hiPort);
15545d8ee958SBrian Somers 	SETLOPORT(*portRange, loPort);
15555d8ee958SBrian Somers 	SETNUMPORTS(*portRange, 0);	/* Error by default */
15565d8ee958SBrian Somers 	if (loPort <= hiPort)
15575d8ee958SBrian Somers 	        SETNUMPORTS(*portRange, hiPort - loPort + 1);
15585d8ee958SBrian Somers 
15595d8ee958SBrian Somers 	if (GETNUMPORTS(*portRange) == 0)
15605d8ee958SBrian Somers 	        errx (1, "invalid port range %s", str);
15615d8ee958SBrian Somers 
15625d8ee958SBrian Somers 	return 0;
15635d8ee958SBrian Somers }
15645d8ee958SBrian Somers 
15655d8ee958SBrian Somers 
1566902cb50aSBrian Somers int StrToProto (const char* str)
156724084f9bSBrian Somers {
156824084f9bSBrian Somers 	if (!strcmp (str, "tcp"))
156924084f9bSBrian Somers 		return IPPROTO_TCP;
157024084f9bSBrian Somers 
157124084f9bSBrian Somers 	if (!strcmp (str, "udp"))
157224084f9bSBrian Somers 		return IPPROTO_UDP;
157324084f9bSBrian Somers 
15740fc81af1SPhilippe Charnier 	errx (1, "unknown protocol %s. Expected tcp or udp", str);
157524084f9bSBrian Somers }
157624084f9bSBrian Somers 
1577902cb50aSBrian Somers int StrToAddrAndPortRange (const char* str, struct in_addr* addr, char* proto, port_range *portRange)
157824084f9bSBrian Somers {
157924084f9bSBrian Somers 	char*	ptr;
158024084f9bSBrian Somers 
158124084f9bSBrian Somers 	ptr = strchr (str, ':');
15820fc81af1SPhilippe Charnier 	if (!ptr)
15830fc81af1SPhilippe Charnier 		errx (1, "%s is missing port number", str);
158424084f9bSBrian Somers 
158524084f9bSBrian Somers 	*ptr = '\0';
158624084f9bSBrian Somers 	++ptr;
158724084f9bSBrian Somers 
158824084f9bSBrian Somers 	StrToAddr (str, addr);
15895d8ee958SBrian Somers 	return StrToPortRange (ptr, proto, portRange);
159024084f9bSBrian Somers }
1591