summaryrefslogtreecommitdiffstats
path: root/sbin/natd
diff options
context:
space:
mode:
Diffstat (limited to 'sbin/natd')
-rw-r--r--sbin/natd/HISTORY146
-rw-r--r--sbin/natd/Makefile12
-rw-r--r--sbin/natd/README50
-rw-r--r--sbin/natd/icmp.c127
-rw-r--r--sbin/natd/natd.8606
-rw-r--r--sbin/natd/natd.c1689
-rw-r--r--sbin/natd/natd.h24
-rw-r--r--sbin/natd/samples/natd.cf.sample92
-rw-r--r--sbin/natd/samples/natd.test14
9 files changed, 2760 insertions, 0 deletions
diff --git a/sbin/natd/HISTORY b/sbin/natd/HISTORY
new file mode 100644
index 0000000..f929e80
--- /dev/null
+++ b/sbin/natd/HISTORY
@@ -0,0 +1,146 @@
+* Version 0.1
+
+ Initial version of natd.
+
+* Version 0.2
+
+ - Alias address can now be set by giving interface name with
+ new (-n) command-line option.
+
+ - New Makefile based on bsd.prog.mk.
+
+ - Error messages are written to syslog
+ after natd has become a daemon.
+
+* Version 1.0
+
+ - Support for using only single socket (-p option)
+
+* Version 1.1
+
+ - -a option now understands a hostname also.
+ - -a option no longer dumps core.
+ - Packet aliasing software upgraded to v. 1.9
+ - added long option names (like -address)
+
+* Version 1.2
+
+ - Fixed core dump with -port option.
+ - Added -Wall to CFLAGS and some headers added to natd.c
+ to get clean compile by Brian Somers [brian@awfulhak.org].
+
+* Version 1.3
+
+ - Aliasing address initialization is delayed until first
+ packet arrives. This allows natd to start up before
+ interface address is set.
+ - SIGTERM is now catched to allow kernel to close
+ existing connections when system is shutting down.
+ - SIGHUP is now catched to allow natd to refresh aliasing
+ address from interface, which might be useful to tun devices.
+
+* Version 1.4
+
+ - Changed command line options to be compatible with
+ command names used in ppp+packetAlias package (which is the
+ original application using aliasing routines).
+
+ The options which map directly to packet aliasing options are:
+
+ -unregistered_only [yes|no]
+ -log [yes|no]
+ -deny_incoming [yes|no]
+ -use_sockets [yes|no]
+ -same_ports [yes|no]
+
+ The short option names are the same as in previous
+ releases.
+
+ - Command line parser rewritten to provide more flexible
+ way to support new packet aliasing options.
+
+ - Support for natd.cf configuration file has been added.
+
+ - SIGHUP no longer causes problems when running without
+ interface name option.
+
+ - When using -interface command line option, routing socket
+ is optionally listened for interface address changes. This
+ mode is activated by -dynamic option.
+
+ - Directory tree reorganized, alias package is now a library.
+
+ - Manual page written by Brian Somers <brian@awfulhak.org> added.
+ - README file updated.
+
+* Version 1.5
+
+ - Support for sending ICMP 'need fragmentation' messages
+ when packet size exceeds mtu size of outgoing network interface.
+
+ - ipfw rule example in manual page fixed.
+
+* Version 1.6
+
+ - Upgrade to new packet aliasing engine (2.1)
+ - redirect_port and redirect_address configuration
+ parameters added.
+ - It is no longer necessary to quote complex parameter values
+ in command line.
+ - Manual page fixed (same_port -> same_ports).
+
+* Version 1.7
+
+ - A bug in command-line parsing fixed (it appeared due
+ to changes made in 1.6).
+
+* Version 1.8
+
+ - Fixed problems with -dynamic option.
+ - Added /var/run/natd.pid
+
+* Version 1.9
+
+ - Changes to manual page by
+ Brian Somers <brian@awfulhak.org> integrated.
+ - Checksum for incoming packets is always recalculated
+ for FreeBSD 2.2 and never recalculated for newer
+ versions. This should fix the problem with wrong
+ checksum of fragmented packets.
+ - Buffer space problem found by Sergio Lenzi <lenzi@bsi.com.br>
+ fixed. Natd now waits with select(2) for buffer space
+ to become available if write fails.
+ - Packet aliasing library upgraded to 2.2.
+
+* Version 1.10
+
+ - Ignored incoming packets are now dropped when
+ deny_incoming option is set to yes.
+ - Packet aliasing library upgraded to 2.4.
+
+* Version 1.11
+
+ - Code cleanup work done in FreeBSD-current development merged.
+ - Port numbers are now unsigned as they should always have been.
+
+* Version 1.12
+
+ - Typos in comment fixed. Copyright message added to
+ source & header files that were missing it.
+ - A small patch to libalias to make static NAT work correctly.
+
+* Version 2.0
+
+ - Upgrade to libalias 3.0 which gives:
+ - Transparent proxy support.
+ - permanent_link is now obsolete, use redirect_port instead.
+ - Drop support for early FreeBSD 2.2 versions
+ - If separate input & output sockets are being used
+ use them to find out packet direction instead of
+ normal mechanism. This can be handy in complex environments
+ with multiple interfaces.
+ - libalias is no longer part of this distribution.
+ - New sample configuration file
+ from Ted Mittelstaedt <tedm@portsoft.com>.
+ - PPTP redirect support by Dru Nelson <dnelson@redwoodsoft.com> added.
+ - Logging enhancements from Martin Machacek <mm@i.cz> added.
diff --git a/sbin/natd/Makefile b/sbin/natd/Makefile
new file mode 100644
index 0000000..ba6730e
--- /dev/null
+++ b/sbin/natd/Makefile
@@ -0,0 +1,12 @@
+# $FreeBSD$
+
+MAINTAINER = ru@FreeBSD.org
+MAINTAINER += ari@suutari.iki.fi
+PROG = natd
+SRCS = natd.c icmp.c
+WARNS= 0
+LDADD = -lalias
+DPADD = ${LIBALIAS}
+MAN = natd.8
+
+.include <bsd.prog.mk>
diff --git a/sbin/natd/README b/sbin/natd/README
new file mode 100644
index 0000000..8d1209c
--- /dev/null
+++ b/sbin/natd/README
@@ -0,0 +1,50 @@
+# $FreeBSD$
+
+ A Network Address Translation Daemon for FreeBSD
+
+
+1. WHAT IS NATD ?
+
+ This is a simple daemon based on FreeBSD divert sockets
+ which performs network address translation (or masquerading)
+ for IP packets (see related RFCs 1631 and 1918).
+ It is based on packet aliasing package (see README.alias)
+ written by Charles Mott (cmott@scientech.com).
+
+ This package works with any network interface (doesn't have
+ to be ppp). I run it on a computer having two ethernet cards,
+ one connected to internet and the other one to local network.
+
+2. GETTING IT RUNNING
+
+ 1) Get FreeBSD 2.2 - I think the divert sockets are
+ not available on earlier versions,
+
+ 2) Compile this software by executing "make".
+
+ 3) Install the software by executing "make install".
+
+ 4) See man natd for further instructions.
+
+3. FTP SITES FOR NATD
+
+ This package is available at ftp://ftp.suutari.iki.fi/pub/natd.
+
+4. AUTHORS
+
+ This program is the result of the efforts of many people
+ at different times:
+
+ Archie Cobbs <archie@whistle.com> Divert sockets
+ Charles Mott <cmott@scientech.com> Packet aliasing engine
+ Eivind Eklund <eivind@dimaga.com> Packet aliasing engine
+ Ari Suutari <suutari@iki.fi> Natd
+ Brian Somers <brian@awfulhak.org> Manual page, glue and
+ bunch of good ideas.
+
+ Happy Networking - comments and fixes are welcome!
+
+ Ari S. (suutari@iki.fi)
+
+
+
diff --git a/sbin/natd/icmp.c b/sbin/natd/icmp.c
new file mode 100644
index 0000000..176adde
--- /dev/null
+++ b/sbin/natd/icmp.c
@@ -0,0 +1,127 @@
+/*
+ * natd - Network Address Translation Daemon for FreeBSD.
+ *
+ * This software is provided free of charge, with no
+ * warranty of any kind, either expressed or implied.
+ * Use at your own risk.
+ *
+ * You may copy, modify and distribute this software (icmp.c) freely.
+ *
+ * Ari Suutari <suutari@iki.fi>
+ *
+ * $FreeBSD$
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <errno.h>
+#include <signal.h>
+
+#include <netdb.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+#include <machine/in_cksum.h>
+
+#include <alias.h>
+
+#include "natd.h"
+
+int SendNeedFragIcmp (int sock, struct ip* failedDgram, int mtu)
+{
+ char icmpBuf[IP_MAXPACKET];
+ struct ip* ip;
+ struct icmp* icmp;
+ int icmpLen;
+ int failBytes;
+ int failHdrLen;
+ struct sockaddr_in addr;
+ int wrote;
+ struct in_addr swap;
+/*
+ * Don't send error if packet is
+ * not the first fragment.
+ */
+ if (ntohs (failedDgram->ip_off) & ~(IP_MF | IP_DF))
+ return 0;
+/*
+ * Dont respond if failed datagram is ICMP.
+ */
+ if (failedDgram->ip_p == IPPROTO_ICMP)
+ return 0;
+/*
+ * Start building the message.
+ */
+ ip = (struct ip*) icmpBuf;
+ icmp = (struct icmp*) (icmpBuf + sizeof (struct ip));
+/*
+ * Complete ICMP part.
+ */
+ icmp->icmp_type = ICMP_UNREACH;
+ icmp->icmp_code = ICMP_UNREACH_NEEDFRAG;
+ icmp->icmp_cksum = 0;
+ icmp->icmp_void = 0;
+ icmp->icmp_nextmtu = htons (mtu);
+/*
+ * Copy header + 64 bits of original datagram.
+ */
+ failHdrLen = (failedDgram->ip_hl << 2);
+ failBytes = failedDgram->ip_len - failHdrLen;
+ if (failBytes > 8)
+ failBytes = 8;
+
+ failBytes += failHdrLen;
+ icmpLen = ICMP_MINLEN + failBytes;
+
+ memcpy (&icmp->icmp_ip, failedDgram, failBytes);
+/*
+ * Calculate checksum.
+ */
+ icmp->icmp_cksum = PacketAliasInternetChecksum ((u_short*) icmp,
+ icmpLen);
+/*
+ * Add IP header using old IP header as template.
+ */
+ memcpy (ip, failedDgram, sizeof (struct ip));
+
+ ip->ip_v = 4;
+ ip->ip_hl = 5;
+ ip->ip_len = htons (sizeof (struct ip) + icmpLen);
+ ip->ip_p = IPPROTO_ICMP;
+ ip->ip_tos = 0;
+
+ swap = ip->ip_dst;
+ ip->ip_dst = ip->ip_src;
+ ip->ip_src = swap;
+
+ PacketAliasIn ((char*) ip, IP_MAXPACKET);
+
+ addr.sin_family = AF_INET;
+ addr.sin_addr = ip->ip_dst;
+ addr.sin_port = 0;
+/*
+ * Put packet into processing queue.
+ */
+ wrote = sendto (sock,
+ icmp,
+ icmpLen,
+ 0,
+ (struct sockaddr*) &addr,
+ sizeof addr);
+
+ if (wrote != icmpLen)
+ Warn ("Cannot send ICMP message.");
+
+ return 1;
+}
+
+
diff --git a/sbin/natd/natd.8 b/sbin/natd/natd.8
new file mode 100644
index 0000000..5ade022
--- /dev/null
+++ b/sbin/natd/natd.8
@@ -0,0 +1,606 @@
+.\" $FreeBSD$
+.Dd June 27, 2000
+.Dt NATD 8
+.Os
+.Sh NAME
+.Nm natd
+.Nd Network Address Translation daemon
+.Sh SYNOPSIS
+.Nm
+.Bk -words
+.Op Fl unregistered_only | u
+.Op Fl log | l
+.Op Fl proxy_only
+.Op Fl reverse
+.Op Fl deny_incoming | d
+.Op Fl use_sockets | s
+.Op Fl same_ports | m
+.Op Fl verbose | v
+.Op Fl dynamic
+.Op Fl in_port | i Ar port
+.Op Fl out_port | o Ar port
+.Op Fl port | p Ar port
+.Op Fl alias_address | a Ar address
+.Op Fl target_address | t Ar address
+.Op Fl interface | n Ar interface
+.Op Fl proxy_rule Ar proxyspec
+.Op Fl redirect_port Ar linkspec
+.Op Fl redirect_proto Ar linkspec
+.Op Fl redirect_address Ar linkspec
+.Op Fl config | f Ar configfile
+.Op Fl log_denied
+.Op Fl log_facility Ar facility_name
+.Op Fl punch_fw Ar firewall_range
+.Op Fl log_ipfw_denied
+.Ek
+.Sh DESCRIPTION
+This program provides a Network Address Translation facility for use
+with
+.Xr divert 4
+sockets under
+.Fx .
+It is intended for use with NICs - if you want to do NAT on a PPP link,
+use the
+.Fl nat
+switch to
+.Xr ppp 8 .
+.Pp
+The
+.Nm
+normally runs in the background as a daemon.
+It is passed raw IP packets as they travel into and out of the machine,
+and will possibly change these before re-injecting them back into the
+IP packet stream.
+.Pp
+It changes all packets destined for another host so that their source
+IP number is that of the current machine.
+For each packet changed in this manner, an internal table entry is
+created to record this fact.
+The source port number is also changed to indicate the table entry
+applying to the packet.
+Packets that are received with a target IP of the current host are
+checked against this internal table.
+If an entry is found, it is used to determine the correct target IP
+number and port to place in the packet.
+.Pp
+The following command line options are available:
+.Bl -tag -width Fl
+.It Fl log | l
+Log various aliasing statistics and information to the file
+.Pa /var/log/alias.log .
+This file is truncated each time
+.Nm
+is started.
+.It Fl deny_incoming | d
+Do not pass incoming packets that have no
+entry in the internal translation table.
+.Pp
+If this option is not used, then such a packet will be altered
+using the rules in
+.Fl target_address
+below, and the entry will be made in the internal translation table.
+.It Fl log_denied
+Log denied incoming packets via
+.Xr syslog 3
+(see also
+.Fl log_facility ) .
+.It Fl log_facility Ar facility_name
+Use specified log facility when logging information via
+.Xr syslog 3 .
+Argument
+.Ar facility_name
+is one of the keywords specified in
+.Xr syslog.conf 5 .
+.It Fl use_sockets | s
+Allocate a
+.Xr socket 2
+in order to establish an FTP data or IRC DCC send connection.
+This option uses more system resources, but guarantees successful
+connections when port numbers conflict.
+.It Fl same_ports | m
+Try to keep the same port number when altering outgoing packets.
+With this option, protocols such as RPC will have a better chance
+of working.
+If it is not possible to maintain the port number, it will be silently
+changed as per normal.
+.It Fl verbose | v
+Do not call
+.Xr daemon 3
+on startup.
+Instead, stay attached to the controlling terminal and display all packet
+alterations to the standard output.
+This option should only be used for debugging purposes.
+.It Fl unregistered_only | u
+Only alter outgoing packets with an
+.Em unregistered
+source address.
+According to RFC 1918, unregistered source addresses are 10.0.0.0/8,
+172.16.0.0/12 and 192.168.0.0/16.
+.It Fl redirect_port Ar proto Xo
+.Ar targetIP Ns : Ns Xo
+.Ar targetPORT Ns Op - Ns Ar targetPORT Xc
+.Op Ar aliasIP Ns : Ns Xo
+.Ar aliasPORT Ns Op - Ns Ar aliasPORT Xc
+.Oo Ar remoteIP Ns Oo : Ns
+.Ar remotePORT Ns Op - Ns Ar remotePORT
+.Oc Oc
+.Xc
+Redirect incoming connections arriving to given port(s) to another host
+and port(s).
+Argument
+.Ar proto
+is either
+.Ar tcp
+or
+.Ar udp ,
+.Ar targetIP
+is the desired target IP number,
+.Ar targetPORT
+is the desired target port number or range,
+.Ar aliasPORT
+is the requested port number or range, and
+.Ar aliasIP
+is the aliasing address.
+Arguments
+.Ar remoteIP
+and
+.Ar remotePORT
+can be used to specify the connection more accurately if necessary.
+The
+.Ar targetPORT
+range and
+.Ar aliasPORT
+range need not be the same numerically, but must have the same size.
+If
+.Ar remotePORT
+is not specified, it is assumed to be all ports.
+If
+.Ar remotePORT
+is specified, it must match the size of
+.Ar targetPORT ,
+or be 0 (all ports).
+For example, the argument
+.Pp
+.Dl Ar tcp inside1:telnet 6666
+.Pp
+means that incoming TCP packets destined for port 6666 on this machine
+will be sent to the telnet port on the inside1 machine.
+.Pp
+.Dl Ar tcp inside2:2300-2399 3300-3399
+.Pp
+will redirect incoming connections on ports 3300-3399 to host
+inside2, ports 2300-2399.
+The mapping is 1:1 meaning port 3300 maps to 2300, 3301 maps to 2301, etc.
+.It Fl redirect_proto Ar proto localIP Oo
+.Ar publicIP Op Ar remoteIP
+.Oc
+Redirect incoming IP packets of protocol
+.Ar proto
+(see
+.Xr protocols 5 )
+destined for
+.Ar publicIP
+address to a
+.Ar localIP
+address and vice versa.
+.Pp
+If
+.Ar publicIP
+is not specified, then the default aliasing address is used.
+If
+.Ar remoteIP
+is specified, then only packets coming from/to
+.Ar remoteIP
+will match the rule.
+.It Fl redirect_address Ar localIP publicIP
+Redirect traffic for public IP address to a machine on the local
+network.
+This function is known as
+.Em static NAT .
+Normally static NAT is useful if your ISP has allocated a small block
+of IP addresses to you, but it can even be used in the case of single
+address:
+.Pp
+.Dl Ar redirect_address 10.0.0.8 0.0.0.0
+.Pp
+The above command would redirect all incoming traffic
+to machine 10.0.0.8.
+.Pp
+If several address aliases specify the same public address
+as follows
+.Bd -literal -offset indent
+.Ar redirect_address 192.168.0.2 public_addr
+.Ar redirect_address 192.168.0.3 public_addr
+.Ar redirect_address 192.168.0.4 public_addr
+.Ed
+.Pp
+the incoming traffic will be directed to the last
+translated local address (192.168.0.4), but outgoing
+traffic from the first two addresses will still be aliased
+to appear from the specified
+.Ar public_addr .
+.It Fl redirect_port Ar proto Xo
+.Ar targetIP Ns : Ns Xo
+.Ar targetPORT Ns Oo , Ns
+.Ar targetIP Ns : Ns Xo
+.Ar targetPORT Ns Oo , Ns
+.Ar ...\&
+.Oc Oc
+.Xc
+.Xc
+.Op Ar aliasIP Ns : Ns Xo
+.Ar aliasPORT
+.Xc
+.Oo Ar remoteIP Ns
+.Op : Ns Ar remotePORT
+.Oc
+.Xc
+.It Fl redirect_address Xo
+.Ar localIP Ns Oo , Ns
+.Ar localIP Ns Oo , Ns
+.Ar ...\&
+.Oc Oc
+.Ar publicIP
+.Xc
+These forms of
+.Fl redirect_port
+and
+.Fl redirect_address
+are used to transparently offload network load on a single server and
+distribute the load across a pool of servers.
+This function is known as
+.Em LSNAT
+(RFC 2391).
+For example, the argument
+.Pp
+.Dl Ar tcp www1:http,www2:http,www3:http www:http
+.Pp
+means that incoming HTTP requests for host www will be transparently
+redirected to one of the www1, www2 or www3, where a host is selected
+simply on a round-robin basis, without regard to load on the net.
+.It Fl dynamic
+If the
+.Fl n
+or
+.Fl interface
+option is used,
+.Nm
+will monitor the routing socket for alterations to the
+.Ar interface
+passed.
+If the interface's IP number is changed,
+.Nm
+will dynamically alter its concept of the alias address.
+.It Fl in_port | i Ar port
+Read from and write to
+.Xr divert 4
+port
+.Ar port ,
+treating all packets as
+.Dq incoming .
+.It Fl out_port | o Ar port
+Read from and write to
+.Xr divert 4
+port
+.Ar port ,
+treating all packets as
+.Dq outgoing .
+.It Fl port | p Ar port
+Read from and write to
+.Xr divert 4
+port
+.Ar port ,
+distinguishing packets as
+.Dq incoming
+or
+.Dq outgoing
+using the rules specified in
+.Xr divert 4 .
+If
+.Ar port
+is not numeric, it is searched for in the
+.Xr services 5
+database.
+If this option is not specified, the divert port named
+.Ar natd
+will be used as a default.
+.It Fl alias_address | a Ar address
+Use
+.Ar address
+as the aliasing address.
+If this option is not specified, the
+.Fl interface
+option must be used.
+The specified address is usually the address assigned to the
+.Dq public
+network interface.
+.Pp
+All data passing
+.Em out
+will be rewritten with a source address equal to
+.Ar address .
+All data coming
+.Em in
+will be checked to see if it matches any already-aliased outgoing
+connection.
+If it does, the packet is altered accordingly.
+If not, all
+.Fl redirect_port ,
+.Fl redirect_proto
+and
+.Fl redirect_address
+assignments are checked and actioned.
+If no other action can be made and if
+.Fl deny_incoming
+is not specified, the packet is delivered to the local machine
+using the rules specified in
+.Fl target_address
+option below.
+.It Fl t | target_address Ar address
+Set the target address.
+When an incoming packet not associated with any pre-existing link
+arrives at the host machine, it will be sent to the specified
+.Ar address .
+.Pp
+The target address may be set to
+.Ar 255.255.255.255 ,
+in which case all new incoming packets go to the alias address set by
+.Fl alias_address
+or
+.Fl interface .
+.Pp
+If this option is not used, or called with the argument
+.Ar 0.0.0.0 ,
+then all new incoming packets go to the address specified in
+the packet.
+This allows external machines to talk directly to internal machines if
+they can route packets to the machine in question.
+.It Fl interface | n Ar interface
+Use
+.Ar interface
+to determine the aliasing address.
+If there is a possibility that the IP number associated with
+.Ar interface
+may change, the
+.Fl dynamic
+option should also be used.
+If this option is not specified, the
+.Fl alias_address
+option must be used.
+.Pp
+The specified
+.Ar interface
+is usually the
+.Dq public
+(or
+.Dq external )
+network interface.
+.It Fl config | f Ar file
+Read configuration from
+.Ar file .
+A
+.Ar file
+should contain a list of options, one per line, in the same form
+as the long form of the above command line options.
+For example, the line
+.Pp
+.Dl alias_address 158.152.17.1
+.Pp
+would specify an alias address of 158.152.17.1.
+Options that do not take an argument are specified with an argument of
+.Ar yes
+or
+.Ar no
+in the configuration file.
+For example, the line
+.Pp
+.Dl log yes
+.Pp
+is synonymous with
+.Fl log .
+.Pp
+Trailing spaces and empty lines are ignored.
+A
+.Ql \&#
+sign will mark the rest of the line as a comment.
+.It Fl reverse
+This option makes
+.Nm
+reverse the way it handles
+.Dq incoming
+and
+.Dq outgoing
+packets, allowing it to operate on the
+.Dq internal
+network interface rather than the
+.Dq external
+one.
+.Pp
+This can be useful in some transparent proxying situations
+when outgoing traffic is redirected to the local machine
+and
+.Nm
+is running on the internal interface (it usually runs on the
+external interface).
+.It Fl proxy_only
+Force
+.Nm
+to perform transparent proxying only.
+Normal address translation is not performed.
+.It Fl proxy_rule Xo
+.Op Ar type encode_ip_hdr | encode_tcp_stream
+.Ar port xxxx
+.Ar server a.b.c.d:yyyy
+.Xc
+Enable transparent proxying.
+Outgoing TCP packets with the given port going through this
+host to any other host are redirected to the given server and port.
+Optionally, the original target address can be encoded into the packet.
+Use
+.Ar encode_ip_hdr
+to put this information into the IP option field or
+.Ar encode_tcp_stream
+to inject the data into the beginning of the TCP stream.
+.It Fl punch_fw Xo
+.Ar basenumber Ns : Ns Ar count
+.Xc
+This option directs
+.Nm
+to
+.Dq punch holes
+in an
+.Xr ipfirewall 4
+based firewall for FTP/IRC DCC connections.
+This is done dynamically by installing temporary firewall rules which
+allow a particular connection (and only that connection) to go through
+the firewall.
+The rules are removed once the corresponding connection terminates.
+.Pp
+A maximum of
+.Ar count
+rules starting from the rule number
+.Ar basenumber
+will be used for punching firewall holes.
+The range will be cleared for all rules on startup.
+.It Fl log_ipfw_denied
+Log when a packet can not be re-injected because an
+.Xr ipfw 8
+rule blocks it.
+This is the default with
+.Fl verbose .
+.El
+.Sh RUNNING NATD
+The following steps are necessary before attempting to run
+.Nm :
+.Bl -enum
+.It
+Build a custom kernel with the following options:
+.Bd -literal -offset indent
+options IPFIREWALL
+options IPDIVERT
+.Ed
+.Pp
+Refer to the handbook for detailed instructions on building a custom
+kernel.
+.It
+Ensure that your machine is acting as a gateway.
+This can be done by specifying the line
+.Pp
+.Dl gateway_enable=YES
+.Pp
+in the
+.Pa /etc/rc.conf
+file or using the command
+.Pp
+.Dl "sysctl net.inet.ip.forwarding=1"
+.Pp
+.It
+If you use the
+.Fl interface
+option, make sure that your interface is already configured.
+If, for example, you wish to specify
+.Ql tun0
+as your
+.Ar interface ,
+and you are using
+.Xr ppp 8
+on that interface, you must make sure that you start
+.Nm ppp
+prior to starting
+.Nm .
+.El
+.Pp
+Running
+.Nm
+is fairly straight forward.
+The line
+.Pp
+.Dl natd -interface ed0
+.Pp
+should suffice in most cases (substituting the correct interface name).
+Please check
+.Xr rc.conf 5
+on how to configure it to be started automatically during boot.
+Once
+.Nm
+is running, you must ensure that traffic is diverted to
+.Nm :
+.Bl -enum
+.It
+You will need to adjust the
+.Pa /etc/rc.firewall
+script to taste.
+If you are not interested in having a firewall, the
+following lines will do:
+.Bd -literal -offset indent
+/sbin/ipfw -f flush
+/sbin/ipfw add divert natd all from any to any via ed0
+/sbin/ipfw add pass all from any to any
+.Ed
+.Pp
+The second line depends on your interface (change
+.Ql ed0
+as appropriate).
+.Pp
+You should be aware of the fact that, with these firewall settings,
+everyone on your local network can fake his source-address using your
+host as gateway.
+If there are other hosts on your local network, you are strongly
+encouraged to create firewall rules that only allow traffic to and
+from trusted hosts.
+.Pp
+If you specify real firewall rules, it is best to specify line 2 at
+the start of the script so that
+.Nm
+sees all packets before they are dropped by the firewall.
+.Pp
+After translation by
+.Nm ,
+packets re-enter the firewall at the rule number following the rule number
+that caused the diversion (not the next rule if there are several at the
+same number).
+.It
+Enable your firewall by setting
+.Pp
+.Dl firewall_enable=YES
+.Pp
+in
+.Pa /etc/rc.conf .
+This tells the system startup scripts to run the
+.Pa /etc/rc.firewall
+script.
+If you do not wish to reboot now, just run this by hand from the console.
+NEVER run this from a remote session unless you put it into the background.
+If you do, you will lock yourself out after the flush takes place, and
+execution of
+.Pa /etc/rc.firewall
+will stop at this point - blocking all accesses permanently.
+Running the script in the background should be enough to prevent this
+disaster.
+.El
+.Sh SEE ALSO
+.Xr divert 4 ,
+.Xr protocols 5 ,
+.Xr rc.conf 5 ,
+.Xr services 5 ,
+.Xr syslog.conf 5 ,
+.Xr ipfw 8 ,
+.Xr ppp 8
+.Sh AUTHORS
+This program is the result of the efforts of many people at different
+times:
+.Pp
+.An Archie Cobbs Aq archie@whistle.com
+(divert sockets)
+.An Charles Mott Aq cmott@scientech.com
+(packet aliasing)
+.An Eivind Eklund Aq perhaps@yes.no
+(IRC support & misc additions)
+.An Ari Suutari Aq suutari@iki.fi
+(natd)
+.An Dru Nelson Aq dnelson@redwoodsoft.com
+(early PPTP support)
+.An Brian Somers Aq brian@awfulhak.org
+(glue)
+.An Ruslan Ermilov Aq ru@FreeBSD.org
+(natd, packet aliasing, glue)
diff --git a/sbin/natd/natd.c b/sbin/natd/natd.c
new file mode 100644
index 0000000..fbb8aed
--- /dev/null
+++ b/sbin/natd/natd.c
@@ -0,0 +1,1689 @@
+/*
+ * natd - Network Address Translation Daemon for FreeBSD.
+ *
+ * This software is provided free of charge, with no
+ * warranty of any kind, either expressed or implied.
+ * Use at your own risk.
+ *
+ * You may copy, modify and distribute this software (natd.c) freely.
+ *
+ * Ari Suutari <suutari@iki.fi>
+ *
+ * $FreeBSD$
+ */
+
+#define SYSLOG_NAMES
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+#include <sys/time.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <machine/in_cksum.h>
+#include <netinet/tcp.h>
+#include <netinet/udp.h>
+#include <netinet/ip_icmp.h>
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/route.h>
+#include <arpa/inet.h>
+
+#include <alias.h>
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <netdb.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "natd.h"
+
+/*
+ * Default values for input and output
+ * divert socket ports.
+ */
+
+#define DEFAULT_SERVICE "natd"
+
+/*
+ * Definition of a port range, and macros to deal with values.
+ * FORMAT: HI 16-bits == first port in range, 0 == all ports.
+ * LO 16-bits == number of ports in range
+ * NOTES: - Port values are not stored in network byte order.
+ */
+
+typedef u_long port_range;
+
+#define GETLOPORT(x) ((x) >> 0x10)
+#define GETNUMPORTS(x) ((x) & 0x0000ffff)
+#define GETHIPORT(x) (GETLOPORT((x)) + GETNUMPORTS((x)))
+
+/* Set y to be the low-port value in port_range variable x. */
+#define SETLOPORT(x,y) ((x) = ((x) & 0x0000ffff) | ((y) << 0x10))
+
+/* Set y to be the number of ports in port_range variable x. */
+#define SETNUMPORTS(x,y) ((x) = ((x) & 0xffff0000) | (y))
+
+/*
+ * Function prototypes.
+ */
+
+static void DoAliasing (int fd, int direction);
+static void DaemonMode (void);
+static void HandleRoutingInfo (int fd);
+static void Usage (void);
+static char* FormatPacket (struct ip*);
+static void PrintPacket (struct ip*);
+static void SyslogPacket (struct ip*, int priority, const char *label);
+static void SetAliasAddressFromIfName (const char *ifName);
+static void InitiateShutdown (int);
+static void Shutdown (int);
+static void RefreshAddr (int);
+static void ParseOption (const char* option, const char* parms);
+static void ReadConfigFile (const char* fileName);
+static void SetupPortRedirect (const char* parms);
+static void SetupProtoRedirect(const char* parms);
+static void SetupAddressRedirect (const char* parms);
+static void StrToAddr (const char* str, struct in_addr* addr);
+static u_short StrToPort (const char* str, const char* proto);
+static int StrToPortRange (const char* str, const char* proto, port_range *portRange);
+static int StrToProto (const char* str);
+static int StrToAddrAndPortRange (const char* str, struct in_addr* addr, char* proto, port_range *portRange);
+static void ParseArgs (int argc, char** argv);
+static void SetupPunchFW(const char *strValue);
+
+/*
+ * Globals.
+ */
+
+static int verbose;
+static int background;
+static int running;
+static int assignAliasAddr;
+static char* ifName;
+static int ifIndex;
+static u_short inPort;
+static u_short outPort;
+static u_short inOutPort;
+static struct in_addr aliasAddr;
+static int dynamicMode;
+static int ifMTU;
+static int aliasOverhead;
+static int icmpSock;
+static int dropIgnoredIncoming;
+static int logDropped;
+static int logFacility;
+static int logIpfwDenied;
+
+int main (int argc, char** argv)
+{
+ int divertIn;
+ int divertOut;
+ int divertInOut;
+ int routeSock;
+ struct sockaddr_in addr;
+ fd_set readMask;
+ int fdMax;
+/*
+ * Initialize packet aliasing software.
+ * Done already here to be able to alter option bits
+ * during command line and configuration file processing.
+ */
+ PacketAliasInit ();
+/*
+ * Parse options.
+ */
+ inPort = 0;
+ outPort = 0;
+ verbose = 0;
+ inOutPort = 0;
+ ifName = NULL;
+ ifMTU = -1;
+ background = 0;
+ running = 1;
+ assignAliasAddr = 0;
+ aliasAddr.s_addr = INADDR_NONE;
+ aliasOverhead = 12;
+ dynamicMode = 0;
+ logDropped = 0;
+ logFacility = LOG_DAEMON;
+ logIpfwDenied = -1;
+
+ ParseArgs (argc, argv);
+/*
+ * Log ipfw(8) denied packets by default in verbose mode.
+ */
+ if (logIpfwDenied == -1)
+ logIpfwDenied = verbose;
+/*
+ * Open syslog channel.
+ */
+ openlog ("natd", LOG_CONS | LOG_PID | (verbose ? LOG_PERROR : 0),
+ logFacility);
+/*
+ * Check that valid aliasing address has been given.
+ */
+ if (aliasAddr.s_addr == INADDR_NONE && ifName == NULL)
+ errx (1, "aliasing address not given");
+
+ if (aliasAddr.s_addr != INADDR_NONE && ifName != NULL)
+ errx (1, "both alias address and interface "
+ "name are not allowed");
+/*
+ * Check that valid port number is known.
+ */
+ if (inPort != 0 || outPort != 0)
+ if (inPort == 0 || outPort == 0)
+ errx (1, "both input and output ports are required");
+
+ if (inPort == 0 && outPort == 0 && inOutPort == 0)
+ ParseOption ("port", DEFAULT_SERVICE);
+
+/*
+ * Check if ignored packets should be dropped.
+ */
+ dropIgnoredIncoming = PacketAliasSetMode (0, 0);
+ dropIgnoredIncoming &= PKT_ALIAS_DENY_INCOMING;
+/*
+ * Create divert sockets. Use only one socket if -p was specified
+ * on command line. Otherwise, create separate sockets for
+ * outgoing and incoming connnections.
+ */
+ if (inOutPort) {
+
+ divertInOut = socket (PF_INET, SOCK_RAW, IPPROTO_DIVERT);
+ if (divertInOut == -1)
+ Quit ("Unable to create divert socket.");
+
+ divertIn = -1;
+ divertOut = -1;
+/*
+ * Bind socket.
+ */
+
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = INADDR_ANY;
+ addr.sin_port = inOutPort;
+
+ if (bind (divertInOut,
+ (struct sockaddr*) &addr,
+ sizeof addr) == -1)
+ Quit ("Unable to bind divert socket.");
+ }
+ else {
+
+ divertIn = socket (PF_INET, SOCK_RAW, IPPROTO_DIVERT);
+ if (divertIn == -1)
+ Quit ("Unable to create incoming divert socket.");
+
+ divertOut = socket (PF_INET, SOCK_RAW, IPPROTO_DIVERT);
+ if (divertOut == -1)
+ Quit ("Unable to create outgoing divert socket.");
+
+ divertInOut = -1;
+
+/*
+ * Bind divert sockets.
+ */
+
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = INADDR_ANY;
+ addr.sin_port = inPort;
+
+ if (bind (divertIn,
+ (struct sockaddr*) &addr,
+ sizeof addr) == -1)
+ Quit ("Unable to bind incoming divert socket.");
+
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = INADDR_ANY;
+ addr.sin_port = outPort;
+
+ if (bind (divertOut,
+ (struct sockaddr*) &addr,
+ sizeof addr) == -1)
+ Quit ("Unable to bind outgoing divert socket.");
+ }
+/*
+ * Create routing socket if interface name specified and in dynamic mode.
+ */
+ routeSock = -1;
+ if (ifName) {
+ if (dynamicMode) {
+
+ routeSock = socket (PF_ROUTE, SOCK_RAW, 0);
+ if (routeSock == -1)
+ Quit ("Unable to create routing info socket.");
+
+ assignAliasAddr = 1;
+ }
+ else
+ SetAliasAddressFromIfName (ifName);
+ }
+/*
+ * Create socket for sending ICMP messages.
+ */
+ icmpSock = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP);
+ if (icmpSock == -1)
+ Quit ("Unable to create ICMP socket.");
+
+/*
+ * And disable reads for the socket, otherwise it slowly fills
+ * up with received icmps which we do not use.
+ */
+ shutdown(icmpSock, SHUT_RD);
+
+/*
+ * Become a daemon unless verbose mode was requested.
+ */
+ if (!verbose)
+ DaemonMode ();
+/*
+ * Catch signals to manage shutdown and
+ * refresh of interface address.
+ */
+ siginterrupt(SIGTERM, 1);
+ siginterrupt(SIGHUP, 1);
+ signal (SIGTERM, InitiateShutdown);
+ signal (SIGHUP, RefreshAddr);
+/*
+ * Set alias address if it has been given.
+ */
+ if (aliasAddr.s_addr != INADDR_NONE)
+ PacketAliasSetAddress (aliasAddr);
+/*
+ * We need largest descriptor number for select.
+ */
+
+ fdMax = -1;
+
+ if (divertIn > fdMax)
+ fdMax = divertIn;
+
+ if (divertOut > fdMax)
+ fdMax = divertOut;
+
+ if (divertInOut > fdMax)
+ fdMax = divertInOut;
+
+ if (routeSock > fdMax)
+ fdMax = routeSock;
+
+ while (running) {
+
+ if (divertInOut != -1 && !ifName) {
+/*
+ * When using only one socket, just call
+ * DoAliasing repeatedly to process packets.
+ */
+ DoAliasing (divertInOut, DONT_KNOW);
+ continue;
+ }
+/*
+ * Build read mask from socket descriptors to select.
+ */
+ FD_ZERO (&readMask);
+/*
+ * Check if new packets are available.
+ */
+ if (divertIn != -1)
+ FD_SET (divertIn, &readMask);
+
+ if (divertOut != -1)
+ FD_SET (divertOut, &readMask);
+
+ if (divertInOut != -1)
+ FD_SET (divertInOut, &readMask);
+/*
+ * Routing info is processed always.
+ */
+ if (routeSock != -1)
+ FD_SET (routeSock, &readMask);
+
+ if (select (fdMax + 1,
+ &readMask,
+ NULL,
+ NULL,
+ NULL) == -1) {
+
+ if (errno == EINTR)
+ continue;
+
+ Quit ("Select failed.");
+ }
+
+ if (divertIn != -1)
+ if (FD_ISSET (divertIn, &readMask))
+ DoAliasing (divertIn, INPUT);
+
+ if (divertOut != -1)
+ if (FD_ISSET (divertOut, &readMask))
+ DoAliasing (divertOut, OUTPUT);
+
+ if (divertInOut != -1)
+ if (FD_ISSET (divertInOut, &readMask))
+ DoAliasing (divertInOut, DONT_KNOW);
+
+ if (routeSock != -1)
+ if (FD_ISSET (routeSock, &readMask))
+ HandleRoutingInfo (routeSock);
+ }
+
+ if (background)
+ unlink (PIDFILE);
+
+ return 0;
+}
+
+static void DaemonMode ()
+{
+ FILE* pidFile;
+
+ daemon (0, 0);
+ background = 1;
+
+ pidFile = fopen (PIDFILE, "w");
+ if (pidFile) {
+
+ fprintf (pidFile, "%d\n", getpid ());
+ fclose (pidFile);
+ }
+}
+
+static void ParseArgs (int argc, char** argv)
+{
+ int arg;
+ char* opt;
+ char parmBuf[256];
+ int len; /* bounds checking */
+
+ for (arg = 1; arg < argc; arg++) {
+
+ opt = argv[arg];
+ if (*opt != '-') {
+
+ warnx ("invalid option %s", opt);
+ Usage ();
+ }
+
+ parmBuf[0] = '\0';
+ len = 0;
+
+ while (arg < argc - 1) {
+
+ if (argv[arg + 1][0] == '-')
+ break;
+
+ if (len) {
+ strncat (parmBuf, " ", sizeof(parmBuf) - (len + 1));
+ len += strlen(parmBuf + len);
+ }
+
+ ++arg;
+ strncat (parmBuf, argv[arg], sizeof(parmBuf) - (len + 1));
+ len += strlen(parmBuf + len);
+
+ }
+
+ ParseOption (opt + 1, (len ? parmBuf : NULL));
+
+ }
+}
+
+static void DoAliasing (int fd, int direction)
+{
+ int bytes;
+ int origBytes;
+ char buf[IP_MAXPACKET];
+ struct sockaddr_in addr;
+ int wrote;
+ int status;
+ int addrSize;
+ struct ip* ip;
+ char msgBuf[80];
+
+ if (assignAliasAddr) {
+
+ SetAliasAddressFromIfName (ifName);
+ assignAliasAddr = 0;
+ }
+/*
+ * Get packet from socket.
+ */
+ addrSize = sizeof addr;
+ origBytes = recvfrom (fd,
+ buf,
+ sizeof buf,
+ 0,
+ (struct sockaddr*) &addr,
+ &addrSize);
+
+ if (origBytes == -1) {
+
+ if (errno != EINTR)
+ Warn ("read from divert socket failed");
+
+ return;
+ }
+/*
+ * This is a IP packet.
+ */
+ ip = (struct ip*) buf;
+ if (direction == DONT_KNOW) {
+ if (addr.sin_addr.s_addr == INADDR_ANY)
+ direction = OUTPUT;
+ else
+ direction = INPUT;
+ }
+
+ if (verbose) {
+/*
+ * Print packet direction and protocol type.
+ */
+ printf (direction == OUTPUT ? "Out " : "In ");
+
+ switch (ip->ip_p) {
+ case IPPROTO_TCP:
+ printf ("[TCP] ");
+ break;
+
+ case IPPROTO_UDP:
+ printf ("[UDP] ");
+ break;
+
+ case IPPROTO_ICMP:
+ printf ("[ICMP] ");
+ break;
+
+ default:
+ printf ("[%d] ", ip->ip_p);
+ break;
+ }
+/*
+ * Print addresses.
+ */
+ PrintPacket (ip);
+ }
+
+ if (direction == OUTPUT) {
+/*
+ * Outgoing packets. Do aliasing.
+ */
+ PacketAliasOut (buf, IP_MAXPACKET);
+ }
+ else {
+
+/*
+ * Do aliasing.
+ */
+ status = PacketAliasIn (buf, IP_MAXPACKET);
+ if (status == PKT_ALIAS_IGNORED &&
+ dropIgnoredIncoming) {
+
+ if (verbose)
+ printf (" dropped.\n");
+
+ if (logDropped)
+ SyslogPacket (ip, LOG_WARNING, "denied");
+
+ return;
+ }
+ }
+/*
+ * Length might have changed during aliasing.
+ */
+ bytes = ntohs (ip->ip_len);
+/*
+ * Update alias overhead size for outgoing packets.
+ */
+ if (direction == OUTPUT &&
+ bytes - origBytes > aliasOverhead)
+ aliasOverhead = bytes - origBytes;
+
+ if (verbose) {
+
+/*
+ * Print addresses after aliasing.
+ */
+ printf (" aliased to\n");
+ printf (" ");
+ PrintPacket (ip);
+ printf ("\n");
+ }
+
+/*
+ * Put packet back for processing.
+ */
+ wrote = sendto (fd,
+ buf,
+ bytes,
+ 0,
+ (struct sockaddr*) &addr,
+ sizeof addr);
+
+ if (wrote != bytes) {
+
+ if (errno == EMSGSIZE) {
+
+ if (direction == OUTPUT &&
+ ifMTU != -1)
+ SendNeedFragIcmp (icmpSock,
+ (struct ip*) buf,
+ ifMTU - aliasOverhead);
+ }
+ else if (errno == EACCES && logIpfwDenied) {
+
+ sprintf (msgBuf, "failed to write packet back");
+ Warn (msgBuf);
+ }
+ }
+}
+
+static void HandleRoutingInfo (int fd)
+{
+ int bytes;
+ struct if_msghdr ifMsg;
+/*
+ * Get packet from socket.
+ */
+ bytes = read (fd, &ifMsg, sizeof ifMsg);
+ if (bytes == -1) {
+
+ Warn ("read from routing socket failed");
+ return;
+ }
+
+ if (ifMsg.ifm_version != RTM_VERSION) {
+
+ Warn ("unexpected packet read from routing socket");
+ return;
+ }
+
+ if (verbose)
+ printf ("Routing message %#x received.\n", ifMsg.ifm_type);
+
+ if ((ifMsg.ifm_type == RTM_NEWADDR || ifMsg.ifm_type == RTM_IFINFO) &&
+ ifMsg.ifm_index == ifIndex) {
+ if (verbose)
+ printf("Interface address/MTU has probably changed.\n");
+ assignAliasAddr = 1;
+ }
+}
+
+static void PrintPacket (struct ip* ip)
+{
+ printf ("%s", FormatPacket (ip));
+}
+
+static void SyslogPacket (struct ip* ip, int priority, const char *label)
+{
+ syslog (priority, "%s %s", label, FormatPacket (ip));
+}
+
+static char* FormatPacket (struct ip* ip)
+{
+ static char buf[256];
+ struct tcphdr* tcphdr;
+ struct udphdr* udphdr;
+ struct icmp* icmphdr;
+ char src[20];
+ char dst[20];
+
+ strcpy (src, inet_ntoa (ip->ip_src));
+ strcpy (dst, inet_ntoa (ip->ip_dst));
+
+ switch (ip->ip_p) {
+ case IPPROTO_TCP:
+ tcphdr = (struct tcphdr*) ((char*) ip + (ip->ip_hl << 2));
+ sprintf (buf, "[TCP] %s:%d -> %s:%d",
+ src,
+ ntohs (tcphdr->th_sport),
+ dst,
+ ntohs (tcphdr->th_dport));
+ break;
+
+ case IPPROTO_UDP:
+ udphdr = (struct udphdr*) ((char*) ip + (ip->ip_hl << 2));
+ sprintf (buf, "[UDP] %s:%d -> %s:%d",
+ src,
+ ntohs (udphdr->uh_sport),
+ dst,
+ ntohs (udphdr->uh_dport));
+ break;
+
+ case IPPROTO_ICMP:
+ icmphdr = (struct icmp*) ((char*) ip + (ip->ip_hl << 2));
+ sprintf (buf, "[ICMP] %s -> %s %u(%u)",
+ src,
+ dst,
+ icmphdr->icmp_type,
+ icmphdr->icmp_code);
+ break;
+
+ default:
+ sprintf (buf, "[%d] %s -> %s ", ip->ip_p, src, dst);
+ break;
+ }
+
+ return buf;
+}
+
+static void
+SetAliasAddressFromIfName(const char *ifn)
+{
+ size_t needed;
+ int mib[6];
+ char *buf, *lim, *next;
+ struct if_msghdr *ifm;
+ struct ifa_msghdr *ifam;
+ struct sockaddr_dl *sdl;
+ struct sockaddr_in *sin;
+
+ mib[0] = CTL_NET;
+ mib[1] = PF_ROUTE;
+ mib[2] = 0;
+ mib[3] = AF_INET; /* Only IP addresses please */
+ mib[4] = NET_RT_IFLIST;
+ mib[5] = 0; /* ifIndex??? */
+/*
+ * Get interface data.
+ */
+ if (sysctl(mib, 6, NULL, &needed, NULL, 0) == -1)
+ err(1, "iflist-sysctl-estimate");
+ if ((buf = malloc(needed)) == NULL)
+ errx(1, "malloc failed");
+ if (sysctl(mib, 6, buf, &needed, NULL, 0) == -1)
+ err(1, "iflist-sysctl-get");
+ lim = buf + needed;
+/*
+ * Loop through interfaces until one with
+ * given name is found. This is done to
+ * find correct interface index for routing
+ * message processing.
+ */
+ ifIndex = 0;
+ next = buf;
+ while (next < lim) {
+ ifm = (struct if_msghdr *)next;
+ next += ifm->ifm_msglen;
+ if (ifm->ifm_version != RTM_VERSION) {
+ if (verbose)
+ warnx("routing message version %d "
+ "not understood", ifm->ifm_version);
+ continue;
+ }
+ if (ifm->ifm_type == RTM_IFINFO) {
+ sdl = (struct sockaddr_dl *)(ifm + 1);
+ if (strlen(ifn) == sdl->sdl_nlen &&
+ strncmp(ifn, sdl->sdl_data, sdl->sdl_nlen) == 0) {
+ ifIndex = ifm->ifm_index;
+ ifMTU = ifm->ifm_data.ifi_mtu;
+ break;
+ }
+ }
+ }
+ if (!ifIndex)
+ errx(1, "unknown interface name %s", ifn);
+/*
+ * Get interface address.
+ */
+ sin = NULL;
+ while (next < lim) {
+ ifam = (struct ifa_msghdr *)next;
+ next += ifam->ifam_msglen;
+ if (ifam->ifam_version != RTM_VERSION) {
+ if (verbose)
+ warnx("routing message version %d "
+ "not understood", ifam->ifam_version);
+ continue;
+ }
+ if (ifam->ifam_type != RTM_NEWADDR)
+ break;
+ if (ifam->ifam_addrs & RTA_IFA) {
+ int i;
+ char *cp = (char *)(ifam + 1);
+
+#define ROUNDUP(a) \
+ ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
+#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len))
+
+ for (i = 1; i < RTA_IFA; i <<= 1)
+ if (ifam->ifam_addrs & i)
+ ADVANCE(cp, (struct sockaddr *)cp);
+ if (((struct sockaddr *)cp)->sa_family == AF_INET) {
+ sin = (struct sockaddr_in *)cp;
+ break;
+ }
+ }
+ }
+ if (sin == NULL)
+ errx(1, "%s: cannot get interface address", ifn);
+
+ PacketAliasSetAddress(sin->sin_addr);
+ syslog(LOG_INFO, "Aliasing to %s, mtu %d bytes",
+ inet_ntoa(sin->sin_addr), ifMTU);
+
+ free(buf);
+}
+
+void Quit (const char* msg)
+{
+ Warn (msg);
+ exit (1);
+}
+
+void Warn (const char* msg)
+{
+ if (background)
+ syslog (LOG_ALERT, "%s (%m)", msg);
+ else
+ warn ("%s", msg);
+}
+
+static void RefreshAddr (int sig)
+{
+ if (ifName)
+ assignAliasAddr = 1;
+}
+
+static void InitiateShutdown (int sig)
+{
+/*
+ * Start timer to allow kernel gracefully
+ * shutdown existing connections when system
+ * is shut down.
+ */
+ siginterrupt(SIGALRM, 1);
+ signal (SIGALRM, Shutdown);
+ alarm (10);
+}
+
+static void Shutdown (int sig)
+{
+ running = 0;
+}
+
+/*
+ * Different options recognized by this program.
+ */
+
+enum Option {
+
+ PacketAliasOption,
+ Verbose,
+ InPort,
+ OutPort,
+ Port,
+ AliasAddress,
+ TargetAddress,
+ InterfaceName,
+ RedirectPort,
+ RedirectProto,
+ RedirectAddress,
+ ConfigFile,
+ DynamicMode,
+ ProxyRule,
+ LogDenied,
+ LogFacility,
+ PunchFW,
+ LogIpfwDenied
+};
+
+enum Param {
+
+ YesNo,
+ Numeric,
+ String,
+ None,
+ Address,
+ Service
+};
+
+/*
+ * Option information structure (used by ParseOption).
+ */
+
+struct OptionInfo {
+
+ enum Option type;
+ int packetAliasOpt;
+ enum Param parm;
+ const char* parmDescription;
+ const char* description;
+ const char* name;
+ const char* shortName;
+};
+
+/*
+ * Table of known options.
+ */
+
+static struct OptionInfo optionTable[] = {
+
+ { PacketAliasOption,
+ PKT_ALIAS_UNREGISTERED_ONLY,
+ YesNo,
+ "[yes|no]",
+ "alias only unregistered addresses",
+ "unregistered_only",
+ "u" },
+
+ { PacketAliasOption,
+ PKT_ALIAS_LOG,
+ YesNo,
+ "[yes|no]",
+ "enable logging",
+ "log",
+ "l" },
+
+ { PacketAliasOption,
+ PKT_ALIAS_PROXY_ONLY,
+ YesNo,
+ "[yes|no]",
+ "proxy only",
+ "proxy_only",
+ NULL },
+
+ { PacketAliasOption,
+ PKT_ALIAS_REVERSE,
+ YesNo,
+ "[yes|no]",
+ "operate in reverse mode",
+ "reverse",
+ NULL },
+
+ { PacketAliasOption,
+ PKT_ALIAS_DENY_INCOMING,
+ YesNo,
+ "[yes|no]",
+ "allow incoming connections",
+ "deny_incoming",
+ "d" },
+
+ { PacketAliasOption,
+ PKT_ALIAS_USE_SOCKETS,
+ YesNo,
+ "[yes|no]",
+ "use sockets to inhibit port conflict",
+ "use_sockets",
+ "s" },
+
+ { PacketAliasOption,
+ PKT_ALIAS_SAME_PORTS,
+ YesNo,
+ "[yes|no]",
+ "try to keep original port numbers for connections",
+ "same_ports",
+ "m" },
+
+ { Verbose,
+ 0,
+ YesNo,
+ "[yes|no]",
+ "verbose mode, dump packet information",
+ "verbose",
+ "v" },
+
+ { DynamicMode,
+ 0,
+ YesNo,
+ "[yes|no]",
+ "dynamic mode, automatically detect interface address changes",
+ "dynamic",
+ NULL },
+
+ { InPort,
+ 0,
+ Service,
+ "number|service_name",
+ "set port for incoming packets",
+ "in_port",
+ "i" },
+
+ { OutPort,
+ 0,
+ Service,
+ "number|service_name",
+ "set port for outgoing packets",
+ "out_port",
+ "o" },
+
+ { Port,
+ 0,
+ Service,
+ "number|service_name",
+ "set port (defaults to natd/divert)",
+ "port",
+ "p" },
+
+ { AliasAddress,
+ 0,
+ Address,
+ "x.x.x.x",
+ "address to use for aliasing",
+ "alias_address",
+ "a" },
+
+ { TargetAddress,
+ 0,
+ Address,
+ "x.x.x.x",
+ "address to use for incoming sessions",
+ "target_address",
+ "t" },
+
+ { InterfaceName,
+ 0,
+ String,
+ "network_if_name",
+ "take aliasing address from interface",
+ "interface",
+ "n" },
+
+ { ProxyRule,
+ 0,
+ String,
+ "[type encode_ip_hdr|encode_tcp_stream] port xxxx server "
+ "a.b.c.d:yyyy",
+ "add transparent proxying / destination NAT",
+ "proxy_rule",
+ NULL },
+
+ { RedirectPort,
+ 0,
+ String,
+ "tcp|udp local_addr:local_port_range[,...] [public_addr:]public_port_range"
+ " [remote_addr[:remote_port_range]]",
+ "redirect a port (or ports) for incoming traffic",
+ "redirect_port",
+ NULL },
+
+ { RedirectProto,
+ 0,
+ String,
+ "proto local_addr [public_addr] [remote_addr]",
+ "redirect packets of a given proto",
+ "redirect_proto",
+ NULL },
+
+ { RedirectAddress,
+ 0,
+ String,
+ "local_addr[,...] public_addr",
+ "define mapping between local and public addresses",
+ "redirect_address",
+ NULL },
+
+ { ConfigFile,
+ 0,
+ String,
+ "file_name",
+ "read options from configuration file",
+ "config",
+ "f" },
+
+ { LogDenied,
+ 0,
+ YesNo,
+ "[yes|no]",
+ "enable logging of denied incoming packets",
+ "log_denied",
+ NULL },
+
+ { LogFacility,
+ 0,
+ String,
+ "facility",
+ "name of syslog facility to use for logging",
+ "log_facility",
+ NULL },
+
+ { PunchFW,
+ 0,
+ String,
+ "basenumber:count",
+ "punch holes in the firewall for incoming FTP/IRC DCC connections",
+ "punch_fw",
+ NULL },
+
+ { LogIpfwDenied,
+ 0,
+ YesNo,
+ "[yes|no]",
+ "log packets converted by natd, but denied by ipfw",
+ "log_ipfw_denied",
+ NULL },
+};
+
+static void ParseOption (const char* option, const char* parms)
+{
+ int i;
+ struct OptionInfo* info;
+ int yesNoValue;
+ int aliasValue;
+ int numValue;
+ u_short uNumValue;
+ const char* strValue;
+ struct in_addr addrValue;
+ int max;
+ char* end;
+ CODE* fac_record = NULL;
+/*
+ * Find option from table.
+ */
+ max = sizeof (optionTable) / sizeof (struct OptionInfo);
+ for (i = 0, info = optionTable; i < max; i++, info++) {
+
+ if (!strcmp (info->name, option))
+ break;
+
+ if (info->shortName)
+ if (!strcmp (info->shortName, option))
+ break;
+ }
+
+ if (i >= max) {
+
+ warnx ("unknown option %s", option);
+ Usage ();
+ }
+
+ uNumValue = 0;
+ yesNoValue = 0;
+ numValue = 0;
+ strValue = NULL;
+/*
+ * Check parameters.
+ */
+ switch (info->parm) {
+ case YesNo:
+ if (!parms)
+ parms = "yes";
+
+ if (!strcmp (parms, "yes"))
+ yesNoValue = 1;
+ else
+ if (!strcmp (parms, "no"))
+ yesNoValue = 0;
+ else
+ errx (1, "%s needs yes/no parameter", option);
+ break;
+
+ case Service:
+ if (!parms)
+ errx (1, "%s needs service name or "
+ "port number parameter",
+ option);
+
+ uNumValue = StrToPort (parms, "divert");
+ break;
+
+ case Numeric:
+ if (parms)
+ numValue = strtol (parms, &end, 10);
+ else
+ end = NULL;
+
+ if (end == parms)
+ errx (1, "%s needs numeric parameter", option);
+ break;
+
+ case String:
+ strValue = parms;
+ if (!strValue)
+ errx (1, "%s needs parameter", option);
+ break;
+
+ case None:
+ if (parms)
+ errx (1, "%s does not take parameters", option);
+ break;
+
+ case Address:
+ if (!parms)
+ errx (1, "%s needs address/host parameter", option);
+
+ StrToAddr (parms, &addrValue);
+ break;
+ }
+
+ switch (info->type) {
+ case PacketAliasOption:
+
+ aliasValue = yesNoValue ? info->packetAliasOpt : 0;
+ PacketAliasSetMode (aliasValue, info->packetAliasOpt);
+ break;
+
+ case Verbose:
+ verbose = yesNoValue;
+ break;
+
+ case DynamicMode:
+ dynamicMode = yesNoValue;
+ break;
+
+ case InPort:
+ inPort = uNumValue;
+ break;
+
+ case OutPort:
+ outPort = uNumValue;
+ break;
+
+ case Port:
+ inOutPort = uNumValue;
+ break;
+
+ case AliasAddress:
+ memcpy (&aliasAddr, &addrValue, sizeof (struct in_addr));
+ break;
+
+ case TargetAddress:
+ PacketAliasSetTarget(addrValue);
+ break;
+
+ case RedirectPort:
+ SetupPortRedirect (strValue);
+ break;
+
+ case RedirectProto:
+ SetupProtoRedirect(strValue);
+ break;
+
+ case RedirectAddress:
+ SetupAddressRedirect (strValue);
+ break;
+
+ case ProxyRule:
+ PacketAliasProxyRule (strValue);
+ break;
+
+ case InterfaceName:
+ if (ifName)
+ free (ifName);
+
+ ifName = strdup (strValue);
+ break;
+
+ case ConfigFile:
+ ReadConfigFile (strValue);
+ break;
+
+ case LogDenied:
+ logDropped = yesNoValue;
+ break;
+
+ case LogFacility:
+
+ fac_record = facilitynames;
+ while (fac_record->c_name != NULL) {
+
+ if (!strcmp (fac_record->c_name, strValue)) {
+
+ logFacility = fac_record->c_val;
+ break;
+
+ }
+ else
+ fac_record++;
+ }
+
+ if(fac_record->c_name == NULL)
+ errx(1, "Unknown log facility name: %s", strValue);
+
+ break;
+
+ case PunchFW:
+ SetupPunchFW(strValue);
+ break;
+
+ case LogIpfwDenied:
+ logIpfwDenied = yesNoValue;;
+ break;
+ }
+}
+
+void ReadConfigFile (const char* fileName)
+{
+ FILE* file;
+ char *buf;
+ size_t len;
+ char *ptr, *p;
+ char* option;
+
+ file = fopen (fileName, "r");
+ if (!file)
+ err(1, "cannot open config file %s", fileName);
+
+ while ((buf = fgetln(file, &len)) != NULL) {
+ if (buf[len - 1] == '\n')
+ buf[len - 1] = '\0';
+ else
+ errx(1, "config file format error: "
+ "last line should end with newline");
+
+/*
+ * Check for comments, strip off trailing spaces.
+ */
+ if ((ptr = strchr(buf, '#')))
+ *ptr = '\0';
+ for (ptr = buf; isspace(*ptr); ++ptr)
+ continue;
+ if (*ptr == '\0')
+ continue;
+ for (p = strchr(buf, '\0'); isspace(*--p);)
+ continue;
+ *++p = '\0';
+
+/*
+ * Extract option name.
+ */
+ option = ptr;
+ while (*ptr && !isspace (*ptr))
+ ++ptr;
+
+ if (*ptr != '\0') {
+
+ *ptr = '\0';
+ ++ptr;
+ }
+/*
+ * Skip white space between name and parms.
+ */
+ while (*ptr && isspace (*ptr))
+ ++ptr;
+
+ ParseOption (option, *ptr ? ptr : NULL);
+ }
+
+ fclose (file);
+}
+
+static void Usage ()
+{
+ int i;
+ int max;
+ struct OptionInfo* info;
+
+ fprintf (stderr, "Recognized options:\n\n");
+
+ max = sizeof (optionTable) / sizeof (struct OptionInfo);
+ for (i = 0, info = optionTable; i < max; i++, info++) {
+
+ fprintf (stderr, "-%-20s %s\n", info->name,
+ info->parmDescription);
+
+ if (info->shortName)
+ fprintf (stderr, "-%-20s %s\n", info->shortName,
+ info->parmDescription);
+
+ fprintf (stderr, " %s\n\n", info->description);
+ }
+
+ exit (1);
+}
+
+void SetupPortRedirect (const char* parms)
+{
+ char buf[128];
+ char* ptr;
+ char* serverPool;
+ struct in_addr localAddr;
+ struct in_addr publicAddr;
+ struct in_addr remoteAddr;
+ port_range portRange;
+ u_short localPort = 0;
+ u_short publicPort = 0;
+ u_short remotePort = 0;
+ u_short numLocalPorts = 0;
+ u_short numPublicPorts = 0;
+ u_short numRemotePorts = 0;
+ int proto;
+ char* protoName;
+ char* separator;
+ int i;
+ struct alias_link *link = NULL;
+
+ strcpy (buf, parms);
+/*
+ * Extract protocol.
+ */
+ protoName = strtok (buf, " \t");
+ if (!protoName)
+ errx (1, "redirect_port: missing protocol");
+
+ proto = StrToProto (protoName);
+/*
+ * Extract local address.
+ */
+ ptr = strtok (NULL, " \t");
+ if (!ptr)
+ errx (1, "redirect_port: missing local address");
+
+ separator = strchr(ptr, ',');
+ if (separator) { /* LSNAT redirection syntax. */
+ localAddr.s_addr = INADDR_NONE;
+ localPort = ~0;
+ numLocalPorts = 1;
+ serverPool = ptr;
+ } else {
+ if ( StrToAddrAndPortRange (ptr, &localAddr, protoName, &portRange) != 0 )
+ errx (1, "redirect_port: invalid local port range");
+
+ localPort = GETLOPORT(portRange);
+ numLocalPorts = GETNUMPORTS(portRange);
+ serverPool = NULL;
+ }
+
+/*
+ * Extract public port and optionally address.
+ */
+ ptr = strtok (NULL, " \t");
+ if (!ptr)
+ errx (1, "redirect_port: missing public port");
+
+ separator = strchr (ptr, ':');
+ if (separator) {
+ if (StrToAddrAndPortRange (ptr, &publicAddr, protoName, &portRange) != 0 )
+ errx (1, "redirect_port: invalid public port range");
+ }
+ else {
+ publicAddr.s_addr = INADDR_ANY;
+ if (StrToPortRange (ptr, protoName, &portRange) != 0)
+ errx (1, "redirect_port: invalid public port range");
+ }
+
+ publicPort = GETLOPORT(portRange);
+ numPublicPorts = GETNUMPORTS(portRange);
+
+/*
+ * Extract remote address and optionally port.
+ */
+ ptr = strtok (NULL, " \t");
+ if (ptr) {
+ separator = strchr (ptr, ':');
+ if (separator) {
+ if (StrToAddrAndPortRange (ptr, &remoteAddr, protoName, &portRange) != 0)
+ errx (1, "redirect_port: invalid remote port range");
+ } else {
+ SETLOPORT(portRange, 0);
+ SETNUMPORTS(portRange, 1);
+ StrToAddr (ptr, &remoteAddr);
+ }
+ }
+ else {
+ SETLOPORT(portRange, 0);
+ SETNUMPORTS(portRange, 1);
+ remoteAddr.s_addr = INADDR_ANY;
+ }
+
+ remotePort = GETLOPORT(portRange);
+ numRemotePorts = GETNUMPORTS(portRange);
+
+/*
+ * Make sure port ranges match up, then add the redirect ports.
+ */
+ if (numLocalPorts != numPublicPorts)
+ errx (1, "redirect_port: port ranges must be equal in size");
+
+ /* Remote port range is allowed to be '0' which means all ports. */
+ if (numRemotePorts != numLocalPorts && (numRemotePorts != 1 || remotePort != 0))
+ errx (1, "redirect_port: remote port must be 0 or equal to local port range in size");
+
+ for (i = 0 ; i < numPublicPorts ; ++i) {
+ /* If remotePort is all ports, set it to 0. */
+ u_short remotePortCopy = remotePort + i;
+ if (numRemotePorts == 1 && remotePort == 0)
+ remotePortCopy = 0;
+
+ link = PacketAliasRedirectPort (localAddr,
+ htons(localPort + i),
+ remoteAddr,
+ htons(remotePortCopy),
+ publicAddr,
+ htons(publicPort + i),
+ proto);
+ }
+
+/*
+ * Setup LSNAT server pool.
+ */
+ if (serverPool != NULL && link != NULL) {
+ ptr = strtok(serverPool, ",");
+ while (ptr != NULL) {
+ if (StrToAddrAndPortRange(ptr, &localAddr, protoName, &portRange) != 0)
+ errx(1, "redirect_port: invalid local port range");
+
+ localPort = GETLOPORT(portRange);
+ if (GETNUMPORTS(portRange) != 1)
+ errx(1, "redirect_port: local port must be single in this context");
+ PacketAliasAddServer(link, localAddr, htons(localPort));
+ ptr = strtok(NULL, ",");
+ }
+ }
+}
+
+void
+SetupProtoRedirect(const char* parms)
+{
+ char buf[128];
+ char* ptr;
+ struct in_addr localAddr;
+ struct in_addr publicAddr;
+ struct in_addr remoteAddr;
+ int proto;
+ char* protoName;
+ struct protoent *protoent;
+
+ strcpy (buf, parms);
+/*
+ * Extract protocol.
+ */
+ protoName = strtok(buf, " \t");
+ if (!protoName)
+ errx(1, "redirect_proto: missing protocol");
+
+ protoent = getprotobyname(protoName);
+ if (protoent == NULL)
+ errx(1, "redirect_proto: unknown protocol %s", protoName);
+ else
+ proto = protoent->p_proto;
+/*
+ * Extract local address.
+ */
+ ptr = strtok(NULL, " \t");
+ if (!ptr)
+ errx(1, "redirect_proto: missing local address");
+ else
+ StrToAddr(ptr, &localAddr);
+/*
+ * Extract optional public address.
+ */
+ ptr = strtok(NULL, " \t");
+ if (ptr)
+ StrToAddr(ptr, &publicAddr);
+ else
+ publicAddr.s_addr = INADDR_ANY;
+/*
+ * Extract optional remote address.
+ */
+ ptr = strtok(NULL, " \t");
+ if (ptr)
+ StrToAddr(ptr, &remoteAddr);
+ else
+ remoteAddr.s_addr = INADDR_ANY;
+/*
+ * Create aliasing link.
+ */
+ (void)PacketAliasRedirectProto(localAddr, remoteAddr, publicAddr,
+ proto);
+}
+
+void SetupAddressRedirect (const char* parms)
+{
+ char buf[128];
+ char* ptr;
+ char* separator;
+ struct in_addr localAddr;
+ struct in_addr publicAddr;
+ char* serverPool;
+ struct alias_link *link;
+
+ strcpy (buf, parms);
+/*
+ * Extract local address.
+ */
+ ptr = strtok (buf, " \t");
+ if (!ptr)
+ errx (1, "redirect_address: missing local address");
+
+ separator = strchr(ptr, ',');
+ if (separator) { /* LSNAT redirection syntax. */
+ localAddr.s_addr = INADDR_NONE;
+ serverPool = ptr;
+ } else {
+ StrToAddr (ptr, &localAddr);
+ serverPool = NULL;
+ }
+/*
+ * Extract public address.
+ */
+ ptr = strtok (NULL, " \t");
+ if (!ptr)
+ errx (1, "redirect_address: missing public address");
+
+ StrToAddr (ptr, &publicAddr);
+ link = PacketAliasRedirectAddr(localAddr, publicAddr);
+
+/*
+ * Setup LSNAT server pool.
+ */
+ if (serverPool != NULL && link != NULL) {
+ ptr = strtok(serverPool, ",");
+ while (ptr != NULL) {
+ StrToAddr(ptr, &localAddr);
+ PacketAliasAddServer(link, localAddr, htons(~0));
+ ptr = strtok(NULL, ",");
+ }
+ }
+}
+
+void StrToAddr (const char* str, struct in_addr* addr)
+{
+ struct hostent* hp;
+
+ if (inet_aton (str, addr))
+ return;
+
+ hp = gethostbyname (str);
+ if (!hp)
+ errx (1, "unknown host %s", str);
+
+ memcpy (addr, hp->h_addr, sizeof (struct in_addr));
+}
+
+u_short StrToPort (const char* str, const char* proto)
+{
+ u_short port;
+ struct servent* sp;
+ char* end;
+
+ port = strtol (str, &end, 10);
+ if (end != str)
+ return htons (port);
+
+ sp = getservbyname (str, proto);
+ if (!sp)
+ errx (1, "unknown service %s/%s", str, proto);
+
+ return sp->s_port;
+}
+
+int StrToPortRange (const char* str, const char* proto, port_range *portRange)
+{
+ char* sep;
+ struct servent* sp;
+ char* end;
+ u_short loPort;
+ u_short hiPort;
+
+ /* First see if this is a service, return corresponding port if so. */
+ sp = getservbyname (str,proto);
+ if (sp) {
+ SETLOPORT(*portRange, ntohs(sp->s_port));
+ SETNUMPORTS(*portRange, 1);
+ return 0;
+ }
+
+ /* Not a service, see if it's a single port or port range. */
+ sep = strchr (str, '-');
+ if (sep == NULL) {
+ SETLOPORT(*portRange, strtol(str, &end, 10));
+ if (end != str) {
+ /* Single port. */
+ SETNUMPORTS(*portRange, 1);
+ return 0;
+ }
+
+ /* Error in port range field. */
+ errx (1, "unknown service %s/%s", str, proto);
+ }
+
+ /* Port range, get the values and sanity check. */
+ sscanf (str, "%hu-%hu", &loPort, &hiPort);
+ SETLOPORT(*portRange, loPort);
+ SETNUMPORTS(*portRange, 0); /* Error by default */
+ if (loPort <= hiPort)
+ SETNUMPORTS(*portRange, hiPort - loPort + 1);
+
+ if (GETNUMPORTS(*portRange) == 0)
+ errx (1, "invalid port range %s", str);
+
+ return 0;
+}
+
+
+int StrToProto (const char* str)
+{
+ if (!strcmp (str, "tcp"))
+ return IPPROTO_TCP;
+
+ if (!strcmp (str, "udp"))
+ return IPPROTO_UDP;
+
+ errx (1, "unknown protocol %s. Expected tcp or udp", str);
+}
+
+int StrToAddrAndPortRange (const char* str, struct in_addr* addr, char* proto, port_range *portRange)
+{
+ char* ptr;
+
+ ptr = strchr (str, ':');
+ if (!ptr)
+ errx (1, "%s is missing port number", str);
+
+ *ptr = '\0';
+ ++ptr;
+
+ StrToAddr (str, addr);
+ return StrToPortRange (ptr, proto, portRange);
+}
+
+static void
+SetupPunchFW(const char *strValue)
+{
+ unsigned int base, num;
+
+ if (sscanf(strValue, "%u:%u", &base, &num) != 2)
+ errx(1, "punch_fw: basenumber:count parameter required");
+
+ PacketAliasSetFWBase(base, num);
+ (void)PacketAliasSetMode(PKT_ALIAS_PUNCH_FW, PKT_ALIAS_PUNCH_FW);
+}
diff --git a/sbin/natd/natd.h b/sbin/natd/natd.h
new file mode 100644
index 0000000..ac0dd75
--- /dev/null
+++ b/sbin/natd/natd.h
@@ -0,0 +1,24 @@
+/*
+ * natd - Network Address Translation Daemon for FreeBSD.
+ *
+ * This software is provided free of charge, with no
+ * warranty of any kind, either expressed or implied.
+ * Use at your own risk.
+ *
+ * You may copy, modify and distribute this software (natd.h) freely.
+ *
+ * Ari Suutari <suutari@iki.fi>
+ *
+ * $FreeBSD$
+ */
+
+#define PIDFILE "/var/run/natd.pid"
+#define INPUT 1
+#define OUTPUT 2
+#define DONT_KNOW 3
+
+extern void Quit (const char* msg);
+extern void Warn (const char* msg);
+extern int SendNeedFragIcmp (int sock, struct ip* failedDgram, int mtu);
+
+
diff --git a/sbin/natd/samples/natd.cf.sample b/sbin/natd/samples/natd.cf.sample
new file mode 100644
index 0000000..d4dcd71
--- /dev/null
+++ b/sbin/natd/samples/natd.cf.sample
@@ -0,0 +1,92 @@
+#
+# $FreeBSD$
+#
+#
+# Configuration file for natd.
+#
+#
+# Enable logging to file /var/log/alias.log
+#
+log no
+#
+# Incoming connections. Should NEVER be set to "yes" if redirect_port
+# or redirect_address statements are activated in this file!
+#
+# Setting to yes provides additional anti-crack protection
+#
+deny_incoming no
+#
+# Use sockets to avoid port clashes. Uses additional system resources, but
+# guarantees successful connections when port numbers conflict
+#
+use_sockets no
+#
+# Avoid port changes if possible when altering outbound packets. Makes rlogin
+# work in most cases.
+#
+same_ports yes
+#
+# Verbose mode. Enables dumping of packets and disables
+# forking to background. Only set to yes for debugging.
+#
+verbose no
+#
+# Divert port. Can be a name in /etc/services or numeric value.
+#
+port 32000
+#
+# Interface name or address being aliased. Either one,
+# not both is required.
+#
+# Obtain interface name from the command output of "ifconfig -a"
+#
+# alias_address 192.168.0.1
+interface ep0
+#
+# Alias unregistered addresses or all addresses. Set this to yes if
+# the inside network is all RFC1918 addresses.
+#
+unregistered_only no
+#
+# Configure permanent links. If you use host names instead
+# of addresses here, be sure that name server works BEFORE
+# natd is up - this is usually not the case. So either use
+# numeric addresses or hosts that are in /etc/hosts.
+#
+# Note: Current versions of FreeBSD all call /etc/rc.firewall
+# BEFORE running named, so if the DNS server and NAT are on the same
+# machine, the nameserver won't be up if natd is called from /etc/rc.firewall
+#
+# Map connections coming to port 30000 to telnet in my_private_host.
+# Remember to allow the connection /etc/rc.firewall also.
+#
+#redirect_port tcp my_private_host:telnet 30000
+#
+# Map connections coming from host.xyz.com to port 30001 to
+# telnet in another_host.
+#redirect_port tcp another_host:telnet 30001 host.xyz.com
+#
+# Static NAT address mapping:
+#
+# ipconfig must apply any legal IP numbers that inside hosts
+# will be known by to the outside interface. These are sometimes known as
+# virtual IP numbers. It's suggested to use the "interface" directive
+# instead of the "alias_address" directive to make it more clear what is
+# going on. (although both will work)
+#
+# DNS in this situation can get hairy. For example, an inside host
+# named aweb.company.com is located at 192.168.1.56, and needs to be
+# accessible through a legal IP number like 198.105.232.1. If both
+# 192.168.1.56 and 198.105.232.1 are set up as address records in the DNS
+# for aweb.company.com, then external hosts attempting to access
+# aweb.company.com may use address 192.168.1.56 which is inaccessible to them.
+#
+# The obvious solution is to use only a single address for the name, the
+# outside address. However, this creates needless traffic through the
+# NAT, because inside hosts will go through the NAT to get to the legal
+# number, even when the inside number is on the same subnet as they are!
+#
+# It's probably not a good idea to use DNS names in redirect_address statements
+#
+#The following mapping points outside address 198.105.232.1 to 192.168.1.56
+#redirect_address 192.168.1.56 198.105.232.1
diff --git a/sbin/natd/samples/natd.test b/sbin/natd/samples/natd.test
new file mode 100644
index 0000000..cfdbd15
--- /dev/null
+++ b/sbin/natd/samples/natd.test
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+ if [ $# != 1 ]
+ then
+ echo "usage: natd.test ifname"
+ exit 1
+ fi
+
+ ipfw flush
+ ipfw add divert 32000 ip from any to any via $1
+ ipfw add pass ip from any to any
+
+ ./natd -port 32000 -interface $1 -verbose
+
OpenPOWER on IntegriCloud