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