diff options
Diffstat (limited to 'sys/netinet/libalias')
-rw-r--r-- | sys/netinet/libalias/HISTORY | 129 | ||||
-rw-r--r-- | sys/netinet/libalias/Makefile | 14 | ||||
-rw-r--r-- | sys/netinet/libalias/alias.c | 1147 | ||||
-rw-r--r-- | sys/netinet/libalias/alias.h | 173 | ||||
-rw-r--r-- | sys/netinet/libalias/alias_db.c | 2239 | ||||
-rw-r--r-- | sys/netinet/libalias/alias_ftp.c | 227 | ||||
-rw-r--r-- | sys/netinet/libalias/alias_irc.c | 316 | ||||
-rw-r--r-- | sys/netinet/libalias/alias_local.h | 105 | ||||
-rw-r--r-- | sys/netinet/libalias/alias_nbt.c | 576 | ||||
-rw-r--r-- | sys/netinet/libalias/alias_old.c | 77 | ||||
-rw-r--r-- | sys/netinet/libalias/alias_util.c | 137 | ||||
-rw-r--r-- | sys/netinet/libalias/libalias.3 | 768 |
12 files changed, 5908 insertions, 0 deletions
diff --git a/sys/netinet/libalias/HISTORY b/sys/netinet/libalias/HISTORY new file mode 100644 index 0000000..3d97fd0 --- /dev/null +++ b/sys/netinet/libalias/HISTORY @@ -0,0 +1,129 @@ +Version 1.0: August 11, 1996 (cjm) + +Version 1.1: August 20, 1996 (cjm) + - Host accepts incoming connections for ports 0 to 1023. + +Version 1.2: September 7, 1996 (cjm) + - Fragment handling error in alias_db.c corrected. + +Version 1.3: September 15, 1996 (cjm) + - Generalized mechanism for handling incoming + connections (no more 0 to 1023 restriction). + + - Increased ICMP support (will handle traceroute now). + + - Improved TCP close connection logic. + +Version 1.4: September 16, 1996 (cjm) + +Version 1.5: September 17, 1996 (cjm) + - Corrected error in handling incoming UDP packets + with zero checksum. + +Version 1.6: September 18, 1996 + - Simplified ICMP data storage. Will now handle + tracert from Win95 and NT as well as FreeBSD + traceroute, which uses UDP packets to non-existent + ports. + +Verstion 1.7: January 9, 1997 (cjm) + - Reduced malloc() activity for ICMP echo and + timestamp requests. + + - Added handling for out-of-order IP fragments. + + - Switched to differential checksum computation + for IP headers (TCP, UDP and ICMP checksums + were already differential). + + - Accepts FTP data connections from other than + port 20. This allows one ftp connections + from two hosts which are both running packet + aliasing. + + - Checksum error on FTP transfers. Problem + in code located by Martin Renters and + Brian Somers. + +Version 1.8: January 14, 1997 (cjm) + - Fixed data type error in function StartPoint() + in alias_db.c (this bug did not exist before v1.7) + Problem in code located by Ari Suutari. + +Version 1.9: February 1, 1997 (Eivind Eklund <perhaps@yes.no>) + - Added support for IRC DCC (ee) + + - Changed the aliasing routines to use ANSI style + throughout (ee) + + - Minor API changes for integration with other + programs than PPP (ee) + + - Fixed minor security hole in alias_ftp.c for + other applications of the aliasing software. + Hole could _not_ manifest in ppp+pktAlias, but + could potentially manifest in other applications + of the aliasing. (ee) + + - Connections initiated from packet aliasing + host machine will not have their port number + aliased unless it conflicts with an aliasing + port already being used. (There is an option + to disable this for debugging) (cjm) + + - Sockets will be allocated in cases where + there might be port interference with the + host machine. This can be disabled in cases + where the ppp host will be acting purely as a + masquerading router and not generate any + traffic of its own. + (cjm) + +Version 2.0: March, 1997 (cjm) + - Aliasing links are cleared only when a host interface address + changes. + + - PacketAliasPermanentLink() API added. + + - Option for only aliasing private, unregistered + IP addresses added. + + - Substantial rework to the aliasing lookup engine. + +Version 2.1: May, 1997 (cjm) + - Continuing rework to the aliasing lookup engine + to support multiple incoming addresses and static + NAT. PacketAliasRedirectPort() and + PacketAliasRedirectAddr() added to API. + + - Now supports outgoing as well as incoming ICMP + error messges. + +Version 2.2: July, 1997 (cjm) + - Rationalized API function names to all begin with + "PacketAlias..." Old function names are retained + for backwards compatitibility. + + - Packet aliasing engine will now free memory of + fragments which are never resolved after a timeout + period. Once a fragment is resolved, it becomes + the users responsibility to free the memory. + +Version 2.3: August 11, 1997 (cjm) + - Problem associated with socket file descriptor + accumulation in alias_db.c corrected. The sockets + had to be closed when a binding failed. Problem + in code located by Gordon Burditt. + +Version 2.4: September 1, 1997 (cjm) + - PKT_ALIAS_UNREGISTERED_ONLY option repaired. + This part of the code was incorrectly re-implemented + in version 2.1. + +Version 2.5: December, 1997 (ee) + - Added PKT_ALIAS_PUNCH_FW mode for firewall + bypass of FTP/IRC DCC data connections. Also added + improved TCP connection monitoring. + +Version 2.6: May, 1998 (amurai) + - Added supporting routine for NetBios over TCP/IP. diff --git a/sys/netinet/libalias/Makefile b/sys/netinet/libalias/Makefile new file mode 100644 index 0000000..8147665 --- /dev/null +++ b/sys/netinet/libalias/Makefile @@ -0,0 +1,14 @@ +LIB= alias +SHLIB_MAJOR= 2 +SHLIB_MINOR= 5 +CFLAGS+=-Wall -I${.CURDIR} +SRCS= alias.c alias_db.c alias_ftp.c alias_irc.c alias_util.c alias_old.c \ + alias_nbt.c + +MAN3=libalias.3 + +beforeinstall: + ${INSTALL} -C -o ${BINOWN} -g ${BINGRP} -m 444 ${.CURDIR}/alias.h \ + ${DESTDIR}/usr/include + +.include <bsd.lib.mk> diff --git a/sys/netinet/libalias/alias.c b/sys/netinet/libalias/alias.c new file mode 100644 index 0000000..f0c0804 --- /dev/null +++ b/sys/netinet/libalias/alias.c @@ -0,0 +1,1147 @@ +/* -*- mode: c; tab-width: 8; c-basic-indent: 4; -*- */ +/* + Alias.c provides supervisory control for the functions of the + packet aliasing software. It consists of routines to monitor + TCP connection state, protocol-specific aliasing routines, + fragment handling and the following outside world functional + interfaces: SaveFragmentPtr, GetFragmentPtr, FragmentAliasIn, + PacketAliasIn and PacketAliasOut. + + The other C program files are briefly described. The data + structure framework which holds information needed to translate + packets is encapsulated in alias_db.c. Data is accessed by + function calls, so other segments of the program need not know + about the underlying data structures. Alias_ftp.c contains + special code for modifying the ftp PORT command used to establish + data connections, while alias_irc.c do the same for IRC + DCC. Alias_util.c contains a few utility routines. + + This software is placed into the public domain with no restrictions + on its distribution. + + Version 1.0 August, 1996 (cjm) + + Version 1.1 August 20, 1996 (cjm) + PPP host accepts incoming connections for ports 0 to 1023. + (Gary Roberts pointed out the need to handle incoming + connections.) + + Version 1.2 September 7, 1996 (cjm) + Fragment handling error in alias_db.c corrected. + (Tom Torrance helped fix this problem.) + + Version 1.4 September 16, 1996 (cjm) + - A more generalized method for handling incoming + connections, without the 0-1023 restriction, is + implemented in alias_db.c + - Improved ICMP support in alias.c. Traceroute + packet streams can now be correctly aliased. + - TCP connection closing logic simplified in + alias.c and now allows for additional 1 minute + "grace period" after FIN or RST is observed. + + Version 1.5 September 17, 1996 (cjm) + Corrected error in handling incoming UDP packets with 0 checksum. + (Tom Torrance helped fix this problem.) + + Version 1.6 September 18, 1996 (cjm) + Simplified ICMP aliasing scheme. Should now support + traceroute from Win95 as well as FreeBSD. + + Version 1.7 January 9, 1997 (cjm) + - Out-of-order fragment handling. + - IP checksum error fixed for ftp transfers + from aliasing host. + - Integer return codes added to all + aliasing/de-aliasing functions. + - Some obsolete comments cleaned up. + - Differential checksum computations for + IP header (TCP, UDP and ICMP were already + differential). + + Version 2.1 May 1997 (cjm) + - Added support for outgoing ICMP error + messages. + - Added two functions PacketAliasIn2() + and PacketAliasOut2() for dynamic address + control (e.g. round-robin allocation of + incoming packets). + + Version 2.2 July 1997 (cjm) + - Rationalized API function names to begin + with "PacketAlias..." + - Eliminated PacketAliasIn2() and + PacketAliasOut2() as poorly conceived. + + See HISTORY file for additional revisions. + +*/ + +#include <stdio.h> +#include <unistd.h> + +#include <sys/param.h> +#include <sys/types.h> + +#include <netinet/in_systm.h> +#include <netinet/in.h> +#include <netinet/ip.h> +#include <netinet/ip_icmp.h> +#include <netinet/tcp.h> +#include <netinet/udp.h> + +#include "alias_local.h" +#include "alias.h" + +#define NETBIOS_NS_PORT_NUMBER 137 +#define NETBIOS_DGM_PORT_NUMBER 138 +#define FTP_CONTROL_PORT_NUMBER 21 +#define FTP_CONTROL_PORT_NUMBER 21 +#define IRC_CONTROL_PORT_NUMBER_1 6667 +#define IRC_CONTROL_PORT_NUMBER_2 6668 + +/* + The following macro is used to update an + internet checksum. "delta" is a 32-bit + accumulation of all the changes to the + checksum (adding in new 16-bit words and + subtracting out old words), and "cksum" + is the checksum value to be updated. +*/ +#define ADJUST_CHECKSUM(acc, cksum) { \ + acc += cksum; \ + if (acc < 0) \ + { \ + acc = -acc; \ + acc = (acc >> 16) + (acc & 0xffff); \ + acc += acc >> 16; \ + cksum = (u_short) ~acc; \ + } \ + else \ + { \ + acc = (acc >> 16) + (acc & 0xffff); \ + acc += acc >> 16; \ + cksum = (u_short) acc; \ + } \ +} + + + + +/* TCP Handling Routines + + TcpMonitorIn() -- These routines monitor TCP connections, and + TcpMonitorOut() -- delete a link node when a connection is closed. + +These routines look for SYN, ACK and RST flags to determine when TCP +connections open and close. When a TCP connection closes, the data +structure containing packet aliasing information is deleted after +a timeout period. +*/ + +/* Local prototypes */ +static void TcpMonitorIn(struct ip *, struct alias_link *); + +static void TcpMonitorOut(struct ip *, struct alias_link *); + + +static void +TcpMonitorIn(struct ip *pip, struct alias_link *link) +{ + struct tcphdr *tc; + + tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2)); + + switch (GetStateIn(link)) + { + case ALIAS_TCP_STATE_NOT_CONNECTED: + if (tc->th_flags & TH_SYN) + SetStateIn(link, ALIAS_TCP_STATE_CONNECTED); + break; + case ALIAS_TCP_STATE_CONNECTED: + if (tc->th_flags & TH_FIN + || tc->th_flags & TH_RST) + SetStateIn(link, ALIAS_TCP_STATE_DISCONNECTED); + break; + } +} + +static void +TcpMonitorOut(struct ip *pip, struct alias_link *link) +{ + struct tcphdr *tc; + + tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2)); + + switch (GetStateOut(link)) + { + case ALIAS_TCP_STATE_NOT_CONNECTED: + if (tc->th_flags & TH_SYN) + SetStateOut(link, ALIAS_TCP_STATE_CONNECTED); + break; + case ALIAS_TCP_STATE_CONNECTED: + if (tc->th_flags & TH_FIN + || tc->th_flags & TH_RST) + SetStateOut(link, ALIAS_TCP_STATE_DISCONNECTED); + break; + } +} + + + + + +/* Protocol Specific Packet Aliasing Routines + + IcmpAliasIn(), IcmpAliasIn1(), IcmpAliasIn2(), IcmpAliasIn3() + IcmpAliasOut(), IcmpAliasOut1(), IcmpAliasOut2(), IcmpAliasOut3() + UdpAliasIn(), UdpAliasOut() + TcpAliasIn(), TcpAliasOut() + +These routines handle protocol specific details of packet aliasing. +One may observe a certain amount of repetitive arithmetic in these +functions, the purpose of which is to compute a revised checksum +without actually summing over the entire data packet, which could be +unnecessarily time consuming. + +The purpose of the packet aliasing routines is to replace the source +address of the outgoing packet and then correctly put it back for +any incoming packets. For TCP and UDP, ports are also re-mapped. + +For ICMP echo/timestamp requests and replies, the following scheme +is used: the id number is replaced by an alias for the outgoing +packet. + +ICMP error messages are handled by looking at the IP fragment +in the data section of the message. + +For TCP and UDP protocols, a port number is chosen for an outgoing +packet, and then incoming packets are identified by IP address and +port numbers. For TCP packets, there is additional logic in the event +that sequence and ack numbers have been altered (as is the case for +FTP data port commands). + +The port numbers used by the packet aliasing module are not true +ports in the Unix sense. No sockets are actually bound to ports. +They are more correctly thought of as placeholders. + +All packets go through the aliasing mechanism, whether they come from +the gateway machine or other machines on a local area network. +*/ + + +/* Local prototypes */ +static int IcmpAliasIn1(struct ip *); +static int IcmpAliasIn2(struct ip *); +static int IcmpAliasIn3(struct ip *); +static int IcmpAliasIn (struct ip *); + +static int IcmpAliasOut1(struct ip *); +static int IcmpAliasOut2(struct ip *); +static int IcmpAliasOut3(struct ip *); +static int IcmpAliasOut (struct ip *); + +static int UdpAliasOut(struct ip *); +static int UdpAliasIn (struct ip *); + +static int TcpAliasOut(struct ip *, int); +static int TcpAliasIn (struct ip *); + + +static int +IcmpAliasIn1(struct ip *pip) +{ +/* + De-alias incoming echo and timestamp replies +*/ + struct alias_link *link; + struct icmp *ic; + + ic = (struct icmp *) ((char *) pip + (pip->ip_hl << 2)); + +/* Get source address from ICMP data field and restore original data */ + link = FindIcmpIn(pip->ip_src, pip->ip_dst, ic->icmp_id); + if (link != NULL) + { + u_short original_id; + int accumulate; + + original_id = GetOriginalPort(link); + +/* Adjust ICMP checksum */ + accumulate = ic->icmp_id; + accumulate -= original_id; + ADJUST_CHECKSUM(accumulate, ic->icmp_cksum) + +/* Put original sequence number back in */ + ic->icmp_id = original_id; + +/* Put original address back into IP header */ + { + struct in_addr original_address; + + original_address = GetOriginalAddress(link); + DifferentialChecksum(&pip->ip_sum, + (u_short *) &original_address, + (u_short *) &pip->ip_dst, + 2); + pip->ip_dst = original_address; + } + + return(PKT_ALIAS_OK); + } + return(PKT_ALIAS_IGNORED); +} + +static int +IcmpAliasIn2(struct ip *pip) +{ +/* + Alias incoming ICMP error messages containing + IP header and first 64 bits of datagram. +*/ + struct ip *ip; + struct icmp *ic, *ic2; + struct udphdr *ud; + struct tcphdr *tc; + struct alias_link *link; + + ic = (struct icmp *) ((char *) pip + (pip->ip_hl << 2)); + ip = (struct ip *) ic->icmp_data; + + ud = (struct udphdr *) ((char *) ip + (ip->ip_hl <<2)); + tc = (struct tcphdr *) ud; + ic2 = (struct icmp *) ud; + + if (ip->ip_p == IPPROTO_UDP) + link = FindUdpTcpIn(ip->ip_dst, ip->ip_src, + ud->uh_dport, ud->uh_sport, + IPPROTO_UDP); + else if (ip->ip_p == IPPROTO_TCP) + link = FindUdpTcpIn(ip->ip_dst, ip->ip_src, + tc->th_dport, tc->th_sport, + IPPROTO_TCP); + else if (ip->ip_p == IPPROTO_ICMP) { + if (ic2->icmp_type == ICMP_ECHO || ic2->icmp_type == ICMP_TSTAMP) + link = FindIcmpIn(ip->ip_dst, ip->ip_src, ic2->icmp_id); + else + link = NULL; + } else + link = NULL; + + if (link != NULL) + { + if (ip->ip_p == IPPROTO_UDP || ip->ip_p == IPPROTO_TCP) + { + u_short *sptr; + int accumulate; + struct in_addr original_address; + u_short original_port; + + original_address = GetOriginalAddress(link); + original_port = GetOriginalPort(link); + +/* Adjust ICMP checksum */ + sptr = (u_short *) &(ip->ip_src); + accumulate = *sptr++; + accumulate += *sptr; + sptr = (u_short *) &original_address; + accumulate -= *sptr++; + accumulate -= *sptr; + accumulate += ud->uh_sport; + accumulate -= original_port; + ADJUST_CHECKSUM(accumulate, ic->icmp_cksum) + +/* Un-alias address in IP header */ + DifferentialChecksum(&pip->ip_sum, + (u_short *) &original_address, + (u_short *) &pip->ip_dst, + 2); + pip->ip_dst = original_address; + +/* Un-alias address and port number of original IP packet +fragment contained in ICMP data section */ + ip->ip_src = original_address; + ud->uh_sport = original_port; + } + else if (pip->ip_p == IPPROTO_ICMP) + { + u_short *sptr; + int accumulate; + struct in_addr original_address; + u_short original_id; + + original_address = GetOriginalAddress(link); + original_id = GetOriginalPort(link); + +/* Adjust ICMP checksum */ + sptr = (u_short *) &(ip->ip_src); + accumulate = *sptr++; + accumulate += *sptr; + sptr = (u_short *) &original_address; + accumulate -= *sptr++; + accumulate -= *sptr; + accumulate += ic2->icmp_id; + accumulate -= original_id; + ADJUST_CHECKSUM(accumulate, ic->icmp_cksum) + +/* Un-alias address in IP header */ + DifferentialChecksum(&pip->ip_sum, + (u_short *) &original_address, + (u_short *) &pip->ip_dst, + 2); + pip->ip_dst = original_address; + +/* Un-alias address of original IP packet and seqence number of + embedded icmp datagram */ + ip->ip_src = original_address; + ic2->icmp_id = original_id; + } + return(PKT_ALIAS_OK); + } + return(PKT_ALIAS_IGNORED); +} + + +static int +IcmpAliasIn3(struct ip *pip) +{ + struct in_addr original_address; + + original_address = FindOriginalAddress(pip->ip_dst); + DifferentialChecksum(&pip->ip_sum, + (u_short *) &original_address, + (u_short *) &pip->ip_dst, + 2); + pip->ip_dst = original_address; + + return PKT_ALIAS_OK; +} + + +static int +IcmpAliasIn(struct ip *pip) +{ + int iresult; + struct icmp *ic; + + ic = (struct icmp *) ((char *) pip + (pip->ip_hl << 2)); + + iresult = PKT_ALIAS_IGNORED; + switch (ic->icmp_type) + { + case ICMP_ECHOREPLY: + case ICMP_TSTAMPREPLY: + if (ic->icmp_code == 0) + { + iresult = IcmpAliasIn1(pip); + } + break; + case ICMP_UNREACH: + case ICMP_SOURCEQUENCH: + case ICMP_TIMXCEED: + case ICMP_PARAMPROB: + iresult = IcmpAliasIn2(pip); + break; + case ICMP_ECHO: + case ICMP_TSTAMP: + iresult = IcmpAliasIn3(pip); + break; + } + return(iresult); +} + + +static int +IcmpAliasOut1(struct ip *pip) +{ +/* + Alias ICMP echo and timestamp packets +*/ + struct alias_link *link; + struct icmp *ic; + + ic = (struct icmp *) ((char *) pip + (pip->ip_hl << 2)); + +/* Save overwritten data for when echo packet returns */ + link = FindIcmpOut(pip->ip_src, pip->ip_dst, ic->icmp_id); + if (link != NULL) + { + u_short alias_id; + int accumulate; + + alias_id = GetAliasPort(link); + +/* Since data field is being modified, adjust ICMP checksum */ + accumulate = ic->icmp_id; + accumulate -= alias_id; + ADJUST_CHECKSUM(accumulate, ic->icmp_cksum) + +/* Alias sequence number */ + ic->icmp_id = alias_id; + +/* Change source address */ + { + struct in_addr alias_address; + + alias_address = GetAliasAddress(link); + DifferentialChecksum(&pip->ip_sum, + (u_short *) &alias_address, + (u_short *) &pip->ip_src, + 2); + pip->ip_src = alias_address; + } + + return(PKT_ALIAS_OK); + } + return(PKT_ALIAS_IGNORED); +} + + +static int +IcmpAliasOut2(struct ip *pip) +{ +/* + Alias outgoing ICMP error messages containing + IP header and first 64 bits of datagram. +*/ + struct in_addr alias_addr; + struct ip *ip; + struct icmp *ic; + + ic = (struct icmp *) ((char *) pip + (pip->ip_hl << 2)); + ip = (struct ip *) ic->icmp_data; + + alias_addr = FindAliasAddress(ip->ip_src); + +/* Alias destination address in IP fragment */ + DifferentialChecksum(&ic->icmp_cksum, + (u_short *) &alias_addr, + (u_short *) &ip->ip_dst, + 2); + ip->ip_dst = alias_addr; + +/* alias source address in IP header */ + DifferentialChecksum(&pip->ip_sum, + (u_short *) &alias_addr, + (u_short *) &pip->ip_src, + 2); + pip->ip_src = alias_addr; + + return PKT_ALIAS_OK; +} + + +static int +IcmpAliasOut3(struct ip *pip) +{ +/* + Handle outgoing echo and timestamp replies. The + only thing which is done in this case is to alias + the source IP address of the packet. +*/ + struct in_addr alias_addr; + + alias_addr = FindAliasAddress(pip->ip_src); + DifferentialChecksum(&pip->ip_sum, + (u_short *) &alias_addr, + (u_short *) &pip->ip_src, + 2); + pip->ip_src = alias_addr; + + return PKT_ALIAS_OK; +} + + +static int +IcmpAliasOut(struct ip *pip) +{ + int iresult; + struct icmp *ic; + + ic = (struct icmp *) ((char *) pip + (pip->ip_hl << 2)); + + iresult = PKT_ALIAS_IGNORED; + switch (ic->icmp_type) + { + case ICMP_ECHO: + case ICMP_TSTAMP: + if (ic->icmp_code == 0) + { + iresult = IcmpAliasOut1(pip); + } + break; + case ICMP_UNREACH: + case ICMP_SOURCEQUENCH: + case ICMP_TIMXCEED: + case ICMP_PARAMPROB: + iresult = IcmpAliasOut2(pip); + break; + case ICMP_ECHOREPLY: + case ICMP_TSTAMPREPLY: + iresult = IcmpAliasOut3(pip); + } + return(iresult); +} + +static int +UdpAliasIn(struct ip *pip) +{ + struct udphdr *ud; + struct alias_link *link; + + ud = (struct udphdr *) ((char *) pip + (pip->ip_hl << 2)); + + link = FindUdpTcpIn(pip->ip_src, pip->ip_dst, + ud->uh_sport, ud->uh_dport, + IPPROTO_UDP); + if (link != NULL) + { + struct in_addr alias_address; + struct in_addr original_address; + u_short alias_port; + int accumulate; + u_short *sptr; + + alias_address = GetAliasAddress(link); + original_address = GetOriginalAddress(link); + alias_port = ud->uh_dport; + ud->uh_dport = GetOriginalPort(link); + +/* If NETBIOS Datagram, It should be alias address in UDP Data, too */ + if (ntohs(ud->uh_dport) == NETBIOS_DGM_PORT_NUMBER + || ntohs(ud->uh_sport) == NETBIOS_DGM_PORT_NUMBER ) + { + AliasHandleUdpNbt(pip, link, &original_address, ud->uh_dport); + } else if (ntohs(ud->uh_dport) == NETBIOS_NS_PORT_NUMBER + || ntohs(ud->uh_sport) == NETBIOS_NS_PORT_NUMBER ) + { + AliasHandleUdpNbtNS(pip, link, + &alias_address, + &alias_port, + &original_address, + &ud->uh_dport ); + } + +/* If UDP checksum is not zero, then adjust since destination port */ +/* is being unaliased and destination port is being altered. */ + if (ud->uh_sum != 0) + { + accumulate = alias_port; + accumulate -= ud->uh_dport; + sptr = (u_short *) &alias_address; + accumulate += *sptr++; + accumulate += *sptr; + sptr = (u_short *) &original_address; + accumulate -= *sptr++; + accumulate -= *sptr; + ADJUST_CHECKSUM(accumulate, ud->uh_sum) + } + +/* Restore original IP address */ + DifferentialChecksum(&pip->ip_sum, + (u_short *) &original_address, + (u_short *) &pip->ip_dst, + 2); + pip->ip_dst = original_address; + return(PKT_ALIAS_OK); + } + return(PKT_ALIAS_IGNORED); +} + +static int +UdpAliasOut(struct ip *pip) +{ + struct udphdr *ud; + struct alias_link *link; + + ud = (struct udphdr *) ((char *) pip + (pip->ip_hl << 2)); + + link = FindUdpTcpOut(pip->ip_src, pip->ip_dst, + ud->uh_sport, ud->uh_dport, + IPPROTO_UDP); + if (link != NULL) + { + u_short alias_port; + struct in_addr alias_address; + + alias_address = GetAliasAddress(link); + alias_port = GetAliasPort(link); + +/* If NETBIOS Datagram, It should be alias address in UDP Data, too */ + if (ntohs(ud->uh_dport) == NETBIOS_DGM_PORT_NUMBER + || ntohs(ud->uh_sport) == NETBIOS_DGM_PORT_NUMBER ) + { + AliasHandleUdpNbt(pip, link, &alias_address, alias_port); + } else if (ntohs(ud->uh_dport) == NETBIOS_NS_PORT_NUMBER + || ntohs(ud->uh_sport) == NETBIOS_NS_PORT_NUMBER ) + { + AliasHandleUdpNbtNS(pip, link, + &pip->ip_src, + &ud->uh_sport, + &alias_address, + &alias_port); + } + +/* If UDP checksum is not zero, adjust since source port is */ +/* being aliased and source address is being altered */ + if (ud->uh_sum != 0) + { + int accumulate; + u_short *sptr; + + accumulate = ud->uh_sport; + accumulate -= alias_port; + sptr = (u_short *) &(pip->ip_src); + accumulate += *sptr++; + accumulate += *sptr; + sptr = (u_short *) &alias_address; + accumulate -= *sptr++; + accumulate -= *sptr; + ADJUST_CHECKSUM(accumulate, ud->uh_sum) + } + +/* Put alias port in UDP header */ + ud->uh_sport = alias_port; + +/* Change source address */ + DifferentialChecksum(&pip->ip_sum, + (u_short *) &alias_address, + (u_short *) &pip->ip_src, + 2); + pip->ip_src = alias_address; + + return(PKT_ALIAS_OK); + } + return(PKT_ALIAS_IGNORED); +} + + + +static int +TcpAliasIn(struct ip *pip) +{ + struct tcphdr *tc; + struct alias_link *link; + + tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2)); + + link = FindUdpTcpIn(pip->ip_src, pip->ip_dst, + tc->th_sport, tc->th_dport, + IPPROTO_TCP); + if (link != NULL) + { + struct in_addr alias_address; + struct in_addr original_address; + u_short alias_port; + int accumulate; + u_short *sptr; + + alias_address = GetAliasAddress(link); + original_address = GetOriginalAddress(link); + alias_port = tc->th_dport; + tc->th_dport = GetOriginalPort(link); + +/* Adjust TCP checksum since destination port is being unaliased */ +/* and destination port is being altered. */ + accumulate = alias_port; + accumulate -= tc->th_dport; + sptr = (u_short *) &alias_address; + accumulate += *sptr++; + accumulate += *sptr; + sptr = (u_short *) &original_address; + accumulate -= *sptr++; + accumulate -= *sptr; + +/* See if ack number needs to be modified */ + if (GetAckModified(link) == 1) + { + int delta; + + delta = GetDeltaAckIn(pip, link); + if (delta != 0) + { + sptr = (u_short *) &tc->th_ack; + accumulate += *sptr++; + accumulate += *sptr; + tc->th_ack = htonl(ntohl(tc->th_ack) - delta); + sptr = (u_short *) &tc->th_ack; + accumulate -= *sptr++; + accumulate -= *sptr; + } + } + + ADJUST_CHECKSUM(accumulate, tc->th_sum); + +/* Restore original IP address */ + DifferentialChecksum(&pip->ip_sum, + (u_short *) &original_address, + (u_short *) &pip->ip_dst, + 2); + pip->ip_dst = original_address; + +/* Monitor TCP connection state */ + TcpMonitorIn(pip, link); + + return(PKT_ALIAS_OK); + } + return(PKT_ALIAS_IGNORED); +} + +static int +TcpAliasOut(struct ip *pip, int maxpacketsize) +{ + struct tcphdr *tc; + struct alias_link *link; + + tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2)); + + link = FindUdpTcpOut(pip->ip_src, pip->ip_dst, + tc->th_sport, tc->th_dport, + IPPROTO_TCP); + if (link !=NULL) + { + struct in_addr alias_address; + u_short alias_port; + int accumulate; + u_short *sptr; + + alias_port = GetAliasPort(link); + alias_address = GetAliasAddress(link); + +/* Monitor tcp connection state */ + TcpMonitorOut(pip, link); + +/* Special processing for ftp connection */ + if (ntohs(tc->th_dport) == FTP_CONTROL_PORT_NUMBER + || ntohs(tc->th_sport) == FTP_CONTROL_PORT_NUMBER) + AliasHandleFtpOut(pip, link, maxpacketsize); + if (ntohs(tc->th_dport) == IRC_CONTROL_PORT_NUMBER_1 + || ntohs(tc->th_dport) == IRC_CONTROL_PORT_NUMBER_2) + AliasHandleIrcOut(pip, link, maxpacketsize); + +/* Adjust TCP checksum since source port is being aliased */ +/* and source address is being altered */ + accumulate = tc->th_sport; + accumulate -= alias_port; + sptr = (u_short *) &(pip->ip_src); + accumulate += *sptr++; + accumulate += *sptr; + sptr = (u_short *) &alias_address; + accumulate -= *sptr++; + accumulate -= *sptr; + +/* Modify sequence number if necessary */ + if (GetAckModified(link) == 1) + { + int delta; + + delta = GetDeltaSeqOut(pip, link); + if (delta != 0) + { + sptr = (u_short *) &tc->th_seq; + accumulate += *sptr++; + accumulate += *sptr; + tc->th_seq = htonl(ntohl(tc->th_seq) + delta); + sptr = (u_short *) &tc->th_seq; + accumulate -= *sptr++; + accumulate -= *sptr; + } + } + + ADJUST_CHECKSUM(accumulate, tc->th_sum) + +/* Put alias address in TCP header */ + tc->th_sport = alias_port; + +/* Change source address */ + DifferentialChecksum(&pip->ip_sum, + (u_short *) &alias_address, + (u_short *) &pip->ip_src, + 2); + pip->ip_src = alias_address; + + return(PKT_ALIAS_OK); + } + return(PKT_ALIAS_IGNORED); +} + + + + +/* Fragment Handling + + FragmentIn() + FragmentOut() + +The packet aliasing module has a limited ability for handling IP +fragments. If the ICMP, TCP or UDP header is in the first fragment +received, then the id number of the IP packet is saved, and other +fragments are identified according to their ID number and IP address +they were sent from. Pointers to unresolved fragments can also be +saved and recalled when a header fragment is seen. +*/ + +/* Local prototypes */ +static int FragmentIn(struct ip *); +static int FragmentOut(struct ip *); + + +static int +FragmentIn(struct ip *pip) +{ + struct alias_link *link; + + link = FindFragmentIn2(pip->ip_src, pip->ip_dst, pip->ip_id); + if (link != NULL) + { + struct in_addr original_address; + + GetFragmentAddr(link, &original_address); + DifferentialChecksum(&pip->ip_sum, + (u_short *) &original_address, + (u_short *) &pip->ip_dst, + 2); + pip->ip_dst = original_address; + + return(PKT_ALIAS_OK); + } + return(PKT_ALIAS_UNRESOLVED_FRAGMENT); +} + + +static int +FragmentOut(struct ip *pip) +{ + struct in_addr alias_address; + + alias_address = FindAliasAddress(pip->ip_src); + DifferentialChecksum(&pip->ip_sum, + (u_short *) &alias_address, + (u_short *) &pip->ip_src, + 2); + pip->ip_src = alias_address; + + return(PKT_ALIAS_OK); +} + + + + + + +/* Outside World Access + + PacketAliasSaveFragment() + PacketAliasGetFragment() + PacketAliasFragmentIn() + PacketAliasIn() + PacketAliasOut() + +(prototypes in alias.h) +*/ + + +int +PacketAliasSaveFragment(char *ptr) +{ + int iresult; + struct alias_link *link; + struct ip *pip; + + pip = (struct ip *) ptr; + link = AddFragmentPtrLink(pip->ip_src, pip->ip_id); + iresult = PKT_ALIAS_ERROR; + if (link != NULL) + { + SetFragmentPtr(link, ptr); + iresult = PKT_ALIAS_OK; + } + return(iresult); +} + + +char * +PacketAliasGetFragment(char *ptr) +{ + struct alias_link *link; + char *fptr; + struct ip *pip; + + pip = (struct ip *) ptr; + link = FindFragmentPtr(pip->ip_src, pip->ip_id); + if (link != NULL) + { + GetFragmentPtr(link, &fptr); + SetFragmentPtr(link, NULL); + SetExpire(link, 0); /* Deletes link */ + + return(fptr); + } + else + { + return(NULL); + } +} + + +void +PacketAliasFragmentIn(char *ptr, /* Points to correctly de-aliased + header fragment */ + char *ptr_fragment /* Points to fragment which must + be de-aliased */ + ) +{ + struct ip *pip; + struct ip *fpip; + + pip = (struct ip *) ptr; + fpip = (struct ip *) ptr_fragment; + + DifferentialChecksum(&fpip->ip_sum, + (u_short *) &pip->ip_dst, + (u_short *) &fpip->ip_dst, + 2); + fpip->ip_dst = pip->ip_dst; +} + + +int +PacketAliasIn(char *ptr, int maxpacketsize) +{ + struct in_addr alias_addr; + struct ip *pip; + int iresult; + + HouseKeeping(); + ClearCheckNewLink(); + pip = (struct ip *) ptr; + alias_addr = pip->ip_dst; + + /* Defense against mangled packets */ + if (ntohs(pip->ip_len) > maxpacketsize + || (pip->ip_hl<<2) > maxpacketsize) + return PKT_ALIAS_IGNORED; + + iresult = PKT_ALIAS_IGNORED; + if ( (ntohs(pip->ip_off) & IP_OFFMASK) == 0 ) + { + switch (pip->ip_p) + { + case IPPROTO_ICMP: + iresult = IcmpAliasIn(pip); + break; + case IPPROTO_UDP: + iresult = UdpAliasIn(pip); + break; + case IPPROTO_TCP: + iresult = TcpAliasIn(pip); + break; + } + + if (ntohs(pip->ip_off) & IP_MF) + { + struct alias_link *link; + + link = FindFragmentIn1(pip->ip_src, alias_addr, pip->ip_id); + if (link != NULL) + { + iresult = PKT_ALIAS_FOUND_HEADER_FRAGMENT; + SetFragmentAddr(link, pip->ip_dst); + } + else + { + iresult = PKT_ALIAS_ERROR; + } + } + } + else + { + iresult = FragmentIn(pip); + } + + return(iresult); +} + + + +/* Unregistered address ranges */ + +/* 10.0.0.0 -> 10.255.255.255 */ +#define UNREG_ADDR_A_LOWER 0x0a000000 +#define UNREG_ADDR_A_UPPER 0x0affffff + +/* 172.16.0.0 -> 172.31.255.255 */ +#define UNREG_ADDR_B_LOWER 0xac100000 +#define UNREG_ADDR_B_UPPER 0xac1fffff + +/* 192.168.0.0 -> 192.168.255.255 */ +#define UNREG_ADDR_C_LOWER 0xc0a80000 +#define UNREG_ADDR_C_UPPER 0xc0a8ffff + + + +int +PacketAliasOut(char *ptr, /* valid IP packet */ + int maxpacketsize /* How much the packet data may grow + (FTP and IRC inline changes) */ + ) +{ + int iresult; + struct in_addr addr_save; + struct ip *pip; + + HouseKeeping(); + ClearCheckNewLink(); + pip = (struct ip *) ptr; + + /* Defense against mangled packets */ + if (ntohs(pip->ip_len) > maxpacketsize + || (pip->ip_hl<<2) > maxpacketsize) + return PKT_ALIAS_IGNORED; + + addr_save = GetDefaultAliasAddress(); + if (packetAliasMode & PKT_ALIAS_UNREGISTERED_ONLY) + { + unsigned int addr; + int iclass; + + iclass = 0; + addr = ntohl(pip->ip_src.s_addr); + if (addr >= UNREG_ADDR_C_LOWER && addr <= UNREG_ADDR_C_UPPER) + iclass = 3; + else if (addr >= UNREG_ADDR_B_LOWER && addr <= UNREG_ADDR_B_UPPER) + iclass = 2; + else if (addr >= UNREG_ADDR_A_LOWER && addr <= UNREG_ADDR_A_UPPER) + iclass = 1; + + if (iclass == 0) + { + SetDefaultAliasAddress(pip->ip_src); + } + } + + iresult = PKT_ALIAS_IGNORED; + if ((ntohs(pip->ip_off) & IP_OFFMASK) == 0) + { + switch (pip->ip_p) + { + case IPPROTO_ICMP: + iresult = IcmpAliasOut(pip); + break; + case IPPROTO_UDP: + iresult = UdpAliasOut(pip); + break; + case IPPROTO_TCP: + iresult = TcpAliasOut(pip, maxpacketsize); + break; + } + } + else + { + iresult = FragmentOut(pip); + } + + SetDefaultAliasAddress(addr_save); + return(iresult); +} diff --git a/sys/netinet/libalias/alias.h b/sys/netinet/libalias/alias.h new file mode 100644 index 0000000..010db98 --- /dev/null +++ b/sys/netinet/libalias/alias.h @@ -0,0 +1,173 @@ +/*lint -save -library Flexelint comment for external headers */ + +/* + Alias.h defines the outside world interfaces for the packet + aliasing software. + + This software is placed into the public domain with no restrictions + on its distribution. + + $Id: alias.h,v 1.7 1998/01/16 12:56:07 bde Exp $ +*/ + + +#ifndef _ALIAS_H_ +#define _ALIAS_H_ + +#ifndef NULL +#define NULL 0 +#endif + +/* Alias link representative (incomplete struct) */ +struct alias_link; + +/* External interfaces (API) to packet aliasing engine */ + +/* Initialization and Control */ + extern void + PacketAliasInit(void); + + extern void + PacketAliasUninit(void); + + extern void + PacketAliasSetAddress(struct in_addr); + + extern unsigned int + PacketAliasSetMode(unsigned int, unsigned int); + +#ifndef NO_FW_PUNCH + extern void + PacketAliasSetFWBase(unsigned int, unsigned int); +#endif + +/* Packet Handling */ + extern int + PacketAliasIn(char *, int maxpacketsize); + + extern int + PacketAliasOut(char *, int maxpacketsize); + +/* Port and Address Redirection */ + extern struct alias_link * + PacketAliasRedirectPort(struct in_addr, u_short, + struct in_addr, u_short, + struct in_addr, u_short, + u_char); + + extern struct alias_link * + PacketAliasRedirectAddr(struct in_addr, + struct in_addr); + + extern void + PacketAliasRedirectDelete(struct alias_link *); + +/* Fragment Handling */ + extern int + PacketAliasSaveFragment(char *); + + extern char * + PacketAliasGetFragment(char *); + + extern void + PacketAliasFragmentIn(char *, char *); + +/* Miscellaneous Functions */ + extern void + PacketAliasSetTarget(struct in_addr addr); + + extern int + PacketAliasCheckNewLink(void); + + extern u_short + PacketAliasInternetChecksum(u_short *, int); + + +/* + In version 2.2, the function names were rationalized + to all be of the form PacketAlias... These are the + old function names for backwards compatibility +*/ +extern int SaveFragmentPtr(char *); +extern char *GetNextFragmentPtr(char *); +extern void FragmentAliasIn(char *, char *); +extern void SetPacketAliasAddress(struct in_addr); +extern void InitPacketAlias(void); +extern unsigned int SetPacketAliasMode(unsigned int, unsigned int); +extern int PacketAliasIn2(char *, struct in_addr, int maxpacketsize); +extern int PacketAliasOut2(char *, struct in_addr, int maxpacketsize); +extern int +PacketAliasPermanentLink(struct in_addr, u_short, + struct in_addr, u_short, + u_short, u_char); +extern u_short InternetChecksum(u_short *, int); + +/* Obsolete constant */ +#define PKT_ALIAS_NEW_LINK 5 + +/********************** Mode flags ********************/ +/* Set these flags using SetPacketAliasMode() */ + +/* If PKT_ALIAS_LOG is set, a message will be printed to + /var/log/alias.log every time a link is created or deleted. This + is useful for debugging */ +#define PKT_ALIAS_LOG 0x01 + +/* If PKT_ALIAS_DENY_INCOMING is set, then incoming connections (e.g. + to ftp, telnet or web servers will be prevented by the aliasing + mechanism. */ +#define PKT_ALIAS_DENY_INCOMING 0x02 + +/* If PKT_ALIAS_SAME_PORTS is set, packets will be attempted sent from + the same port as they originated on. This allows eg rsh to work + *99% of the time*, but _not_ 100%. (It will be slightly flakey + instead of not working at all.) This mode bit is set by + PacketAliasInit(), so it is a default mode of operation. */ +#define PKT_ALIAS_SAME_PORTS 0x04 + +/* If PKT_ALIAS_USE_SOCKETS is set, then when partially specified + links (e.g. destination port and/or address is zero), the packet + aliasing engine will attempt to allocate a socket for the aliasing + port it chooses. This will avoid interference with the host + machine. Fully specified links do not require this. This bit + is set after a call to PacketAliasInit(), so it is a default + mode of operation.*/ +#define PKT_ALIAS_USE_SOCKETS 0x08 + +/* If PKT_ALIAS_UNREGISTERED_ONLY is set, then only packets with with + unregistered source addresses will be aliased (along with those + of the ppp host maching itself. Private addresses are those + in the following ranges: + + 10.0.0.0 -> 10.255.255.255 + 172.16.0.0 -> 172.31.255.255 + 192.168.0.0 -> 192.168.255.255 */ +#define PKT_ALIAS_UNREGISTERED_ONLY 0x10 + +/* If PKT_ALIAS_RESET_ON_ADDR_CHANGE is set, then the table of dynamic + aliasing links will be reset whenever PacketAliasSetAddress() + changes the default aliasing address. If the default aliasing + address is left unchanged by this functions call, then the + table of dynamic aliasing links will be left intact. This + bit is set after a call to PacketAliasInit(). */ +#define PKT_ALIAS_RESET_ON_ADDR_CHANGE 0x20 + +#ifndef NO_FW_PUNCH +/* If PKT_ALIAS_PUNCH_FW is set, active FTP and IRC DCC connections + will create a 'hole' in the firewall to allow the transfers to + work. Where (IPFW "line-numbers") the hole is created is + controlled by PacketAliasSetFWBase(base, size). The hole will be + attached to that particular alias_link, so when the link goes away + so do the hole. */ +#define PKT_ALIAS_PUNCH_FW 0x40 +#endif + +/* Return Codes */ +#define PKT_ALIAS_ERROR -1 +#define PKT_ALIAS_OK 1 +#define PKT_ALIAS_IGNORED 2 +#define PKT_ALIAS_UNRESOLVED_FRAGMENT 3 +#define PKT_ALIAS_FOUND_HEADER_FRAGMENT 4 + +#endif +/*lint -restore */ diff --git a/sys/netinet/libalias/alias_db.c b/sys/netinet/libalias/alias_db.c new file mode 100644 index 0000000..7bbb191 --- /dev/null +++ b/sys/netinet/libalias/alias_db.c @@ -0,0 +1,2239 @@ +/* -*- mode: c; tab-width: 8; c-basic-indent: 4; -*- + Alias_db.c encapsulates all data structures used for storing + packet aliasing data. Other parts of the aliasing software + access data through functions provided in this file. + + Data storage is based on the notion of a "link", which is + established for ICMP echo/reply packets, UDP datagrams and + TCP stream connections. A link stores the original source + and destination addresses. For UDP and TCP, it also stores + source and destination port numbers, as well as an alias + port number. Links are also used to store information about + fragments. + + There is a facility for sweeping through and deleting old + links as new packets are sent through. A simple timeout is + used for ICMP and UDP links. TCP links are left alone unless + there is an incomplete connection, in which case the link + can be deleted after a certain amount of time. + + + This software is placed into the public domain with no restrictions + on its distribution. + + Initial version: August, 1996 (cjm) + + Version 1.4: September 16, 1996 (cjm) + Facility for handling incoming links added. + + Version 1.6: September 18, 1996 (cjm) + ICMP data handling simplified. + + Version 1.7: January 9, 1997 (cjm) + Fragment handling simplified. + Saves pointers for unresolved fragments. + Permits links for unspecied remote ports + or unspecified remote addresses. + Fixed bug which did not properly zero port + table entries after a link was deleted. + Cleaned up some obsolete comments. + + Version 1.8: January 14, 1997 (cjm) + Fixed data type error in StartPoint(). + (This error did not exist prior to v1.7 + and was discovered and fixed by Ari Suutari) + + Version 1.9: February 1, 1997 + Optionally, connections initiated from packet aliasing host + machine will will not have their port number aliased unless it + conflicts with an aliasing port already being used. (cjm) + + All options earlier being #ifdef'ed now are available through + a new interface, SetPacketAliasMode(). This allow run time + control (which is now available in PPP+pktAlias through the + 'alias' keyword). (ee) + + Added ability to create an alias port without + either destination address or port specified. + port type = ALIAS_PORT_UNKNOWN_DEST_ALL (ee) + + Removed K&R style function headers + and general cleanup. (ee) + + Added packetAliasMode to replace compiler #defines's (ee) + + Allocates sockets for partially specified + ports if ALIAS_USE_SOCKETS defined. (cjm) + + Version 2.0: March, 1997 + SetAliasAddress() will now clean up alias links + if the aliasing address is changed. (cjm) + + PacketAliasPermanentLink() function added to support permanent + links. (J. Fortes suggested the need for this.) + Examples: + + (192.168.0.1, port 23) <-> alias port 6002, unknown dest addr/port + + (192.168.0.2, port 21) <-> alias port 3604, known dest addr + unknown dest port + + These permament links allow for incoming connections to + machines on the local network. They can be given with a + user-chosen amount of specificity, with increasing specificity + meaning more security. (cjm) + + Quite a bit of rework to the basic engine. The portTable[] + array, which kept track of which ports were in use was replaced + by a table/linked list structure. (cjm) + + SetExpire() function added. (cjm) + + DeleteLink() no longer frees memory association with a pointer + to a fragment (this bug was first recognized by E. Eklund in + v1.9). + + Version 2.1: May, 1997 (cjm) + Packet aliasing engine reworked so that it can handle + multiple external addresses rather than just a single + host address. + + PacketAliasRedirectPort() and PacketAliasRedirectAddr() + added to the API. The first function is a more generalized + version of PacketAliasPermanentLink(). The second function + implements static network address translation. + + See HISTORY file for additional revisions. +*/ + + +/* System include files */ +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> + +#include <sys/errno.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <sys/types.h> + +/* BSD network include files */ +#include <netinet/in_systm.h> +#include <netinet/in.h> +#include <netinet/ip.h> +#include <netinet/tcp.h> +#include <arpa/inet.h> + +#include "alias.h" +#include "alias_local.h" + + + +/* + Constants (note: constants are also defined + near relevant functions or structs) +*/ + +/* Sizes of input and output link tables */ +#define LINK_TABLE_OUT_SIZE 101 +#define LINK_TABLE_IN_SIZE 4001 + +/* Parameters used for cleanup of expired links */ +#define ALIAS_CLEANUP_INTERVAL_SECS 60 +#define ALIAS_CLEANUP_MAX_SPOKES 30 + +/* Timouts (in seconds) for different link types) */ +#define ICMP_EXPIRE_TIME 60 +#define UDP_EXPIRE_TIME 60 +#define FRAGMENT_ID_EXPIRE_TIME 10 +#define FRAGMENT_PTR_EXPIRE_TIME 30 + +/* TCP link expire time for different cases */ +/* When the link has been used and closed - minimal grace time to + allow ACKs and potential re-connect in FTP (XXX - is this allowed?) */ +#ifndef TCP_EXPIRE_DEAD +# define TCP_EXPIRE_DEAD 10 +#endif + +/* When the link has been used and closed on one side - the other side + is allowed to still send data */ +#ifndef TCP_EXPIRE_SINGLEDEAD +# define TCP_EXPIRE_SINGLEDEAD 90 +#endif + +/* When the link isn't yet up */ +#ifndef TCP_EXPIRE_INITIAL +# define TCP_EXPIRE_INITIAL 300 +#endif + +/* When the link is up */ +#ifndef TCP_EXPIRE_CONNECTED +# define TCP_EXPIRE_CONNECTED 86400 +#endif + + +/* Dummy port number codes used for FindLinkIn/Out() and AddLink(). + These constants can be anything except zero, which indicates an + unknown port numbea. */ + +#define NO_DEST_PORT 1 +#define NO_SRC_PORT 1 + + + +/* Data Structures + + The fundamental data structure used in this program is + "struct alias_link". Whenever a TCP connection is made, + a UDP datagram is sent out, or an ICMP echo request is made, + a link record is made (if it has not already been created). + The link record is identified by the source address/port + and the destination address/port. In the case of an ICMP + echo request, the source port is treated as being equivalent + with the 16-bit id number of the ICMP packet. + + The link record also can store some auxiliary data. For + TCP connections that have had sequence and acknowledgment + modifications, data space is available to track these changes. + A state field is used to keep track in changes to the tcp + connection state. Id numbers of fragments can also be + stored in the auxiliary space. Pointers to unresolved + framgents can also be stored. + + The link records support two independent chainings. Lookup + tables for input and out tables hold the initial pointers + the link chains. On input, the lookup table indexes on alias + port and link type. On output, the lookup table indexes on + source addreess, destination address, source port, destination + port and link type. +*/ + +struct ack_data_record /* used to save changes to ack/seq numbers */ +{ + u_long ack_old; + u_long ack_new; + int delta; + int active; +}; + +struct tcp_state /* Information about tcp connection */ +{ + int in; /* State for outside -> inside */ + int out; /* State for inside -> outside */ + int index; /* Index to ack data array */ + int ack_modified; /* Indicates whether ack and seq numbers */ + /* been modified */ +}; + +#define N_LINK_TCP_DATA 3 /* Number of distinct ack number changes + saved for a modified TCP stream */ +struct tcp_dat +{ + struct tcp_state state; + struct ack_data_record ack[N_LINK_TCP_DATA]; + int fwhole; /* Which firewall record is used for this hole? */ +}; + +struct alias_link /* Main data structure */ +{ + struct in_addr src_addr; /* Address and port information */ + struct in_addr dst_addr; /* . */ + struct in_addr alias_addr; /* . */ + u_short src_port; /* . */ + u_short dst_port; /* . */ + u_short alias_port; /* . */ + + int link_type; /* Type of link: tcp, udp, icmp, frag */ + +/* values for link_type */ +#define LINK_ICMP 1 +#define LINK_UDP 2 +#define LINK_TCP 3 +#define LINK_FRAGMENT_ID 4 +#define LINK_FRAGMENT_PTR 5 +#define LINK_ADDR 6 + + int flags; /* indicates special characteristics */ + +/* flag bits */ +#define LINK_UNKNOWN_DEST_PORT 0x01 +#define LINK_UNKNOWN_DEST_ADDR 0x02 +#define LINK_PERMANENT 0x04 +#define LINK_PARTIALLY_SPECIFIED 0x03 /* logical-or of first two bits */ +#define LINK_UNFIREWALLED 0x08 + + int timestamp; /* Time link was last accessed */ + int expire_time; /* Expire time for link */ + + int sockfd; /* socket descriptor */ + + u_int start_point_out; /* Index number in output lookup table */ + u_int start_point_in; + struct alias_link *next_out; /* Linked list pointers for input and */ + struct alias_link *last_out; /* output tables */ + struct alias_link *next_in; /* . */ + struct alias_link *last_in; /* . */ + + union /* Auxiliary data */ + { + char *frag_ptr; + struct in_addr frag_addr; + struct tcp_dat *tcp; + } data; +}; + + + + + +/* Global Variables + + The global variables listed here are only accessed from + within alias_db.c and so are prefixed with the static + designation. +*/ + +int packetAliasMode; /* Mode flags */ + /* - documented in alias.h */ + +static struct in_addr aliasAddress; /* Address written onto source */ + /* field of IP packet. */ + +static struct in_addr targetAddress; /* IP address incoming packets */ + /* are sent to if no aliasing */ + /* link already exists */ + +static struct in_addr nullAddress; /* Used as a dummy parameter for */ + /* some function calls */ +static struct alias_link * +linkTableOut[LINK_TABLE_OUT_SIZE]; /* Lookup table of pointers to */ + /* chains of link records. Each */ +static struct alias_link * /* link record is doubly indexed */ +linkTableIn[LINK_TABLE_IN_SIZE]; /* into input and output lookup */ + /* tables. */ + +static int icmpLinkCount; /* Link statistics */ +static int udpLinkCount; +static int tcpLinkCount; +static int fragmentIdLinkCount; +static int fragmentPtrLinkCount; +static int sockCount; + +static int cleanupIndex; /* Index to chain of link table */ + /* being inspected for old links */ + +static int timeStamp; /* System time in seconds for */ + /* current packet */ + +static int lastCleanupTime; /* Last time IncrementalCleanup() */ + /* was called */ + +static int houseKeepingResidual; /* used by HouseKeeping() */ + +static int deleteAllLinks; /* If equal to zero, DeleteLink() */ + /* will not remove permanent links */ + +static FILE *monitorFile; /* File descriptor for link */ + /* statistics monitoring file */ + +static int newDefaultLink; /* Indicates if a new aliasing */ + /* link has been created after a */ + /* call to PacketAliasIn/Out(). */ + +#ifndef NO_FW_PUNCH +static int fireWallFD = -1; /* File descriptor to be able to */ + /* control firewall. Opened by */ + /* PacketAliasSetMode on first */ + /* setting the PKT_ALIAS_PUNCH_FW */ + /* flag. */ +#endif + + + + + + +/* Internal utility routines (used only in alias_db.c) + +Lookup table starting points: + StartPointIn() -- link table initial search point for + outgoing packets + StartPointOut() -- port table initial search point for + incoming packets + +Miscellaneous: + SeqDiff() -- difference between two TCP sequences + ShowAliasStats() -- send alias statistics to a monitor file +*/ + + +/* Local prototypes */ +static u_int StartPointIn(struct in_addr, u_short, int); + +static u_int StartPointOut(struct in_addr, struct in_addr, + u_short, u_short, int); + +static int SeqDiff(u_long, u_long); + +static void ShowAliasStats(void); + +#ifndef NO_FW_PUNCH +/* Firewall control */ +static void InitPunchFW(void); +static void UninitPunchFW(void); +static void ClearFWHole(struct alias_link *link); +#endif + +/* Log file control */ +static void InitPacketAliasLog(void); +static void UninitPacketAliasLog(void); + +static u_int +StartPointIn(struct in_addr alias_addr, + u_short alias_port, + int link_type) +{ + u_int n; + + n = alias_addr.s_addr; + n += alias_port; + n += link_type; + return(n % LINK_TABLE_IN_SIZE); +} + + +static u_int +StartPointOut(struct in_addr src_addr, struct in_addr dst_addr, + u_short src_port, u_short dst_port, int link_type) +{ + u_int n; + + n = src_addr.s_addr; + n += dst_addr.s_addr; + n += src_port; + n += dst_port; + n += link_type; + + return(n % LINK_TABLE_OUT_SIZE); +} + + +static int +SeqDiff(u_long x, u_long y) +{ +/* Return the difference between two TCP sequence numbers */ + +/* + This function is encapsulated in case there are any unusual + arithmetic conditions that need to be considered. +*/ + + return (ntohl(y) - ntohl(x)); +} + + +static void +ShowAliasStats(void) +{ +/* Used for debugging */ + + if (packetAliasMode & PKT_ALIAS_LOG) + { + fprintf(monitorFile, "icmp=%d, udp=%d, tcp=%d, frag_id=%d frag_ptr=%d", + icmpLinkCount, + udpLinkCount, + tcpLinkCount, + fragmentIdLinkCount, + fragmentPtrLinkCount); + + fprintf(monitorFile, " / tot=%d (sock=%d)\n", + icmpLinkCount + udpLinkCount + + tcpLinkCount + + fragmentIdLinkCount + + fragmentPtrLinkCount, + sockCount); + + fflush(monitorFile); + } +} + + + + + +/* Internal routines for finding, deleting and adding links + +Port Allocation: + GetNewPort() -- find and reserve new alias port number + GetSocket() -- try to allocate a socket for a given port + +Link creation and deletion: + CleanupAliasData() - remove all link chains from lookup table + IncrementalCleanup() - look for stale links in a single chain + DeleteLink() - remove link + AddLink() - add link + ReLink() - change link + +Link search: + FindLinkOut() - find link for outgoing packets + FindLinkIn() - find link for incoming packets +*/ + +/* Local prototypes */ +static int GetNewPort(struct alias_link *, int); + +static u_short GetSocket(u_short, int *, int); + +static void CleanupAliasData(void); + +static void IncrementalCleanup(void); + +static void DeleteLink(struct alias_link *); + +static struct alias_link * +AddLink(struct in_addr, struct in_addr, struct in_addr, + u_short, u_short, int, int); + +static struct alias_link * +ReLink(struct alias_link *, + struct in_addr, struct in_addr, struct in_addr, + u_short, u_short, int, int); + +static struct alias_link * +FindLinkOut(struct in_addr, struct in_addr, u_short, u_short, int); + +static struct alias_link * +FindLinkIn(struct in_addr, struct in_addr, u_short, u_short, int, int); + + +#define ALIAS_PORT_BASE 0x08000 +#define ALIAS_PORT_MASK 0x07fff +#define GET_NEW_PORT_MAX_ATTEMPTS 20 + +#define GET_ALIAS_PORT -1 +#define GET_ALIAS_ID GET_ALIAS_PORT + +/* GetNewPort() allocates port numbers. Note that if a port number + is already in use, that does not mean that it cannot be used by + another link concurrently. This is because GetNewPort() looks for + unused triplets: (dest addr, dest port, alias port). */ + +static int +GetNewPort(struct alias_link *link, int alias_port_param) +{ + int i; + int max_trials; + u_short port_sys; + u_short port_net; + +/* + Description of alias_port_param for GetNewPort(). When + this parameter is zero or positive, it precisely specifies + the port number. GetNewPort() will return this number + without check that it is in use. + + Whis this parameter is -1, it indicates to get a randomly + selected port number. +*/ + + if (alias_port_param == GET_ALIAS_PORT) + { + /* + * The aliasing port is automatically selected + * by one of two methods below: + */ + max_trials = GET_NEW_PORT_MAX_ATTEMPTS; + + if (packetAliasMode & PKT_ALIAS_SAME_PORTS) + { + /* + * When the ALIAS_SAME_PORTS option is + * chosen, the first try will be the + * actual source port. If this is already + * in use, the remainder of the trials + * will be random. + */ + port_net = link->src_port; + port_sys = ntohs(port_net); + } + else + { + /* First trial and all subsequent are random. */ + port_sys = random() & ALIAS_PORT_MASK; + port_sys += ALIAS_PORT_BASE; + port_net = htons(port_sys); + } + } + else if (alias_port_param >= 0 && alias_port_param < 0x10000) + { + link->alias_port = (u_short) alias_port_param; + return(0); + } + else + { + fprintf(stderr, "PacketAlias/GetNewPort(): "); + fprintf(stderr, "input parameter error\n"); + return(-1); + } + + +/* Port number search */ + for (i=0; i<max_trials; i++) + { + int go_ahead; + struct alias_link *search_result; + + search_result = FindLinkIn(link->dst_addr, link->alias_addr, + link->dst_port, port_net, + link->link_type, 0); + + if (search_result == NULL) + go_ahead = 1; + else if (!(link->flags & LINK_PARTIALLY_SPECIFIED) + && (search_result->flags & LINK_PARTIALLY_SPECIFIED)) + go_ahead = 1; + else + go_ahead = 0; + + if (go_ahead) + { + if ((packetAliasMode && PKT_ALIAS_USE_SOCKETS) + && (link->flags & LINK_PARTIALLY_SPECIFIED)) + { + if (GetSocket(port_net, &link->sockfd, link->link_type)) + { + link->alias_port = port_net; + return(0); + } + } + else + { + link->alias_port = port_net; + return(0); + } + } + + port_sys = random() & ALIAS_PORT_MASK; + port_sys += ALIAS_PORT_BASE; + port_net = htons(port_sys); + } + + fprintf(stderr, "PacketAlias/GetnewPort(): "); + fprintf(stderr, "could not find free port\n"); + + return(-1); +} + + +static u_short +GetSocket(u_short port_net, int *sockfd, int link_type) +{ + int err; + int sock; + struct sockaddr_in sock_addr; + + if (link_type == LINK_TCP) + sock = socket(AF_INET, SOCK_STREAM, 0); + else if (link_type == LINK_UDP) + sock = socket(AF_INET, SOCK_DGRAM, 0); + else + { + fprintf(stderr, "PacketAlias/GetSocket(): "); + fprintf(stderr, "incorrect link type\n"); + return(0); + } + + if (sock < 0) + { + fprintf(stderr, "PacketAlias/GetSocket(): "); + fprintf(stderr, "socket() error %d\n", *sockfd); + return(0); + } + + sock_addr.sin_family = AF_INET; + sock_addr.sin_addr.s_addr = htonl(INADDR_ANY); + sock_addr.sin_port = port_net; + + err = bind(sock, + (struct sockaddr *) &sock_addr, + sizeof(sock_addr)); + if (err == 0) + { + sockCount++; + *sockfd = sock; + return(1); + } + else + { + close(sock); + return(0); + } +} + + +static void +CleanupAliasData(void) +{ + struct alias_link *link; + int i, icount; + + icount = 0; + for (i=0; i<LINK_TABLE_OUT_SIZE; i++) + { + link = linkTableOut[i]; + while (link != NULL) + { + struct alias_link *link_next; + link_next = link->next_out; + icount++; + DeleteLink(link); + link = link_next; + } + } + + cleanupIndex =0; +} + + +static void +IncrementalCleanup(void) +{ + int icount; + struct alias_link *link; + + icount = 0; + link = linkTableOut[cleanupIndex++]; + while (link != NULL) + { + int idelta; + struct alias_link *link_next; + + link_next = link->next_out; + idelta = timeStamp - link->timestamp; + switch (link->link_type) + { + case LINK_ICMP: + case LINK_UDP: + case LINK_FRAGMENT_ID: + case LINK_FRAGMENT_PTR: + if (idelta > link->expire_time) + { + DeleteLink(link); + icount++; + } + break; + case LINK_TCP: + if (idelta > link->expire_time) + { + struct tcp_dat *tcp_aux; + + tcp_aux = link->data.tcp; + if (tcp_aux->state.in != ALIAS_TCP_STATE_CONNECTED + || tcp_aux->state.out != ALIAS_TCP_STATE_CONNECTED) + { + DeleteLink(link); + icount++; + } + } + break; + } + link = link_next; + } + + if (cleanupIndex == LINK_TABLE_OUT_SIZE) + cleanupIndex = 0; +} + +void +DeleteLink(struct alias_link *link) +{ + struct alias_link *link_last; + struct alias_link *link_next; + +/* Don't do anything if the link is marked permanent */ + if (deleteAllLinks == 0 && link->flags & LINK_PERMANENT) + return; + +#ifndef NO_FW_PUNCH +/* Delete associatied firewall hole, if any */ + ClearFWHole(link); +#endif + +/* Adjust output table pointers */ + link_last = link->last_out; + link_next = link->next_out; + + if (link_last != NULL) + link_last->next_out = link_next; + else + linkTableOut[link->start_point_out] = link_next; + + if (link_next != NULL) + link_next->last_out = link_last; + +/* Adjust input table pointers */ + link_last = link->last_in; + link_next = link->next_in; + + if (link_last != NULL) + link_last->next_in = link_next; + else + linkTableIn[link->start_point_in] = link_next; + + if (link_next != NULL) + link_next->last_in = link_last; + +/* Close socket, if one has been allocated */ + if (link->sockfd != -1) + { + sockCount--; + close(link->sockfd); + } + +/* Link-type dependent cleanup */ + switch(link->link_type) + { + case LINK_ICMP: + icmpLinkCount--; + break; + case LINK_UDP: + udpLinkCount--; + break; + case LINK_TCP: + tcpLinkCount--; + if (link->data.tcp != NULL) + free(link->data.tcp); + break; + case LINK_FRAGMENT_ID: + fragmentIdLinkCount--; + break; + case LINK_FRAGMENT_PTR: + fragmentPtrLinkCount--; + if (link->data.frag_ptr != NULL) + free(link->data.frag_ptr); + break; + } + +/* Free memory */ + free(link); + +/* Write statistics, if logging enabled */ + if (packetAliasMode & PKT_ALIAS_LOG) + { + ShowAliasStats(); + } +} + + +static struct alias_link * +AddLink(struct in_addr src_addr, + struct in_addr dst_addr, + struct in_addr alias_addr, + u_short src_port, + u_short dst_port, + int alias_port_param, /* if less than zero, alias */ + int link_type) /* port will be automatically */ +{ /* chosen. If greater than */ + u_int start_point; /* zero, equal to alias port */ + struct alias_link *link; + struct alias_link *first_link; + + link = malloc(sizeof(struct alias_link)); + if (link != NULL) + { + /* If either the aliasing address or source address are + equal to the default device address (equal to the + global variable aliasAddress), then set the alias + address field of the link record to zero */ + + if (src_addr.s_addr == aliasAddress.s_addr) + src_addr.s_addr = 0; + + if (alias_addr.s_addr == aliasAddress.s_addr) + alias_addr.s_addr = 0; + + /* Basic initialization */ + link->src_addr = src_addr; + link->dst_addr = dst_addr; + link->src_port = src_port; + link->alias_addr = alias_addr; + link->dst_port = dst_port; + link->link_type = link_type; + link->sockfd = -1; + link->flags = 0; + link->timestamp = timeStamp; + + /* Expiration time */ + switch (link_type) + { + case LINK_ICMP: + link->expire_time = ICMP_EXPIRE_TIME; + break; + case LINK_UDP: + link->expire_time = UDP_EXPIRE_TIME; + break; + case LINK_TCP: + link->expire_time = TCP_EXPIRE_INITIAL; + break; + case LINK_FRAGMENT_ID: + link->expire_time = FRAGMENT_ID_EXPIRE_TIME; + break; + case LINK_FRAGMENT_PTR: + link->expire_time = FRAGMENT_PTR_EXPIRE_TIME; + break; + } + + /* Determine alias flags */ + if (dst_addr.s_addr == 0) + link->flags |= LINK_UNKNOWN_DEST_ADDR; + if (dst_port == 0) + link->flags |= LINK_UNKNOWN_DEST_PORT; + + /* Determine alias port */ + if (GetNewPort(link, alias_port_param) != 0) + { + free(link); + return(NULL); + } + + /* Set up pointers for output lookup table */ + start_point = StartPointOut(src_addr, dst_addr, + src_port, dst_port, link_type); + first_link = linkTableOut[start_point]; + + link->last_out = NULL; + link->next_out = first_link; + link->start_point_out = start_point; + + if (first_link != NULL) + first_link->last_out = link; + + linkTableOut[start_point] = link; + + /* Set up pointers for input lookup table */ + start_point = StartPointIn(alias_addr, link->alias_port, link_type); + first_link = linkTableIn[start_point]; + + link->last_in = NULL; + link->next_in = first_link; + link->start_point_in = start_point; + + if (first_link != NULL) + first_link->last_in = link; + + linkTableIn[start_point] = link; + + /* Link-type dependent initialization */ + switch(link_type) + { + struct tcp_dat *aux_tcp; + + case LINK_ICMP: + icmpLinkCount++; + break; + case LINK_UDP: + udpLinkCount++; + break; + case LINK_TCP: + aux_tcp = malloc(sizeof(struct tcp_dat)); + link->data.tcp = aux_tcp; + if (aux_tcp != NULL) + { + int i; + + tcpLinkCount++; + aux_tcp->state.in = ALIAS_TCP_STATE_NOT_CONNECTED; + aux_tcp->state.out = ALIAS_TCP_STATE_NOT_CONNECTED; + aux_tcp->state.index = 0; + aux_tcp->state.ack_modified = 0; + for (i=0; i<N_LINK_TCP_DATA; i++) + aux_tcp->ack[i].active = 0; + aux_tcp->fwhole = -1; + } + else + { + fprintf(stderr, "PacketAlias/AddLink: "); + fprintf(stderr, " cannot allocate auxiliary TCP data\n"); + } + break; + case LINK_FRAGMENT_ID: + fragmentIdLinkCount++; + break; + case LINK_FRAGMENT_PTR: + fragmentPtrLinkCount++; + break; + } + } + else + { + fprintf(stderr, "PacketAlias/AddLink(): "); + fprintf(stderr, "malloc() call failed.\n"); + } + + if (packetAliasMode & PKT_ALIAS_LOG) + { + ShowAliasStats(); + } + + return(link); +} + +static struct alias_link * +ReLink(struct alias_link *old_link, + struct in_addr src_addr, + struct in_addr dst_addr, + struct in_addr alias_addr, + u_short src_port, + u_short dst_port, + int alias_port_param, /* if less than zero, alias */ + int link_type) /* port will be automatically */ +{ /* chosen. If greater than */ + struct alias_link *new_link; /* zero, equal to alias port */ + + new_link = AddLink(src_addr, dst_addr, alias_addr, + src_port, dst_port, alias_port_param, + link_type); +#ifndef NO_FW_PUNCH + if (new_link != NULL && + old_link->link_type == LINK_TCP && + old_link->data.tcp && + old_link->data.tcp->fwhole > 0) { + PunchFWHole(new_link); + } +#endif + DeleteLink(old_link); + return new_link; +} + +static struct alias_link * +FindLinkOut(struct in_addr src_addr, + struct in_addr dst_addr, + u_short src_port, + u_short dst_port, + int link_type) +{ + u_int i; + struct alias_link *link; + + if (src_addr.s_addr == aliasAddress.s_addr) + src_addr.s_addr = 0; + + i = StartPointOut(src_addr, dst_addr, src_port, dst_port, link_type); + link = linkTableOut[i]; + while (link != NULL) + { + if (link->src_addr.s_addr == src_addr.s_addr + && link->dst_addr.s_addr == dst_addr.s_addr + && link->dst_port == dst_port + && link->src_port == src_port + && link->link_type == link_type) + { + link->timestamp = timeStamp; + break; + } + link = link->next_out; + } + + return(link); +} + + +struct alias_link * +FindLinkIn(struct in_addr dst_addr, + struct in_addr alias_addr, + u_short dst_port, + u_short alias_port, + int link_type, + int replace_partial_links) +{ + int flags_in; + u_int start_point; + struct alias_link *link; + struct alias_link *link_fully_specified; + struct alias_link *link_unknown_all; + struct alias_link *link_unknown_dst_addr; + struct alias_link *link_unknown_dst_port; + +/* Initialize pointers */ + link_fully_specified = NULL; + link_unknown_all = NULL; + link_unknown_dst_addr = NULL; + link_unknown_dst_port = NULL; + +/* If either the dest addr or port is unknown, the search + loop will have to know about this. */ + + flags_in = 0; + if (dst_addr.s_addr == 0) + flags_in |= LINK_UNKNOWN_DEST_ADDR; + if (dst_port == 0) + flags_in |= LINK_UNKNOWN_DEST_PORT; + +/* The following allows permanent links to be + be specified as using the default aliasing address + (i.e. device interface address) without knowing + in advance what that address is. */ + + if (alias_addr.s_addr == aliasAddress.s_addr) + alias_addr.s_addr = 0; + +/* Search loop */ + start_point = StartPointIn(alias_addr, alias_port, link_type); + link = linkTableIn[start_point]; + while (link != NULL) + { + int flags; + + flags = flags_in | link->flags; + if (!(flags & LINK_PARTIALLY_SPECIFIED)) + { + if (link->alias_addr.s_addr == alias_addr.s_addr + && link->alias_port == alias_port + && link->dst_addr.s_addr == dst_addr.s_addr + && link->dst_port == dst_port + && link->link_type == link_type) + { + link_fully_specified = link; + break; + } + } + else if ((flags & LINK_UNKNOWN_DEST_ADDR) + && (flags & LINK_UNKNOWN_DEST_PORT)) + { + if (link->alias_addr.s_addr == alias_addr.s_addr + && link->alias_port == alias_port + && link->link_type == link_type) + { + if (link_unknown_all == NULL) + link_unknown_all = link; + } + } + else if (flags & LINK_UNKNOWN_DEST_ADDR) + { + if (link->alias_addr.s_addr == alias_addr.s_addr + && link->alias_port == alias_port + && link->link_type == link_type + && link->dst_port == dst_port) + { + if (link_unknown_dst_addr == NULL) + link_unknown_dst_addr = link; + } + } + else if (flags & LINK_UNKNOWN_DEST_PORT) + { + if (link->alias_addr.s_addr == alias_addr.s_addr + && link->alias_port == alias_port + && link->link_type == link_type + && link->dst_addr.s_addr == dst_addr.s_addr) + { + if (link_unknown_dst_port == NULL) + link_unknown_dst_port = link; + } + } + link = link->next_in; + } + + + + if (link_fully_specified != NULL) + { + return link_fully_specified; + } + else if (link_unknown_dst_port != NULL) + { + return replace_partial_links + ? ReLink(link_unknown_dst_port, + link_unknown_dst_port->src_addr, dst_addr, alias_addr, + link_unknown_dst_port->src_port, dst_port, alias_port, + link_type) + : link_unknown_dst_port; + } + else if (link_unknown_dst_addr != NULL) + { + return replace_partial_links + ? ReLink(link_unknown_dst_addr, + link_unknown_dst_addr->src_addr, dst_addr, alias_addr, + link_unknown_dst_addr->src_port, dst_port, alias_port, + link_type) + : link_unknown_dst_addr; + } + else if (link_unknown_all != NULL) + { + return replace_partial_links + ? ReLink(link_unknown_all, + link_unknown_all->src_addr, dst_addr, alias_addr, + link_unknown_all->src_port, dst_port, alias_port, + link_type) + : link_unknown_all; + } + else + { + return(NULL); + } +} + + + + +/* External routines for finding/adding links + +-- "external" means outside alias_db.c, but within alias*.c -- + + FindIcmpIn(), FindIcmpOut() + FindFragmentIn1(), FindFragmentIn2() + AddFragmentPtrLink(), FindFragmentPtr() + FindUdpTcpIn(), FindUdpTcpOut() + FindOriginalAddress(), FindAliasAddress() + +(prototypes in alias_local.h) +*/ + + +struct alias_link * +FindIcmpIn(struct in_addr dst_addr, + struct in_addr alias_addr, + u_short id_alias) +{ + return FindLinkIn(dst_addr, alias_addr, + NO_DEST_PORT, id_alias, + LINK_ICMP, 0); +} + + +struct alias_link * +FindIcmpOut(struct in_addr src_addr, + struct in_addr dst_addr, + u_short id) +{ + struct alias_link * link; + + link = FindLinkOut(src_addr, dst_addr, + id, NO_DEST_PORT, + LINK_ICMP); + if (link == NULL) + { + struct in_addr alias_addr; + + alias_addr = FindAliasAddress(src_addr); + link = AddLink(src_addr, dst_addr, alias_addr, + id, NO_DEST_PORT, GET_ALIAS_ID, + LINK_ICMP); + } + + return(link); +} + + +struct alias_link * +FindFragmentIn1(struct in_addr dst_addr, + struct in_addr alias_addr, + u_short ip_id) +{ + struct alias_link *link; + + link = FindLinkIn(dst_addr, alias_addr, + NO_DEST_PORT, ip_id, + LINK_FRAGMENT_ID, 0); + + if (link == NULL) + { + link = AddLink(nullAddress, dst_addr, alias_addr, + NO_SRC_PORT, NO_DEST_PORT, ip_id, + LINK_FRAGMENT_ID); + } + + return(link); +} + + +struct alias_link * +FindFragmentIn2(struct in_addr dst_addr, /* Doesn't add a link if one */ + struct in_addr alias_addr, /* is not found. */ + u_short ip_id) +{ + return FindLinkIn(dst_addr, alias_addr, + NO_DEST_PORT, ip_id, + LINK_FRAGMENT_ID, 0); +} + + +struct alias_link * +AddFragmentPtrLink(struct in_addr dst_addr, + u_short ip_id) +{ + return AddLink(nullAddress, dst_addr, nullAddress, + NO_SRC_PORT, NO_DEST_PORT, ip_id, + LINK_FRAGMENT_PTR); +} + + +struct alias_link * +FindFragmentPtr(struct in_addr dst_addr, + u_short ip_id) +{ + return FindLinkIn(dst_addr, nullAddress, + NO_DEST_PORT, ip_id, + LINK_FRAGMENT_PTR, 0); +} + + +struct alias_link * +FindUdpTcpIn(struct in_addr dst_addr, + struct in_addr alias_addr, + u_short dst_port, + u_short alias_port, + u_char proto) +{ + int link_type; + struct alias_link *link; + + switch (proto) + { + case IPPROTO_UDP: + link_type = LINK_UDP; + break; + case IPPROTO_TCP: + link_type = LINK_TCP; + break; + default: + return NULL; + break; + } + + link = FindLinkIn(dst_addr, alias_addr, + dst_port, alias_port, + link_type, 1); + + if ( !(packetAliasMode & PKT_ALIAS_DENY_INCOMING) && link == NULL) + { + struct in_addr target_addr; + + target_addr = FindOriginalAddress(alias_addr); + link = AddLink(target_addr, dst_addr, alias_addr, + alias_port, dst_port, alias_port, + link_type); + } + + return(link); +} + + +struct alias_link * +FindUdpTcpOut(struct in_addr src_addr, + struct in_addr dst_addr, + u_short src_port, + u_short dst_port, + u_char proto) +{ + int link_type; + struct alias_link *link; + + switch (proto) + { + case IPPROTO_UDP: + link_type = LINK_UDP; + break; + case IPPROTO_TCP: + link_type = LINK_TCP; + break; + default: + return NULL; + break; + } + + link = FindLinkOut(src_addr, dst_addr, src_port, dst_port, link_type); + + if (link == NULL) + { + struct in_addr alias_addr; + + alias_addr = FindAliasAddress(src_addr); + link = AddLink(src_addr, dst_addr, alias_addr, + src_port, dst_port, GET_ALIAS_PORT, + link_type); + } + + return(link); +} + + +struct in_addr +FindOriginalAddress(struct in_addr alias_addr) +{ + struct alias_link *link; + + link = FindLinkIn(nullAddress, alias_addr, + 0, 0, LINK_ADDR, 0); + if (link == NULL) + { + newDefaultLink = 1; + if (targetAddress.s_addr != 0) + return targetAddress; + else + return alias_addr; + } + else + { + if (link->src_addr.s_addr == 0) + return aliasAddress; + else + return link->src_addr; + } +} + + +struct in_addr +FindAliasAddress(struct in_addr original_addr) +{ + struct alias_link *link; + + link = FindLinkOut(original_addr, nullAddress, + 0, 0, LINK_ADDR); + if (link == NULL) + { + return aliasAddress; + } + else + { + if (link->alias_addr.s_addr == 0) + return aliasAddress; + else + return link->alias_addr; + } +} + + +/* External routines for getting or changing link data + (external to alias_db.c, but internal to alias*.c) + + SetFragmentData(), GetFragmentData() + SetFragmentPtr(), GetFragmentPtr() + SetStateIn(), SetStateOut(), GetStateIn(), GetStateOut() + GetOriginalAddress(), GetDestAddress(), GetAliasAddress() + GetOriginalPort(), GetAliasPort() + SetAckModified(), GetAckModified() + GetDeltaAckIn(), GetDeltaSeqOut(), AddSeq() +*/ + + +void +SetFragmentAddr(struct alias_link *link, struct in_addr src_addr) +{ + link->data.frag_addr = src_addr; +} + + +void +GetFragmentAddr(struct alias_link *link, struct in_addr *src_addr) +{ + *src_addr = link->data.frag_addr; +} + + +void +SetFragmentPtr(struct alias_link *link, char *fptr) +{ + link->data.frag_ptr = fptr; +} + + +void +GetFragmentPtr(struct alias_link *link, char **fptr) +{ + *fptr = link->data.frag_ptr; +} + + +void +SetStateIn(struct alias_link *link, int state) +{ + /* TCP input state */ + switch (state) { + case ALIAS_TCP_STATE_DISCONNECTED: + if (link->data.tcp->state.out != ALIAS_TCP_STATE_CONNECTED) { + link->expire_time = TCP_EXPIRE_DEAD; + } else { + link->expire_time = TCP_EXPIRE_SINGLEDEAD; + } + link->data.tcp->state.in = state; + break; + case ALIAS_TCP_STATE_CONNECTED: + link->expire_time = TCP_EXPIRE_CONNECTED; + /*FALLTHROUGH*/ + case ALIAS_TCP_STATE_NOT_CONNECTED: + link->data.tcp->state.in = state; + break; + default: + abort(); + } +} + + +void +SetStateOut(struct alias_link *link, int state) +{ + /* TCP output state */ + switch (state) { + case ALIAS_TCP_STATE_DISCONNECTED: + if (link->data.tcp->state.in != ALIAS_TCP_STATE_CONNECTED) { + link->expire_time = TCP_EXPIRE_DEAD; + } else { + link->expire_time = TCP_EXPIRE_SINGLEDEAD; + } + link->data.tcp->state.out = state; + break; + case ALIAS_TCP_STATE_CONNECTED: + link->expire_time = TCP_EXPIRE_CONNECTED; + /*FALLTHROUGH*/ + case ALIAS_TCP_STATE_NOT_CONNECTED: + link->data.tcp->state.out = state; + break; + default: + abort(); + } +} + + +int +GetStateIn(struct alias_link *link) +{ + /* TCP input state */ + return link->data.tcp->state.in; +} + + +int +GetStateOut(struct alias_link *link) +{ + /* TCP output state */ + return link->data.tcp->state.out; +} + + +struct in_addr +GetOriginalAddress(struct alias_link *link) +{ + if (link->src_addr.s_addr == 0) + return aliasAddress; + else + return(link->src_addr); +} + + +struct in_addr +GetDestAddress(struct alias_link *link) +{ + return(link->dst_addr); +} + + +struct in_addr +GetAliasAddress(struct alias_link *link) +{ + if (link->alias_addr.s_addr == 0) + return aliasAddress; + else + return link->alias_addr; +} + + +struct in_addr +GetDefaultAliasAddress() +{ + return aliasAddress; +} + + +void +SetDefaultAliasAddress(struct in_addr alias_addr) +{ + aliasAddress = alias_addr; +} + + +u_short +GetOriginalPort(struct alias_link *link) +{ + return(link->src_port); +} + + +u_short +GetAliasPort(struct alias_link *link) +{ + return(link->alias_port); +} + +u_short +GetDestPort(struct alias_link *link) +{ + return(link->dst_port); +} + +void +SetAckModified(struct alias_link *link) +{ +/* Indicate that ack numbers have been modified in a TCP connection */ + link->data.tcp->state.ack_modified = 1; +} + + +int +GetAckModified(struct alias_link *link) +{ +/* See if ack numbers have been modified */ + return link->data.tcp->state.ack_modified; +} + + +int +GetDeltaAckIn(struct ip *pip, struct alias_link *link) +{ +/* +Find out how much the ack number has been altered for an incoming +TCP packet. To do this, a circular list is ack numbers where the TCP +packet size was altered is searched. +*/ + + int i; + struct tcphdr *tc; + int delta, ack_diff_min; + u_long ack; + + tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2)); + ack = tc->th_ack; + + delta = 0; + ack_diff_min = -1; + for (i=0; i<N_LINK_TCP_DATA; i++) + { + struct ack_data_record x; + + x = link->data.tcp->ack[i]; + if (x.active == 1) + { + int ack_diff; + + ack_diff = SeqDiff(x.ack_new, ack); + if (ack_diff >= 0) + { + if (ack_diff_min >= 0) + { + if (ack_diff < ack_diff_min) + { + delta = x.delta; + ack_diff_min = ack_diff; + } + } + else + { + delta = x.delta; + ack_diff_min = ack_diff; + } + } + } + } + return (delta); +} + + +int +GetDeltaSeqOut(struct ip *pip, struct alias_link *link) +{ +/* +Find out how much the seq number has been altered for an outgoing +TCP packet. To do this, a circular list is ack numbers where the TCP +packet size was altered is searched. +*/ + + int i; + struct tcphdr *tc; + int delta, seq_diff_min; + u_long seq; + + tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2)); + seq = tc->th_seq; + + delta = 0; + seq_diff_min = -1; + for (i=0; i<N_LINK_TCP_DATA; i++) + { + struct ack_data_record x; + + x = link->data.tcp->ack[i]; + if (x.active == 1) + { + int seq_diff; + + seq_diff = SeqDiff(x.ack_old, seq); + if (seq_diff >= 0) + { + if (seq_diff_min >= 0) + { + if (seq_diff < seq_diff_min) + { + delta = x.delta; + seq_diff_min = seq_diff; + } + } + else + { + delta = x.delta; + seq_diff_min = seq_diff; + } + } + } + } + return (delta); +} + + +void +AddSeq(struct ip *pip, struct alias_link *link, int delta) +{ +/* +When a TCP packet has been altered in length, save this +information in a circular list. If enough packets have +been altered, then this list will begin to overwrite itself. +*/ + + struct tcphdr *tc; + struct ack_data_record x; + int hlen, tlen, dlen; + int i; + + tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2)); + + hlen = (pip->ip_hl + tc->th_off) << 2; + tlen = ntohs(pip->ip_len); + dlen = tlen - hlen; + + x.ack_old = htonl(ntohl(tc->th_seq) + dlen); + x.ack_new = htonl(ntohl(tc->th_seq) + dlen + delta); + x.delta = delta; + x.active = 1; + + i = link->data.tcp->state.index; + link->data.tcp->ack[i] = x; + + i++; + if (i == N_LINK_TCP_DATA) + link->data.tcp->state.index = 0; + else + link->data.tcp->state.index = i; +} + +void +SetExpire(struct alias_link *link, int expire) +{ + if (expire == 0) + { + link->flags &= ~LINK_PERMANENT; + DeleteLink(link); + } + else if (expire == -1) + { + link->flags |= LINK_PERMANENT; + } + else if (expire > 0) + { + link->expire_time = expire; + } + else + { + fprintf(stderr, "PacketAlias/SetExpire(): "); + fprintf(stderr, "error in expire parameter\n"); + } +} + +void +ClearCheckNewLink(void) +{ + newDefaultLink = 0; +} + + +/* Miscellaneous Functions + + HouseKeeping() + InitPacketAliasLog() + UninitPacketAliasLog() +*/ + +/* + Whenever an outgoing or incoming packet is handled, HouseKeeping() + is called to find and remove timed-out aliasing links. Logic exists + to sweep through the entire table and linked list structure + every 60 seconds. + + (prototype in alias_local.h) +*/ + +void +HouseKeeping(void) +{ + int i, n, n100; + struct timeval tv; + struct timezone tz; + + /* + * Save system time (seconds) in global variable timeStamp for + * use by other functions. This is done so as not to unnecessarily + * waste timeline by making system calls. + */ + gettimeofday(&tv, &tz); + timeStamp = tv.tv_sec; + + /* Compute number of spokes (output table link chains) to cover */ + n100 = LINK_TABLE_OUT_SIZE * 100 + houseKeepingResidual; + n100 *= timeStamp - lastCleanupTime; + n100 /= ALIAS_CLEANUP_INTERVAL_SECS; + + n = n100/100; + + /* Handle different cases */ + if (n > ALIAS_CLEANUP_MAX_SPOKES) + { + n = ALIAS_CLEANUP_MAX_SPOKES; + lastCleanupTime = timeStamp; + houseKeepingResidual = 0; + + for (i=0; i<n; i++) + IncrementalCleanup(); + } + else if (n > 0) + { + lastCleanupTime = timeStamp; + houseKeepingResidual = n100 - 100*n; + + for (i=0; i<n; i++) + IncrementalCleanup(); + } + else if (n < 0) + { + fprintf(stderr, "PacketAlias/HouseKeeping(): "); + fprintf(stderr, "something unexpected in time values\n"); + lastCleanupTime = timeStamp; + houseKeepingResidual = 0; + } +} + + +/* Init the log file and enable logging */ +static void +InitPacketAliasLog(void) +{ + if ((~packetAliasMode & PKT_ALIAS_LOG) + && (monitorFile = fopen("/var/log/alias.log", "w"))) + { + packetAliasMode |= PKT_ALIAS_LOG; + fprintf(monitorFile, + "PacketAlias/InitPacketAliasLog: Packet alias logging enabled.\n"); + } +} + + +/* Close the log-file and disable logging. */ +static void +UninitPacketAliasLog(void) +{ + if( monitorFile ) + fclose(monitorFile); + packetAliasMode &= ~PKT_ALIAS_LOG; +} + + + + + + +/* Outside world interfaces + +-- "outside world" means other than alias*.c routines -- + + PacketAliasRedirectPort() + PacketAliasRedirectAddr() + PacketAliasRedirectDelete() + PacketAliasSetAddress() + PacketAliasInit() + PacketAliasUninit() + PacketAliasSetMode() + +(prototypes in alias.h) +*/ + +/* Redirection from a specific public addr:port to a + a private addr:port */ +struct alias_link * +PacketAliasRedirectPort(struct in_addr src_addr, u_short src_port, + struct in_addr dst_addr, u_short dst_port, + struct in_addr alias_addr, u_short alias_port, + u_char proto) +{ + int link_type; + struct alias_link *link; + + switch(proto) + { + case IPPROTO_UDP: + link_type = LINK_UDP; + break; + case IPPROTO_TCP: + link_type = LINK_TCP; + break; + default: + fprintf(stderr, "PacketAliasRedirectPort(): "); + fprintf(stderr, "only TCP and UDP protocols allowed\n"); + return NULL; + } + + link = AddLink(src_addr, dst_addr, alias_addr, + src_port, dst_port, alias_port, + link_type); + + if (link != NULL) + { + link->flags |= LINK_PERMANENT; + } + else + { + fprintf(stderr, "PacketAliasRedirectPort(): " + "call to AddLink() failed\n"); + } + + return link; +} + + +/* Static address translation */ +struct alias_link * +PacketAliasRedirectAddr(struct in_addr src_addr, + struct in_addr alias_addr) +{ + struct alias_link *link; + + link = AddLink(src_addr, nullAddress, alias_addr, + 0, 0, 0, + LINK_ADDR); + + if (link != NULL) + { + link->flags |= LINK_PERMANENT; + } + else + { + fprintf(stderr, "PacketAliasRedirectAddr(): " + "call to AddLink() failed\n"); + } + + return link; +} + + +void +PacketAliasRedirectDelete(struct alias_link *link) +{ +/* This is a dangerous function to put in the API, + because an invalid pointer can crash the program. */ + + deleteAllLinks = 1; + DeleteLink(link); + deleteAllLinks = 0; +} + + +void +PacketAliasSetAddress(struct in_addr addr) +{ + if (packetAliasMode & PKT_ALIAS_RESET_ON_ADDR_CHANGE + && aliasAddress.s_addr != addr.s_addr) + CleanupAliasData(); + + aliasAddress = addr; +} + + +void +PacketAliasSetTarget(struct in_addr target_addr) +{ + targetAddress = target_addr; +} + + +void +PacketAliasInit(void) +{ + int i; + struct timeval tv; + struct timezone tz; + static int firstCall = 1; + + if (firstCall == 1) + { + gettimeofday(&tv, &tz); + timeStamp = tv.tv_sec; + lastCleanupTime = tv.tv_sec; + houseKeepingResidual = 0; + + for (i=0; i<LINK_TABLE_OUT_SIZE; i++) + linkTableOut[i] = NULL; + for (i=0; i<LINK_TABLE_IN_SIZE; i++) + linkTableIn[i] = NULL; + + atexit(PacketAliasUninit); + firstCall = 0; + } + else + { + deleteAllLinks = 1; + CleanupAliasData(); + deleteAllLinks = 0; + } + + aliasAddress.s_addr = 0; + targetAddress.s_addr = 0; + + icmpLinkCount = 0; + udpLinkCount = 0; + tcpLinkCount = 0; + fragmentIdLinkCount = 0; + fragmentPtrLinkCount = 0; + sockCount = 0; + + cleanupIndex =0; + + packetAliasMode = PKT_ALIAS_SAME_PORTS + | PKT_ALIAS_USE_SOCKETS + | PKT_ALIAS_RESET_ON_ADDR_CHANGE; +} + +void +PacketAliasUninit(void) { + deleteAllLinks = 1; + CleanupAliasData(); + deleteAllLinks = 0; + UninitPacketAliasLog(); +#ifndef NO_FW_PUNCH + UninitPunchFW(); +#endif +} + + +/* Change mode for some operations */ +unsigned int +PacketAliasSetMode( + unsigned int flags, /* Which state to bring flags to */ + unsigned int mask /* Mask of which flags to affect (use 0 to do a + probe for flag values) */ +) +{ +/* Enable logging? */ + if (flags & mask & PKT_ALIAS_LOG) + { + InitPacketAliasLog(); /* Do the enable */ + } else +/* _Disable_ logging? */ + if (~flags & mask & PKT_ALIAS_LOG) { + UninitPacketAliasLog(); + } + +#ifndef NO_FW_PUNCH +/* Start punching holes in the firewall? */ + if (flags & mask & PKT_ALIAS_PUNCH_FW) { + InitPunchFW(); + } else +/* Stop punching holes in the firewall? */ + if (~flags & mask & PKT_ALIAS_PUNCH_FW) { + UninitPunchFW(); + } +#endif + +/* Other flags can be set/cleared without special action */ + packetAliasMode = (flags & mask) | (packetAliasMode & ~mask); + return packetAliasMode; +} + + +int +PacketAliasCheckNewLink(void) +{ + return newDefaultLink; +} + + +#ifndef NO_FW_PUNCH + +/***************** + Code to support firewall punching. This shouldn't really be in this + file, but making variables global is evil too. + ****************/ + +/* Firewall include files */ +#include <sys/queue.h> +#include <net/if.h> +#include <netinet/ip_fw.h> +#include <string.h> +#include <err.h> + +static void ClearAllFWHoles(void); + +static int fireWallBaseNum; /* The first firewall entry free for our use */ +static int fireWallNumNums; /* How many entries can we use? */ +static int fireWallActiveNum; /* Which entry did we last use? */ +static char *fireWallField; /* bool array for entries */ + +#define fw_setfield(field, num) \ +do { \ + (field)[num] = 1; \ +} /*lint -save -e717 */ while(0) /*lint -restore */ +#define fw_clrfield(field, num) \ +do { \ + (field)[num] = 0; \ +} /*lint -save -e717 */ while(0) /*lint -restore */ +#define fw_tstfield(field, num) ((field)[num]) + +void +PacketAliasSetFWBase(unsigned int base, unsigned int num) { + fireWallBaseNum = base; + fireWallNumNums = num; +} + +static void +InitPunchFW(void) { + fireWallField = malloc(fireWallNumNums); + if (fireWallField) { + memset(fireWallField, 0, fireWallNumNums); + if (fireWallFD < 0) { + fireWallFD = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); + } + ClearAllFWHoles(); + fireWallActiveNum = fireWallBaseNum; + } +} + +static void +UninitPunchFW(void) { + ClearAllFWHoles(); + if (fireWallFD >= 0) + close(fireWallFD); + fireWallFD = -1; + if (fireWallField) + free(fireWallField); + fireWallField = NULL; + packetAliasMode &= ~PKT_ALIAS_PUNCH_FW; +} + +/* Make a certain link go through the firewall */ +void +PunchFWHole(struct alias_link *link) { + int r; /* Result code */ + struct ip_fw rule; /* On-the-fly built rule */ + int fwhole; /* Where to punch hole */ + +/* Don't do anything unless we are asked to */ + if ( !(packetAliasMode & PKT_ALIAS_PUNCH_FW) || + fireWallFD < 0 || + link->link_type != LINK_TCP || + !link->data.tcp) + return; + + memset(&rule, 0, sizeof rule); + +/** Build rule **/ + + /* Find empty slot */ + for (fwhole = fireWallActiveNum; + fwhole < fireWallBaseNum + fireWallNumNums && + fw_tstfield(fireWallField, fwhole); + fwhole++) + ; + if (fwhole >= fireWallBaseNum + fireWallNumNums || + fw_tstfield(fireWallField, fwhole)) { + for (fwhole = fireWallBaseNum; + fwhole < fireWallActiveNum && + fw_tstfield(fireWallField, fwhole); + fwhole++) + ; + if (fwhole == fireWallActiveNum) { + /* No rule point empty - we can't punch more holes. */ + fireWallActiveNum = fireWallBaseNum; + fprintf(stderr, "libalias: Unable to create firewall hole!\n"); + return; + } + } + /* Start next search at next position */ + fireWallActiveNum = fwhole+1; + + /* Build generic part of the two rules */ + rule.fw_number = fwhole; + rule.fw_nports = 1; /* Number of source ports; dest ports follow */ + rule.fw_flg = IP_FW_F_ACCEPT; + rule.fw_prot = IPPROTO_TCP; + rule.fw_smsk.s_addr = INADDR_BROADCAST; + rule.fw_dmsk.s_addr = INADDR_BROADCAST; + + /* Build and apply specific part of the rules */ + rule.fw_src = GetOriginalAddress(link); + rule.fw_dst = GetDestAddress(link); + rule.fw_uar.fw_pts[0] = ntohs(GetOriginalPort(link)); + rule.fw_uar.fw_pts[1] = ntohs(GetDestPort(link)); + + /* Skip non-bound links - XXX should not be strictly necessary, + but seems to leave hole if not done. Leak of non-bound links? + (Code should be left even if the problem is fixed - it is a + clear optimization) */ + if (rule.fw_uar.fw_pts[0] != 0 && rule.fw_uar.fw_pts[1] != 0) { + r = setsockopt(fireWallFD, IPPROTO_IP, IP_FW_ADD, &rule, sizeof rule); + if (r) + err(1, "alias punch inbound(1) setsockopt(IP_FW_ADD)"); + rule.fw_src = GetDestAddress(link); + rule.fw_dst = GetOriginalAddress(link); + rule.fw_uar.fw_pts[0] = ntohs(GetDestPort(link)); + rule.fw_uar.fw_pts[1] = ntohs(GetOriginalPort(link)); + r = setsockopt(fireWallFD, IPPROTO_IP, IP_FW_ADD, &rule, sizeof rule); + if (r) + err(1, "alias punch inbound(2) setsockopt(IP_FW_ADD)"); + } +/* Indicate hole applied */ + link->data.tcp->fwhole = fwhole; + fw_setfield(fireWallField, fwhole); +} + +/* Remove a hole in a firewall associated with a particular alias + link. Calling this too often is harmless. */ +static void +ClearFWHole(struct alias_link *link) { + if (link->link_type == LINK_TCP && link->data.tcp) { + int fwhole = link->data.tcp->fwhole; /* Where is the firewall hole? */ + struct ip_fw rule; + + if (fwhole < 0) + return; + + memset(&rule, 0, sizeof rule); + rule.fw_number = fwhole; + while (!setsockopt(fireWallFD, IPPROTO_IP, IP_FW_DEL, &rule, sizeof rule)) + ; + fw_clrfield(fireWallField, fwhole); + link->data.tcp->fwhole = -1; + } +} + +/* Clear out the entire range dedicated to firewall holes. */ +static void +ClearAllFWHoles(void) { + struct ip_fw rule; /* On-the-fly built rule */ + int i; + + if (fireWallFD < 0) + return; + + memset(&rule, 0, sizeof rule); + for (i = fireWallBaseNum; i < fireWallBaseNum + fireWallNumNums; i++) { + rule.fw_number = i; + while (!setsockopt(fireWallFD, IPPROTO_IP, IP_FW_DEL, &rule, sizeof rule)) + ; + } + memset(fireWallField, 0, fireWallNumNums); +} +#endif diff --git a/sys/netinet/libalias/alias_ftp.c b/sys/netinet/libalias/alias_ftp.c new file mode 100644 index 0000000..c6d449d --- /dev/null +++ b/sys/netinet/libalias/alias_ftp.c @@ -0,0 +1,227 @@ +/* + Alias_ftp.c performs special processing for FTP sessions under + TCP. Specifically, when a PORT command from the client side + is sent, it is intercepted and modified. The address is changed + to the gateway machine and an aliasing port is used. + + For this routine to work, the PORT command must fit entirely + into a single TCP packet. This is typically the case, but exceptions + can easily be envisioned under the actual specifications. + + Probably the most troubling aspect of the approach taken here is + that the new PORT command will typically be a different length, and + this causes a certain amount of bookkeeping to keep track of the + changes of sequence and acknowledgment numbers, since the client + machine is totally unaware of the modification to the TCP stream. + + + This software is placed into the public domain with no restrictions + on its distribution. + + Initial version: August, 1996 (cjm) + + Version 1.6 + Brian Somers and Martin Renters identified an IP checksum + error for modified IP packets. + + Version 1.7: January 9, 1996 (cjm) + Differental checksum computation for change + in IP packet length. + + Version 2.1: May, 1997 (cjm) + Very minor changes to conform with + local/global/function naming conventions + withing the packet alising module. + + See HISTORY file for record of revisions. +*/ + +/* Includes */ +#include <ctype.h> +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <netinet/in_systm.h> +#include <netinet/in.h> +#include <netinet/ip.h> +#include <netinet/tcp.h> + +#include "alias_local.h" + +static void NewFtpPortCommand(struct ip *, struct alias_link *, struct in_addr, u_short, int); + + + +void +AliasHandleFtpOut( +struct ip *pip, /* IP packet to examine/patch */ +struct alias_link *link, /* The link to go through (aliased port) */ +int maxpacketsize /* The maximum size this packet can grow to (including headers) */) +{ + int hlen, tlen, dlen; + struct in_addr true_addr; + u_short true_port; + char *sptr; + struct tcphdr *tc; + +/* Calculate data length of TCP packet */ + tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2)); + hlen = (pip->ip_hl + tc->th_off) << 2; + tlen = ntohs(pip->ip_len); + dlen = tlen - hlen; + +/* Return is data length is too long or too short */ + if (dlen<10 || dlen>80) + return; + +/* Place string pointer and beginning of data */ + sptr = (char *) pip; + sptr += hlen; + +/* Parse through string using state diagram method */ + { + char ch, zero; + int i, state; + u_long a1, a2, a3, a4; + u_short p1, p2; + + a1=0; a2=0; a3=0; a4=0; p1=0; p2=0; + zero = '0'; + state=-4; + for (i=0; i<dlen; i++) + { + ch = sptr[i]; + switch (state) + { + case -4: if (ch == 'P') state=-3; else return; break; + case -3: if (ch == 'O') state=-2; else return; break; + case -2: if (ch == 'R') state=-1; else return; break; + case -1: if (ch == 'T') state= 0; else return; break; + + case 0 : + if (isdigit(ch)) {a1=ch-zero; state=1 ;} break; + case 1 : + if (isdigit(ch)) a1=10*a1+ch-zero; else state=2 ; break; + case 2 : + if (isdigit(ch)) {a2=ch-zero; state=3 ;} break; + case 3 : + if (isdigit(ch)) a2=10*a2+ch-zero; else state=4 ; break; + case 4 : + if (isdigit(ch)) {a3=ch-zero; state=5 ;} break; + case 5 : + if (isdigit(ch)) a3=10*a3+ch-zero; else state=6 ; break; + case 6 : + if (isdigit(ch)) {a4=ch-zero; state=7 ;} break; + case 7 : + if (isdigit(ch)) a4=10*a4+ch-zero; else state=8 ; break; + case 8 : + if (isdigit(ch)) {p1=ch-zero; state=9 ;} break; + case 9 : + if (isdigit(ch)) p1=10*p1+ch-zero; else state=10; break; + case 10: + if (isdigit(ch)) {p2=ch-zero; state=11;} break; + case 11: + if (isdigit(ch)) p2=10*p2+ch-zero; break; + } + } + + if (state == 11) + { + true_port = htons((p1<<8) + p2); + true_addr.s_addr = htonl((a1<<24) + (a2<<16) +(a3<<8) + a4); + NewFtpPortCommand(pip, link, true_addr, true_port, maxpacketsize); + } + } +} + +static void +NewFtpPortCommand(struct ip *pip, + struct alias_link *link, + struct in_addr true_addr, + u_short true_port, + int maxpacketsize) +{ + struct alias_link *ftp_link; + +/* Establish link to address and port found in PORT command */ + ftp_link = FindUdpTcpOut(true_addr, GetDestAddress(link), + true_port, 0, IPPROTO_TCP); + + if (ftp_link != NULL) + { + int slen, hlen, tlen, dlen; + struct tcphdr *tc; + +#ifndef NO_FW_PUNCH +/* Punch hole in firewall */ + PunchFWHole(ftp_link); +#endif + +/* Calculate data length of TCP packet */ + tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2)); + hlen = (pip->ip_hl + tc->th_off) << 2; + tlen = ntohs(pip->ip_len); + dlen = tlen - hlen; + +/* Create new PORT command */ + { + char stemp[80]; + char *sptr; + u_short alias_port; + u_char *ptr; + int a1, a2, a3, a4, p1, p2; + struct in_addr alias_address; + +/* Decompose alias address into quad format */ + alias_address = GetAliasAddress(link); + ptr = (u_char *) &alias_address.s_addr; + a1 = *ptr++; a2=*ptr++; a3=*ptr++; a4=*ptr; + +/* Decompose alias port into pair format */ + alias_port = GetAliasPort(ftp_link); + ptr = (char *) &alias_port; + p1 = *ptr++; p2=*ptr; + +/* Generate command string */ + sprintf(stemp, "PORT %d,%d,%d,%d,%d,%d\r\n", + a1,a2,a3,a4,p1,p2); + +/* Save string length for IP header modification */ + slen = strlen(stemp); + +/* Copy into IP packet */ + sptr = (char *) pip; sptr += hlen; + strncpy(sptr, stemp, maxpacketsize-hlen); + } + +/* Save information regarding modified seq and ack numbers */ + { + int delta; + + SetAckModified(link); + delta = GetDeltaSeqOut(pip, link); + AddSeq(pip, link, delta+slen-dlen); + } + +/* Revise IP header */ + { + u_short new_len; + + new_len = htons(hlen + slen); + DifferentialChecksum(&pip->ip_sum, + &new_len, + &pip->ip_len, + 1); + pip->ip_len = new_len; + } + +/* Compute TCP checksum for revised packet */ + tc->th_sum = 0; + tc->th_sum = TcpChecksum(pip); + } + else + { + fprintf(stderr, + "PacketAlias/HandleFtpOut: Cannot allocate FTP data port\n"); + } +} diff --git a/sys/netinet/libalias/alias_irc.c b/sys/netinet/libalias/alias_irc.c new file mode 100644 index 0000000..910e934 --- /dev/null +++ b/sys/netinet/libalias/alias_irc.c @@ -0,0 +1,316 @@ +/* Alias_irc.c intercepts packages contain IRC CTCP commands, and + changes DCC commands to export a port on the aliasing host instead + of an aliased host. + + For this routine to work, the DCC command must fit entirely into a + single TCP packet. This will usually happen, but is not + guaranteed. + + The interception is likely to change the length of the packet. + The handling of this is copied more-or-less verbatim from + ftp_alias.c + + This software is placed into the public domain with no restrictions + on its distribution. + + Initial version: Eivind Eklund <perhaps@yes.no> (ee) 97-01-29 + + Version 2.1: May, 1997 (cjm) + Very minor changes to conform with + local/global/function naming conventions + withing the packet alising module. +*/ + +/* Includes */ +#include <ctype.h> +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <netinet/in_systm.h> +#include <netinet/in.h> +#include <netinet/ip.h> +#include <netinet/tcp.h> +#include <limits.h> + +#include "alias_local.h" + +/* Local defines */ +#define DBprintf(a) + + +void +AliasHandleIrcOut(struct ip *pip, /* IP packet to examine */ + struct alias_link *link, /* Which link are we on? */ + int maxsize /* Maximum size of IP packet including headers */ + ) +{ + int hlen, tlen, dlen; + struct in_addr true_addr; + u_short true_port; + char *sptr; + struct tcphdr *tc; + int i; /* Iterator through the source */ + +/* Calculate data length of TCP packet */ + tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2)); + hlen = (pip->ip_hl + tc->th_off) << 2; + tlen = ntohs(pip->ip_len); + dlen = tlen - hlen; + + /* Return if data length is too short - assume an entire PRIVMSG in each packet. */ + if (dlen<sizeof(":A!a@n.n PRIVMSG A :aDCC 1 1a")-1) + return; + +/* Place string pointer at beginning of data */ + sptr = (char *) pip; + sptr += hlen; + maxsize -= hlen; /* We're interested in maximum size of data, not packet */ + + /* Search for a CTCP command [Note 1] */ + for( i=0; i<dlen; i++ ) { + if(sptr[i]=='\001') + goto lFOUND_CTCP; + } + return; /* No CTCP commands in */ + /* Handle CTCP commands - the buffer may have to be copied */ +lFOUND_CTCP: + { + char newpacket[65536]; /* Estimate of maximum packet size :) */ + int copyat = i; /* Same */ + int iCopy = 0; /* How much data have we written to copy-back string? */ + unsigned long org_addr; /* Original IP address */ + unsigned short org_port; /* Original source port address */ + lCTCP_START: + if( i >= dlen || iCopy >= sizeof(newpacket) ) + goto lPACKET_DONE; + newpacket[iCopy++] = sptr[i++]; /* Copy the CTCP start character */ + /* Start of a CTCP */ + if( i+4 >= dlen ) /* Too short for DCC */ + goto lBAD_CTCP; + if( sptr[i+0] != 'D' ) + goto lBAD_CTCP; + if( sptr[i+1] != 'C' ) + goto lBAD_CTCP; + if( sptr[i+2] != 'C' ) + goto lBAD_CTCP; + if( sptr[i+3] != ' ' ) + goto lBAD_CTCP; + /* We have a DCC command - handle it! */ + i+= 4; /* Skip "DCC " */ + if( iCopy+4 > sizeof(newpacket) ) + goto lPACKET_DONE; + newpacket[iCopy++] = 'D'; + newpacket[iCopy++] = 'C'; + newpacket[iCopy++] = 'C'; + newpacket[iCopy++] = ' '; + + DBprintf(("Found DCC\n")); + /* Skip any extra spaces (should not occur according to + protocol, but DCC breaks CTCP protocol anyway */ + while(sptr[i] == ' ') { + if( ++i >= dlen) { + DBprintf(("DCC packet terminated in just spaces\n")); + goto lPACKET_DONE; + } + } + + DBprintf(("Transferring command...\n")); + while(sptr[i] != ' ') { + newpacket[iCopy++] = sptr[i]; + if( ++i >= dlen || iCopy >= sizeof(newpacket) ) { + DBprintf(("DCC packet terminated during command\n")); + goto lPACKET_DONE; + } + } + /* Copy _one_ space */ + if( i+1 < dlen && iCopy < sizeof(newpacket) ) + newpacket[iCopy++] = sptr[i++]; + + DBprintf(("Done command - removing spaces\n")); + /* Skip any extra spaces (should not occur according to + protocol, but DCC breaks CTCP protocol anyway */ + while(sptr[i] == ' ') { + if( ++i >= dlen ) { + DBprintf(("DCC packet terminated in just spaces (post-command)\n")); + goto lPACKET_DONE; + } + } + + DBprintf(("Transferring filename...\n")); + while(sptr[i] != ' ') { + newpacket[iCopy++] = sptr[i]; + if( ++i >= dlen || iCopy >= sizeof(newpacket) ) { + DBprintf(("DCC packet terminated during filename\n")); + goto lPACKET_DONE; + } + } + /* Copy _one_ space */ + if( i+1 < dlen && iCopy < sizeof(newpacket) ) + newpacket[iCopy++] = sptr[i++]; + + DBprintf(("Done filename - removing spaces\n")); + /* Skip any extra spaces (should not occur according to + protocol, but DCC breaks CTCP protocol anyway */ + while(sptr[i] == ' ') { + if( ++i >= dlen ) { + DBprintf(("DCC packet terminated in just spaces (post-filename)\n")); + goto lPACKET_DONE; + } + } + + DBprintf(("Fetching IP address\n")); + /* Fetch IP address */ + org_addr = 0; + while(i<dlen && isdigit(sptr[i])) { + if( org_addr > ULONG_MAX/10UL ) { /* Terminate on overflow */ + DBprintf(("DCC Address overflow (org_addr == 0x%08lx, next char %c\n", org_addr, sptr[i])); + goto lBAD_CTCP; + } + org_addr *= 10; + org_addr += sptr[i++]-'0'; + } + DBprintf(("Skipping space\n")); + if( i+1 >= dlen || sptr[i] != ' ' ) { + DBprintf(("Overflow (%d >= %d) or bad character (%02x) terminating IP address\n", i+1, dlen, sptr[i])); + goto lBAD_CTCP; + } + /* Skip any extra spaces (should not occur according to + protocol, but DCC breaks CTCP protocol anyway, so we might + as well play it safe */ + while(sptr[i] == ' ') { + if( ++i >= dlen ) { + DBprintf(("Packet failure - space overflow.\n")); + goto lPACKET_DONE; + } + } + DBprintf(("Fetching port number\n")); + /* Fetch source port */ + org_port = 0; + while(i<dlen && isdigit(sptr[i])) { + if( org_port > 6554 ) { /* Terminate on overflow (65536/10 rounded up*/ + DBprintf(("DCC: port number overflow\n")); + goto lBAD_CTCP; + } + org_port *= 10; + org_port += sptr[i++]-'0'; + } + /* Skip illegal addresses (or early termination) */ + if( i >= dlen || (sptr[i] != '\001' && sptr[i] != ' ') ) { + DBprintf(("Bad port termination\n")); + goto lBAD_CTCP; + } + DBprintf(("Got IP %lu and port %u\n", org_addr, (unsigned)org_port)); + + /* We've got the address and port - now alias it */ + { + struct alias_link *dcc_link; + struct in_addr destaddr; + + + true_port = htons(org_port); + true_addr.s_addr = htonl(org_addr); + destaddr.s_addr = 0; + + /* Steal the FTP_DATA_PORT - it doesn't really matter, and this + would probably allow it through at least _some_ + firewalls. */ + dcc_link = FindUdpTcpOut (true_addr, + destaddr, + true_port, + 0, IPPROTO_TCP); + DBprintf(("Got a DCC link\n")); + if ( dcc_link ) { + struct in_addr alias_address; /* Address from aliasing */ + u_short alias_port; /* Port given by aliasing */ + +#ifndef NO_FW_PUNCH + /* Generate firewall hole as appropriate */ + PunchFWHole(dcc_link); +#endif + + alias_address = GetAliasAddress(link); + iCopy += snprintf(&newpacket[iCopy], + sizeof(newpacket)-iCopy, + "%lu ", (u_long)htonl(alias_address.s_addr)); + if( iCopy >= sizeof(newpacket) ) { /* Truncated/fit exactly - bad news */ + DBprintf(("DCC constructed packet overflow.\n")); + goto lBAD_CTCP; + } + alias_port = GetAliasPort(dcc_link); + iCopy += snprintf(&newpacket[iCopy], + sizeof(newpacket)-iCopy, + "%u", htons(alias_port) ); + /* Done - truncated cases will be taken care of by lBAD_CTCP */ + DBprintf(("Aliased IP %lu and port %u\n", alias_address.s_addr, (unsigned)alias_port)); + } + } + /* An uninteresting CTCP - state entered right after '\001' has + been pushed. Also used to copy the rest of a DCC, after IP + address and port has been handled */ + lBAD_CTCP: + for(; i<dlen && iCopy<sizeof(newpacket); i++,iCopy++) { + newpacket[iCopy] = sptr[i]; /* Copy CTCP unchanged */ + if(sptr[i] == '\001') { + goto lNORMAL_TEXT; + } + } + goto lPACKET_DONE; + /* Normal text */ + lNORMAL_TEXT: + for(; i<dlen && iCopy<sizeof(newpacket); i++,iCopy++) { + newpacket[iCopy] = sptr[i]; /* Copy CTCP unchanged */ + if(sptr[i] == '\001') { + goto lCTCP_START; + } + } + /* Handle the end of a packet */ + lPACKET_DONE: + iCopy = iCopy > maxsize-copyat ? maxsize-copyat : iCopy; + memcpy(sptr+copyat, newpacket, iCopy); + +/* Save information regarding modified seq and ack numbers */ + { + int delta; + + SetAckModified(link); + delta = GetDeltaSeqOut(pip, link); + AddSeq(pip, link, delta+copyat+iCopy-dlen); + } + + /* Revise IP header */ + { + u_short new_len; + + new_len = htons(hlen + iCopy + copyat); + DifferentialChecksum(&pip->ip_sum, + &new_len, + &pip->ip_len, + 1); + pip->ip_len = new_len; + } + + /* Compute TCP checksum for revised packet */ + tc->th_sum = 0; + tc->th_sum = TcpChecksum(pip); + return; + } +} + +/* Notes: + [Note 1] + The initial search will most often fail; it could be replaced with a 32-bit specific search. + Such a search would be done for 32-bit unsigned value V: + V ^= 0x01010101; (Search is for null bytes) + if( ((V-0x01010101)^V) & 0x80808080 ) { + (found a null bytes which was a 01 byte) + } + To assert that the processor is 32-bits, do + extern int ircdccar[32]; (32 bits) + extern int ircdccar[CHAR_BIT*sizeof(unsigned int)]; + which will generate a type-error on all but 32-bit machines. + + [Note 2] This routine really ought to be replaced with one that + creates a transparent proxy on the aliasing host, to allow arbitary + changes in the TCP stream. This should not be too difficult given + this base; I (ee) will try to do this some time later. + */ diff --git a/sys/netinet/libalias/alias_local.h b/sys/netinet/libalias/alias_local.h new file mode 100644 index 0000000..16d0aaa --- /dev/null +++ b/sys/netinet/libalias/alias_local.h @@ -0,0 +1,105 @@ +/* -*- mode: c; tab-width: 3; c-basic-offset: 3; -*- + Alias_local.h contains the function prototypes for alias.c, + alias_db.c, alias_util.c and alias_ftp.c, alias_irc.c (as well + as any future add-ons). It is intended to be used only within + the aliasing software. Outside world interfaces are defined + in alias.h + + This software is placed into the public domain with no restrictions + on its distribution. + + Initial version: August, 1996 (cjm) + + <updated several times by original author and Eivind Eiklund> +*/ +#ifndef ALIAS_LOCAL_H +#define ALIAS_LOCAL_H + +extern int packetAliasMode; + +struct alias_link; + +/* General utilities */ +u_short IpChecksum(struct ip *); +u_short TcpChecksum(struct ip *); +void DifferentialChecksum(u_short *, u_short *, u_short *, int); + +/* Internal data access */ +struct alias_link * +FindIcmpIn(struct in_addr, struct in_addr, u_short); + +struct alias_link * +FindIcmpOut(struct in_addr, struct in_addr, u_short); + +struct alias_link * +FindFragmentIn1(struct in_addr, struct in_addr, u_short); + +struct alias_link * +FindFragmentIn2(struct in_addr, struct in_addr, u_short); + +struct alias_link * +AddFragmentPtrLink(struct in_addr, u_short); + +struct alias_link * +FindFragmentPtr(struct in_addr, u_short); + +struct alias_link * +FindUdpTcpIn (struct in_addr, struct in_addr, u_short, u_short, u_char); + +struct alias_link * +FindUdpTcpOut(struct in_addr, struct in_addr, u_short, u_short, u_char); + +struct in_addr +FindOriginalAddress(struct in_addr); + +struct in_addr +FindAliasAddress(struct in_addr); + +/* External data access/modification */ +void GetFragmentAddr(struct alias_link *, struct in_addr *); +void SetFragmentAddr(struct alias_link *, struct in_addr); +void GetFragmentPtr(struct alias_link *, char **); +void SetFragmentPtr(struct alias_link *, char *); +void SetStateIn(struct alias_link *, int); +void SetStateOut(struct alias_link *, int); +int GetStateIn(struct alias_link *); +int GetStateOut(struct alias_link *); +struct in_addr GetOriginalAddress(struct alias_link *); +struct in_addr GetDestAddress(struct alias_link *); +struct in_addr GetAliasAddress(struct alias_link *); +struct in_addr GetDefaultAliasAddress(void); +void SetDefaultAliasAddress(struct in_addr); +u_short GetOriginalPort(struct alias_link *); +u_short GetAliasPort(struct alias_link *); +void SetAckModified(struct alias_link *); +int GetAckModified(struct alias_link *); +int GetDeltaAckIn(struct ip *, struct alias_link *); +int GetDeltaSeqOut(struct ip *, struct alias_link *); +void AddSeq(struct ip *, struct alias_link *, int); +void SetExpire(struct alias_link *, int); +void ClearCheckNewLink(void); +#ifndef NO_FW_PUNCH +void PunchFWHole(struct alias_link *); +#endif + + +/* Housekeeping function */ +void HouseKeeping(void); + +/* Tcp specfic routines */ +/*lint -save -library Suppress flexelint warnings */ +void AliasHandleFtpOut(struct ip *, struct alias_link *, int); +void AliasHandleIrcOut(struct ip *pip, struct alias_link *link, int maxsize ); +void AliasHandleUdpNbt(struct ip *, struct alias_link *, struct in_addr *, u_short); +void AliasHandleUdpNbtNS(struct ip *, struct alias_link *, struct in_addr *, u_short *, struct in_addr *, u_short *); + + + +enum alias_tcp_state { + ALIAS_TCP_STATE_NOT_CONNECTED, + ALIAS_TCP_STATE_CONNECTED, + ALIAS_TCP_STATE_DISCONNECTED +}; + +/*lint -restore */ +#endif /* defined(ALIAS_LOCAL_H) */ diff --git a/sys/netinet/libalias/alias_nbt.c b/sys/netinet/libalias/alias_nbt.c new file mode 100644 index 0000000..f3581a7 --- /dev/null +++ b/sys/netinet/libalias/alias_nbt.c @@ -0,0 +1,576 @@ +/* + * Written by Atsushi Murai <amurai@spec.co.jp> + * + * Copyright (C) 1998, System Planning and Engineering Co. All rights reserverd. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the System Planning and Engineering Co. The name of the + * SPEC may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * $Id:$ + * + * TODO: + * oClean up. + * oConsidering for word alignment for other platform. + */ +/* + alias_nbt.c performs special processing for NetBios over TCP/IP + sessions by UDP. + + Initial version: May, 1998 (Atsushi Murai <amurai@spec.co.jp>) + + See HISTORY file for record of revisions. +*/ + +/* Includes */ +#include <ctype.h> +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <netinet/in_systm.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netinet/ip.h> +#include <netinet/udp.h> +#include <netinet/tcp.h> + +#include "alias_local.h" + +#define ADJUST_CHECKSUM(acc, cksum) { \ + acc += cksum; \ + if (acc < 0) \ + { \ + acc = -acc; \ + acc = (acc >> 16) + (acc & 0xffff); \ + acc += acc >> 16; \ + cksum = (u_short) ~acc; \ + } \ + else \ + { \ + acc = (acc >> 16) + (acc & 0xffff); \ + acc += acc >> 16; \ + cksum = (u_short) acc; \ + } \ +} + +typedef struct { + struct in_addr oldaddr; + u_short oldport; + struct in_addr newaddr; + u_short newport; + u_short *uh_sum; +} NBTArguments; + +typedef struct { + unsigned char type; + unsigned char flags; + u_short id; + struct in_addr source_ip; + u_short source_port; + u_short len; + u_short offset; +} NbtDataHeader; + +#define OpQuery 0 +#define OpUnknown 4 +#define OpRegist 5 +#define OpRelease 6 +#define OpWACK 7 +#define OpRefresh 8 +typedef struct { + u_short nametrid; + u_short dir:1, opcode:4, nmflags:7, rcode:4; + u_short qdcount; + u_short ancount; + u_short nscount; + u_short arcount; +} NbtNSHeader; + +#define FMT_ERR 0x1 +#define SRV_ERR 0x2 +#define IMP_ERR 0x4 +#define RFS_ERR 0x5 +#define ACT_ERR 0x6 +#define CFT_ERR 0x7 + +/******************************************************************* + * copy an IP address from one buffer to another * + *******************************************************************/ +void putip(void *dest,void *src) +{ + memcpy(dest,src,4); +} + +void PrintRcode( u_char rcode ) { + + switch (rcode) { + case FMT_ERR: + printf("\nFormat Error."); + case SRV_ERR: + printf("\nSever failure."); + case IMP_ERR: + printf("\nUnsupported request error.\n"); + case RFS_ERR: + printf("\nRefused error.\n"); + case ACT_ERR: + printf("\nActive error.\n"); + case CFT_ERR: + printf("\nName in conflict error.\n"); + default: + printf("\n???=%0x\n", rcode ); + + } +} + + +/* Handling Name field */ +u_char *AliasHandleName ( u_char *p ) { + + u_char *s; + u_char c; + int compress; + + /* Following length field */ + if (*p & 0xc0 ) { + p = p + 2; + return ((u_char *)p); + } + while ( ( *p & 0x3f) != 0x00 ) { + s = p + 1; + if ( *p == 0x20 ) + compress = 1; + else + compress = 0; + + /* Get next length field */ + p = (u_char *)(p + (*p & 0x3f) + 1); +#ifdef DEBUG + printf(":"); +#endif + while (s < p) { + if ( compress == 1 ) { + c = (u_char )(((((*s & 0x0f) << 4) | (*(s+1) & 0x0f)) - 0x11)); +#ifdef DEBUG + if (isprint( c ) ) + printf("%c", c ); + else + printf("<0x%02x>", c ); +#endif + s +=2; + } else { +#ifdef DEBUG + printf("%c", *s); +#endif + s++; + } + } +#ifdef DEBUG + printf(":"); +#endif + fflush(stdout); + } + + /* Set up to out of Name field */ + p++; + return ((u_char *)p); +} + +/* + * NetBios Datagram Handler (IP/UDP) + */ +#define DGM_DIRECT_UNIQ 0x10 +#define DGM_DIRECT_GROUP 0x11 +#define DGM_BROADCAST 0x12 +#define DGM_ERROR 0x13 +#define DGM_QUERY 0x14 +#define DGM_POSITIVE_RES 0x15 +#define DGM_NEGATIVE_RES 0x16 + +void AliasHandleUdpNbt( + struct ip *pip, /* IP packet to examine/patch */ + struct alias_link *link, + struct in_addr *alias_address, + u_short alias_port ) +{ + struct udphdr * uh; + NbtDataHeader *ndh; + u_char *p; + + /* Calculate data length of UDP packet */ + uh = (struct udphdr *) ((char *) pip + (pip->ip_hl << 2)); + ndh = (NbtDataHeader *)((char *)uh + (sizeof (struct udphdr))); +#ifdef DEBUG + printf("\nType=%02x,", ndh->type ); +#endif + switch ( ndh->type ) { + case DGM_DIRECT_UNIQ: + case DGM_DIRECT_GROUP: + case DGM_BROADCAST: + p = (u_char *)ndh + 14; + p = AliasHandleName ( p ); /* Source Name */ + p = AliasHandleName ( p ); /* Destination Name */ + break; + case DGM_ERROR: + p = (u_char *)ndh + 11; + break; + case DGM_QUERY: + case DGM_POSITIVE_RES: + case DGM_NEGATIVE_RES: + p = (u_char *)ndh + 10; + p = AliasHandleName ( p ); /* Destination Name */ + break; + } +#ifdef DEBUG + printf("%s:%d-->", inet_ntoa(ndh->source_ip), ntohs(ndh->source_port) ); +#endif + /* Doing a IP address and Port number Translation */ + if ( uh->uh_sum != 0 ) { + int acc; + u_short *sptr; + acc = ndh->source_port; + acc -= alias_port; + sptr = (u_short *) &(ndh->source_ip); + acc += *sptr++; + acc += *sptr; + sptr = (u_short *) alias_address; + acc -= *sptr++; + acc -= *sptr; + ADJUST_CHECKSUM(acc, uh->uh_sum) + } + ndh->source_ip = *alias_address; + ndh->source_port = alias_port; +#ifdef DEBUG + printf("%s:%d\n", inet_ntoa(ndh->source_ip), ntohs(ndh->source_port) ); + fflush(stdout); +#endif +} +/* Question Section */ +#define QS_TYPE_NB 0x0020 +#define QS_TYPE_NBSTAT 0x0021 +#define QS_CLAS_IN 0x0001 +typedef struct { + u_short type; /* The type of Request */ + u_short class; /* The class of Request */ +} NBTNsQuestion; + +u_char *AliasHandleQuestion(u_short count, + NBTNsQuestion *q, + NBTArguments *nbtarg) +{ + + while ( count != 0 ) { + /* Name Filed */ + q = (NBTNsQuestion *)AliasHandleName((u_char *)q ); + + /* Type and Class filed */ + switch ( ntohs(q->type) ) { + case QS_TYPE_NB: + case QS_TYPE_NBSTAT: + q= q+1; + break; + default: + printf("\nUnknown Type on Question %0x\n", ntohs(q->type) ); + break; + } + count--; + } + + /* Set up to out of Question Section */ + return ((u_char *)q); +} + +/* Resource Record */ +#define RR_TYPE_A 0x0001 +#define RR_TYPE_NS 0x0002 +#define RR_TYPE_NULL 0x000a +#define RR_TYPE_NB 0x0020 +#define RR_TYPE_NBSTAT 0x0021 +#define RR_CLAS_IN 0x0001 +#define SizeOfNsResource 8 +typedef struct { + u_short type; + u_short class; + unsigned int ttl; + u_short rdlen; +} NBTNsResource; + +#define SizeOfNsRNB 6 +typedef struct { + u_short g:1, ont:2, resv:13; + struct in_addr addr; +} NBTNsRNB; + +u_char *AliasHandleResourceNB( NBTNsResource *q, + NBTArguments *nbtarg) +{ + NBTNsRNB *nb; + u_short bcount; + + /* Check out a length */ + bcount = ntohs(q->rdlen); + + /* Forward to Resource NB position */ + nb = (NBTNsRNB *)((u_char *)q + SizeOfNsResource); + + /* Processing all in_addr array */ +#ifdef DEBUG + printf("NB rec[%s", inet_ntoa(nbtarg->oldaddr)); + printf("->%s, %dbytes] ",inet_ntoa(nbtarg->newaddr ), bcount); +#endif + while ( bcount != 0 ) { +#ifdef DEBUG + printf("<%s>", inet_ntoa(nb->addr) ); +#endif + if (!bcmp(&nbtarg->oldaddr,&nb->addr, sizeof(struct in_addr) ) ) { + if ( *nbtarg->uh_sum != 0 ) { + int acc; + u_short *sptr; + + sptr = (u_short *) &(nb->addr); + acc = *sptr++; + acc += *sptr; + sptr = (u_short *) &(nbtarg->newaddr); + acc -= *sptr++; + acc -= *sptr; + ADJUST_CHECKSUM(acc, *nbtarg->uh_sum) + } + + nb->addr = nbtarg->newaddr; +#ifdef DEBUG + printf("O"); +#endif + } +#ifdef DEBUG + else { + printf("."); + } +#endif + nb=(NBTNsRNB *)((u_char *)nb + SizeOfNsRNB); + bcount -= SizeOfNsRNB; + } + + return ((u_char *)nb); +} + +#define SizeOfResourceA 6 +typedef struct { + struct in_addr addr; +} NBTNsResourceA; + +u_char *AliasHandleResourceA( NBTNsResource *q, + NBTArguments *nbtarg) +{ + NBTNsResourceA *a; + u_short bcount; + + /* Forward to Resource A position */ + a = (NBTNsResourceA *)( (u_char *)q + sizeof(NBTNsResource) ); + + /* Check out of length */ + bcount = ntohs(q->rdlen); + + /* Processing all in_addr array */ +#ifdef DEBUG + printf("Arec [%s", inet_ntoa(nbtarg->oldaddr)); + printf("->%s]",inet_ntoa(nbtarg->newaddr )); +#endif + while ( bcount != 0 ) { +#ifdef DEBUG + printf("..%s", inet_ntoa(a->addr) ); +#endif + if ( !bcmp(&nbtarg->oldaddr, &a->addr, sizeof(struct in_addr) ) ) { + if ( *nbtarg->uh_sum != 0 ) { + int acc; + u_short *sptr; + + sptr = (u_short *) &(a->addr); /* Old */ + acc = *sptr++; + acc += *sptr; + sptr = (u_short *) &nbtarg->newaddr; /* New */ + acc -= *sptr++; + acc -= *sptr; + ADJUST_CHECKSUM(acc, *nbtarg->uh_sum) + } + + a->addr = nbtarg->newaddr; + } + a++; /*XXXX*/ + bcount -= SizeOfResourceA; + } + return ((u_char *)a); +} + +typedef struct { + u_short opcode:4, flags:8, resv:4; +} NBTNsResourceNULL; + +u_char *AliasHandleResourceNULL( NBTNsResource *q, + NBTArguments *nbtarg) +{ + NBTNsResourceNULL *n; + u_short bcount; + + /* Forward to Resource NULL position */ + n = (NBTNsResourceNULL *)( (u_char *)q + sizeof(NBTNsResource) ); + + /* Check out of length */ + bcount = ntohs(q->rdlen); + + /* Processing all in_addr array */ + while ( bcount != 0 ) { + n++; + bcount -= sizeof(NBTNsResourceNULL); + } + + return ((u_char *)n); +} + +u_char *AliasHandleResourceNS( NBTNsResource *q, + NBTArguments *nbtarg) +{ + NBTNsResourceNULL *n; + u_short bcount; + + /* Forward to Resource NULL position */ + n = (NBTNsResourceNULL *)( (u_char *)q + sizeof(NBTNsResource) ); + + /* Check out of length */ + bcount = ntohs(q->rdlen); + + /* Resource Record Name Filed */ + q = (NBTNsResource *)AliasHandleName( (u_char *)n ); /* XXX */ + + return ((u_char *)n + bcount); +} + +typedef struct { + u_short numnames; +} NBTNsResourceNBSTAT; + +u_char *AliasHandleResourceNBSTAT( NBTNsResource *q, + NBTArguments *nbtarg) +{ + NBTNsResourceNBSTAT *n; + u_short bcount; + + /* Forward to Resource NBSTAT position */ + n = (NBTNsResourceNBSTAT *)( (u_char *)q + sizeof(NBTNsResource) ); + + /* Check out of length */ + bcount = ntohs(q->rdlen); + + return ((u_char *)n + bcount); +} + +u_char *AliasHandleResource(u_short count, + NBTNsResource *q, + NBTArguments *nbtarg) +{ + while ( count != 0 ) { + /* Resource Record Name Filed */ + q = (NBTNsResource *)AliasHandleName( (u_char *)q ); +#ifdef DEBUG + printf("type=%02x, count=%d\n", ntohs(q->type), count ); +#endif + + /* Type and Class filed */ + switch ( ntohs(q->type) ) { + case RR_TYPE_NB: + q = (NBTNsResource *)AliasHandleResourceNB( q, nbtarg ); + break; + case RR_TYPE_A: + q = (NBTNsResource *)AliasHandleResourceA( q, nbtarg ); + break; + case RR_TYPE_NS: + q = (NBTNsResource *)AliasHandleResourceNS( q, nbtarg ); + break; + case RR_TYPE_NULL: + q = (NBTNsResource *)AliasHandleResourceNULL( q, nbtarg ); + break; + case RR_TYPE_NBSTAT: + q = (NBTNsResource *)AliasHandleResourceNBSTAT( q, nbtarg ); + break; + default: printf("\nUnknown Type of Resource %0x\n", + ntohs(q->type) ); + break; + } + count--; + } + fflush(stdout); + return ((u_char *)q); +} + +void AliasHandleUdpNbtNS( + struct ip *pip, /* IP packet to examine/patch */ + struct alias_link *link, + struct in_addr *alias_address, + u_short *alias_port, + struct in_addr *original_address, + u_short *original_port ) +{ + struct udphdr * uh; + NbtNSHeader * nsh; + u_short dlen; + u_char * p; + NBTArguments nbtarg; + + /* Set up Common Parameter */ + nbtarg.oldaddr = *alias_address; + nbtarg.oldport = *alias_port; + nbtarg.newaddr = *original_address; + nbtarg.newport = *original_port; + + /* Calculate data length of UDP packet */ + uh = (struct udphdr *) ((char *) pip + (pip->ip_hl << 2)); + nbtarg.uh_sum = &(uh->uh_sum); + dlen = ntohs( uh->uh_ulen ); + nsh = (NbtNSHeader *)((char *)uh + (sizeof(struct udphdr))); + p = (u_char *)(nsh + 1); + +#ifdef DEBUG + printf(" [%s] ID=%02x, op=%01x, flag=%02x, rcode=%01x, qd=%04x, an=%04x, ns=%04x, ar=%04x, [%d]-->", + nsh->dir ? "Response": "Request", + nsh->nametrid, + nsh->opcode, + nsh->nmflags, + nsh->rcode, + ntohs(nsh->qdcount), + ntohs(nsh->ancount), + ntohs(nsh->nscount), + ntohs(nsh->arcount), + (u_char *)p -(u_char *)nsh); +#endif + + /* Question Entries */ + if (ntohs(nsh->qdcount) !=0 ) { + p = AliasHandleQuestion(ntohs(nsh->qdcount), (NBTNsQuestion *)p, &nbtarg ); + } + + /* Answer Resource Records */ + if (ntohs(nsh->ancount) !=0 ) { + p = AliasHandleResource(ntohs(nsh->ancount), (NBTNsResource *)p, &nbtarg ); + } + + /* Authority Resource Recodrs */ + if (ntohs(nsh->nscount) !=0 ) { + p = AliasHandleResource(ntohs(nsh->nscount), (NBTNsResource *)p, &nbtarg ); + } + + /* Additional Resource Recodrs */ + if (ntohs(nsh->arcount) !=0 ) { + p = AliasHandleResource(ntohs(nsh->arcount), (NBTNsResource *)p, &nbtarg ); + } + +#ifdef DEBUG + PrintRcode(nsh->rcode); +#endif + return; +} diff --git a/sys/netinet/libalias/alias_old.c b/sys/netinet/libalias/alias_old.c new file mode 100644 index 0000000..3f634d4 --- /dev/null +++ b/sys/netinet/libalias/alias_old.c @@ -0,0 +1,77 @@ +/* + This file can be considered a junk pile of old functions that + are either obsolete or have had their names changed. In the + transition from alias2.1 to alias2.2, all the function names + were rationalized so that they began with "PacketAlias..." + + These functions are included for backwards compatibility. +*/ + +#include <sys/types.h> +#include <netinet/in_systm.h> +#include <netinet/in.h> +#include <netinet/ip.h> +#include "alias.h" +#include "alias_local.h" + +void +InitPacketAlias(void) +{ + PacketAliasInit(); +} + +void +SetPacketAliasAddress(struct in_addr addr) +{ + PacketAliasSetAddress(addr); +} + +unsigned int +SetPacketAliasMode(unsigned int flags, unsigned int mask) +{ + return PacketAliasSetMode(flags, mask); +} + +int +PacketAliasPermanentLink(struct in_addr src_addr, u_short src_port, + struct in_addr dst_addr, u_short dst_port, + u_short alias_port, u_char proto) +{ + struct alias_link *link; + struct in_addr null_address; + + null_address.s_addr = 0; + link = PacketAliasRedirectPort(src_addr, src_port, + dst_addr, dst_port, + null_address, alias_port, + proto); + + if (link == NULL) + return -1; + else + return 0; +} + +int +SaveFragmentPtr(char *ptr) +{ + return PacketAliasSaveFragment(ptr); +} + +char * +GetNextFragmentPtr(char *ptr) +{ + return PacketAliasGetFragment(ptr); +} + +void +FragmentAliasIn(char *header, char *fragment) +{ + PacketAliasFragmentIn(header, fragment); +} + +u_short +InternetChecksum(u_short *ptr, int len) +{ + return PacketAliasInternetChecksum(ptr, len); +} diff --git a/sys/netinet/libalias/alias_util.c b/sys/netinet/libalias/alias_util.c new file mode 100644 index 0000000..fe07653 --- /dev/null +++ b/sys/netinet/libalias/alias_util.c @@ -0,0 +1,137 @@ +/* + Alias_util.h contains general utilities used by other functions + in the packet aliasing module. At the moment, there are functions + for computing IP header and TCP packet checksums. + + The checksum routines are based upon example code in a Unix networking + text written by Stevens (sorry, I can't remember the title -- but + at least this is a good author). + + Initial Version: August, 1996 (cjm) + + Version 1.7: January 9, 1997 + Added differential checksum update function. +*/ + +/* +Note: the checksum routines assume that the actual checksum word has +been zeroed out. If the checksum workd is filled with the proper value, +then these routines will give a result of zero (useful for testing +purposes); +*/ + +#include <sys/types.h> +#include <netinet/in_systm.h> +#include <netinet/in.h> +#include <netinet/ip.h> +#include <netinet/tcp.h> + +#include "alias.h" +#include "alias_local.h" + +u_short +PacketAliasInternetChecksum(u_short *ptr, int nbytes) +{ + int sum, oddbyte; + + sum = 0; + while (nbytes > 1) + { + sum += *ptr++; + nbytes -= 2; + } + if (nbytes == 1) + { + oddbyte = 0; + *((u_char *) &oddbyte) = *(u_char *) ptr; + sum += oddbyte; + } + sum = (sum >> 16) + (sum & 0xffff); + sum += (sum >> 16); + return(~sum); +} + +u_short +IpChecksum(struct ip *pip) +{ + return( PacketAliasInternetChecksum((u_short *) pip, + (pip->ip_hl << 2)) ); + +} + +u_short +TcpChecksum(struct ip *pip) +{ + u_short *ptr; + struct tcphdr *tc; + int nhdr, ntcp, nbytes; + int sum, oddbyte; + + nhdr = pip->ip_hl << 2; + ntcp = ntohs(pip->ip_len) - nhdr; + + tc = (struct tcphdr *) ((char *) pip + nhdr); + ptr = (u_short *) tc; + +/* Add up TCP header and data */ + nbytes = ntcp; + sum = 0; + while (nbytes > 1) + { + sum += *ptr++; + nbytes -= 2; + } + if (nbytes == 1) + { + oddbyte = 0; + *((u_char *) &oddbyte) = *(u_char *) ptr; + sum += oddbyte; + } + +/* "Pseudo-header" data */ + ptr = (u_short *) &(pip->ip_dst); + sum += *ptr++; + sum += *ptr; + ptr = (u_short *) &(pip->ip_src); + sum += *ptr++; + sum += *ptr; + sum += htons((u_short) ntcp); + sum += htons((u_short) pip->ip_p); + +/* Roll over carry bits */ + sum = (sum >> 16) + (sum & 0xffff); + sum += (sum >> 16); + +/* Return checksum */ + return((u_short) ~sum); +} + + +void +DifferentialChecksum(u_short *cksum, u_short *new, u_short *old, int n) +{ + int i; + int accumulate; + + accumulate = *cksum; + for (i=0; i<n; i++) + { + accumulate -= *new++; + accumulate += *old++; + } + + if (accumulate < 0) + { + accumulate = -accumulate; + accumulate = (accumulate >> 16) + (accumulate & 0xffff); + accumulate += accumulate >> 16; + *cksum = (u_short) ~accumulate; + } + else + { + accumulate = (accumulate >> 16) + (accumulate & 0xffff); + accumulate += accumulate >> 16; + *cksum = (u_short) accumulate; + } +} + diff --git a/sys/netinet/libalias/libalias.3 b/sys/netinet/libalias/libalias.3 new file mode 100644 index 0000000..b3fcc91 --- /dev/null +++ b/sys/netinet/libalias/libalias.3 @@ -0,0 +1,768 @@ +.Dd July, 1997 +.Dt "libalias" 3 +.Os +.Sh NAME +.Nm "libalias" +Packet Aliasing Library. A collection of +functions for aliasing and de-aliasing +of IP packets, intended for masquerading and +network address translation (NAT). + +.Sh SYNOPSIS +.Fd #include <sys/types.h> +.Fd #include <netinet/in.h> +.Fd #include <alias.h> + +Function prototypes are given in the main body +of the text. + +.Sh CONTENTS +.Bd -literal -offset left +1. Introduction +2. Initialization and Control + 2.1 PacketAliasInit() + 2.2 PacketAliasUninit() + 2.3 PacketAliasSetAddress() + 2.4 PacketAliasSetMode() + 2.5 PacketAliasSetFWBase() +3. Packet Handling + 3.1 PacketAliasOut() + 3.2 PacketAliasIn() +4. Port and Address Redirection + 4.1 PacketAliasRedirectPort() + 4.2 PacketAliasRedirectAddr() + 4.3 PacketAliasRedirectDelete() +5. Fragment Handling + 5.1 PacketAliasSaveFragment() + 5.2 PacketAliasGetFragment() + 5.3 PacketAliasFragmentIn() +6. Miscellaneous Functions + 6.1 PacketAliasSetTarget() + 6.2 PacketAliasCheckNewLink() + 6.3 PacketAliasInternetChecksum() +7. Authors +8. Acknowledgments + +Appendix A: Conceptual Background + A.1 Aliasing Links + A.2 Static and Dynamic Links + A.3 Partially Specified Links + A.4 Dynamic Link Creation +.Ed + +.Sh 1. Introduction +This library is a moderately portable +set of functions designed to assist +in the process of IP masquerading and +network address translation. Outgoing +packets from a local network with +unregistered IP addresses can be aliased +to appear as if they came from an +accessible IP address. Incoming packets +are then de-aliased so that they are sent +to the correct machine on the local network. + +A certain amount of flexibility is built +into the packet aliasing engine. In +the simplest mode of operation, a +many-to-one address mapping takes place +between local network and the packet +aliasing host. This is known as IP +masquerading. In addition, one-to-one +mappings between local and public addresses +can also be implemented, which is known as +static NAT. In between these extremes, +different groups of private addresses +can be linked to different public addresses, +comprising several distinct many-to-one +mappings. Also, a given public address +and port can be statically redirected to +a private address/port. + +The packet aliasing engine was designed +to operate in user space outside of the +kernel, without any access to private +kernel data structure, but the source code +can also be ported to a kernel environment. + +.Sh 2. Initialization and Control +Two specific functions, PacketAliasInit() +and PacketAliasSetAddress(), must always be +called before any packet handling may be +performed. In addition, the operating mode +of the packet aliasing engine can be customized +by calling PacketAliasSetMode(). +.Ss 2.1 PacketAliasInit() + +.Ft void +.Fn PacketAliasInit "void" + +This function has no argument or return +value and is used to initialize internal +data structures. The following mode bits +are always set after calling +PacketAliasInit(). See section 2.3 for +the meaning of these mode bits. +.Bd -literal -offset indent + PKT_ALIAS_USE_SAME_PORTS + PKT_ALIAS_USE_SOCKETS + PKT_ALIAS_RESET_ON_ADDR_CHANGE + +.Ed +This function will always return the packet +aliasing engine to the same initial state. +PacketAliasSetAddress() must be called afterwards, +and any desired changes from the default mode +bits listed above require a call to +PacketAliasSetMode(). + +It is mandatory that this function be called +at the beginning of a program prior to any +packet handling. +.Ss 2.2 PacketAliasUninit() + +.Ft void +.Fn PacketAliasUninit "void" + +This function has no argument or return +value and is used to clear any resources +attached to internal data structures. + +This functions should be called when a +program stop using the aliasing engine; +it do, among other things, clear out any +firewall holes. To provide backwards +compatibility and extra security, it is +added to the atexit() chain by +PacketAliasInit(). Calling it multiple +times is harmless. +.Ss 2.3 PacketAliasSetAddress() + +.Ft void +.Fn PacketAliasSetAddress "struct in_addr addr" + +This function sets the source address to which +outgoing packets from the local area network +are aliased. All outgoing packets are remapped +to this address unless overridden by a static +address mapping established by +PacketAliasRedirectAddr(). + +If the PKT_ALIAS_RESET_ON_ADDR_CHANGE mode bit +is set (the default mode of operation), then +the internal aliasing link tables will be reset +any time the aliasing address changes, as if +PacketAliasReset() were called. This is useful +for interfaces such as ppp where the IP +address may or may not change on successive +dial-up attempts. + +If the PKT_ALIAS_RESET_ON_ADDR_CHANGE mode bit +is set to zero, this function can also be used to +dynamically change the aliasing address on a +packet to packet basis (it is a low overhead +call). + +It is mandatory that this function be called +prior to any packet handling. +.Ss 2.4 PacketAliasSetMode() + +.Ft unsigned int +.Fn PacketAliasSetMode "unsigned int mode" "unsigned int mask" + +This function sets or clears mode bits +according to the value of +.Em mode . +Only bits marked in +.Em mask +are affected. The following mode bits are +defined in alias.h: +.Bl -hang -offset left +.It PKT_ALIAS_LOG. +Enables logging /var/log/alias.log. The log file +shows total numbers of links (icmp, tcp, udp) each +time an aliasing link is created or deleted. Mainly +useful for debugging when the log file is viewed +continuously with "tail -f". +.It PKT_ALIAS_DENY_INCOMING. +If this mode bit is set, all incoming packets +associated with new TCP connections or new +UDP transactions will be marked for being +ignored (PacketAliasIn() return code +PKT_ALIAS_IGNORED) by the calling program. +Response packets to connections or transactions +initiated from the packet aliasing host or +local network will be unaffected. This mode +bit is useful for implementing a one-way firewall. +.It PKT_ALIAS_SAME_PORTS. +If this mode bit is set, the packet aliasing +engine will attempt to leave the alias port +numbers unchanged from the actual local port +number. This can be done as long as the +quintuple (proto, alias addr, alias port, +remote addr, remote port) is unique. If a +conflict exists, an new aliasing port number is +chosen even if this mode bit is set. +.It PKT_ALIAS_USE_SOCKETS. +This bit should be set when the the packet +aliasing host originates network traffic as +well as forwards it. When the packet aliasing +host is waiting for a connection from an +unknown host address or unknown port number +(e.g. an FTP data connection), this mode bit +specifies that a socket be allocated as a place +holder to prevent port conflicts. Once a +connection is established, usually within a +minute or so, the socket is closed. +.It PKT_ALIAS_UNREGISTERED_ONLY. +If this mode bit is set, traffic on the +local network which does not originate from +unregistered address spaces will be ignored. +Standard Class A, B and C unregistered addresses +are: +.Bd -literal -offset indent + 10.0.0.0 -> 10.255.255.255 (Class A subnet) + 172.16.0.0 -> 172.31.255.255 (Class B subnets) + 192.168.0.0 -> 192.168.255.255 (Class C subnets) + +.Ed +This option is useful in the case that +packet aliasing host has both registered and +unregistered subnets on different interfaces. +The registered subnet is fully accessible to +the outside world, so traffic from it doesn't +need to be passed through the packet aliasing +engine. +.It PKT_ALIAS_RESET_ON_ADDR_CHANGE. +When this mode bit is set and +PacketAliasSetAddress() is called to change +the aliasing address, the internal link table +of the packet aliasing engine will be cleared. +This operating mode is useful for ppp links +where the interface address can sometimes +change or remain the same between dial-ups. +If this mode bit is not set, it the link table +will never be reset in the event of an +address change. +.It PKT_ALIAS_PUNCH_FW. +This option make libalias `punch holes' in an +ipfw based firewall for FTP/IRC DCC connections. +The holes punched are bound by from/to IP address +and port; it will not be possible to use a hole +for another connection. A hole is removed when +the connection that use it die. To cater for +unexpected death of a program using libalias (e.g +kill -9), changing the state of the flag will +clear the entire ipfw range allocated for holes. +This will also happen on the initial call to +PacketAliasSetFWBase(). This call must happen +prior to setting this flag. + +.El + +.Ss 2.5 PacketAliasSetFWBase() + +.Ft void +.Fn PacketAliasSetFWBase "unsigned int base" "unsigned int num" + +Set IPFW range allocated for punching firewall holes (with the +PKT_ALIAS_PUNCH_FW flag). The range will be cleared for all rules on +initialization. + +.Sh 3. Packet Handling +The packet handling functions are used to +modify incoming (remote->local) and outgoing +(local->remote) packets. The calling program +is responsible for receiving and sending +packets via network interfaces. + +Along with PacketAliasInit() and PacketAliasSetAddress(), +the two packet handling functions, PacketAliasIn() +and PacketAliasOut(), comprise minimal set of functions +needed for a basic IP masquerading implementation. +.Ss 3.1 PacketAliasIn() + +.Ft int +.Fn PacketAliasIn "char *buffer" "int maxpacketsize" + +An incoming packet coming from a remote machine to +the local network is de-aliased by this function. +The IP packet is pointed to by +.Em buffer , +and +.Em maxpacketsize +indicates the size of the data structure containing +the packet and should be at least as large as the +actual packet size. + +Return codes: +.Bl -hang -offset left +.It PKT_ALIAS_ERROR. +An internal error within the packet aliasing +engine occurred. +.It PKT_ALIAS_OK. +The packet aliasing process was successful. +.It PKT_ALIAS_IGNORED. +The packet was ignored and not de-aliased. +This can happen if the protocal is unrecognized, +possibly an ICMP message type is not handled or +if incoming packets for new connections are being +ignored (see PKT_ALIAS_DENY_INCOMING in section +2.2). +.It PKT_ALIAS_UNRESOLVED_FRAGMENT. +This is returned when a fragment cannot be +resolved because the header fragment has not +been sent yet. In this situation, fragments +must be saved with PacketAliasSaveFragment() +until a header fragment is found. +.It PKT_ALIAS_FOUND_HEADER_FRAGMENT. +The packet aliasing process was successful, +and a header fragment was found. This is a +signal to retrieve any unresolved fragments +with PacketAliasGetFragment() and de-alias +them with PacketAliasFragmentIn(). +.El +.Ss 3.2 PacketAliasOut() + +.Ft int +.Fn PacketAliasIn "char *buffer" "int maxpacketsize" + +An outgoing packet coming from the local network +to a remote machine is aliased by this function. +The IP packet is pointed to by +.Em buffer r, +and +.Em maxpacketsize +indicates the maximum packet size permissible +should the packet length be changed. IP encoding +protocols place address and port information in +the encapsulated data stream which have to be +modified and can account for changes in packet +length. Well known examples of such protocols +are FTP and IRC DCC. + +Return codes: +.Bl -hang -offset left +.It PKT_ALIAS_ERROR. +An internal error within the packet aliasing +engine occurred. +.It PKT_ALIAS_OK. +The packet aliasing process was successful. +.It PKT_ALIAS_IGNORED. +The packet was ignored and not de-aliased. +This can happen if the protocal is unrecognized, +or possibly an ICMP message type is not handled. +.El + +.Sh 4. Port and Address Redirection +The functions described in this section allow machines +on the local network to be accessible in some degree +to new incoming connections from the external network. +Individual ports can be re-mapped or static network +address translations can be designated. +.Ss 4.1 PacketAliasRedirectPort() + +.Ft struct alias_link * +.Fo PacketAliasRedirectPort +.Fa "struct in_addr local_addr" +.Fa "u_short local_port" +.Fa "struct in_addr remote_addr" +.Fa "u_short remote_port" +.Fa "struct in_addr alias_addr" +.Fa "u_short alias_port" +.Fa "u_char proto" +.Fc + +This function specifies that traffic from a +given remote address/port to an alias address/port +be redirected to a specified local address/port. +The parameter +.Em proto +can be either IPPROTO_TCP or IPPROTO_UDP, as +defined in <netinet/in.h>. + +If +.Em local_addr +or +.Em alias_addr +is zero, this indicates that the packet aliasing +address as established by PacketAliasSetAddress() +is to be used. Even if PacketAliasAddress() is +called to change the address after PacketAliasRedirectPort() +is called, a zero reference will track this change. + +If +.Em remote_addr +is zero, this indicates to redirect packets from +any remote address. Likewise, if +.Em remote_port +is zero, this indicates to redirect packets originating +from any remote port number. Almost always, the remote +port specification will be zero, but non-zero remote +addresses can be sometimes be useful for firewalling. +If two calls to PacketAliasRedirectPort() overlap in +their address/port specifications, then the most recent +call will have precedence. + +This function returns a pointer which can subsequently +be used by PacketAliasRedirectDelete(). If NULL is +returned, then the function call did not complete +successfully. + +All port numbers are in network address byte order, +so it is necessary to use htons() to convert these +parameters from internally readable numbers to +network byte order. Addresses are also in network +byte order, which is implicit in the use of the +.Em struct in_addr +data type. +.Ss 4.2 PacketAliasRedirectAddr() + +.Ft struct alias_link * +.Fo PacketAliasRedirectAddr +.Fa "struct in_addr local_addr" +.Fa "struct in_addr alias_addr" +.Fc + +This function desgnates that all incoming +traffic to +.Em alias_addr +be redirected to +.Em local_addr. +Similarly, all outgoing traffic from +.Em local_addr +is aliased to +.Em alias_addr . + +If +.Em local_addr +or +.Em alias_addr +is zero, this indicates that the packet aliasing +address as established by PacketAliasSetAddress() +is to be used. Even if PacketAliasAddress() is +called to change the address after PacketAliasRedirectAddr() +is called, a zero reference will track this change. + +If subsequent calls to PacketAliasRedirectAddr() +use the same aliasing address, all new incoming +traffic to this aliasing address will be redirected +to the local address made in the last function call, +but new traffic all of the local machines designated +in the several function calls will be aliased to +the same address. Consider the following example: +.Bd -literal -offset left + PacketAliasRedirectAddr(inet_aton("192.168.0.2"), + inet_aton("141.221.254.101")); + PacketAliasRedirectAddr(inet_aton("192.168.0.3"), + inet_aton("141.221.254.101")); + PacketAliasRedirectAddr(inet_aton("192.168.0.4"), + inet_aton("141.221.254.101")); +.Ed + +Any outgoing connections such as telnet or ftp +from 192.168.0.2, 102.168.0.3, 192.168.0.4 will +appear to come from 141.221.254.101. Any incoming +connections to 141.221.254.101 will be directed +to 192.168.0.4. + +Any calls to PacketAliasRedirectPort() will +have precedence over address mappings designated +by PacketAliasRedirectAddr(). + +This function returns a pointer which can subsequently +be used by PacketAliasRedirectDelete(). If NULL is +returned, then the function call did not complete +successfully. +.Ss 4.3 PacketAliasRedirectDelete() + +.Ft void +.Fn PacketAliasRedirectDelete "struct alias_link *ptr" + +This function will delete a specific static redirect +rule entered by PacketAliasRedirectPort() or +PacketAliasRedirectAddr(). The parameter +.Em ptr +is the pointer returned by either of the redirection +functions. If an invalid pointer is passed to +PacketAliasRedirectDelete(), then a program crash +or unpredictable operation could result, so it is +necessary to be careful using this function. + +.Sh 5. Fragment Handling +The functions in this section are used to deal with +incoming fragments. + +Outgoing fragments are handled within PacketAliasOut() +by changing the address according to any +applicable mapping set by PacketAliasRedirectAddress(), +or the default aliasing address set by +PacketAliasSetAddress(). + +Incoming fragments are handled in one of two ways. +If the header of a fragmented IP packet has already +been seen, then all subsequent fragments will be +re-mapped in the same manner the header fragment +was. Fragments which arrive before the header +are saved and then retrieved once the header fragment +has been resolved. +.Ss 5.1 PacketAliasSaveFragment() + +.Ft int +.Fn PacketAliasSaveFragment "char *ptr" + +When PacketAliasIn() returns +PKT_ALIAS_UNRESOLVED_FRAGMENT, this +function can be used to save the pointer to +the unresolved fragment. + +It is implicitly assumed that +.Em ptr +points to a block of memory allocated by +malloc(). If the fragment is never +resolved, the packet aliasing engine will +automatically free the memory after a +timeout period. [Eventually this function +should be modified so that a callback +function for freeing memory is passed as +an argument.] + +This function returns PKT_ALIAS_OK if it +was successful and PKT_ALIAS_ERROR if there +was an error. +.Ss 5.2 PacketAliasGetNextFragment() + +.Ft char * +.Fn PacketAliasGetFragment "char *buffer" + +This function can be used to retrieve fragment +pointers saved by PacketAliasSaveFragment(). +The IP header fragment pointed to by +Em buffer +is the header fragment indicated when +PacketAliasIn() returns PKT_ALIAS_FOUND_HEADER_FRAGMENT. +Once a a fragment pointer is retrieved, it +becomes the calling program's responsibility +to free the dynamically allocated memory for +the fragment. + +PacketAliasGetFragment() can be called +sequentially until there are no more fragments +available, at which time it returns NULL. +.Ss 5.3 PacketAliasFragmentIn() + +.Ft void +.Fn PacketAliasFragmentIn "char *header" "char *fragment" + +When a fragment is retrieved with +PacketAliasGetFragment(), it can then be +de-aliased with a call to PacketAliasFragmentIn(). +.Em header +is the pointer to a header fragment used as a +template, and +.Em fragment +is the pointer to the packet to be de-aliased. + +.Sh 6. Miscellaneous Functions + +.Ss 6.1 PacketAliasSetTarget() + +.Ft void +.Fn PacketAliasSetTarget "struct in_addr addr" + +When an incoming packet not associated with +any pre-existing aliasing link arrives at the +host machine, it will be sent to the address +indicated by a call to PacketAliasSetTarget(). + +If this function is not called, or is called +with a zero address argument, then all new +incoming packets go to the address set by +PacketAliasSetAddress. +.Ss 6.2 PacketAliasCheckNewLink() + +.Ft int +.Fn PacketAliasCheckNewLink "void" + +This function returns a non-zero value when +a new aliasing link is created. In circumstances +where incoming traffic is being sequentially +sent to different local servers, this function +can be used to trigger when PacketAliasSetTarget() +is called to change the default target address. +.Ss 6.3 PacketAliasInternetChecksum() + +.Ft u_short +.Fn PacketAliasInternetChecksum "u_short *buffer" "int nbytes" + +This is a utility function that does not seem +to be available elswhere and is included as a +convenience. It computes the internet checksum, +which is used in both IP and protocol-specific +headers (TCP, UDP, ICMP). + +.Em buffer +points to the data block to be checksummed, and +.Em nbytes +is the number of bytes. The 16-bit checksum +field should be zeroed before computing the checksum. + +Checksums can also be verified by operating on a block +of data including its checksum. If the checksum is +valid, PacketAliasInternetChecksum() will return zero. + +.Sh 7. Authors +Charles Mott (cmott@srv.net), versions 1.0 - 1.8, 2.0 - 2.4. + +Eivind Eklund (eivind@freebsd.org), versions 1.8b, 1.9 and +2.5. Added IRC DCC support as well as contributing a number of +architectural improvements; added the firewall bypass +for FTP/IRC DCC. + +.Sh 8. Acknowledgments + +Listed below, in approximate chronological +order, are individuals who have provided +valuable comments and/or debugging assistance. + +.Bl -inset -compact -offset left +.It Gary Roberts +.It Tom Torrance +.It Reto Burkhalter +.It Martin Renters +.It Brian Somers +.It Paul Traina +.It Ari Suutari +.It Dave Remien +.It J. Fortes +.It Andrzej Bialeki +.It Gordon Burditt +.El + +.Sh Appendix: Conceptual Background +This appendix is intended for those who +are planning to modify the source code or want +to create somewhat esoteric applications using +the packet aliasing functions. + +The conceptual framework under which the +packet aliasing engine operates is described here. +Central to the discussion is the idea of an +"aliasing link" which describes the relationship +for a given packet transaction between the local +machine, aliased identity and remote machine. It +is discussed how such links come into existence +and are destroyed. +.Ss A.1 Aliasing Links +There is a notion of an "aliasing link", +which is 7-tuple describing a specific +translation: +.Bd -literal -offset indent +(local addr, local port, alias addr, alias port, + remote addr, remote port, protocol) +.Ed + +Outgoing packets have the local address and +port number replaced with the alias address +and port number. Incoming packets undergo the +reverse process. The packet aliasing engine +attempts to match packets against an internal +table of aliasing links to determine how to +modify a given IP packet. Both the IP +header and protocol dependent headers are +modified as necessary. Aliasing links are +created and deleted as necessary according +to network traffic. + +Protocols can be TCP, UDP or even ICMP in +certain circumstances. (Some types of ICMP +packets can be aliased according to sequence +or id number which acts as an equivalent port +number for identifying how individual packets +should be handled.) + +Each aliasing link must have a unique +combination of the following five quantities: +alias address/port, remote address/port +and protocol. This ensures that several +machines on a local network can share the +same aliased IP address. In cases where +conflicts might arise, the aliasing port +is chosen so that uniqueness is maintained. +.Ss A.2 Static and Dynamic Links +Aliasing links can either be static or dynamic. +Static links persist indefinitely and represent +fixed rules for translating IP packets. Dynamic +links come into existence for a specific TCP +connection or UDP transaction or ICMP echo +sequence. For the case of TCP, the connection +can be monitored to see when the associated +aliasing link should be deleted. Aliasing links +for UDP transactions (and ICMP echo and timestamp +requests) work on a simple timeout rule. When +no activity is observed on a dynamic link for +a certain amount of time it is automatically +deleted. Timeout rules also apply to TCP +connections which do not open or close +properly. +.Ss A.3 Partially Specified Aliasing Links +Aliasing links can be partially specified, +meaning that the remote address and/or remote +ports are unknown. In this case, when a packet +matching the incomplete specification is found, +a fully specified dynamic link is created. If +the original partially specified link is dynamic, +it will be deleted after the fully specified link +is created, otherwise it will persist. + +For instance, a partially specified link might +be +.Bd -literal -offset indent +(192.168.0.4, 23, 204.228.203.215, 8066, 0, 0, tcp) +.Ed + +The zeros denote unspecified components for +the remote address and port. If this link were +static it would have the effect of redirecting +all incoming traffic from port 8066 of +204.228.203.215 to port 23 (telnet) of machine +192.168.0.4 on the local network. Each +individual telnet connection would initiate +the creation of a distinct dynamic link. +.Ss A.4 Dynamic Link Creation +In addition to aliasing links, there are +also address mappings that can be stored +within the internal data table of the packet +aliasing mechanism. +.Bd -literal -offset indent +(local addr, alias addr) +.Ed + +Address mappings are searched when creating +new dynamic links. + +All outgoing packets from the local network +automatically create a dynamic link if +they do not match an already existing fully +specified link. If an address mapping exists +for the the outgoing packet, this determines +the alias address to be used. If no mapping +exists, then a default address, usually the +address of the packet aliasing host, is used. +If necessary, this default address can be +changed as often as each individual packet +arrives. + +The aliasing port number is determined +such that the new dynamic link does not +conflict with any existing links. In the +default operating mode, the packet aliasing +engine attempts to set the aliasing port +equal to the local port number. If this +results in a conflict, then port numbers +are randomly chosen until a unique aliasing +link can be established. In an alternate +operating mode, the first choice of an +aliasing port is also random and unrelated +to the local port number. + |