natd.c revision 28045
1/*
2 * natd - Network Address Translation Daemon for FreeBSD.
3 *
4 * This software ois provided free of charge, with no
5 * warranty of any kind, either expressed or implied.
6 * Use at your own risk.
7 *
8 * You may copy, modify and distribute this software (natd.c) freely.
9 *
10 * Ari Suutari <suutari@iki.fi>
11 *
12 */
13
14#include <stdlib.h>
15#include <stdio.h>
16#include <unistd.h>
17#include <string.h>
18#include <ctype.h>
19
20#include <sys/types.h>
21#include <sys/socket.h>
22#include <sys/time.h>
23#include <errno.h>
24#include <signal.h>
25
26#include <netdb.h>
27
28#include <netinet/in.h>
29#include <netinet/in_systm.h>
30#include <netinet/ip.h>
31#include <machine/in_cksum.h>
32#include <netinet/tcp.h>
33#include <sys/ioctl.h>
34#include <net/if.h>
35#include <net/route.h>
36#include <arpa/inet.h>
37
38#include <syslog.h>
39#include <alias.h>
40
41#include "natd.h"
42
43/*
44 * Default values for input and output
45 * divert socket ports.
46 */
47
48#define	DEFAULT_SERVICE	"natd"
49
50/*
51 * Function prototypes.
52 */
53
54static void DoAliasing (int fd);
55static void DaemonMode ();
56static void HandleRoutingInfo (int fd);
57static void Usage ();
58static void PrintPacket (struct ip*);
59static void SetAliasAddressFromIfName (char* ifName);
60static void InitiateShutdown ();
61static void Shutdown ();
62static void RefreshAddr ();
63static void ParseOption (char* option, char* parms, int cmdLine);
64static void ReadConfigFile (char* fileName);
65static void SetupPermanentLink (char* parms);
66static void SetupPortRedirect (char* parms);
67static void SetupAddressRedirect (char* parms);
68static void StrToAddr (char* str, struct in_addr* addr);
69static int  StrToPort (char* str, char* proto);
70static int  StrToProto (char* str);
71static int  StrToAddrAndPort (char* str, struct in_addr* addr, char* proto);
72static void ParseArgs (int argc, char** argv);
73static void FlushPacketBuffer (int fd);
74
75/*
76 * Globals.
77 */
78
79static	int			verbose;
80static 	int			background;
81static	int			running;
82static	int			assignAliasAddr;
83static	char*			ifName;
84static  int			ifIndex;
85static	int			inPort;
86static	int			outPort;
87static	int			inOutPort;
88static	struct in_addr		aliasAddr;
89static 	int			dynamicMode;
90static  int			ifMTU;
91static	int			aliasOverhead;
92static 	int			icmpSock;
93static	char			packetBuf[IP_MAXPACKET];
94static 	int			packetLen;
95static	struct sockaddr_in	packetAddr;
96static 	int			packetSock;
97
98int main (int argc, char** argv)
99{
100	int			divertIn;
101	int			divertOut;
102	int			divertInOut;
103	int			routeSock;
104	struct sockaddr_in	addr;
105	fd_set			readMask;
106	fd_set			writeMask;
107	int			fdMax;
108/*
109 * Initialize packet aliasing software.
110 * Done already here to be able to alter option bits
111 * during command line and configuration file processing.
112 */
113	PacketAliasInit ();
114/*
115 * Parse options.
116 */
117	inPort			= 0;
118	outPort			= 0;
119	verbose 		= 0;
120	inOutPort		= 0;
121	ifName			= NULL;
122	ifMTU			= -1;
123	background		= 0;
124	running			= 1;
125	assignAliasAddr		= 0;
126	aliasAddr.s_addr	= INADDR_NONE;
127	aliasOverhead		= 12;
128	dynamicMode		= 0;
129/*
130 * Mark packet buffer empty.
131 */
132	packetSock		= -1;
133
134	ParseArgs (argc, argv);
135/*
136 * Check that valid aliasing address has been given.
137 */
138	if (aliasAddr.s_addr == INADDR_NONE && ifName == NULL) {
139
140		fprintf (stderr, "Aliasing address not given.\n");
141		exit (1);
142	}
143
144	if (aliasAddr.s_addr != INADDR_NONE && ifName != NULL) {
145
146		fprintf (stderr, "Both alias address and interface name "
147				 "are not allowed.\n");
148		exit (1);
149	}
150/*
151 * Check that valid port number is known.
152 */
153	if (inPort != 0 || outPort != 0)
154		if (inPort == 0 || outPort == 0) {
155
156			fprintf (stderr, "Both input and output ports"
157					 " are required.\n");
158			exit (1);
159		}
160
161	if (inPort == 0 && outPort == 0 && inOutPort == 0)
162		ParseOption ("port", DEFAULT_SERVICE, 0);
163
164/*
165 * Create divert sockets. Use only one socket if -p was specified
166 * on command line. Otherwise, create separate sockets for
167 * outgoing and incoming connnections.
168 */
169	if (inOutPort) {
170
171		divertInOut = socket (PF_INET, SOCK_RAW, IPPROTO_DIVERT);
172		if (divertInOut == -1)
173			Quit ("Unable to create divert socket.");
174
175		divertIn  = -1;
176		divertOut = -1;
177/*
178 * Bind socket.
179 */
180
181		addr.sin_family		= AF_INET;
182		addr.sin_addr.s_addr	= INADDR_ANY;
183		addr.sin_port		= inOutPort;
184
185		if (bind (divertInOut,
186			  (struct sockaddr*) &addr,
187			  sizeof addr) == -1)
188			Quit ("Unable to bind divert socket.");
189	}
190	else {
191
192		divertIn = socket (PF_INET, SOCK_RAW, IPPROTO_DIVERT);
193		if (divertIn == -1)
194			Quit ("Unable to create incoming divert socket.");
195
196		divertOut = socket (PF_INET, SOCK_RAW, IPPROTO_DIVERT);
197		if (divertOut == -1)
198			Quit ("Unable to create outgoing divert socket.");
199
200		divertInOut = -1;
201
202/*
203 * Bind divert sockets.
204 */
205
206		addr.sin_family		= AF_INET;
207		addr.sin_addr.s_addr	= INADDR_ANY;
208		addr.sin_port		= inPort;
209
210		if (bind (divertIn,
211			  (struct sockaddr*) &addr,
212			  sizeof addr) == -1)
213			Quit ("Unable to bind incoming divert socket.");
214
215		addr.sin_family		= AF_INET;
216		addr.sin_addr.s_addr	= INADDR_ANY;
217		addr.sin_port		= outPort;
218
219		if (bind (divertOut,
220			  (struct sockaddr*) &addr,
221			  sizeof addr) == -1)
222			Quit ("Unable to bind outgoing divert socket.");
223	}
224/*
225 * Create routing socket if interface name specified.
226 */
227	if (ifName && dynamicMode) {
228
229		routeSock = socket (PF_ROUTE, SOCK_RAW, 0);
230		if (routeSock == -1)
231			Quit ("Unable to create routing info socket.");
232	}
233	else
234		routeSock = -1;
235/*
236 * Create socket for sending ICMP messages.
237 */
238	icmpSock = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP);
239	if (icmpSock == -1)
240		Quit ("Unable to create ICMP socket.");
241/*
242 * Become a daemon unless verbose mode was requested.
243 */
244	if (!verbose)
245		DaemonMode ();
246/*
247 * Catch signals to manage shutdown and
248 * refresh of interface address.
249 */
250	signal (SIGTERM, InitiateShutdown);
251	signal (SIGHUP, RefreshAddr);
252/*
253 * Set alias address if it has been given.
254 */
255	if (aliasAddr.s_addr != INADDR_NONE)
256		PacketAliasSetAddress (aliasAddr);
257
258/*
259 * We need largest descriptor number for select.
260 */
261
262	fdMax = -1;
263
264	if (divertIn > fdMax)
265		fdMax = divertIn;
266
267	if (divertOut > fdMax)
268		fdMax = divertOut;
269
270	if (divertInOut > fdMax)
271		fdMax = divertInOut;
272
273	if (routeSock > fdMax)
274		fdMax = routeSock;
275
276	while (running) {
277
278		if (divertInOut != -1 && !ifName && packetSock == -1) {
279/*
280 * When using only one socket, just call
281 * DoAliasing repeatedly to process packets.
282 */
283			DoAliasing (divertInOut);
284			continue;
285		}
286/*
287 * Build read mask from socket descriptors to select.
288 */
289		FD_ZERO (&readMask);
290		FD_ZERO (&writeMask);
291
292/*
293 * If there is unsent packet in buffer, use select
294 * to check when socket comes writable again.
295 */
296		if (packetSock != -1) {
297
298			FD_SET (packetSock, &writeMask);
299		}
300		else {
301/*
302 * No unsent packet exists - safe to check if
303 * new ones are available.
304 */
305			if (divertIn != -1)
306				FD_SET (divertIn, &readMask);
307
308			if (divertOut != -1)
309				FD_SET (divertOut, &readMask);
310
311			if (divertInOut != -1)
312				FD_SET (divertInOut, &readMask);
313		}
314/*
315 * Routing info is processed always.
316 */
317		if (routeSock != -1)
318			FD_SET (routeSock, &readMask);
319
320		if (select (fdMax + 1,
321			    &readMask,
322			    &writeMask,
323			    NULL,
324			    NULL) == -1) {
325
326			if (errno == EINTR)
327				continue;
328
329			Quit ("Select failed.");
330		}
331
332		if (packetSock != -1)
333			if (FD_ISSET (packetSock, &writeMask))
334				FlushPacketBuffer (packetSock);
335
336		if (divertIn != -1)
337			if (FD_ISSET (divertIn, &readMask))
338				DoAliasing (divertIn);
339
340		if (divertOut != -1)
341			if (FD_ISSET (divertOut, &readMask))
342				DoAliasing (divertOut);
343
344		if (divertInOut != -1)
345			if (FD_ISSET (divertInOut, &readMask))
346				DoAliasing (divertInOut);
347
348		if (routeSock != -1)
349			if (FD_ISSET (routeSock, &readMask))
350				HandleRoutingInfo (routeSock);
351	}
352
353	if (background)
354		unlink (PIDFILE);
355
356	return 0;
357}
358
359static void DaemonMode ()
360{
361	FILE*	pidFile;
362
363	daemon (0, 0);
364	background = 1;
365
366	pidFile = fopen (PIDFILE, "w");
367	if (pidFile) {
368
369		fprintf (pidFile, "%d\n", getpid ());
370		fclose (pidFile);
371	}
372}
373
374static void ParseArgs (int argc, char** argv)
375{
376	int		arg;
377	char*		parm;
378	char*		opt;
379	char		parmBuf[256];
380
381	for (arg = 1; arg < argc; arg++) {
382
383		opt  = argv[arg];
384		if (*opt != '-') {
385
386			fprintf (stderr, "Invalid option %s.\n", opt);
387			Usage ();
388		}
389
390		parm = NULL;
391		parmBuf[0] = '\0';
392
393		while (arg < argc - 1) {
394
395			if (argv[arg + 1][0] == '-')
396				break;
397
398			if (parm)
399				strcat (parmBuf, " ");
400
401			++arg;
402			parm = parmBuf;
403			strcat (parmBuf, argv[arg]);
404		}
405
406		ParseOption (opt + 1, parm, 1);
407	}
408}
409
410static void DoAliasing (int fd)
411{
412	int			bytes;
413	int			origBytes;
414	int			addrSize;
415	struct ip*		ip;
416
417	if (assignAliasAddr) {
418
419		SetAliasAddressFromIfName (ifName);
420		assignAliasAddr = 0;
421	}
422/*
423 * Get packet from socket.
424 */
425	addrSize  = sizeof packetAddr;
426	origBytes = recvfrom (fd,
427			      packetBuf,
428			      sizeof packetBuf,
429			      0,
430			      (struct sockaddr*) &packetAddr,
431			      &addrSize);
432
433	if (origBytes == -1) {
434
435		if (errno != EINTR)
436			Warn ("Read from divert socket failed.");
437
438		return;
439	}
440/*
441 * This is a IP packet.
442 */
443	ip = (struct ip*) packetBuf;
444
445	if (verbose) {
446
447/*
448 * Print packet direction and protocol type.
449 */
450
451		if (packetAddr.sin_addr.s_addr == INADDR_ANY)
452			printf ("Out ");
453		else
454			printf ("In  ");
455
456		switch (ip->ip_p) {
457		case IPPROTO_TCP:
458			printf ("[TCP]  ");
459			break;
460
461		case IPPROTO_UDP:
462			printf ("[UDP]  ");
463			break;
464
465		case IPPROTO_ICMP:
466			printf ("[ICMP] ");
467			break;
468
469		default:
470			printf ("[?]    ");
471			break;
472		}
473/*
474 * Print addresses.
475 */
476		PrintPacket (ip);
477	}
478
479	if (packetAddr.sin_addr.s_addr == INADDR_ANY) {
480/*
481 * Outgoing packets. Do aliasing.
482 */
483		PacketAliasOut (packetBuf, IP_MAXPACKET);
484	}
485	else {
486/*
487 * Do aliasing.
488 */
489		PacketAliasIn (packetBuf, IP_MAXPACKET);
490	}
491/*
492 * Length might have changed during aliasing.
493 */
494	bytes = ntohs (ip->ip_len);
495/*
496 * Update alias overhead size for outgoing packets.
497 */
498	if (packetAddr.sin_addr.s_addr == INADDR_ANY &&
499	    bytes - origBytes > aliasOverhead)
500		aliasOverhead = bytes - origBytes;
501
502	if (verbose) {
503
504/*
505 * Print addresses after aliasing.
506 */
507		printf (" aliased to\n");
508		printf ("           ");
509		PrintPacket (ip);
510		printf ("\n");
511	}
512
513	packetLen  = bytes;
514	packetSock = fd;
515	FlushPacketBuffer (fd);
516}
517
518static void FlushPacketBuffer (int fd)
519{
520	int			wrote;
521	char			msgBuf[80];
522/*
523 * Put packet back for processing.
524 */
525	wrote = sendto (fd,
526		        packetBuf,
527	    		packetLen,
528	    		0,
529	    		(struct sockaddr*) &packetAddr,
530	    		sizeof packetAddr);
531
532	if (wrote != packetLen) {
533/*
534 * If buffer space is not available,
535 * just return. Main loop will take care of
536 * retrying send when space becomes available.
537 */
538		if (errno == ENOBUFS)
539			return;
540
541		if (errno == EMSGSIZE) {
542
543			if (packetAddr.sin_addr.s_addr == INADDR_ANY &&
544			    ifMTU != -1)
545				SendNeedFragIcmp (icmpSock,
546						  (struct ip*) packetBuf,
547						  ifMTU - aliasOverhead);
548		}
549		else {
550
551			sprintf (msgBuf, "Failed to write packet back.");
552			Warn (msgBuf);
553		}
554	}
555
556	packetSock = -1;
557}
558
559static void HandleRoutingInfo (int fd)
560{
561	int			bytes;
562	struct if_msghdr	ifMsg;
563/*
564 * Get packet from socket.
565 */
566	bytes = read (fd, &ifMsg, sizeof ifMsg);
567	if (bytes == -1) {
568
569		Warn ("Read from routing socket failed.");
570		return;
571	}
572
573	if (ifMsg.ifm_version != RTM_VERSION) {
574
575		Warn ("Unexpected packet read from routing socket.");
576		return;
577	}
578
579	if (verbose)
580		printf ("Routing message %X received.\n", ifMsg.ifm_type);
581
582	if (ifMsg.ifm_type != RTM_NEWADDR)
583		return;
584
585	if (verbose && ifMsg.ifm_index == ifIndex)
586		printf ("Interface address has changed.\n");
587
588	if (ifMsg.ifm_index == ifIndex)
589		assignAliasAddr = 1;
590}
591
592static void PrintPacket (struct ip* ip)
593{
594	struct tcphdr*	tcphdr;
595
596	if (ip->ip_p == IPPROTO_TCP)
597		tcphdr = (struct tcphdr*) ((char*) ip + (ip->ip_hl << 2));
598	else
599		tcphdr = NULL;
600
601	printf ("%s", inet_ntoa (ip->ip_src));
602	if (tcphdr)
603		printf (":%d", ntohs (tcphdr->th_sport));
604
605	printf (" -> ");
606	printf ("%s", inet_ntoa (ip->ip_dst));
607	if (tcphdr)
608		printf (":%d", ntohs (tcphdr->th_dport));
609}
610
611static void SetAliasAddressFromIfName (char* ifName)
612{
613	struct ifconf		cf;
614	struct ifreq		buf[32];
615	char			msg[80];
616	struct ifreq*		ifPtr;
617	int			extra;
618	int			helperSock;
619	int			bytes;
620	struct sockaddr_in*	addr;
621	int			found;
622	struct ifreq		req;
623	char			last[10];
624/*
625 * Create a dummy socket to access interface information.
626 */
627	helperSock = socket (AF_INET, SOCK_DGRAM, 0);
628	if (helperSock == -1) {
629
630		Quit ("Failed to create helper socket.");
631		exit (1);
632	}
633
634	cf.ifc_len = sizeof (buf);
635	cf.ifc_req = buf;
636/*
637 * Get interface data.
638 */
639	if (ioctl (helperSock, SIOCGIFCONF, &cf) == -1) {
640
641		Quit ("Ioctl SIOCGIFCONF failed.");
642		exit (1);
643	}
644
645	ifIndex	= 0;
646	ifPtr	= buf;
647	bytes	= cf.ifc_len;
648	found   = 0;
649	last[0] = '\0';
650/*
651 * Loop through interfaces until one with
652 * given name is found. This is done to
653 * find correct interface index for routing
654 * message processing.
655 */
656	while (bytes) {
657
658		if (ifPtr->ifr_addr.sa_family == AF_INET &&
659                    !strcmp (ifPtr->ifr_name, ifName)) {
660
661			found = 1;
662			break;
663		}
664
665		if (strcmp (last, ifPtr->ifr_name)) {
666
667			strcpy (last, ifPtr->ifr_name);
668			++ifIndex;
669		}
670
671		extra = ifPtr->ifr_addr.sa_len - sizeof (struct sockaddr);
672
673		ifPtr++;
674		ifPtr = (struct ifreq*) ((char*) ifPtr + extra);
675		bytes -= sizeof (struct ifreq) + extra;
676	}
677
678	if (!found) {
679
680		close (helperSock);
681		sprintf (msg, "Unknown interface name %s.\n", ifName);
682		Quit (msg);
683	}
684/*
685 * Get MTU size.
686 */
687	strcpy (req.ifr_name, ifName);
688
689	if (ioctl (helperSock, SIOCGIFMTU, &req) == -1)
690		Quit ("Cannot get interface mtu size.");
691
692	ifMTU = req.ifr_mtu;
693/*
694 * Get interface address.
695 */
696	if (ioctl (helperSock, SIOCGIFADDR, &req) == -1)
697		Quit ("Cannot get interface address.");
698
699	addr = (struct sockaddr_in*) &req.ifr_addr;
700	SetPacketAliasAddress (addr->sin_addr);
701	syslog (LOG_INFO, "Aliasing to %s, mtu %d bytes",
702			  inet_ntoa (addr->sin_addr),
703			  ifMTU);
704
705	close (helperSock);
706}
707
708void Quit (char* msg)
709{
710	Warn (msg);
711	exit (1);
712}
713
714void Warn (char* msg)
715{
716	if (background)
717		syslog (LOG_ALERT, "%s (%m)", msg);
718	else
719		perror (msg);
720}
721
722static void RefreshAddr ()
723{
724	signal (SIGHUP, RefreshAddr);
725	if (ifName)
726		assignAliasAddr = 1;
727}
728
729static void InitiateShutdown ()
730{
731/*
732 * Start timer to allow kernel gracefully
733 * shutdown existing connections when system
734 * is shut down.
735 */
736	signal (SIGALRM, Shutdown);
737	alarm (10);
738}
739
740static void Shutdown ()
741{
742	running = 0;
743}
744
745/*
746 * Different options recognized by this program.
747 */
748
749enum Option {
750
751	PacketAliasOption,
752	Verbose,
753	InPort,
754	OutPort,
755	Port,
756	AliasAddress,
757	InterfaceName,
758	PermanentLink,
759	RedirectPort,
760	RedirectAddress,
761	ConfigFile,
762	DynamicMode
763};
764
765enum Param {
766
767	YesNo,
768	Numeric,
769	String,
770	None,
771	Address,
772	Service
773};
774
775/*
776 * Option information structure (used by ParseOption).
777 */
778
779struct OptionInfo {
780
781	enum Option		type;
782	int			packetAliasOpt;
783	enum Param		parm;
784	char*			parmDescription;
785	char*			description;
786	char*			name;
787	char*			shortName;
788};
789
790/*
791 * Table of known options.
792 */
793
794static struct OptionInfo optionTable[] = {
795
796	{ PacketAliasOption,
797		PKT_ALIAS_UNREGISTERED_ONLY,
798		YesNo,
799		"[yes|no]",
800		"alias only unregistered addresses",
801		"unregistered_only",
802		"u" },
803
804	{ PacketAliasOption,
805		PKT_ALIAS_LOG,
806		YesNo,
807		"[yes|no]",
808		"enable logging",
809		"log",
810		"l" },
811
812	{ PacketAliasOption,
813		PKT_ALIAS_DENY_INCOMING,
814		YesNo,
815		"[yes|no]",
816		"allow incoming connections",
817		"deny_incoming",
818		"d" },
819
820	{ PacketAliasOption,
821		PKT_ALIAS_USE_SOCKETS,
822		YesNo,
823		"[yes|no]",
824		"use sockets to inhibit port conflict",
825		"use_sockets",
826		"s" },
827
828	{ PacketAliasOption,
829		PKT_ALIAS_SAME_PORTS,
830		YesNo,
831		"[yes|no]",
832		"try to keep original port numbers for connections",
833		"same_ports",
834		"m" },
835
836	{ Verbose,
837		0,
838		YesNo,
839		"[yes|no]",
840		"verbose mode, dump packet information",
841		"verbose",
842		"v" },
843
844	{ DynamicMode,
845		0,
846		YesNo,
847		"[yes|no]",
848		"dynamic mode, automatically detect interface address changes",
849		"dynamic",
850		NULL },
851
852	{ InPort,
853		0,
854		Service,
855		"number|service_name",
856		"set port for incoming packets",
857		"in_port",
858		"i" },
859
860	{ OutPort,
861		0,
862		Service,
863		"number|service_name",
864		"set port for outgoing packets",
865		"out_port",
866		"o" },
867
868	{ Port,
869		0,
870		Service,
871		"number|service_name",
872		"set port (defaults to natd/divert)",
873		"port",
874		"p" },
875
876	{ AliasAddress,
877		0,
878		Address,
879		"x.x.x.x",
880		"address to use for aliasing",
881		"alias_address",
882		"a" },
883
884	{ InterfaceName,
885		0,
886		String,
887	        "network_if_name",
888		"take aliasing address from interface",
889		"interface",
890		"n" },
891
892	{ PermanentLink,
893		0,
894		String,
895	        "tcp|udp src:port dst:port alias",
896		"define permanent link for incoming connection",
897		"permanent_link",
898		NULL },
899
900	{ RedirectPort,
901		0,
902		String,
903	        "tcp|udp local_addr:local_port [public_addr:]public_port"
904	 	" [remote_addr[:remote_port]]",
905		"redirect a port for incoming traffic",
906		"redirect_port",
907		NULL },
908
909	{ RedirectAddress,
910		0,
911		String,
912	        "local_addr public_addr",
913		"define mapping between local and public addresses",
914		"redirect_address",
915		NULL },
916
917	{ ConfigFile,
918		0,
919		String,
920		"file_name",
921		"read options from configuration file",
922		"config",
923		"f" }
924};
925
926static void ParseOption (char* option, char* parms, int cmdLine)
927{
928	int			i;
929	struct OptionInfo*	info;
930	int			yesNoValue;
931	int			aliasValue;
932	int			numValue;
933	char*			strValue;
934	struct in_addr		addrValue;
935	int			max;
936	char*			end;
937/*
938 * Find option from table.
939 */
940	max = sizeof (optionTable) / sizeof (struct OptionInfo);
941	for (i = 0, info = optionTable; i < max; i++, info++) {
942
943		if (!strcmp (info->name, option))
944			break;
945
946		if (info->shortName)
947			if (!strcmp (info->shortName, option))
948				break;
949	}
950
951	if (i >= max) {
952
953		fprintf (stderr, "Unknown option %s.\n", option);
954		Usage ();
955	}
956
957	yesNoValue	= 0;
958	numValue	= 0;
959	strValue	= NULL;
960/*
961 * Check parameters.
962 */
963	switch (info->parm) {
964	case YesNo:
965		if (!parms)
966			parms = "yes";
967
968		if (!strcmp (parms, "yes"))
969			yesNoValue = 1;
970		else
971			if (!strcmp (parms, "no"))
972				yesNoValue = 0;
973			else {
974
975				fprintf (stderr, "%s needs yes/no parameter.\n",
976						 option);
977				exit (1);
978			}
979		break;
980
981	case Service:
982		if (!parms) {
983
984			fprintf (stderr, "%s needs service name or "
985					 "port number  parameter.\n",
986					 option);
987			exit (1);
988		}
989
990		numValue = StrToPort (parms, "divert");
991		break;
992
993	case Numeric:
994		if (parms)
995			numValue = strtol (parms, &end, 10);
996		else
997			end = parms;
998
999		if (end == parms) {
1000
1001			fprintf (stderr, "%s needs numeric parameter.\n",
1002					 option);
1003			exit (1);
1004		}
1005		break;
1006
1007	case String:
1008		strValue = parms;
1009		if (!strValue) {
1010
1011			fprintf (stderr, "%s needs parameter.\n",
1012					 option);
1013			exit (1);
1014		}
1015		break;
1016
1017	case None:
1018		if (parms) {
1019
1020			fprintf (stderr, "%s does not take parameters.\n",
1021					 option);
1022			exit (1);
1023		}
1024		break;
1025
1026	case Address:
1027		if (!parms) {
1028
1029			fprintf (stderr, "%s needs address/host parameter.\n",
1030					 option);
1031			exit (1);
1032		}
1033
1034		StrToAddr (parms, &addrValue);
1035		break;
1036	}
1037
1038	switch (info->type) {
1039	case PacketAliasOption:
1040
1041		aliasValue = yesNoValue ? info->packetAliasOpt : 0;
1042		PacketAliasSetMode (aliasValue, info->packetAliasOpt);
1043		break;
1044
1045	case Verbose:
1046		verbose = yesNoValue;
1047		break;
1048
1049	case DynamicMode:
1050		dynamicMode = yesNoValue;
1051		break;
1052
1053	case InPort:
1054		inPort = numValue;
1055		break;
1056
1057	case OutPort:
1058		outPort = numValue;
1059		break;
1060
1061	case Port:
1062		inOutPort = numValue;
1063		break;
1064
1065	case AliasAddress:
1066		memcpy (&aliasAddr, &addrValue, sizeof (struct in_addr));
1067		break;
1068
1069	case PermanentLink:
1070		SetupPermanentLink (strValue);
1071		break;
1072
1073	case RedirectPort:
1074		SetupPortRedirect (strValue);
1075		break;
1076
1077	case RedirectAddress:
1078		SetupAddressRedirect (strValue);
1079		break;
1080
1081	case InterfaceName:
1082		if (ifName)
1083			free (ifName);
1084
1085		ifName = strdup (strValue);
1086		assignAliasAddr = 1;
1087		break;
1088
1089	case ConfigFile:
1090		ReadConfigFile (strValue);
1091		break;
1092	}
1093}
1094
1095void ReadConfigFile (char* fileName)
1096{
1097	FILE*	file;
1098	char	buf[128];
1099	char*	ptr;
1100	char*	option;
1101
1102	file = fopen (fileName, "r");
1103	if (!file) {
1104
1105		sprintf (buf, "Cannot open config file %s.\n", fileName);
1106		Quit (buf);
1107	}
1108
1109	while (fgets (buf, sizeof (buf), file)) {
1110
1111		ptr = strchr (buf, '\n');
1112		if (!ptr) {
1113
1114			fprintf (stderr, "config line too link: %s\n", buf);
1115			exit (1);
1116		}
1117
1118		*ptr = '\0';
1119		if (buf[0] == '#')
1120			continue;
1121
1122		ptr = buf;
1123/*
1124 * Skip white space at beginning of line.
1125 */
1126		while (*ptr && isspace (*ptr))
1127			++ptr;
1128
1129		if (*ptr == '\0')
1130			continue;
1131/*
1132 * Extract option name.
1133 */
1134		option = ptr;
1135		while (*ptr && !isspace (*ptr))
1136			++ptr;
1137
1138		if (*ptr != '\0') {
1139
1140			*ptr = '\0';
1141			++ptr;
1142		}
1143/*
1144 * Skip white space between name and parms.
1145 */
1146		while (*ptr && isspace (*ptr))
1147			++ptr;
1148
1149		ParseOption (option, *ptr ? ptr : NULL, 0);
1150	}
1151
1152	fclose (file);
1153}
1154
1155static void Usage ()
1156{
1157	int			i;
1158	int			max;
1159	struct OptionInfo*	info;
1160
1161	fprintf (stderr, "Recognized options:\n\n");
1162
1163	max = sizeof (optionTable) / sizeof (struct OptionInfo);
1164	for (i = 0, info = optionTable; i < max; i++, info++) {
1165
1166		fprintf (stderr, "-%-20s %s\n", info->name,
1167						info->parmDescription);
1168
1169		if (info->shortName)
1170			fprintf (stderr, "-%-20s %s\n", info->shortName,
1171							info->parmDescription);
1172
1173		fprintf (stderr, "      %s\n\n", info->description);
1174	}
1175
1176	exit (1);
1177}
1178
1179void SetupPermanentLink (char* parms)
1180{
1181	char		buf[128];
1182	char*		ptr;
1183	struct in_addr	srcAddr;
1184	struct in_addr	dstAddr;
1185	int		srcPort;
1186	int		dstPort;
1187	int		aliasPort;
1188	int		proto;
1189	char*		protoName;
1190
1191	strcpy (buf, parms);
1192/*
1193 * Extract protocol.
1194 */
1195	protoName = strtok (buf, " \t");
1196	if (!protoName) {
1197
1198		fprintf (stderr, "permanent_link: missing protocol.\n");
1199		exit (1);
1200	}
1201
1202	proto = StrToProto (protoName);
1203/*
1204 * Extract source address.
1205 */
1206	ptr = strtok (NULL, " \t");
1207	if (!ptr) {
1208
1209		fprintf (stderr, "permanent_link: missing src address.\n");
1210		exit (1);
1211	}
1212
1213	srcPort = StrToAddrAndPort (ptr, &srcAddr, protoName);
1214/*
1215 * Extract destination address.
1216 */
1217	ptr = strtok (NULL, " \t");
1218	if (!ptr) {
1219
1220		fprintf (stderr, "permanent_link: missing dst address.\n");
1221		exit (1);
1222	}
1223
1224	dstPort = StrToAddrAndPort (ptr, &dstAddr, protoName);
1225/*
1226 * Export alias port.
1227 */
1228	ptr = strtok (NULL, " \t");
1229	if (!ptr) {
1230
1231		fprintf (stderr, "permanent_link: missing alias port.\n");
1232		exit (1);
1233	}
1234
1235	aliasPort = StrToPort (ptr, protoName);
1236
1237	PacketAliasPermanentLink (srcAddr,
1238				  srcPort,
1239				  dstAddr,
1240				  dstPort,
1241				  aliasPort,
1242				  proto);
1243}
1244
1245void SetupPortRedirect (char* parms)
1246{
1247	char		buf[128];
1248	char*		ptr;
1249	struct in_addr	localAddr;
1250	struct in_addr	publicAddr;
1251	struct in_addr	remoteAddr;
1252	int		localPort;
1253	int		publicPort;
1254	int		remotePort;
1255	int		proto;
1256	char*		protoName;
1257	char*		separator;
1258
1259	strcpy (buf, parms);
1260/*
1261 * Extract protocol.
1262 */
1263	protoName = strtok (buf, " \t");
1264	if (!protoName) {
1265
1266		fprintf (stderr, "redirect_port: missing protocol.\n");
1267		exit (1);
1268	}
1269
1270	proto = StrToProto (protoName);
1271/*
1272 * Extract local address.
1273 */
1274	ptr = strtok (NULL, " \t");
1275	if (!ptr) {
1276
1277		fprintf (stderr, "redirect_port: missing local address.\n");
1278		exit (1);
1279	}
1280
1281	localPort = StrToAddrAndPort (ptr, &localAddr, protoName);
1282/*
1283 * Extract public port and optinally address.
1284 */
1285	ptr = strtok (NULL, " \t");
1286	if (!ptr) {
1287
1288		fprintf (stderr, "redirect_port: missing public port.\n");
1289		exit (1);
1290	}
1291
1292	separator = strchr (ptr, ':');
1293	if (separator)
1294		publicPort = StrToAddrAndPort (ptr, &publicAddr, protoName);
1295	else {
1296
1297		publicAddr.s_addr = INADDR_ANY;
1298		publicPort = StrToPort (ptr, protoName);
1299	}
1300
1301/*
1302 * Extract remote address and optionally port.
1303 */
1304	ptr = strtok (NULL, " \t");
1305	if (ptr) {
1306
1307
1308		separator = strchr (ptr, ':');
1309		if (separator)
1310			remotePort = StrToAddrAndPort (ptr,
1311						       &remoteAddr,
1312						       protoName);
1313		else {
1314
1315			remotePort = 0;
1316			StrToAddr (ptr, &remoteAddr);
1317		}
1318	}
1319	else {
1320
1321		remotePort = 0;
1322		remoteAddr.s_addr = INADDR_ANY;
1323	}
1324
1325	PacketAliasRedirectPort (localAddr,
1326				 localPort,
1327				 remoteAddr,
1328				 remotePort,
1329				 publicAddr,
1330				 publicPort,
1331				 proto);
1332}
1333
1334void SetupAddressRedirect (char* parms)
1335{
1336	char		buf[128];
1337	char*		ptr;
1338	struct in_addr	localAddr;
1339	struct in_addr	publicAddr;
1340
1341	strcpy (buf, parms);
1342/*
1343 * Extract local address.
1344 */
1345	ptr = strtok (buf, " \t");
1346	if (!ptr) {
1347
1348		fprintf (stderr, "redirect_address: missing local address.\n");
1349		exit (1);
1350	}
1351
1352	StrToAddr (ptr, &localAddr);
1353/*
1354 * Extract public address.
1355 */
1356	ptr = strtok (NULL, " \t");
1357	if (!ptr) {
1358
1359		fprintf (stderr, "redirect_address: missing public address.\n");
1360		exit (1);
1361	}
1362
1363	StrToAddr (ptr, &publicAddr);
1364	PacketAliasRedirectAddr (localAddr, publicAddr);
1365}
1366
1367void StrToAddr (char* str, struct in_addr* addr)
1368{
1369	struct hostent* hp;
1370
1371	if (inet_aton (str, addr))
1372		return;
1373
1374	hp = gethostbyname (str);
1375	if (!hp) {
1376
1377		fprintf (stderr, "Unknown host %s.\n", str);
1378		exit (1);
1379	}
1380
1381	memcpy (addr, hp->h_addr, sizeof (struct in_addr));
1382}
1383
1384int StrToPort (char* str, char* proto)
1385{
1386	int		port;
1387	struct servent*	sp;
1388	char*		end;
1389
1390	port = strtol (str, &end, 10);
1391	if (end != str)
1392		return htons (port);
1393
1394	sp = getservbyname (str, proto);
1395	if (!sp) {
1396
1397		fprintf (stderr, "Unknown service %s/%s.\n",
1398				 str, proto);
1399		exit (1);
1400	}
1401
1402	return sp->s_port;
1403}
1404
1405int StrToProto (char* str)
1406{
1407	if (!strcmp (str, "tcp"))
1408		return IPPROTO_TCP;
1409
1410	if (!strcmp (str, "udp"))
1411		return IPPROTO_UDP;
1412
1413	fprintf (stderr, "Unknown protocol %s. Expected tcp or udp.\n", str);
1414	exit (1);
1415}
1416
1417int StrToAddrAndPort (char* str, struct in_addr* addr, char* proto)
1418{
1419	char*	ptr;
1420
1421	ptr = strchr (str, ':');
1422	if (!ptr) {
1423
1424		fprintf (stderr, "%s is missing port number.\n", str);
1425		exit (1);
1426	}
1427
1428	*ptr = '\0';
1429	++ptr;
1430
1431	StrToAddr (str, addr);
1432	return StrToPort (ptr, proto);
1433}
1434
1435