diff options
author | brian <brian@FreeBSD.org> | 1997-05-23 04:41:31 +0000 |
---|---|---|
committer | brian <brian@FreeBSD.org> | 1997-05-23 04:41:31 +0000 |
commit | cf067e235ad379654a27faa2ed5d9368e611a7ec (patch) | |
tree | f49fa908ee07340301e185351af2d46820e99884 | |
download | FreeBSD-src-cf067e235ad379654a27faa2ed5d9368e611a7ec.zip FreeBSD-src-cf067e235ad379654a27faa2ed5d9368e611a7ec.tar.gz |
Create the alias library. This is currently only used by
ppp (or will be shortly). Natd can now be updated to use
this library rather than carrying its own version of the code.
Submitted by: Charles Mott <cmott@srv.net>
-rw-r--r-- | lib/libalias/Makefile | 11 | ||||
-rw-r--r-- | lib/libalias/alias.c | 1142 | ||||
-rw-r--r-- | lib/libalias/alias.h | 102 | ||||
-rw-r--r-- | lib/libalias/alias_db.c | 1972 | ||||
-rw-r--r-- | lib/libalias/alias_ftp.c | 220 | ||||
-rw-r--r-- | lib/libalias/alias_irc.c | 311 | ||||
-rw-r--r-- | lib/libalias/alias_local.h | 91 | ||||
-rw-r--r-- | lib/libalias/alias_util.c | 135 |
8 files changed, 3984 insertions, 0 deletions
diff --git a/lib/libalias/Makefile b/lib/libalias/Makefile new file mode 100644 index 0000000..6081e48 --- /dev/null +++ b/lib/libalias/Makefile @@ -0,0 +1,11 @@ +LIB= alias +SHLIB_MAJOR= 2 +SHLIB_MINOR= 1 +CFLAGS+=-Wall -I${.CURDIR} +SRCS= alias.c alias_db.c alias_ftp.c alias_irc.c alias_util.c + +beforeinstall: + ${INSTALL} -C -o ${BINOWN} -g ${BINGRP} -m 444 ${.CURDIR}/alias.h \ + ${DESTDIR}/usr/include + +.include <bsd.lib.mk> diff --git a/lib/libalias/alias.c b/lib/libalias/alias.c new file mode 100644 index 0000000..1a9eb36 --- /dev/null +++ b/lib/libalias/alias.c @@ -0,0 +1,1142 @@ +/* -*- 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). +*/ + +#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 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 0: + if (tc->th_flags & TH_SYN) SetStateIn(link, 1); + break; + case 1: + if (tc->th_flags & TH_FIN + || tc->th_flags & TH_RST) SetStateIn(link, 2); + } +} + +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 0: + if (tc->th_flags & TH_SYN) SetStateOut(link, 1); + break; + case 1: + if (tc->th_flags & TH_FIN + || tc->th_flags & TH_RST) SetStateOut(link, 2); + } +} + + + + + +/* 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 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 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 TCP 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 + + SaveFragmentPtr() + GetNextFragmentPtr() + FragmentAliasIn() + PacketAliasIn() + PacketAliasOut() + PacketAliasIn2() + PacketAliasOut2() + +(prototypes in alias.h) +*/ + + +int +SaveFragmentPtr(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 * +GetNextFragmentPtr(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); + SetExpire(link, 0); /* Deletes link */ + + return(fptr); + } + else + { + return(NULL); + } +} + + +void +FragmentAliasIn(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(); + pip = (struct ip *) ptr; + alias_addr = pip->ip_dst; + + 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(); + pip = (struct ip *) ptr; + + 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); +} + + +int +PacketAliasIn2(char *ptr, /* valid IP packet */ + struct in_addr addr, /* Default aliasing address */ + int maxpacketsize /* How much the packet data may grow + (FTP and IRC inline changes) */ + ) +{ + int result_code; + struct in_addr addr_save; + + ClearNewDefaultLink(); + addr_save = GetDefaultAliasAddress(); + + SetDefaultTargetAddress(addr); + result_code = PacketAliasIn(ptr, maxpacketsize); + ClearDefaultTargetAddress(); + + if (result_code == PKT_ALIAS_OK) + { + if (CheckNewDefaultLink()) + return PKT_ALIAS_NEW_LINK; + } + + return result_code; +} + + +int +PacketAliasOut2(char *ptr, /* valid IP packet */ + struct in_addr addr, /* Default aliasing address */ + int maxpacketsize /* How much the packet data may grow + (FTP and IRC inline changes) */ + ) +{ + int result_code; + struct in_addr addr_save; + + ClearNewDefaultLink(); + addr_save = GetDefaultAliasAddress(); + + SetDefaultAliasAddress(addr); + result_code = PacketAliasOut(ptr, maxpacketsize); + SetDefaultAliasAddress(addr_save); + + if (result_code == PKT_ALIAS_OK) + { + if (CheckNewDefaultLink()) + return PKT_ALIAS_NEW_LINK; + } + + return result_code; +} diff --git a/lib/libalias/alias.h b/lib/libalias/alias.h new file mode 100644 index 0000000..0063802 --- /dev/null +++ b/lib/libalias/alias.h @@ -0,0 +1,102 @@ +/*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. +*/ + + +#ifndef _ALIAS_H_ +#define _ALIAS_H_ + +/* Alias link representativei (incomplete struct) */ +struct alias_link; + +/* External interfaces (API) to packet aliasing engine */ +extern int SaveFragmentPtr(char *); +extern char *GetNextFragmentPtr(char *); +extern void FragmentAliasIn(char *, char *); +extern int PacketAliasIn(char *, int maxpacketsize); +extern int PacketAliasOut(char *, int maxpacketsize); +extern int PacketAliasIn2(char *, struct in_addr, int maxpacketsize); +extern int PacketAliasOut2(char *, struct in_addr, int maxpacketsize); +extern void SetPacketAliasAddress(struct in_addr); +extern void InitPacketAlias(void); +extern void InitPacketAliasLog(void); +extern void UninitPacketAliasLog(void); +extern unsigned int SetPacketAliasMode(unsigned int, unsigned int); +extern struct alias_link * +PacketAliasRedirectPort(struct in_addr, u_short, + struct in_addr, u_short, + struct in_addr, u_short, + u_char); +extern int +PacketAliasPermanentLink(struct in_addr, u_short, + struct in_addr, u_short, + u_short, u_char); +extern struct alias_link * +PacketAliasRedirectAddr(struct in_addr, + struct in_addr); +void PacketAliasRedirectDelete(struct alias_link *); + + +/* InternetChecksum() is not specifically part of the + packet aliasing API, but is sometimes needed outside + the module. (~for instance, natd uses it to create + an ICMP error message when interface size is + exceeded.) */ + +extern u_short InternetChecksum(u_short *, int); + + +/********************** 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 1 + +/* 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 2 + +/* If PKT_ALIAS_SAME_PORTS is set, packets will be attempted sent from + the same port as they originated on. This allow eg rsh to work + *99% of the time*, but _not_ 100%. (It will be slightly flakey + instead of not working at all.) */ +#define PKT_ALIAS_SAME_PORTS 4 + +/* 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. */ +#define PKT_ALIAS_USE_SOCKETS 8 + +/* 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 16 + + + +/* 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 +#define PKT_ALIAS_NEW_LINK 5 + +#endif +/*lint -restore */ diff --git a/lib/libalias/alias_db.c b/lib/libalias/alias_db.c new file mode 100644 index 0000000..dafd9f8 --- /dev/null +++ b/lib/libalias/alias_db.c @@ -0,0 +1,1972 @@ +/* -*- 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. Eiklund 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. +*/ + + +/* 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 TCP_EXPIRE_TIME 90 +#define FRAGMENT_ID_EXPIRE_TIME 10 +#define FRAGMENT_PTR_EXPIRE_TIME 30 + +/* 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]; +}; + +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 */ + + 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 */ + { + struct in_addr frag_addr; + char *frag_ptr; + 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 firstCall = 1; /* Needed by InitAlias() */ + +static int newDefaultLink; /* Indicates if a new aliasing */ + /* link has been created after a */ + /* call to PacketAliasIn/Out(). */ + + + + + + +/* 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); + + +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 + +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 * +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 + { + 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]; + linkTableOut[i] = NULL; + 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 != 1 + || tcp_aux->state.out != 1) + { + 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; + +/* 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--; + 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_TIME; + 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 = 0; + aux_tcp->state.out = 0; + 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; + } + 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 * +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) + { + if (replace_partial_links) + { + link = AddLink(link_unknown_dst_port->src_addr, dst_addr, + alias_addr, + link_unknown_dst_port->src_port, dst_port, + alias_port, link_type); + DeleteLink(link_unknown_dst_port); + return(link); + } + else + { + return(link_unknown_dst_port); + } + } + else if (link_unknown_dst_addr != NULL) + { + if (replace_partial_links) + { + link = AddLink(link_unknown_dst_addr->src_addr, dst_addr, + alias_addr, + link_unknown_dst_addr->src_port, dst_port, + alias_port, link_type); + DeleteLink(link_unknown_dst_addr); + return(link); + } + else + { + return(link_unknown_dst_addr); + } + } + else if (link_unknown_all != NULL) + { + if (replace_partial_links) + { + link = AddLink(link_unknown_all->src_addr, dst_addr, + alias_addr, + link_unknown_all->src_port, dst_port, + alias_port, link_type); + DeleteLink(link_unknown_all); + return(link); + } + else + { + return(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() + ClearNewLink() + CheckNewLink() +*/ + + +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 */ + (link->data.tcp)->state.in = state; +} + + +void +SetStateOut(struct alias_link *link, int state) +{ + /* TCP output state */ + (link->data.tcp)->state.out = state; +} + + +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; +} + + +void +SetDefaultTargetAddress(struct in_addr target_addr) +{ + targetAddress = target_addr; +} + + +void ClearDefaultTargetAddress(void) +{ + targetAddress.s_addr = 0; +} + + +u_short +GetOriginalPort(struct alias_link *link) +{ + return(link->src_port); +} + + +u_short +GetAliasPort(struct alias_link *link) +{ + return(link->alias_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 +ClearNewDefaultLink(void) +{ + newDefaultLink = 0; +} + + +int +CheckNewDefaultLink(void) +{ + return newDefaultLink; +} + + + +/* + 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; + } +} + + + + + +/* Outside world interfaces + +-- "outside world" means other than alias*.c routines -- + + PacketAliasRedirectPort() + PacketAliasRedirectAddr() + SetPacketAliasAddress() + InitPacketAliasLog() + UninitPacketAliasLog() + InitPacketAlias() + SetPacketAliasMode() + +(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; +} + + +/* This function is slightly less generalized than + PacketAliasRedirectPort and is included for backwards + compatibility */ +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; + + link = PacketAliasRedirectPort(src_addr, src_port, + dst_addr, dst_port, + nullAddress, alias_port, + proto); + + if (link == NULL) + return -1; + else + return 0; +} + + +/* 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 very dangerous function to put in the API, + because an invalid pointer can crash the program. */ + + deleteAllLinks = 1; + DeleteLink(link); + deleteAllLinks = 0; +} + + +void +SetPacketAliasAddress(struct in_addr addr) +{ + if (aliasAddress.s_addr != addr.s_addr) + { + CleanupAliasData(); + aliasAddress = addr; + } +} + + +/* Init the log file and enable logging */ +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. */ +void +UninitPacketAliasLog(void) +{ + if( monitorFile ) + fclose(monitorFile); + packetAliasMode &= ~PKT_ALIAS_LOG; +} + + +void +InitPacketAlias(void) +{ + int i; + struct timeval tv; + struct timezone tz; + + 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; + + firstCall = 0; + } + else + { + deleteAllLinks = 1; + CleanupAliasData(); + deleteAllLinks = 0; + } + + aliasAddress.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; + + if (packetAliasMode & PKT_ALIAS_LOG) + { + InitPacketAliasLog(); + fprintf(monitorFile, "Packet aliasing initialized.\n"); + } +} + + +/* Change mode for some operations */ +unsigned int +SetPacketAliasMode +( + 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 */ + } +/* _Disable_ logging? */ + if (~flags & mask & PKT_ALIAS_LOG) { + UninitPacketAliasLog(); + } + +/* Other flags can be set/cleared without special action */ + packetAliasMode = (flags & mask) | (packetAliasMode & ~mask); + return packetAliasMode; +} + + +/* + Clear all packet aliasing links, but leave mode + flags unchanged. Typically used when the interface + address changes and all existing links become + invalid. +*/ diff --git a/lib/libalias/alias_ftp.c b/lib/libalias/alias_ftp.c new file mode 100644 index 0000000..e03c04b --- /dev/null +++ b/lib/libalias/alias_ftp.c @@ -0,0 +1,220 @@ +/* + 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. +*/ + +/* 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; + +/* 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 = (char *) &alias_address; + 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/lib/libalias/alias_irc.c b/lib/libalias/alias_irc.c new file mode 100644 index 0000000..3657368 --- /dev/null +++ b/lib/libalias/alias_irc.c @@ -0,0 +1,311 @@ +/* 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 */ + + alias_address = GetAliasAddress(link); + iCopy += snprintf(&newpacket[iCopy], + sizeof(newpacket)-iCopy, + "%lu ", 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/lib/libalias/alias_local.h b/lib/libalias/alias_local.h new file mode 100644 index 0000000..dd1a2f4 --- /dev/null +++ b/lib/libalias/alias_local.h @@ -0,0 +1,91 @@ +/* -*- 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> +*/ + +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); +void SetDefaultTargetAddress(struct in_addr); +void ClearDefaultTargetAddress(void); +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 ClearNewDefaultLink(void); +int CheckNewDefaultLink(void); + +/* 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 ); +/*lint -restore */ diff --git a/lib/libalias/alias_util.c b/lib/libalias/alias_util.c new file mode 100644 index 0000000..e5906ca --- /dev/null +++ b/lib/libalias/alias_util.c @@ -0,0 +1,135 @@ +/* + 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_local.h" + +u_short +InternetChecksum(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( InternetChecksum((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; + } +} + |