diff options
| author | cvs2svn <cvs2svn@FreeBSD.org> | 2005-04-20 19:11:07 +0000 |
|---|---|---|
| committer | cvs2svn <cvs2svn@FreeBSD.org> | 2005-04-20 19:11:07 +0000 |
| commit | 79c906b55063e4f0f0d3281363970da4e619cf56 (patch) | |
| tree | 09cc3ca07e7d7250dd9d9d550c76eacaf462ca91 /lib/libalias | |
| parent | 7b100a2efc2b0855b788d000f7b2215fb73a5221 (diff) | |
| parent | 1e4e7f70c04a80eee7cfe0ecc6787272a43ba8bc (diff) | |
Notes
Diffstat (limited to 'lib/libalias')
| -rw-r--r-- | lib/libalias/HISTORY | 145 | ||||
| -rw-r--r-- | lib/libalias/Makefile | 14 | ||||
| -rw-r--r-- | lib/libalias/alias.c | 1433 | ||||
| -rw-r--r-- | lib/libalias/alias.h | 265 | ||||
| -rw-r--r-- | lib/libalias/alias_cuseeme.c | 123 | ||||
| -rw-r--r-- | lib/libalias/alias_db.c | 2788 | ||||
| -rw-r--r-- | lib/libalias/alias_ftp.c | 675 | ||||
| -rw-r--r-- | lib/libalias/alias_irc.c | 386 | ||||
| -rw-r--r-- | lib/libalias/alias_local.h | 355 | ||||
| -rw-r--r-- | lib/libalias/alias_nbt.c | 722 | ||||
| -rw-r--r-- | lib/libalias/alias_old.c | 205 | ||||
| -rw-r--r-- | lib/libalias/alias_pptp.c | 373 | ||||
| -rw-r--r-- | lib/libalias/alias_proxy.c | 812 | ||||
| -rw-r--r-- | lib/libalias/alias_skinny.c | 350 | ||||
| -rw-r--r-- | lib/libalias/alias_smedia.c | 445 | ||||
| -rw-r--r-- | lib/libalias/alias_util.c | 164 | ||||
| -rw-r--r-- | lib/libalias/libalias.3 | 1018 |
17 files changed, 10273 insertions, 0 deletions
diff --git a/lib/libalias/HISTORY b/lib/libalias/HISTORY new file mode 100644 index 0000000000000..c5bca59cac1f1 --- /dev/null +++ b/lib/libalias/HISTORY @@ -0,0 +1,145 @@ +$FreeBSD$ + +Version 1.0: August 11, 1996 (cjm) + +Version 1.1: August 20, 1996 (cjm) + - Host accepts incoming connections for ports 0 to 1023. + +Version 1.2: September 7, 1996 (cjm) + - Fragment handling error in alias_db.c corrected. + +Version 1.3: September 15, 1996 (cjm) + - Generalized mechanism for handling incoming + connections (no more 0 to 1023 restriction). + + - Increased ICMP support (will handle traceroute now). + + - Improved TCP close connection logic. + +Version 1.4: September 16, 1996 (cjm) + +Version 1.5: September 17, 1996 (cjm) + - Corrected error in handling incoming UDP packets + with zero checksum. + +Version 1.6: September 18, 1996 + - Simplified ICMP data storage. Will now handle + tracert from Win95 and NT as well as FreeBSD + traceroute, which uses UDP packets to non-existent + ports. + +Version 1.7: January 9, 1997 (cjm) + - Reduced malloc() activity for ICMP echo and + timestamp requests. + + - Added handling for out-of-order IP fragments. + + - Switched to differential checksum computation + for IP headers (TCP, UDP and ICMP checksums + were already differential). + + - Accepts FTP data connections from other than + port 20. This allows one ftp connections + from two hosts which are both running packet + aliasing. + + - Checksum error on FTP transfers. Problem + in code located by Martin Renters and + Brian Somers. + +Version 1.8: January 14, 1997 (cjm) + - Fixed data type error in function StartPoint() + in alias_db.c (this bug did not exist before v1.7) + Problem in code located by Ari Suutari. + +Version 1.9: February 1, 1997 (Eivind Eklund <perhaps@yes.no>) + - Added support for IRC DCC (ee) + + - Changed the aliasing routines to use ANSI style + throughout (ee) + + - Minor API changes for integration with other + programs than PPP (ee) + + - Fixed minor security hole in alias_ftp.c for + other applications of the aliasing software. + Hole could _not_ manifest in ppp+pktAlias, but + could potentially manifest in other applications + of the aliasing. (ee) + + - Connections initiated from packet aliasing + host machine will not have their port number + aliased unless it conflicts with an aliasing + port already being used. (There is an option + to disable this for debugging) (cjm) + + - Sockets will be allocated in cases where + there might be port interference with the + host machine. This can be disabled in cases + where the ppp host will be acting purely as a + masquerading router and not generate any + traffic of its own. + (cjm) + +Version 2.0: March, 1997 (cjm) + - Aliasing links are cleared only when a host interface address + changes. + + - PacketAliasPermanentLink() API added. + + - Option for only aliasing private, unregistered + IP addresses added. + + - Substantial rework to the aliasing lookup engine. + +Version 2.1: May, 1997 (cjm) + - Continuing rework to the aliasing lookup engine + to support multiple incoming addresses and static + NAT. PacketAliasRedirectPort() and + PacketAliasRedirectAddr() added to API. + + - Now supports outgoing as well as incoming ICMP + error messages. + +Version 2.2: July, 1997 (cjm) + - Rationalized API function names to all begin with + "PacketAlias..." Old function names are retained + for backwards compatibility. + + - Packet aliasing engine will now free memory of + fragments which are never resolved after a timeout + period. Once a fragment is resolved, it becomes + the users responsibility to free the memory. + +Version 2.3: August 11, 1997 (cjm) + - Problem associated with socket file descriptor + accumulation in alias_db.c corrected. The sockets + had to be closed when a binding failed. Problem + in code located by Gordon Burditt. + +Version 2.4: September 1, 1997 (cjm) + - PKT_ALIAS_UNREGISTERED_ONLY option repaired. + This part of the code was incorrectly re-implemented + in version 2.1. + +Version 2.5: December, 1997 (ee) + - Added PKT_ALIAS_PUNCH_FW mode for firewall + bypass of FTP/IRC DCC data connections. Also added + improved TCP connection monitoring. + +Version 2.6: May, 1998 (amurai) + - Added supporting routine for NetBios over TCP/IP. + +Version 3.0: January 1, 1999 + - Transparent proxying support added. + - PPTP redirecting support added based on patches + contributed by Dru Nelson <dnelson@redwoodsoft.com>. + +Version 3.1: May, 2000 (Erik Salander, erik@whistle.com) + - Added support to alias 227 replies, allows aliasing for + FTP servers in passive mode. + - Added support for PPTP aliasing. + +Version 3.2: July, 2000 (Erik Salander, erik@whistle.com and + Junichi Satoh, junichi@junichi.org) + - Added support for streaming media (RTSP and PNA) aliasing. diff --git a/lib/libalias/Makefile b/lib/libalias/Makefile new file mode 100644 index 0000000000000..dc6eb2df26179 --- /dev/null +++ b/lib/libalias/Makefile @@ -0,0 +1,14 @@ +# $FreeBSD$ + +LIB= alias +SHLIBDIR?= /lib +SHLIB_MAJOR= 4 +MAN= libalias.3 +SRCS= alias.c alias_cuseeme.c alias_db.c alias_ftp.c alias_irc.c \ + alias_nbt.c alias_pptp.c alias_proxy.c alias_skinny.c alias_smedia.c \ + alias_util.c alias_old.c +INCS= alias.h +WARNS?= 6 +NO_WERROR= + +.include <bsd.lib.mk> diff --git a/lib/libalias/alias.c b/lib/libalias/alias.c new file mode 100644 index 0000000000000..3f52e57d6b2dd --- /dev/null +++ b/lib/libalias/alias.c @@ -0,0 +1,1433 @@ +/*- + * Copyright (c) 2001 Charles Mott <cm@linktel.net> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + 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 does the same for IRC + DCC. Alias_util.c contains a few utility routines. + + Version 1.0 August, 1996 (cjm) + + Version 1.1 August 20, 1996 (cjm) + PPP host accepts incoming connections for ports 0 to 1023. + (Gary Roberts pointed out the need to handle incoming + connections.) + + Version 1.2 September 7, 1996 (cjm) + Fragment handling error in alias_db.c corrected. + (Tom Torrance helped fix this problem.) + + Version 1.4 September 16, 1996 (cjm) + - A more generalized method for handling incoming + connections, without the 0-1023 restriction, is + implemented in alias_db.c + - Improved ICMP support in alias.c. Traceroute + packet streams can now be correctly aliased. + - TCP connection closing logic simplified in + alias.c and now allows for additional 1 minute + "grace period" after FIN or RST is observed. + + Version 1.5 September 17, 1996 (cjm) + Corrected error in handling incoming UDP packets with 0 checksum. + (Tom Torrance helped fix this problem.) + + Version 1.6 September 18, 1996 (cjm) + Simplified ICMP aliasing scheme. Should now support + traceroute from Win95 as well as FreeBSD. + + Version 1.7 January 9, 1997 (cjm) + - Out-of-order fragment handling. + - IP checksum error fixed for ftp transfers + from aliasing host. + - Integer return codes added to all + aliasing/de-aliasing functions. + - Some obsolete comments cleaned up. + - Differential checksum computations for + IP header (TCP, UDP and ICMP were already + differential). + + Version 2.1 May 1997 (cjm) + - Added support for outgoing ICMP error + messages. + - Added two functions PacketAliasIn2() + and PacketAliasOut2() for dynamic address + control (e.g. round-robin allocation of + incoming packets). + + Version 2.2 July 1997 (cjm) + - Rationalized API function names to begin + with "PacketAlias..." + - Eliminated PacketAliasIn2() and + PacketAliasOut2() as poorly conceived. + + Version 2.3 Dec 1998 (dillon) + - Major bounds checking additions, see FreeBSD/CVS + + Version 3.1 May, 2000 (salander) + - Added hooks to handle PPTP. + + Version 3.2 July, 2000 (salander and satoh) + - Added PacketUnaliasOut routine. + - Added hooks to handle RTSP/RTP. + + See HISTORY file for additional revisions. +*/ + +#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 <stdio.h> + +#include "alias_local.h" +#include "alias.h" + +#define NETBIOS_NS_PORT_NUMBER 137 +#define NETBIOS_DGM_PORT_NUMBER 138 +#define FTP_CONTROL_PORT_NUMBER 21 +#define IRC_CONTROL_PORT_NUMBER_1 6667 +#define IRC_CONTROL_PORT_NUMBER_2 6668 +#define CUSEEME_PORT_NUMBER 7648 +#define RTSP_CONTROL_PORT_NUMBER_1 554 +#define RTSP_CONTROL_PORT_NUMBER_2 7070 +#define TFTP_PORT_NUMBER 69 +#define PPTP_CONTROL_PORT_NUMBER 1723 + +static __inline int +twowords(void *p) +{ + uint8_t *c = p; + +#if BYTE_ORDER == LITTLE_ENDIAN + uint16_t s1 = ((uint16_t)c[1] << 8) + (uint16_t)c[0]; + uint16_t s2 = ((uint16_t)c[3] << 8) + (uint16_t)c[2]; +#else + uint16_t s1 = ((uint16_t)c[0] << 8) + (uint16_t)c[1]; + uint16_t s2 = ((uint16_t)c[2] << 8) + (uint16_t)c[3]; +#endif + return (s1 + s2); +} + +/* TCP Handling Routines + + TcpMonitorIn() -- These routines monitor TCP connections, and + TcpMonitorOut() delete a link when a connection is closed. + +These routines look for SYN, FIN 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 *lnk) +{ + struct tcphdr *tc; + + tc = (struct tcphdr *)ip_next(pip); + + switch (GetStateIn(lnk)) { + case ALIAS_TCP_STATE_NOT_CONNECTED: + if (tc->th_flags & TH_RST) + SetStateIn(lnk, ALIAS_TCP_STATE_DISCONNECTED); + else if (tc->th_flags & TH_SYN) + SetStateIn(lnk, ALIAS_TCP_STATE_CONNECTED); + break; + case ALIAS_TCP_STATE_CONNECTED: + if (tc->th_flags & (TH_FIN | TH_RST)) + SetStateIn(lnk, ALIAS_TCP_STATE_DISCONNECTED); + break; + } +} + +static void +TcpMonitorOut(struct ip *pip, struct alias_link *lnk) +{ + struct tcphdr *tc; + + tc = (struct tcphdr *)ip_next(pip); + + switch (GetStateOut(lnk)) { + case ALIAS_TCP_STATE_NOT_CONNECTED: + if (tc->th_flags & TH_RST) + SetStateOut(lnk, ALIAS_TCP_STATE_DISCONNECTED); + else if (tc->th_flags & TH_SYN) + SetStateOut(lnk, ALIAS_TCP_STATE_CONNECTED); + break; + case ALIAS_TCP_STATE_CONNECTED: + if (tc->th_flags & (TH_FIN | TH_RST)) + SetStateOut(lnk, ALIAS_TCP_STATE_DISCONNECTED); + break; + } +} + + + + + +/* Protocol Specific Packet Aliasing Routines + + IcmpAliasIn(), IcmpAliasIn1(), IcmpAliasIn2() + IcmpAliasOut(), IcmpAliasOut1(), IcmpAliasOut2() + ProtoAliasIn(), ProtoAliasOut() + 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 in 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 libalias *, struct ip *); +static int IcmpAliasIn2(struct libalias *, struct ip *); +static int IcmpAliasIn(struct libalias *, struct ip *); + +static int IcmpAliasOut1(struct libalias *, struct ip *, int create); +static int IcmpAliasOut2(struct libalias *, struct ip *); +static int IcmpAliasOut(struct libalias *, struct ip *, int create); + +static int ProtoAliasIn(struct libalias *, struct ip *); +static int ProtoAliasOut(struct libalias *, struct ip *, int create); + +static int UdpAliasIn(struct libalias *, struct ip *); +static int UdpAliasOut(struct libalias *, struct ip *, int create); + +static int TcpAliasIn(struct libalias *, struct ip *); +static int TcpAliasOut(struct libalias *, struct ip *, int, int create); + + +static int +IcmpAliasIn1(struct libalias *la, struct ip *pip) +{ +/* + De-alias incoming echo and timestamp replies. + Alias incoming echo and timestamp requests. +*/ + struct alias_link *lnk; + struct icmp *ic; + + ic = (struct icmp *)ip_next(pip); + +/* Get source address from ICMP data field and restore original data */ + lnk = FindIcmpIn(la, pip->ip_src, pip->ip_dst, ic->icmp_id, 1); + if (lnk != NULL) { + u_short original_id; + int accumulate; + + original_id = GetOriginalPort(lnk); + +/* 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(lnk); + DifferentialChecksum(&pip->ip_sum, + &original_address, &pip->ip_dst, 2); + pip->ip_dst = original_address; + } + + return (PKT_ALIAS_OK); + } + return (PKT_ALIAS_IGNORED); +} + +static int +IcmpAliasIn2(struct libalias *la, 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 *lnk; + + ic = (struct icmp *)ip_next(pip); + ip = &ic->icmp_ip; + + ud = (struct udphdr *)ip_next(ip); + tc = (struct tcphdr *)ip_next(ip); + ic2 = (struct icmp *)ip_next(ip); + + if (ip->ip_p == IPPROTO_UDP) + lnk = FindUdpTcpIn(la, ip->ip_dst, ip->ip_src, + ud->uh_dport, ud->uh_sport, + IPPROTO_UDP, 0); + else if (ip->ip_p == IPPROTO_TCP) + lnk = FindUdpTcpIn(la, ip->ip_dst, ip->ip_src, + tc->th_dport, tc->th_sport, + IPPROTO_TCP, 0); + else if (ip->ip_p == IPPROTO_ICMP) { + if (ic2->icmp_type == ICMP_ECHO || ic2->icmp_type == ICMP_TSTAMP) + lnk = FindIcmpIn(la, ip->ip_dst, ip->ip_src, ic2->icmp_id, 0); + else + lnk = NULL; + } else + lnk = NULL; + + if (lnk != NULL) { + if (ip->ip_p == IPPROTO_UDP || ip->ip_p == IPPROTO_TCP) { + int accumulate, accumulate2; + struct in_addr original_address; + u_short original_port; + + original_address = GetOriginalAddress(lnk); + original_port = GetOriginalPort(lnk); + +/* Adjust ICMP checksum */ + accumulate = twowords(&ip->ip_src); + accumulate -= twowords(&original_address); + accumulate += ud->uh_sport; + accumulate -= original_port; + accumulate2 = accumulate; + accumulate2 += ip->ip_sum; + ADJUST_CHECKSUM(accumulate, ip->ip_sum); + accumulate2 -= ip->ip_sum; + ADJUST_CHECKSUM(accumulate2, ic->icmp_cksum); + +/* Un-alias address in IP header */ + DifferentialChecksum(&pip->ip_sum, + &original_address, &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 (ip->ip_p == IPPROTO_ICMP) { + int accumulate, accumulate2; + struct in_addr original_address; + u_short original_id; + + original_address = GetOriginalAddress(lnk); + original_id = GetOriginalPort(lnk); + +/* Adjust ICMP checksum */ + accumulate = twowords(&ip->ip_src); + accumulate -= twowords(&original_address); + accumulate += ic2->icmp_id; + accumulate -= original_id; + accumulate2 = accumulate; + accumulate2 += ip->ip_sum; + ADJUST_CHECKSUM(accumulate, ip->ip_sum); + accumulate2 -= ip->ip_sum; + ADJUST_CHECKSUM(accumulate2, ic->icmp_cksum); + +/* Un-alias address in IP header */ + DifferentialChecksum(&pip->ip_sum, + &original_address, &pip->ip_dst, 2); + pip->ip_dst = original_address; + +/* Un-alias address of original IP packet and sequence 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 +IcmpAliasIn(struct libalias *la, struct ip *pip) +{ + int iresult; + struct icmp *ic; + +/* Return if proxy-only mode is enabled */ + if (la->packetAliasMode & PKT_ALIAS_PROXY_ONLY) + return (PKT_ALIAS_OK); + + ic = (struct icmp *)ip_next(pip); + + iresult = PKT_ALIAS_IGNORED; + switch (ic->icmp_type) { + case ICMP_ECHOREPLY: + case ICMP_TSTAMPREPLY: + if (ic->icmp_code == 0) { + iresult = IcmpAliasIn1(la, pip); + } + break; + case ICMP_UNREACH: + case ICMP_SOURCEQUENCH: + case ICMP_TIMXCEED: + case ICMP_PARAMPROB: + iresult = IcmpAliasIn2(la, pip); + break; + case ICMP_ECHO: + case ICMP_TSTAMP: + iresult = IcmpAliasIn1(la, pip); + break; + } + return (iresult); +} + + +static int +IcmpAliasOut1(struct libalias *la, struct ip *pip, int create) +{ +/* + Alias outgoing echo and timestamp requests. + De-alias outgoing echo and timestamp replies. +*/ + struct alias_link *lnk; + struct icmp *ic; + + ic = (struct icmp *)ip_next(pip); + +/* Save overwritten data for when echo packet returns */ + lnk = FindIcmpOut(la, pip->ip_src, pip->ip_dst, ic->icmp_id, create); + if (lnk != NULL) { + u_short alias_id; + int accumulate; + + alias_id = GetAliasPort(lnk); + +/* 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(lnk); + DifferentialChecksum(&pip->ip_sum, + &alias_address, &pip->ip_src, 2); + pip->ip_src = alias_address; + } + + return (PKT_ALIAS_OK); + } + return (PKT_ALIAS_IGNORED); +} + + +static int +IcmpAliasOut2(struct libalias *la, struct ip *pip) +{ +/* + Alias outgoing 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 *lnk; + + ic = (struct icmp *)ip_next(pip); + ip = &ic->icmp_ip; + + ud = (struct udphdr *)ip_next(ip); + tc = (struct tcphdr *)ip_next(ip); + ic2 = (struct icmp *)ip_next(ip); + + if (ip->ip_p == IPPROTO_UDP) + lnk = FindUdpTcpOut(la, ip->ip_dst, ip->ip_src, + ud->uh_dport, ud->uh_sport, + IPPROTO_UDP, 0); + else if (ip->ip_p == IPPROTO_TCP) + lnk = FindUdpTcpOut(la, ip->ip_dst, ip->ip_src, + tc->th_dport, tc->th_sport, + IPPROTO_TCP, 0); + else if (ip->ip_p == IPPROTO_ICMP) { + if (ic2->icmp_type == ICMP_ECHO || ic2->icmp_type == ICMP_TSTAMP) + lnk = FindIcmpOut(la, ip->ip_dst, ip->ip_src, ic2->icmp_id, 0); + else + lnk = NULL; + } else + lnk = NULL; + + if (lnk != NULL) { + if (ip->ip_p == IPPROTO_UDP || ip->ip_p == IPPROTO_TCP) { + int accumulate; + struct in_addr alias_address; + u_short alias_port; + + alias_address = GetAliasAddress(lnk); + alias_port = GetAliasPort(lnk); + +/* Adjust ICMP checksum */ + accumulate = twowords(&ip->ip_dst); + accumulate -= twowords(&alias_address); + accumulate += ud->uh_dport; + accumulate -= alias_port; + ADJUST_CHECKSUM(accumulate, ic->icmp_cksum); + +/* + * Alias address in IP header if it comes from the host + * the original TCP/UDP packet was destined for. + */ + if (pip->ip_src.s_addr == ip->ip_dst.s_addr) { + DifferentialChecksum(&pip->ip_sum, + &alias_address, &pip->ip_src, 2); + pip->ip_src = alias_address; + } +/* Alias address and port number of original IP packet +fragment contained in ICMP data section */ + ip->ip_dst = alias_address; + ud->uh_dport = alias_port; + } else if (ip->ip_p == IPPROTO_ICMP) { + int accumulate; + struct in_addr alias_address; + u_short alias_id; + + alias_address = GetAliasAddress(lnk); + alias_id = GetAliasPort(lnk); + +/* Adjust ICMP checksum */ + accumulate = twowords(&ip->ip_dst); + accumulate -= twowords(&alias_address); + accumulate += ic2->icmp_id; + accumulate -= alias_id; + ADJUST_CHECKSUM(accumulate, ic->icmp_cksum); + +/* + * Alias address in IP header if it comes from the host + * the original ICMP message was destined for. + */ + if (pip->ip_src.s_addr == ip->ip_dst.s_addr) { + DifferentialChecksum(&pip->ip_sum, + &alias_address, &pip->ip_src, 2); + pip->ip_src = alias_address; + } +/* Alias address of original IP packet and sequence number of + embedded ICMP datagram */ + ip->ip_dst = alias_address; + ic2->icmp_id = alias_id; + } + return (PKT_ALIAS_OK); + } + return (PKT_ALIAS_IGNORED); +} + + +static int +IcmpAliasOut(struct libalias *la, struct ip *pip, int create) +{ + int iresult; + struct icmp *ic; + + (void)create; + +/* Return if proxy-only mode is enabled */ + if (la->packetAliasMode & PKT_ALIAS_PROXY_ONLY) + return (PKT_ALIAS_OK); + + ic = (struct icmp *)ip_next(pip); + + iresult = PKT_ALIAS_IGNORED; + switch (ic->icmp_type) { + case ICMP_ECHO: + case ICMP_TSTAMP: + if (ic->icmp_code == 0) { + iresult = IcmpAliasOut1(la, pip, create); + } + break; + case ICMP_UNREACH: + case ICMP_SOURCEQUENCH: + case ICMP_TIMXCEED: + case ICMP_PARAMPROB: + iresult = IcmpAliasOut2(la, pip); + break; + case ICMP_ECHOREPLY: + case ICMP_TSTAMPREPLY: + iresult = IcmpAliasOut1(la, pip, create); + } + return (iresult); +} + + + +static int +ProtoAliasIn(struct libalias *la, struct ip *pip) +{ +/* + Handle incoming IP packets. The + only thing which is done in this case is to alias + the dest IP address of the packet to our inside + machine. +*/ + struct alias_link *lnk; + +/* Return if proxy-only mode is enabled */ + if (la->packetAliasMode & PKT_ALIAS_PROXY_ONLY) + return (PKT_ALIAS_OK); + + lnk = FindProtoIn(la, pip->ip_src, pip->ip_dst, pip->ip_p); + if (lnk != NULL) { + struct in_addr original_address; + + original_address = GetOriginalAddress(lnk); + +/* Restore original IP address */ + DifferentialChecksum(&pip->ip_sum, + &original_address, &pip->ip_dst, 2); + pip->ip_dst = original_address; + + return (PKT_ALIAS_OK); + } + return (PKT_ALIAS_IGNORED); +} + + +static int +ProtoAliasOut(struct libalias *la, struct ip *pip, int create) +{ +/* + Handle outgoing IP packets. The + only thing which is done in this case is to alias + the source IP address of the packet. +*/ + struct alias_link *lnk; + + (void)create; + +/* Return if proxy-only mode is enabled */ + if (la->packetAliasMode & PKT_ALIAS_PROXY_ONLY) + return (PKT_ALIAS_OK); + + lnk = FindProtoOut(la, pip->ip_src, pip->ip_dst, pip->ip_p); + if (lnk != NULL) { + struct in_addr alias_address; + + alias_address = GetAliasAddress(lnk); + +/* Change source address */ + DifferentialChecksum(&pip->ip_sum, + &alias_address, &pip->ip_src, 2); + pip->ip_src = alias_address; + + return (PKT_ALIAS_OK); + } + return (PKT_ALIAS_IGNORED); +} + + +static int +UdpAliasIn(struct libalias *la, struct ip *pip) +{ + struct udphdr *ud; + struct alias_link *lnk; + +/* Return if proxy-only mode is enabled */ + if (la->packetAliasMode & PKT_ALIAS_PROXY_ONLY) + return (PKT_ALIAS_OK); + + ud = (struct udphdr *)ip_next(pip); + + lnk = FindUdpTcpIn(la, pip->ip_src, pip->ip_dst, + ud->uh_sport, ud->uh_dport, + IPPROTO_UDP, 1); + if (lnk != NULL) { + struct in_addr alias_address; + struct in_addr original_address; + u_short alias_port; + int accumulate; + int r = 0; + + alias_address = GetAliasAddress(lnk); + original_address = GetOriginalAddress(lnk); + alias_port = ud->uh_dport; + ud->uh_dport = GetOriginalPort(lnk); + +/* Special processing for IP encoding protocols */ + if (ntohs(ud->uh_dport) == CUSEEME_PORT_NUMBER) + AliasHandleCUSeeMeIn(la, pip, original_address); +/* If NETBIOS Datagram, It should be alias address in UDP Data, too */ + else if (ntohs(ud->uh_dport) == NETBIOS_DGM_PORT_NUMBER + || ntohs(ud->uh_sport) == NETBIOS_DGM_PORT_NUMBER) + r = AliasHandleUdpNbt(la, pip, lnk, &original_address, ud->uh_dport); + else if (ntohs(ud->uh_dport) == NETBIOS_NS_PORT_NUMBER + || ntohs(ud->uh_sport) == NETBIOS_NS_PORT_NUMBER) + r = AliasHandleUdpNbtNS(la, pip, lnk, &alias_address, &alias_port, + &original_address, &ud->uh_dport); + +/* If UDP checksum is not zero, then adjust since destination port */ +/* is being unaliased and destination address is being altered. */ + if (ud->uh_sum != 0) { + accumulate = alias_port; + accumulate -= ud->uh_dport; + accumulate += twowords(&alias_address); + accumulate -= twowords(&original_address); + ADJUST_CHECKSUM(accumulate, ud->uh_sum); + } +/* Restore original IP address */ + DifferentialChecksum(&pip->ip_sum, + &original_address, &pip->ip_dst, 2); + pip->ip_dst = original_address; + + /* + * If we cannot figure out the packet, ignore it. + */ + if (r < 0) + return (PKT_ALIAS_IGNORED); + else + return (PKT_ALIAS_OK); + } + return (PKT_ALIAS_IGNORED); +} + +static int +UdpAliasOut(struct libalias *la, struct ip *pip, int create) +{ + struct udphdr *ud; + struct alias_link *lnk; + +/* Return if proxy-only mode is enabled */ + if (la->packetAliasMode & PKT_ALIAS_PROXY_ONLY) + return (PKT_ALIAS_OK); + + ud = (struct udphdr *)ip_next(pip); + + lnk = FindUdpTcpOut(la, pip->ip_src, pip->ip_dst, + ud->uh_sport, ud->uh_dport, + IPPROTO_UDP, create); + if (lnk != NULL) { + u_short alias_port; + struct in_addr alias_address; + + alias_address = GetAliasAddress(lnk); + alias_port = GetAliasPort(lnk); + +/* Special processing for IP encoding protocols */ + if (ntohs(ud->uh_dport) == CUSEEME_PORT_NUMBER) + AliasHandleCUSeeMeOut(la, pip, lnk); +/* If NETBIOS Datagram, It should be alias address in UDP Data, too */ + else if (ntohs(ud->uh_dport) == NETBIOS_DGM_PORT_NUMBER + || ntohs(ud->uh_sport) == NETBIOS_DGM_PORT_NUMBER) + AliasHandleUdpNbt(la, pip, lnk, &alias_address, alias_port); + else if (ntohs(ud->uh_dport) == NETBIOS_NS_PORT_NUMBER + || ntohs(ud->uh_sport) == NETBIOS_NS_PORT_NUMBER) + AliasHandleUdpNbtNS(la, pip, lnk, &pip->ip_src, &ud->uh_sport, + &alias_address, &alias_port); +/* + * We don't know in advance what TID the TFTP server will choose, + * so we create a wilcard link (destination port is unspecified) + * that will match any TID from a given destination. + */ + else if (ntohs(ud->uh_dport) == TFTP_PORT_NUMBER) + FindRtspOut(la, pip->ip_src, pip->ip_dst, + ud->uh_sport, alias_port, IPPROTO_UDP); + +/* 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; + + accumulate = ud->uh_sport; + accumulate -= alias_port; + accumulate += twowords(&pip->ip_src); + accumulate -= twowords(&alias_address); + ADJUST_CHECKSUM(accumulate, ud->uh_sum); + } +/* Put alias port in UDP header */ + ud->uh_sport = alias_port; + +/* Change source address */ + DifferentialChecksum(&pip->ip_sum, + &alias_address, &pip->ip_src, 2); + pip->ip_src = alias_address; + + return (PKT_ALIAS_OK); + } + return (PKT_ALIAS_IGNORED); +} + + + +static int +TcpAliasIn(struct libalias *la, struct ip *pip) +{ + struct tcphdr *tc; + struct alias_link *lnk; + + tc = (struct tcphdr *)ip_next(pip); + + lnk = FindUdpTcpIn(la, pip->ip_src, pip->ip_dst, + tc->th_sport, tc->th_dport, + IPPROTO_TCP, + !(la->packetAliasMode & PKT_ALIAS_PROXY_ONLY)); + if (lnk != NULL) { + struct in_addr alias_address; + struct in_addr original_address; + struct in_addr proxy_address; + u_short alias_port; + u_short proxy_port; + int accumulate; + +/* Special processing for IP encoding protocols */ + if (ntohs(tc->th_dport) == PPTP_CONTROL_PORT_NUMBER + || ntohs(tc->th_sport) == PPTP_CONTROL_PORT_NUMBER) + AliasHandlePptpIn(la, pip, lnk); + else if (la->skinnyPort != 0 && (ntohs(tc->th_dport) == la->skinnyPort + || ntohs(tc->th_sport) == la->skinnyPort)) + AliasHandleSkinny(la, pip, lnk); + + alias_address = GetAliasAddress(lnk); + original_address = GetOriginalAddress(lnk); + proxy_address = GetProxyAddress(lnk); + alias_port = tc->th_dport; + tc->th_dport = GetOriginalPort(lnk); + proxy_port = GetProxyPort(lnk); + +/* Adjust TCP checksum since destination port is being unaliased */ +/* and destination port is being altered. */ + accumulate = alias_port; + accumulate -= tc->th_dport; + accumulate += twowords(&alias_address); + accumulate -= twowords(&original_address); + +/* If this is a proxy, then modify the TCP source port and + checksum accumulation */ + if (proxy_port != 0) { + accumulate += tc->th_sport; + tc->th_sport = proxy_port; + accumulate -= tc->th_sport; + accumulate += twowords(&pip->ip_src); + accumulate -= twowords(&proxy_address); + } +/* See if ACK number needs to be modified */ + if (GetAckModified(lnk) == 1) { + int delta; + + delta = GetDeltaAckIn(pip, lnk); + if (delta != 0) { + accumulate += twowords(&tc->th_ack); + tc->th_ack = htonl(ntohl(tc->th_ack) - delta); + accumulate -= twowords(&tc->th_ack); + } + } + ADJUST_CHECKSUM(accumulate, tc->th_sum); + +/* Restore original IP address */ + accumulate = twowords(&pip->ip_dst); + pip->ip_dst = original_address; + accumulate -= twowords(&pip->ip_dst); + +/* If this is a transparent proxy packet, then modify the source + address */ + if (proxy_address.s_addr != 0) { + accumulate += twowords(&pip->ip_src); + pip->ip_src = proxy_address; + accumulate -= twowords(&pip->ip_src); + } + ADJUST_CHECKSUM(accumulate, pip->ip_sum); + +/* Monitor TCP connection state */ + TcpMonitorIn(pip, lnk); + + return (PKT_ALIAS_OK); + } + return (PKT_ALIAS_IGNORED); +} + +static int +TcpAliasOut(struct libalias *la, struct ip *pip, int maxpacketsize, int create) +{ + int proxy_type; + u_short dest_port; + u_short proxy_server_port; + struct in_addr dest_address; + struct in_addr proxy_server_address; + struct tcphdr *tc; + struct alias_link *lnk; + + tc = (struct tcphdr *)ip_next(pip); + + proxy_type = ProxyCheck(la, pip, &proxy_server_address, &proxy_server_port); + + if (proxy_type == 0 && (la->packetAliasMode & PKT_ALIAS_PROXY_ONLY)) + return (PKT_ALIAS_OK); + +/* If this is a transparent proxy, save original destination, + then alter the destination and adjust checksums */ + dest_port = tc->th_dport; + dest_address = pip->ip_dst; + if (proxy_type != 0) { + int accumulate; + + accumulate = tc->th_dport; + tc->th_dport = proxy_server_port; + accumulate -= tc->th_dport; + accumulate += twowords(&pip->ip_dst); + accumulate -= twowords(&proxy_server_address); + ADJUST_CHECKSUM(accumulate, tc->th_sum); + + accumulate = twowords(&pip->ip_dst); + pip->ip_dst = proxy_server_address; + accumulate -= twowords(&pip->ip_dst); + ADJUST_CHECKSUM(accumulate, pip->ip_sum); + } + lnk = FindUdpTcpOut(la, pip->ip_src, pip->ip_dst, + tc->th_sport, tc->th_dport, + IPPROTO_TCP, create); + if (lnk == NULL) + return (PKT_ALIAS_IGNORED); + if (lnk != NULL) { + u_short alias_port; + struct in_addr alias_address; + int accumulate; + +/* Save original destination address, if this is a proxy packet. + Also modify packet to include destination encoding. This may + change the size of IP header. */ + if (proxy_type != 0) { + SetProxyPort(lnk, dest_port); + SetProxyAddress(lnk, dest_address); + ProxyModify(la, lnk, pip, maxpacketsize, proxy_type); + tc = (struct tcphdr *)ip_next(pip); + } +/* Get alias address and port */ + alias_port = GetAliasPort(lnk); + alias_address = GetAliasAddress(lnk); + +/* Monitor TCP connection state */ + TcpMonitorOut(pip, lnk); + +/* Special processing for IP encoding protocols */ + if (ntohs(tc->th_dport) == FTP_CONTROL_PORT_NUMBER + || ntohs(tc->th_sport) == FTP_CONTROL_PORT_NUMBER) + AliasHandleFtpOut(la, pip, lnk, maxpacketsize); + else if (ntohs(tc->th_dport) == IRC_CONTROL_PORT_NUMBER_1 + || ntohs(tc->th_dport) == IRC_CONTROL_PORT_NUMBER_2) + AliasHandleIrcOut(la, pip, lnk, maxpacketsize); + else if (ntohs(tc->th_dport) == RTSP_CONTROL_PORT_NUMBER_1 + || ntohs(tc->th_sport) == RTSP_CONTROL_PORT_NUMBER_1 + || ntohs(tc->th_dport) == RTSP_CONTROL_PORT_NUMBER_2 + || ntohs(tc->th_sport) == RTSP_CONTROL_PORT_NUMBER_2) + AliasHandleRtspOut(la, pip, lnk, maxpacketsize); + else if (ntohs(tc->th_dport) == PPTP_CONTROL_PORT_NUMBER + || ntohs(tc->th_sport) == PPTP_CONTROL_PORT_NUMBER) + AliasHandlePptpOut(la, pip, lnk); + else if (la->skinnyPort != 0 && (ntohs(tc->th_sport) == la->skinnyPort + || ntohs(tc->th_dport) == la->skinnyPort)) + AliasHandleSkinny(la, pip, lnk); + +/* Adjust TCP checksum since source port is being aliased */ +/* and source address is being altered */ + accumulate = tc->th_sport; + tc->th_sport = alias_port; + accumulate -= tc->th_sport; + accumulate += twowords(&pip->ip_src); + accumulate -= twowords(&alias_address); + +/* Modify sequence number if necessary */ + if (GetAckModified(lnk) == 1) { + int delta; + + delta = GetDeltaSeqOut(pip, lnk); + if (delta != 0) { + accumulate += twowords(&tc->th_seq); + tc->th_seq = htonl(ntohl(tc->th_seq) + delta); + accumulate -= twowords(&tc->th_seq); + } + } + ADJUST_CHECKSUM(accumulate, tc->th_sum); + +/* Change source address */ + accumulate = twowords(&pip->ip_src); + pip->ip_src = alias_address; + accumulate -= twowords(&pip->ip_src); + ADJUST_CHECKSUM(accumulate, pip->ip_sum); + + 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 libalias *, struct ip *); +static int FragmentOut(struct libalias *, struct ip *); + + +static int +FragmentIn(struct libalias *la, struct ip *pip) +{ + struct alias_link *lnk; + + lnk = FindFragmentIn2(la, pip->ip_src, pip->ip_dst, pip->ip_id); + if (lnk != NULL) { + struct in_addr original_address; + + GetFragmentAddr(lnk, &original_address); + DifferentialChecksum(&pip->ip_sum, + &original_address, &pip->ip_dst, 2); + pip->ip_dst = original_address; + + return (PKT_ALIAS_OK); + } + return (PKT_ALIAS_UNRESOLVED_FRAGMENT); +} + + +static int +FragmentOut(struct libalias *la, struct ip *pip) +{ + struct in_addr alias_address; + + alias_address = FindAliasAddress(la, pip->ip_src); + DifferentialChecksum(&pip->ip_sum, + &alias_address, &pip->ip_src, 2); + pip->ip_src = alias_address; + + return (PKT_ALIAS_OK); +} + + + + + + +/* Outside World Access + + PacketAliasSaveFragment() + PacketAliasGetFragment() + PacketAliasFragmentIn() + PacketAliasIn() + PacketAliasOut() + PacketUnaliasOut() + +(prototypes in alias.h) +*/ + + +int +LibAliasSaveFragment(struct libalias *la, char *ptr) +{ + int iresult; + struct alias_link *lnk; + struct ip *pip; + + pip = (struct ip *)ptr; + lnk = AddFragmentPtrLink(la, pip->ip_src, pip->ip_id); + iresult = PKT_ALIAS_ERROR; + if (lnk != NULL) { + SetFragmentPtr(lnk, ptr); + iresult = PKT_ALIAS_OK; + } + return (iresult); +} + + +char * +LibAliasGetFragment(struct libalias *la, char *ptr) +{ + struct alias_link *lnk; + char *fptr; + struct ip *pip; + + pip = (struct ip *)ptr; + lnk = FindFragmentPtr(la, pip->ip_src, pip->ip_id); + if (lnk != NULL) { + GetFragmentPtr(lnk, &fptr); + SetFragmentPtr(lnk, NULL); + SetExpire(lnk, 0); /* Deletes link */ + + return (fptr); + } else { + return (NULL); + } +} + + +void +LibAliasFragmentIn(struct libalias *la, 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; + + (void)la; + pip = (struct ip *)ptr; + fpip = (struct ip *)ptr_fragment; + + DifferentialChecksum(&fpip->ip_sum, + &pip->ip_dst, &fpip->ip_dst, 2); + fpip->ip_dst = pip->ip_dst; +} + + +int +LibAliasIn(struct libalias *la, char *ptr, int maxpacketsize) +{ + struct in_addr alias_addr; + struct ip *pip; + int iresult; + + if (la->packetAliasMode & PKT_ALIAS_REVERSE) { + la->packetAliasMode &= ~PKT_ALIAS_REVERSE; + iresult = PacketAliasOut(ptr, maxpacketsize); + la->packetAliasMode |= PKT_ALIAS_REVERSE; + return (iresult); + } + HouseKeeping(la); + ClearCheckNewLink(la); + pip = (struct ip *)ptr; + alias_addr = pip->ip_dst; + + /* Defense against mangled packets */ + if (ntohs(pip->ip_len) > maxpacketsize + || (pip->ip_hl << 2) > maxpacketsize) + return (PKT_ALIAS_IGNORED); + + iresult = PKT_ALIAS_IGNORED; + if ((ntohs(pip->ip_off) & IP_OFFMASK) == 0) { + switch (pip->ip_p) { + case IPPROTO_ICMP: + iresult = IcmpAliasIn(la, pip); + break; + case IPPROTO_UDP: + iresult = UdpAliasIn(la, pip); + break; + case IPPROTO_TCP: + iresult = TcpAliasIn(la, pip); + break; + case IPPROTO_GRE: + if (la->packetAliasMode & PKT_ALIAS_PROXY_ONLY || + AliasHandlePptpGreIn(la, pip) == 0) + iresult = PKT_ALIAS_OK; + else + iresult = ProtoAliasIn(la, pip); + break; + default: + iresult = ProtoAliasIn(la, pip); + break; + } + + if (ntohs(pip->ip_off) & IP_MF) { + struct alias_link *lnk; + + lnk = FindFragmentIn1(la, pip->ip_src, alias_addr, pip->ip_id); + if (lnk != NULL) { + iresult = PKT_ALIAS_FOUND_HEADER_FRAGMENT; + SetFragmentAddr(lnk, pip->ip_dst); + } else { + iresult = PKT_ALIAS_ERROR; + } + } + } else { + iresult = FragmentIn(la, 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 +LibAliasOut(struct libalias *la, char *ptr, /* valid IP packet */ + int maxpacketsize /* How much the packet data may grow (FTP + * and IRC inline changes) */ +) +{ + return (LibAliasOutTry(la, ptr, maxpacketsize, 1)); +} + +int +LibAliasOutTry(struct libalias *la, char *ptr, /* valid IP packet */ + int maxpacketsize, /* How much the packet data may grow (FTP + * and IRC inline changes) */ + int create /* Create new entries ? */ +) +{ + int iresult; + struct in_addr addr_save; + struct ip *pip; + + if (la->packetAliasMode & PKT_ALIAS_REVERSE) { + la->packetAliasMode &= ~PKT_ALIAS_REVERSE; + iresult = PacketAliasIn(ptr, maxpacketsize); + la->packetAliasMode |= PKT_ALIAS_REVERSE; + return (iresult); + } + HouseKeeping(la); + ClearCheckNewLink(la); + pip = (struct ip *)ptr; + + /* Defense against mangled packets */ + if (ntohs(pip->ip_len) > maxpacketsize + || (pip->ip_hl << 2) > maxpacketsize) + return (PKT_ALIAS_IGNORED); + + addr_save = GetDefaultAliasAddress(la); + if (la->packetAliasMode & PKT_ALIAS_UNREGISTERED_ONLY) { + u_long 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(la, pip->ip_src); + } + } else if (la->packetAliasMode & PKT_ALIAS_PROXY_ONLY) { + SetDefaultAliasAddress(la, pip->ip_src); + } + iresult = PKT_ALIAS_IGNORED; + if ((ntohs(pip->ip_off) & IP_OFFMASK) == 0) { + switch (pip->ip_p) { + case IPPROTO_ICMP: + iresult = IcmpAliasOut(la, pip, create); + break; + case IPPROTO_UDP: + iresult = UdpAliasOut(la, pip, create); + break; + case IPPROTO_TCP: + iresult = TcpAliasOut(la, pip, maxpacketsize, create); + break; + case IPPROTO_GRE: + if (AliasHandlePptpGreOut(la, pip) == 0) + iresult = PKT_ALIAS_OK; + else + iresult = ProtoAliasOut(la, pip, create); + break; + default: + iresult = ProtoAliasOut(la, pip, create); + break; + } + } else { + iresult = FragmentOut(la, pip); + } + + SetDefaultAliasAddress(la, addr_save); + return (iresult); +} + +int +LibAliasUnaliasOut(struct libalias *la, char *ptr, /* valid IP packet */ + int maxpacketsize /* for error checking */ +) +{ + struct ip *pip; + struct icmp *ic; + struct udphdr *ud; + struct tcphdr *tc; + struct alias_link *lnk; + int iresult = PKT_ALIAS_IGNORED; + + pip = (struct ip *)ptr; + + /* Defense against mangled packets */ + if (ntohs(pip->ip_len) > maxpacketsize + || (pip->ip_hl << 2) > maxpacketsize) + return (iresult); + + ud = (struct udphdr *)ip_next(pip); + tc = (struct tcphdr *)ip_next(pip); + ic = (struct icmp *)ip_next(pip); + + /* Find a link */ + if (pip->ip_p == IPPROTO_UDP) + lnk = FindUdpTcpIn(la, pip->ip_dst, pip->ip_src, + ud->uh_dport, ud->uh_sport, + IPPROTO_UDP, 0); + else if (pip->ip_p == IPPROTO_TCP) + lnk = FindUdpTcpIn(la, pip->ip_dst, pip->ip_src, + tc->th_dport, tc->th_sport, + IPPROTO_TCP, 0); + else if (pip->ip_p == IPPROTO_ICMP) + lnk = FindIcmpIn(la, pip->ip_dst, pip->ip_src, ic->icmp_id, 0); + else + lnk = NULL; + + /* Change it from an aliased packet to an unaliased packet */ + if (lnk != NULL) { + if (pip->ip_p == IPPROTO_UDP || pip->ip_p == IPPROTO_TCP) { + int accumulate; + struct in_addr original_address; + u_short original_port; + + original_address = GetOriginalAddress(lnk); + original_port = GetOriginalPort(lnk); + + /* Adjust TCP/UDP checksum */ + accumulate = twowords(&pip->ip_src); + accumulate -= twowords(&original_address); + + if (pip->ip_p == IPPROTO_UDP) { + accumulate += ud->uh_sport; + accumulate -= original_port; + ADJUST_CHECKSUM(accumulate, ud->uh_sum); + } else { + accumulate += tc->th_sport; + accumulate -= original_port; + ADJUST_CHECKSUM(accumulate, tc->th_sum); + } + + /* Adjust IP checksum */ + DifferentialChecksum(&pip->ip_sum, + &original_address, &pip->ip_src, 2); + + /* Un-alias source address and port number */ + pip->ip_src = original_address; + if (pip->ip_p == IPPROTO_UDP) + ud->uh_sport = original_port; + else + tc->th_sport = original_port; + + iresult = PKT_ALIAS_OK; + + } else if (pip->ip_p == IPPROTO_ICMP) { + + int accumulate; + struct in_addr original_address; + u_short original_id; + + original_address = GetOriginalAddress(lnk); + original_id = GetOriginalPort(lnk); + + /* Adjust ICMP checksum */ + accumulate = twowords(&pip->ip_src); + accumulate -= twowords(&original_address); + accumulate += ic->icmp_id; + accumulate -= original_id; + ADJUST_CHECKSUM(accumulate, ic->icmp_cksum); + + /* Adjust IP checksum */ + DifferentialChecksum(&pip->ip_sum, + &original_address, &pip->ip_src, 2); + + /* Un-alias source address and port number */ + pip->ip_src = original_address; + ic->icmp_id = original_id; + + iresult = PKT_ALIAS_OK; + } + } + return (iresult); + +} diff --git a/lib/libalias/alias.h b/lib/libalias/alias.h new file mode 100644 index 0000000000000..1deec9f083157 --- /dev/null +++ b/lib/libalias/alias.h @@ -0,0 +1,265 @@ +/* lint -save -library Flexelint comment for external headers */ + +/*- + * Copyright (c) 2001 Charles Mott <cm@linktel.net> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/*- + * 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_ + +/* + * The external interface to libalias, the packet aliasing engine. + * + * There are two sets of functions: + * + * PacketAlias*() the old API which doesn't take an instance pointer + * and therefore can only have one packet engine at a time. + * + * LibAlias*() the new API which takes as first argument a pointer to + * the instance of the packet aliasing engine. + * + * The functions otherwise correspond to each other one for one, except + * for the LibAliasUnaliasOut()/PacketUnaliasOut() function which were + * were misnamed in the old API. + */ + +/* + * The instance structure + */ +struct libalias; + +/* + * An anonymous structure, a pointer to which is returned from + * PacketAliasRedirectAddr(), PacketAliasRedirectPort() or + * PacketAliasRedirectProto(), passed to PacketAliasAddServer(), + * and freed by PacketAliasRedirectDelete(). + */ +struct alias_link; + + +/* OLD API */ + +/* Initialization and control functions. */ +void PacketAliasInit(void); +void PacketAliasSetAddress(struct in_addr _addr); +void PacketAliasSetFWBase(unsigned int _base, unsigned int _num); +void PacketAliasSetSkinnyPort(unsigned int _port); +unsigned int + PacketAliasSetMode(unsigned int _flags, unsigned int _mask); +void PacketAliasUninit(void); + +/* Packet Handling functions. */ +int PacketAliasIn(char *_ptr, int _maxpacketsize); +int PacketAliasOut(char *_ptr, int _maxpacketsize); +int PacketUnaliasOut(char *_ptr, int _maxpacketsize); + +/* Port and address redirection functions. */ + + +int +PacketAliasAddServer(struct alias_link *_lnk, + struct in_addr _addr, unsigned short _port); +struct alias_link * +PacketAliasRedirectAddr(struct in_addr _src_addr, + struct in_addr _alias_addr); +int PacketAliasRedirectDynamic(struct alias_link *_lnk); +void PacketAliasRedirectDelete(struct alias_link *_lnk); +struct alias_link * +PacketAliasRedirectPort(struct in_addr _src_addr, + unsigned short _src_port, struct in_addr _dst_addr, + unsigned short _dst_port, struct in_addr _alias_addr, + unsigned short _alias_port, unsigned char _proto); +struct alias_link * +PacketAliasRedirectProto(struct in_addr _src_addr, + struct in_addr _dst_addr, struct in_addr _alias_addr, + unsigned char _proto); + +/* Fragment Handling functions. */ +void PacketAliasFragmentIn(char *_ptr, char *_ptr_fragment); +char *PacketAliasGetFragment(char *_ptr); +int PacketAliasSaveFragment(char *_ptr); + +/* Miscellaneous functions. */ +int PacketAliasCheckNewLink(void); +unsigned short + PacketAliasInternetChecksum(unsigned short *_ptr, int _nbytes); +void PacketAliasSetTarget(struct in_addr _target_addr); + +/* Transparent proxying routines. */ +int PacketAliasProxyRule(const char *_cmd); + +/* NEW API */ + +/* Initialization and control functions. */ +struct libalias *LibAliasInit(struct libalias *); +void LibAliasSetAddress(struct libalias *, struct in_addr _addr); +void LibAliasSetFWBase(struct libalias *, unsigned int _base, unsigned int _num); +void LibAliasSetSkinnyPort(struct libalias *, unsigned int _port); +unsigned int + LibAliasSetMode(struct libalias *, unsigned int _flags, unsigned int _mask); +void LibAliasUninit(struct libalias *); + +/* Packet Handling functions. */ +int LibAliasIn (struct libalias *, char *_ptr, int _maxpacketsize); +int LibAliasOut(struct libalias *, char *_ptr, int _maxpacketsize); +int LibAliasOutTry(struct libalias *, char *_ptr, int _maxpacketsize, int _create); +int LibAliasUnaliasOut(struct libalias *, char *_ptr, int _maxpacketsize); + +/* Port and address redirection functions. */ + +int +LibAliasAddServer(struct libalias *, struct alias_link *_lnk, + struct in_addr _addr, unsigned short _port); +struct alias_link * +LibAliasRedirectAddr(struct libalias *, struct in_addr _src_addr, + struct in_addr _alias_addr); +int LibAliasRedirectDynamic(struct libalias *, struct alias_link *_lnk); +void LibAliasRedirectDelete(struct libalias *, struct alias_link *_lnk); +struct alias_link * +LibAliasRedirectPort(struct libalias *, struct in_addr _src_addr, + unsigned short _src_port, struct in_addr _dst_addr, + unsigned short _dst_port, struct in_addr _alias_addr, + unsigned short _alias_port, unsigned char _proto); +struct alias_link * +LibAliasRedirectProto(struct libalias *, struct in_addr _src_addr, + struct in_addr _dst_addr, struct in_addr _alias_addr, + unsigned char _proto); + +/* Fragment Handling functions. */ +void LibAliasFragmentIn(struct libalias *, char *_ptr, char *_ptr_fragment); +char *LibAliasGetFragment(struct libalias *, char *_ptr); +int LibAliasSaveFragment(struct libalias *, char *_ptr); + +/* Miscellaneous functions. */ +int LibAliasCheckNewLink(struct libalias *); +unsigned short + LibAliasInternetChecksum(struct libalias *, unsigned short *_ptr, int _nbytes); +void LibAliasSetTarget(struct libalias *, struct in_addr _target_addr); + +/* Transparent proxying routines. */ +int LibAliasProxyRule(struct libalias *, const char *_cmd); + + +/* + * Mode flags and other constants. + */ + + +/* Mode flags, set using PacketAliasSetMode() */ + +/* + * If PKT_ALIAS_LOG is set, a message will be printed to /var/log/alias.log + * every time a link is created or deleted. This is useful for debugging. + */ +#define PKT_ALIAS_LOG 0x01 + +/* + * If PKT_ALIAS_DENY_INCOMING is set, then incoming connections (e.g. to ftp, + * telnet or web servers will be prevented by the aliasing mechanism. + */ +#define PKT_ALIAS_DENY_INCOMING 0x02 + +/* + * If PKT_ALIAS_SAME_PORTS is set, packets will be attempted sent from the + * same port as they originated on. This allows e.g. rsh to work *99% of the + * time*, but _not_ 100% (it will be slightly flakey instead of not working + * at all). This mode bit is set by PacketAliasInit(), so it is a default + * mode of operation. + */ +#define PKT_ALIAS_SAME_PORTS 0x04 + +/* + * If PKT_ALIAS_USE_SOCKETS is set, then when partially specified links (e.g. + * destination port and/or address is zero), the packet aliasing engine will + * attempt to allocate a socket for the aliasing port it chooses. This will + * avoid interference with the host machine. Fully specified links do not + * require this. This bit is set after a call to PacketAliasInit(), so it is + * a default mode of operation. + */ +#define PKT_ALIAS_USE_SOCKETS 0x08 + +/*- + * If PKT_ALIAS_UNREGISTERED_ONLY is set, then only packets with + * unregistered source addresses will be aliased. Private + * addresses are those in the following ranges: + * + * 10.0.0.0 -> 10.255.255.255 + * 172.16.0.0 -> 172.31.255.255 + * 192.168.0.0 -> 192.168.255.255 + */ +#define PKT_ALIAS_UNREGISTERED_ONLY 0x10 + +/* + * If PKT_ALIAS_RESET_ON_ADDR_CHANGE is set, then the table of dynamic + * aliasing links will be reset whenever PacketAliasSetAddress() changes the + * default aliasing address. If the default aliasing address is left + * unchanged by this function call, then the table of dynamic aliasing links + * will be left intact. This bit is set after a call to PacketAliasInit(). + */ +#define PKT_ALIAS_RESET_ON_ADDR_CHANGE 0x20 + +#ifndef NO_FW_PUNCH +/* + * If PKT_ALIAS_PUNCH_FW is set, active FTP and IRC DCC connections will + * create a 'hole' in the firewall to allow the transfers to work. The + * ipfw rule number that the hole is created with is controlled by + * PacketAliasSetFWBase(). The hole will be attached to that + * particular alias_link, so when the link goes away the hole is deleted. + */ +#define PKT_ALIAS_PUNCH_FW 0x100 +#endif + +/* + * If PKT_ALIAS_PROXY_ONLY is set, then NAT will be disabled and only + * transparent proxying is performed. + */ +#define PKT_ALIAS_PROXY_ONLY 0x40 + +/* + * If PKT_ALIAS_REVERSE is set, the actions of PacketAliasIn() and + * PacketAliasOut() are reversed. + */ +#define PKT_ALIAS_REVERSE 0x80 + +/* Function return codes. */ +#define PKT_ALIAS_ERROR -1 +#define PKT_ALIAS_OK 1 +#define PKT_ALIAS_IGNORED 2 +#define PKT_ALIAS_UNRESOLVED_FRAGMENT 3 +#define PKT_ALIAS_FOUND_HEADER_FRAGMENT 4 + +#endif /* !_ALIAS_H_ */ + +/* lint -restore */ diff --git a/lib/libalias/alias_cuseeme.c b/lib/libalias/alias_cuseeme.c new file mode 100644 index 0000000000000..cbacc55cad8d2 --- /dev/null +++ b/lib/libalias/alias_cuseeme.c @@ -0,0 +1,123 @@ +/*- + * Copyright (c) 1998 Brian Somers <brian@Awfulhak.org> + * with the aid of code written by + * Junichi SATOH <junichi@astec.co.jp> 1996, 1997. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <stdio.h> +#include <sys/types.h> +#include <netinet/in_systm.h> +#include <netinet/in.h> +#include <netinet/ip.h> +#include <netinet/udp.h> + +#include "alias_local.h" + +/* CU-SeeMe Data Header */ +struct cu_header { + u_int16_t dest_family; + u_int16_t dest_port; + u_int32_t dest_addr; + int16_t family; + u_int16_t port; + u_int32_t addr; + u_int32_t seq; + u_int16_t msg; + u_int16_t data_type; + u_int16_t packet_len; +}; + +/* Open Continue Header */ +struct oc_header { + u_int16_t client_count; /* Number of client info structs */ + u_int32_t seq_no; + char user_name [20]; + char reserved [4]; /* flags, version stuff, etc */ +}; + +/* client info structures */ +struct client_info { + u_int32_t address;/* Client address */ + char reserved [8]; /* Flags, pruning bitfield, packet + * counts etc */ +}; + +void +AliasHandleCUSeeMeOut(struct libalias *la, struct ip *pip, struct alias_link *lnk) +{ + struct udphdr *ud = ip_next(pip); + + if (ntohs(ud->uh_ulen) - sizeof(struct udphdr) >= sizeof(struct cu_header)) { + struct cu_header *cu; + struct alias_link *cu_lnk; + + cu = udp_next(ud); + if (cu->addr) + cu->addr = (u_int32_t) GetAliasAddress(lnk).s_addr; + + cu_lnk = FindUdpTcpOut(la, pip->ip_src, GetDestAddress(lnk), + ud->uh_dport, 0, IPPROTO_UDP, 1); + +#ifndef NO_FW_PUNCH + if (cu_lnk) + PunchFWHole(cu_lnk); +#endif + } +} + +void +AliasHandleCUSeeMeIn(struct libalias *la, struct ip *pip, struct in_addr original_addr) +{ + struct in_addr alias_addr; + struct udphdr *ud; + struct cu_header *cu; + struct oc_header *oc; + struct client_info *ci; + char *end; + int i; + + (void)la; + alias_addr.s_addr = pip->ip_dst.s_addr; + ud = ip_next(pip); + cu = udp_next(ud); + oc = (struct oc_header *)(cu + 1); + ci = (struct client_info *)(oc + 1); + end = (char *)ud + ntohs(ud->uh_ulen); + + if ((char *)oc <= end) { + if (cu->dest_addr) + cu->dest_addr = (u_int32_t) original_addr.s_addr; + if (ntohs(cu->data_type) == 101) + /* Find and change our address */ + for (i = 0; (char *)(ci + 1) <= end && i < oc->client_count; i++, ci++) + if (ci->address == (u_int32_t) alias_addr.s_addr) { + ci->address = (u_int32_t) original_addr.s_addr; + break; + } + } +} diff --git a/lib/libalias/alias_db.c b/lib/libalias/alias_db.c new file mode 100644 index 0000000000000..f9c953b27b1f4 --- /dev/null +++ b/lib/libalias/alias_db.c @@ -0,0 +1,2788 @@ +/*- + * Copyright (c) 2001 Charles Mott <cm@linktel.net> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + 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. + + + 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 unspecified 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 are now available through + a new interface, SetPacketAliasMode(). This allows 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 permanent links allow for incoming connections to + machines on the local network. They can be given with a + user-chosen amount of specificity, with increasing specificity + meaning more security. (cjm) + + Quite a bit of rework to the basic engine. The portTable[] + array, which kept track of which ports were in use was replaced + by a table/linked list structure. (cjm) + + SetExpire() function added. (cjm) + + DeleteLink() no longer frees memory association with a pointer + to a fragment (this bug was first recognized by E. Eklund in + v1.9). + + Version 2.1: May, 1997 (cjm) + Packet aliasing engine reworked so that it can handle + multiple external addresses rather than just a single + host address. + + PacketAliasRedirectPort() and PacketAliasRedirectAddr() + added to the API. The first function is a more generalized + version of PacketAliasPermanentLink(). The second function + implements static network address translation. + + Version 3.2: July, 2000 (salander and satoh) + Added FindNewPortGroup to get contiguous range of port values. + + Added QueryUdpTcpIn and QueryUdpTcpOut to look for an aliasing + link but not actually add one. + + Added FindRtspOut, which is closely derived from FindUdpTcpOut, + except that the alias port (from FindNewPortGroup) is provided + as input. + + See HISTORY file for additional revisions. +*/ + + +/* System include files */ +#include <errno.h> +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> + +#include <sys/queue.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" + + +static LIST_HEAD(, libalias) instancehead = LIST_HEAD_INITIALIZER(instancehead); + + +/* + Constants (note: constants are also defined + near relevant functions or structs) +*/ + +/* Parameters used for cleanup of expired links */ +#define ALIAS_CLEANUP_INTERVAL_SECS 60 +#define ALIAS_CLEANUP_MAX_SPOKES 30 + +/* Timeouts (in seconds) for different link types */ +#define ICMP_EXPIRE_TIME 60 +#define UDP_EXPIRE_TIME 60 +#define PROTO_EXPIRE_TIME 60 +#define FRAGMENT_ID_EXPIRE_TIME 10 +#define FRAGMENT_PTR_EXPIRE_TIME 30 + +/* TCP link expire time for different cases */ +/* When the link has been used and closed - minimal grace time to + allow ACKs and potential re-connect in FTP (XXX - is this allowed?) */ +#ifndef TCP_EXPIRE_DEAD +#define TCP_EXPIRE_DEAD 10 +#endif + +/* When the link has been used and closed on one side - the other side + is allowed to still send data */ +#ifndef TCP_EXPIRE_SINGLEDEAD +#define TCP_EXPIRE_SINGLEDEAD 90 +#endif + +/* When the link isn't yet up */ +#ifndef TCP_EXPIRE_INITIAL +#define TCP_EXPIRE_INITIAL 300 +#endif + +/* When the link is up */ +#ifndef TCP_EXPIRE_CONNECTED +#define TCP_EXPIRE_CONNECTED 86400 +#endif + + +/* Dummy port number codes used for FindLinkIn/Out() and AddLink(). + These constants can be anything except zero, which indicates an + unknown port number. */ + +#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 + fragments 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 address, destination address, source port, destination + port and link type. +*/ + +struct ack_data_record { /* used to save changes to ACK/sequence + * 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 + * sequence numbers */ + /* been modified */ +}; + +#define N_LINK_TCP_DATA 3 /* Number of distinct ACK number changes + * saved for a modified TCP stream */ +struct tcp_dat { + struct tcp_state state; + struct ack_data_record ack[N_LINK_TCP_DATA]; + int fwhole; /* Which firewall record is used for this + * hole? */ +}; + +struct server { /* LSNAT server pool (circular list) */ + struct in_addr addr; + u_short port; + struct server *next; +}; + +struct alias_link { /* Main data structure */ + struct libalias *la; + struct in_addr src_addr; /* Address and port information */ + struct in_addr dst_addr; + struct in_addr alias_addr; + struct in_addr proxy_addr; + u_short src_port; + u_short dst_port; + u_short alias_port; + u_short proxy_port; + struct server *server; + + int link_type; /* Type of link: TCP, UDP, ICMP, + * proto, frag */ + +/* values for link_type */ +#define LINK_ICMP IPPROTO_ICMP +#define LINK_UDP IPPROTO_UDP +#define LINK_TCP IPPROTO_TCP +#define LINK_FRAGMENT_ID (IPPROTO_MAX + 1) +#define LINK_FRAGMENT_PTR (IPPROTO_MAX + 2) +#define LINK_ADDR (IPPROTO_MAX + 3) +#define LINK_PPTP (IPPROTO_MAX + 4) + + int flags; /* indicates special characteristics */ + int pflags; /* protocol-specific flags */ + +/* flag bits */ +#define LINK_UNKNOWN_DEST_PORT 0x01 +#define LINK_UNKNOWN_DEST_ADDR 0x02 +#define LINK_PERMANENT 0x04 +#define LINK_PARTIALLY_SPECIFIED 0x03 /* logical-or of first two bits */ +#define LINK_UNFIREWALLED 0x08 + + int timestamp; /* Time link was last accessed */ + int expire_time; /* Expire time for link */ + + int sockfd; /* socket descriptor */ + + LIST_ENTRY (alias_link) list_out; /* Linked list of + * pointers for */ + LIST_ENTRY (alias_link) list_in; /* input and output + * lookup tables */ + + union { /* Auxiliary data */ + char *frag_ptr; + struct in_addr frag_addr; + struct tcp_dat *tcp; + } data; +}; + +/* Internal utility routines (used only in alias_db.c) + +Lookup table starting points: + StartPointIn() -- link table initial search point for + incoming packets + StartPointOut() -- link table initial search point for + outgoing 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(struct libalias *); + +#ifndef NO_FW_PUNCH +/* Firewall control */ +static void InitPunchFW(struct libalias *); +static void UninitPunchFW(struct libalias *); +static void ClearFWHole(struct alias_link *); + +#endif + +/* Log file control */ +static void InitPacketAliasLog(struct libalias *); +static void UninitPacketAliasLog(struct libalias *); + +static u_int +StartPointIn(struct in_addr alias_addr, + u_short alias_port, + int link_type) +{ + u_int n; + + n = alias_addr.s_addr; + if (link_type != LINK_PPTP) + 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; + if (link_type != LINK_PPTP) { + 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(struct libalias *la) +{ +/* Used for debugging */ + + if (la->monitorFile) { + fprintf(la->monitorFile, + "icmp=%d, udp=%d, tcp=%d, pptp=%d, proto=%d, frag_id=%d frag_ptr=%d", + la->icmpLinkCount, + la->udpLinkCount, + la->tcpLinkCount, + la->pptpLinkCount, + la->protoLinkCount, + la->fragmentIdLinkCount, + la->fragmentPtrLinkCount); + + fprintf(la->monitorFile, " / tot=%d (sock=%d)\n", + la->icmpLinkCount + la->udpLinkCount + + la->tcpLinkCount + + la->pptpLinkCount + + la->protoLinkCount + + la->fragmentIdLinkCount + + la->fragmentPtrLinkCount, + la->sockCount); + + fflush(la->monitorFile); + } +} + + + + + +/* Internal routines for finding, deleting and adding links + +Port Allocation: + GetNewPort() -- find and reserve new alias port number + GetSocket() -- try to allocate a socket for a given port + +Link creation and deletion: + CleanupAliasData() - remove all link chains from lookup table + IncrementalCleanup() - look for stale links in a single chain + DeleteLink() - remove link + AddLink() - add link + ReLink() - change link + +Link search: + FindLinkOut() - find link for outgoing packets + FindLinkIn() - find link for incoming packets + +Port search: + FindNewPortGroup() - find an available group of ports +*/ + +/* Local prototypes */ +static int GetNewPort(struct libalias *, struct alias_link *, int); + +static u_short GetSocket(struct libalias *, u_short, int *, int); + +static void CleanupAliasData(struct libalias *); + +static void IncrementalCleanup(struct libalias *); + +static void DeleteLink(struct alias_link *); + +static struct alias_link * +AddLink(struct libalias *, struct in_addr, struct in_addr, struct in_addr, + u_short, u_short, int, int); + +static struct alias_link * +ReLink(struct alias_link *, + struct in_addr, struct in_addr, struct in_addr, + u_short, u_short, int, int); + +static struct alias_link * + FindLinkOut (struct libalias *, struct in_addr, struct in_addr, u_short, u_short, int, int); + +static struct alias_link * + FindLinkIn (struct libalias *, struct in_addr, struct in_addr, u_short, u_short, int, int); + + +#define ALIAS_PORT_BASE 0x08000 +#define ALIAS_PORT_MASK 0x07fff +#define ALIAS_PORT_MASK_EVEN 0x07ffe +#define GET_NEW_PORT_MAX_ATTEMPTS 20 + +#define GET_ALIAS_PORT -1 +#define GET_ALIAS_ID GET_ALIAS_PORT + +#define FIND_EVEN_ALIAS_BASE 1 + +/* 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 libalias *la, struct alias_link *lnk, 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. + + When this parameter is GET_ALIAS_PORT, 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 (la->packetAliasMode & PKT_ALIAS_SAME_PORTS) { + /* + * When the PKT_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 = lnk->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) { + lnk->alias_port = (u_short) alias_port_param; + return (0); + } else { +#ifdef DEBUG + fprintf(stderr, "PacketAlias/GetNewPort(): "); + fprintf(stderr, "input parameter error\n"); +#endif + return (-1); + } + + +/* Port number search */ + for (i = 0; i < max_trials; i++) { + int go_ahead; + struct alias_link *search_result; + + search_result = FindLinkIn(la, lnk->dst_addr, lnk->alias_addr, + lnk->dst_port, port_net, + lnk->link_type, 0); + + if (search_result == NULL) + go_ahead = 1; + else if (!(lnk->flags & LINK_PARTIALLY_SPECIFIED) + && (search_result->flags & LINK_PARTIALLY_SPECIFIED)) + go_ahead = 1; + else + go_ahead = 0; + + if (go_ahead) { + if ((la->packetAliasMode & PKT_ALIAS_USE_SOCKETS) + && (lnk->flags & LINK_PARTIALLY_SPECIFIED) + && ((lnk->link_type == LINK_TCP) || + (lnk->link_type == LINK_UDP))) { + if (GetSocket(la, port_net, &lnk->sockfd, lnk->link_type)) { + lnk->alias_port = port_net; + return (0); + } + } else { + lnk->alias_port = port_net; + return (0); + } + } + port_sys = random() & ALIAS_PORT_MASK; + port_sys += ALIAS_PORT_BASE; + port_net = htons(port_sys); + } + +#ifdef DEBUG + fprintf(stderr, "PacketAlias/GetnewPort(): "); + fprintf(stderr, "could not find free port\n"); +#endif + + return (-1); +} + + +static u_short +GetSocket(struct libalias *la, 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 { +#ifdef DEBUG + fprintf(stderr, "PacketAlias/GetSocket(): "); + fprintf(stderr, "incorrect link type\n"); +#endif + return (0); + } + + if (sock < 0) { +#ifdef DEBUG + fprintf(stderr, "PacketAlias/GetSocket(): "); + fprintf(stderr, "socket() error %d\n", *sockfd); +#endif + 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) { + la->sockCount++; + *sockfd = sock; + return (1); + } else { + close(sock); + return (0); + } +} + + +/* FindNewPortGroup() returns a base port number for an available + range of contiguous 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 FindNewPortGroup() + looks for unused triplets: (dest addr, dest port, alias port). */ + +int +FindNewPortGroup(struct libalias *la, + struct in_addr dst_addr, + struct in_addr alias_addr, + u_short src_port, + u_short dst_port, + u_short port_count, + u_char proto, + u_char align) +{ + int i, j; + int max_trials; + u_short port_sys; + int link_type; + + /* + * Get link_type from protocol + */ + + switch (proto) { + case IPPROTO_UDP: + link_type = LINK_UDP; + break; + case IPPROTO_TCP: + link_type = LINK_TCP; + break; + default: + return (0); + break; + } + + /* + * The aliasing port is automatically selected by one of two + * methods below: + */ + max_trials = GET_NEW_PORT_MAX_ATTEMPTS; + + if (la->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_sys = ntohs(src_port); + + } else { + + /* First trial and all subsequent are random. */ + if (align == FIND_EVEN_ALIAS_BASE) + port_sys = random() & ALIAS_PORT_MASK_EVEN; + else + port_sys = random() & ALIAS_PORT_MASK; + + port_sys += ALIAS_PORT_BASE; + } + +/* Port number search */ + for (i = 0; i < max_trials; i++) { + + struct alias_link *search_result; + + for (j = 0; j < port_count; j++) + if (0 != (search_result = FindLinkIn(la, dst_addr, alias_addr, + dst_port, htons(port_sys + j), + link_type, 0))) + break; + + /* Found a good range, return base */ + if (j == port_count) + return (htons(port_sys)); + + /* Find a new base to try */ + if (align == FIND_EVEN_ALIAS_BASE) + port_sys = random() & ALIAS_PORT_MASK_EVEN; + else + port_sys = random() & ALIAS_PORT_MASK; + + port_sys += ALIAS_PORT_BASE; + } + +#ifdef DEBUG + fprintf(stderr, "PacketAlias/FindNewPortGroup(): "); + fprintf(stderr, "could not find free port(s)\n"); +#endif + + return (0); +} + +static void +CleanupAliasData(struct libalias *la) +{ + struct alias_link *lnk; + int i, icount; + + icount = 0; + for (i = 0; i < LINK_TABLE_OUT_SIZE; i++) { + lnk = LIST_FIRST(&la->linkTableOut[i]); + while (lnk != NULL) { + struct alias_link *link_next; + + link_next = LIST_NEXT(lnk, list_out); + icount++; + DeleteLink(lnk); + lnk = link_next; + } + } + + la->cleanupIndex = 0; +} + + +static void +IncrementalCleanup(struct libalias *la) +{ + int icount; + struct alias_link *lnk; + + icount = 0; + lnk = LIST_FIRST(&la->linkTableOut[la->cleanupIndex++]); + while (lnk != NULL) { + int idelta; + struct alias_link *link_next; + + link_next = LIST_NEXT(lnk, list_out); + idelta = la->timeStamp - lnk->timestamp; + switch (lnk->link_type) { + case LINK_TCP: + if (idelta > lnk->expire_time) { + struct tcp_dat *tcp_aux; + + tcp_aux = lnk->data.tcp; + if (tcp_aux->state.in != ALIAS_TCP_STATE_CONNECTED + || tcp_aux->state.out != ALIAS_TCP_STATE_CONNECTED) { + DeleteLink(lnk); + icount++; + } + } + break; + default: + if (idelta > lnk->expire_time) { + DeleteLink(lnk); + icount++; + } + break; + } + lnk = link_next; + } + + if (la->cleanupIndex == LINK_TABLE_OUT_SIZE) + la->cleanupIndex = 0; +} + +static void +DeleteLink(struct alias_link *lnk) +{ + struct libalias *la = lnk->la; + +/* Don't do anything if the link is marked permanent */ + if (la->deleteAllLinks == 0 && lnk->flags & LINK_PERMANENT) + return; + +#ifndef NO_FW_PUNCH +/* Delete associated firewall hole, if any */ + ClearFWHole(lnk); +#endif + +/* Free memory allocated for LSNAT server pool */ + if (lnk->server != NULL) { + struct server *head, *curr, *next; + + head = curr = lnk->server; + do { + next = curr->next; + free(curr); + } while ((curr = next) != head); + } +/* Adjust output table pointers */ + LIST_REMOVE(lnk, list_out); + +/* Adjust input table pointers */ + LIST_REMOVE(lnk, list_in); + +/* Close socket, if one has been allocated */ + if (lnk->sockfd != -1) { + la->sockCount--; + close(lnk->sockfd); + } +/* Link-type dependent cleanup */ + switch (lnk->link_type) { + case LINK_ICMP: + la->icmpLinkCount--; + break; + case LINK_UDP: + la->udpLinkCount--; + break; + case LINK_TCP: + la->tcpLinkCount--; + free(lnk->data.tcp); + break; + case LINK_PPTP: + la->pptpLinkCount--; + break; + case LINK_FRAGMENT_ID: + la->fragmentIdLinkCount--; + break; + case LINK_FRAGMENT_PTR: + la->fragmentPtrLinkCount--; + if (lnk->data.frag_ptr != NULL) + free(lnk->data.frag_ptr); + break; + case LINK_ADDR: + break; + default: + la->protoLinkCount--; + break; + } + +/* Free memory */ + free(lnk); + +/* Write statistics, if logging enabled */ + if (la->packetAliasMode & PKT_ALIAS_LOG) { + ShowAliasStats(la); + } +} + + +static struct alias_link * +AddLink(struct libalias *la, 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 *lnk; + + lnk = malloc(sizeof(struct alias_link)); + if (lnk != NULL) { + /* Basic initialization */ + lnk->la = la; + lnk->src_addr = src_addr; + lnk->dst_addr = dst_addr; + lnk->alias_addr = alias_addr; + lnk->proxy_addr.s_addr = INADDR_ANY; + lnk->src_port = src_port; + lnk->dst_port = dst_port; + lnk->proxy_port = 0; + lnk->server = NULL; + lnk->link_type = link_type; + lnk->sockfd = -1; + lnk->flags = 0; + lnk->pflags = 0; + lnk->timestamp = la->timeStamp; + + /* Expiration time */ + switch (link_type) { + case LINK_ICMP: + lnk->expire_time = ICMP_EXPIRE_TIME; + break; + case LINK_UDP: + lnk->expire_time = UDP_EXPIRE_TIME; + break; + case LINK_TCP: + lnk->expire_time = TCP_EXPIRE_INITIAL; + break; + case LINK_PPTP: + lnk->flags |= LINK_PERMANENT; /* no timeout. */ + break; + case LINK_FRAGMENT_ID: + lnk->expire_time = FRAGMENT_ID_EXPIRE_TIME; + break; + case LINK_FRAGMENT_PTR: + lnk->expire_time = FRAGMENT_PTR_EXPIRE_TIME; + break; + case LINK_ADDR: + break; + default: + lnk->expire_time = PROTO_EXPIRE_TIME; + break; + } + + /* Determine alias flags */ + if (dst_addr.s_addr == INADDR_ANY) + lnk->flags |= LINK_UNKNOWN_DEST_ADDR; + if (dst_port == 0) + lnk->flags |= LINK_UNKNOWN_DEST_PORT; + + /* Determine alias port */ + if (GetNewPort(la, lnk, alias_port_param) != 0) { + free(lnk); + return (NULL); + } + /* Link-type dependent initialization */ + switch (link_type) { + struct tcp_dat *aux_tcp; + + case LINK_ICMP: + la->icmpLinkCount++; + break; + case LINK_UDP: + la->udpLinkCount++; + break; + case LINK_TCP: + aux_tcp = malloc(sizeof(struct tcp_dat)); + if (aux_tcp != NULL) { + int i; + + la->tcpLinkCount++; + aux_tcp->state.in = ALIAS_TCP_STATE_NOT_CONNECTED; + aux_tcp->state.out = ALIAS_TCP_STATE_NOT_CONNECTED; + aux_tcp->state.index = 0; + aux_tcp->state.ack_modified = 0; + for (i = 0; i < N_LINK_TCP_DATA; i++) + aux_tcp->ack[i].active = 0; + aux_tcp->fwhole = -1; + lnk->data.tcp = aux_tcp; + } else { +#ifdef DEBUG + fprintf(stderr, "PacketAlias/AddLink: "); + fprintf(stderr, " cannot allocate auxiliary TCP data\n"); +#endif + free(lnk); + return (NULL); + } + break; + case LINK_PPTP: + la->pptpLinkCount++; + break; + case LINK_FRAGMENT_ID: + la->fragmentIdLinkCount++; + break; + case LINK_FRAGMENT_PTR: + la->fragmentPtrLinkCount++; + break; + case LINK_ADDR: + break; + default: + la->protoLinkCount++; + break; + } + + /* Set up pointers for output lookup table */ + start_point = StartPointOut(src_addr, dst_addr, + src_port, dst_port, link_type); + LIST_INSERT_HEAD(&la->linkTableOut[start_point], lnk, list_out); + + /* Set up pointers for input lookup table */ + start_point = StartPointIn(alias_addr, lnk->alias_port, link_type); + LIST_INSERT_HEAD(&la->linkTableIn[start_point], lnk, list_in); + } else { +#ifdef DEBUG + fprintf(stderr, "PacketAlias/AddLink(): "); + fprintf(stderr, "malloc() call failed.\n"); +#endif + } + + if (la->packetAliasMode & PKT_ALIAS_LOG) { + ShowAliasStats(la); + } + return (lnk); +} + +static struct alias_link * +ReLink(struct alias_link *old_lnk, + struct in_addr src_addr, + struct in_addr dst_addr, + struct in_addr alias_addr, + u_short src_port, + u_short dst_port, + int alias_port_param, /* if less than zero, alias */ + int link_type) +{ /* port will be automatically *//* chosen. + * If greater than */ + struct alias_link *new_lnk; /* zero, equal to alias port */ + struct libalias *la = old_lnk->la; + + new_lnk = AddLink(la, src_addr, dst_addr, alias_addr, + src_port, dst_port, alias_port_param, + link_type); +#ifndef NO_FW_PUNCH + if (new_lnk != NULL && + old_lnk->link_type == LINK_TCP && + old_lnk->data.tcp->fwhole > 0) { + PunchFWHole(new_lnk); + } +#endif + DeleteLink(old_lnk); + return (new_lnk); +} + +static struct alias_link * +_FindLinkOut(struct libalias *la, struct in_addr src_addr, + struct in_addr dst_addr, + u_short src_port, + u_short dst_port, + int link_type, + int replace_partial_links) +{ + u_int i; + struct alias_link *lnk; + + i = StartPointOut(src_addr, dst_addr, src_port, dst_port, link_type); + LIST_FOREACH(lnk, &la->linkTableOut[i], list_out) { + if (lnk->src_addr.s_addr == src_addr.s_addr + && lnk->server == NULL + && lnk->dst_addr.s_addr == dst_addr.s_addr + && lnk->dst_port == dst_port + && lnk->src_port == src_port + && lnk->link_type == link_type) { + lnk->timestamp = la->timeStamp; + break; + } + } + +/* Search for partially specified links. */ + if (lnk == NULL && replace_partial_links) { + if (dst_port != 0 && dst_addr.s_addr != INADDR_ANY) { + lnk = _FindLinkOut(la, src_addr, dst_addr, src_port, 0, + link_type, 0); + if (lnk == NULL) + lnk = _FindLinkOut(la, src_addr, la->nullAddress, src_port, + dst_port, link_type, 0); + } + if (lnk == NULL && + (dst_port != 0 || dst_addr.s_addr != INADDR_ANY)) { + lnk = _FindLinkOut(la, src_addr, la->nullAddress, src_port, 0, + link_type, 0); + } + if (lnk != NULL) { + lnk = ReLink(lnk, + src_addr, dst_addr, lnk->alias_addr, + src_port, dst_port, lnk->alias_port, + link_type); + } + } + return (lnk); +} + +static struct alias_link * +FindLinkOut(struct libalias *la, struct in_addr src_addr, + struct in_addr dst_addr, + u_short src_port, + u_short dst_port, + int link_type, + int replace_partial_links) +{ + struct alias_link *lnk; + + lnk = _FindLinkOut(la, src_addr, dst_addr, src_port, dst_port, + link_type, replace_partial_links); + + if (lnk == NULL) { + /* + * The following allows permanent links to be specified as + * using the default source address (i.e. device interface + * address) without knowing in advance what that address + * is. + */ + if (la->aliasAddress.s_addr != INADDR_ANY && + src_addr.s_addr == la->aliasAddress.s_addr) { + lnk = _FindLinkOut(la, la->nullAddress, dst_addr, src_port, dst_port, + link_type, replace_partial_links); + } + } + return (lnk); +} + + +static struct alias_link * +_FindLinkIn(struct libalias *la, 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 *lnk; + struct alias_link *lnk_fully_specified; + struct alias_link *lnk_unknown_all; + struct alias_link *lnk_unknown_dst_addr; + struct alias_link *lnk_unknown_dst_port; + +/* Initialize pointers */ + lnk_fully_specified = NULL; + lnk_unknown_all = NULL; + lnk_unknown_dst_addr = NULL; + lnk_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 == INADDR_ANY) + flags_in |= LINK_UNKNOWN_DEST_ADDR; + if (dst_port == 0) + flags_in |= LINK_UNKNOWN_DEST_PORT; + +/* Search loop */ + start_point = StartPointIn(alias_addr, alias_port, link_type); + LIST_FOREACH(lnk, &la->linkTableIn[start_point], list_in) { + int flags; + + flags = flags_in | lnk->flags; + if (!(flags & LINK_PARTIALLY_SPECIFIED)) { + if (lnk->alias_addr.s_addr == alias_addr.s_addr + && lnk->alias_port == alias_port + && lnk->dst_addr.s_addr == dst_addr.s_addr + && lnk->dst_port == dst_port + && lnk->link_type == link_type) { + lnk_fully_specified = lnk; + break; + } + } else if ((flags & LINK_UNKNOWN_DEST_ADDR) + && (flags & LINK_UNKNOWN_DEST_PORT)) { + if (lnk->alias_addr.s_addr == alias_addr.s_addr + && lnk->alias_port == alias_port + && lnk->link_type == link_type) { + if (lnk_unknown_all == NULL) + lnk_unknown_all = lnk; + } + } else if (flags & LINK_UNKNOWN_DEST_ADDR) { + if (lnk->alias_addr.s_addr == alias_addr.s_addr + && lnk->alias_port == alias_port + && lnk->link_type == link_type + && lnk->dst_port == dst_port) { + if (lnk_unknown_dst_addr == NULL) + lnk_unknown_dst_addr = lnk; + } + } else if (flags & LINK_UNKNOWN_DEST_PORT) { + if (lnk->alias_addr.s_addr == alias_addr.s_addr + && lnk->alias_port == alias_port + && lnk->link_type == link_type + && lnk->dst_addr.s_addr == dst_addr.s_addr) { + if (lnk_unknown_dst_port == NULL) + lnk_unknown_dst_port = lnk; + } + } + } + + + + if (lnk_fully_specified != NULL) { + lnk_fully_specified->timestamp = la->timeStamp; + lnk = lnk_fully_specified; + } else if (lnk_unknown_dst_port != NULL) + lnk = lnk_unknown_dst_port; + else if (lnk_unknown_dst_addr != NULL) + lnk = lnk_unknown_dst_addr; + else if (lnk_unknown_all != NULL) + lnk = lnk_unknown_all; + else + return (NULL); + + if (replace_partial_links && + (lnk->flags & LINK_PARTIALLY_SPECIFIED || lnk->server != NULL)) { + struct in_addr src_addr; + u_short src_port; + + if (lnk->server != NULL) { /* LSNAT link */ + src_addr = lnk->server->addr; + src_port = lnk->server->port; + lnk->server = lnk->server->next; + } else { + src_addr = lnk->src_addr; + src_port = lnk->src_port; + } + + lnk = ReLink(lnk, + src_addr, dst_addr, alias_addr, + src_port, dst_port, alias_port, + link_type); + } + return (lnk); +} + +static struct alias_link * +FindLinkIn(struct libalias *la, 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) +{ + struct alias_link *lnk; + + lnk = _FindLinkIn(la, dst_addr, alias_addr, dst_port, alias_port, + link_type, replace_partial_links); + + if (lnk == NULL) { + /* + * The following allows permanent links to be specified as + * using the default aliasing address (i.e. device + * interface address) without knowing in advance what that + * address is. + */ + if (la->aliasAddress.s_addr != INADDR_ANY && + alias_addr.s_addr == la->aliasAddress.s_addr) { + lnk = _FindLinkIn(la, dst_addr, la->nullAddress, dst_port, alias_port, + link_type, replace_partial_links); + } + } + return (lnk); +} + + + + +/* External routines for finding/adding links + +-- "external" means outside alias_db.c, but within alias*.c -- + + FindIcmpIn(), FindIcmpOut() + FindFragmentIn1(), FindFragmentIn2() + AddFragmentPtrLink(), FindFragmentPtr() + FindProtoIn(), FindProtoOut() + FindUdpTcpIn(), FindUdpTcpOut() + AddPptp(), FindPptpOutByCallId(), FindPptpInByCallId(), + FindPptpOutByPeerCallId(), FindPptpInByPeerCallId() + FindOriginalAddress(), FindAliasAddress() + +(prototypes in alias_local.h) +*/ + + +struct alias_link * +FindIcmpIn(struct libalias *la, struct in_addr dst_addr, + struct in_addr alias_addr, + u_short id_alias, + int create) +{ + struct alias_link *lnk; + + lnk = FindLinkIn(la, dst_addr, alias_addr, + NO_DEST_PORT, id_alias, + LINK_ICMP, 0); + if (lnk == NULL && create && !(la->packetAliasMode & PKT_ALIAS_DENY_INCOMING)) { + struct in_addr target_addr; + + target_addr = FindOriginalAddress(la, alias_addr); + lnk = AddLink(la, target_addr, dst_addr, alias_addr, + id_alias, NO_DEST_PORT, id_alias, + LINK_ICMP); + } + return (lnk); +} + + +struct alias_link * +FindIcmpOut(struct libalias *la, struct in_addr src_addr, + struct in_addr dst_addr, + u_short id, + int create) +{ + struct alias_link *lnk; + + lnk = FindLinkOut(la, src_addr, dst_addr, + id, NO_DEST_PORT, + LINK_ICMP, 0); + if (lnk == NULL && create) { + struct in_addr alias_addr; + + alias_addr = FindAliasAddress(la, src_addr); + lnk = AddLink(la, src_addr, dst_addr, alias_addr, + id, NO_DEST_PORT, GET_ALIAS_ID, + LINK_ICMP); + } + return (lnk); +} + + +struct alias_link * +FindFragmentIn1(struct libalias *la, struct in_addr dst_addr, + struct in_addr alias_addr, + u_short ip_id) +{ + struct alias_link *lnk; + + lnk = FindLinkIn(la, dst_addr, alias_addr, + NO_DEST_PORT, ip_id, + LINK_FRAGMENT_ID, 0); + + if (lnk == NULL) { + lnk = AddLink(la, la->nullAddress, dst_addr, alias_addr, + NO_SRC_PORT, NO_DEST_PORT, ip_id, + LINK_FRAGMENT_ID); + } + return (lnk); +} + + +struct alias_link * +FindFragmentIn2(struct libalias *la, 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(la, dst_addr, alias_addr, + NO_DEST_PORT, ip_id, + LINK_FRAGMENT_ID, 0); +} + + +struct alias_link * +AddFragmentPtrLink(struct libalias *la, struct in_addr dst_addr, + u_short ip_id) +{ + return AddLink(la, la->nullAddress, dst_addr, la->nullAddress, + NO_SRC_PORT, NO_DEST_PORT, ip_id, + LINK_FRAGMENT_PTR); +} + + +struct alias_link * +FindFragmentPtr(struct libalias *la, struct in_addr dst_addr, + u_short ip_id) +{ + return FindLinkIn(la, dst_addr, la->nullAddress, + NO_DEST_PORT, ip_id, + LINK_FRAGMENT_PTR, 0); +} + + +struct alias_link * +FindProtoIn(struct libalias *la, struct in_addr dst_addr, + struct in_addr alias_addr, + u_char proto) +{ + struct alias_link *lnk; + + lnk = FindLinkIn(la, dst_addr, alias_addr, + NO_DEST_PORT, 0, + proto, 1); + + if (lnk == NULL && !(la->packetAliasMode & PKT_ALIAS_DENY_INCOMING)) { + struct in_addr target_addr; + + target_addr = FindOriginalAddress(la, alias_addr); + lnk = AddLink(la, target_addr, dst_addr, alias_addr, + NO_SRC_PORT, NO_DEST_PORT, 0, + proto); + } + return (lnk); +} + + +struct alias_link * +FindProtoOut(struct libalias *la, struct in_addr src_addr, + struct in_addr dst_addr, + u_char proto) +{ + struct alias_link *lnk; + + lnk = FindLinkOut(la, src_addr, dst_addr, + NO_SRC_PORT, NO_DEST_PORT, + proto, 1); + + if (lnk == NULL) { + struct in_addr alias_addr; + + alias_addr = FindAliasAddress(la, src_addr); + lnk = AddLink(la, src_addr, dst_addr, alias_addr, + NO_SRC_PORT, NO_DEST_PORT, 0, + proto); + } + return (lnk); +} + + +struct alias_link * +FindUdpTcpIn(struct libalias *la, struct in_addr dst_addr, + struct in_addr alias_addr, + u_short dst_port, + u_short alias_port, + u_char proto, + int create) +{ + int link_type; + struct alias_link *lnk; + + switch (proto) { + case IPPROTO_UDP: + link_type = LINK_UDP; + break; + case IPPROTO_TCP: + link_type = LINK_TCP; + break; + default: + return (NULL); + break; + } + + lnk = FindLinkIn(la, dst_addr, alias_addr, + dst_port, alias_port, + link_type, create); + + if (lnk == NULL && create && !(la->packetAliasMode & PKT_ALIAS_DENY_INCOMING)) { + struct in_addr target_addr; + + target_addr = FindOriginalAddress(la, alias_addr); + lnk = AddLink(la, target_addr, dst_addr, alias_addr, + alias_port, dst_port, alias_port, + link_type); + } + return (lnk); +} + + +struct alias_link * +FindUdpTcpOut(struct libalias *la, struct in_addr src_addr, + struct in_addr dst_addr, + u_short src_port, + u_short dst_port, + u_char proto, + int create) +{ + int link_type; + struct alias_link *lnk; + + switch (proto) { + case IPPROTO_UDP: + link_type = LINK_UDP; + break; + case IPPROTO_TCP: + link_type = LINK_TCP; + break; + default: + return (NULL); + break; + } + + lnk = FindLinkOut(la, src_addr, dst_addr, src_port, dst_port, link_type, create); + + if (lnk == NULL && create) { + struct in_addr alias_addr; + + alias_addr = FindAliasAddress(la, src_addr); + lnk = AddLink(la, src_addr, dst_addr, alias_addr, + src_port, dst_port, GET_ALIAS_PORT, + link_type); + } + return (lnk); +} + + +struct alias_link * +AddPptp(struct libalias *la, struct in_addr src_addr, + struct in_addr dst_addr, + struct in_addr alias_addr, + u_int16_t src_call_id) +{ + struct alias_link *lnk; + + lnk = AddLink(la, src_addr, dst_addr, alias_addr, + src_call_id, 0, GET_ALIAS_PORT, + LINK_PPTP); + + return (lnk); +} + + +struct alias_link * +FindPptpOutByCallId(struct libalias *la, struct in_addr src_addr, + struct in_addr dst_addr, + u_int16_t src_call_id) +{ + u_int i; + struct alias_link *lnk; + + i = StartPointOut(src_addr, dst_addr, 0, 0, LINK_PPTP); + LIST_FOREACH(lnk, &la->linkTableOut[i], list_out) + if (lnk->link_type == LINK_PPTP && + lnk->src_addr.s_addr == src_addr.s_addr && + lnk->dst_addr.s_addr == dst_addr.s_addr && + lnk->src_port == src_call_id) + break; + + return (lnk); +} + + +struct alias_link * +FindPptpOutByPeerCallId(struct libalias *la, struct in_addr src_addr, + struct in_addr dst_addr, + u_int16_t dst_call_id) +{ + u_int i; + struct alias_link *lnk; + + i = StartPointOut(src_addr, dst_addr, 0, 0, LINK_PPTP); + LIST_FOREACH(lnk, &la->linkTableOut[i], list_out) + if (lnk->link_type == LINK_PPTP && + lnk->src_addr.s_addr == src_addr.s_addr && + lnk->dst_addr.s_addr == dst_addr.s_addr && + lnk->dst_port == dst_call_id) + break; + + return (lnk); +} + + +struct alias_link * +FindPptpInByCallId(struct libalias *la, struct in_addr dst_addr, + struct in_addr alias_addr, + u_int16_t dst_call_id) +{ + u_int i; + struct alias_link *lnk; + + i = StartPointIn(alias_addr, 0, LINK_PPTP); + LIST_FOREACH(lnk, &la->linkTableIn[i], list_in) + if (lnk->link_type == LINK_PPTP && + lnk->dst_addr.s_addr == dst_addr.s_addr && + lnk->alias_addr.s_addr == alias_addr.s_addr && + lnk->dst_port == dst_call_id) + break; + + return (lnk); +} + + +struct alias_link * +FindPptpInByPeerCallId(struct libalias *la, struct in_addr dst_addr, + struct in_addr alias_addr, + u_int16_t alias_call_id) +{ + struct alias_link *lnk; + + lnk = FindLinkIn(la, dst_addr, alias_addr, + 0 /* any */ , alias_call_id, + LINK_PPTP, 0); + + + return (lnk); +} + + +struct alias_link * +FindRtspOut(struct libalias *la, struct in_addr src_addr, + struct in_addr dst_addr, + u_short src_port, + u_short alias_port, + u_char proto) +{ + int link_type; + struct alias_link *lnk; + + switch (proto) { + case IPPROTO_UDP: + link_type = LINK_UDP; + break; + case IPPROTO_TCP: + link_type = LINK_TCP; + break; + default: + return (NULL); + break; + } + + lnk = FindLinkOut(la, src_addr, dst_addr, src_port, 0, link_type, 1); + + if (lnk == NULL) { + struct in_addr alias_addr; + + alias_addr = FindAliasAddress(la, src_addr); + lnk = AddLink(la, src_addr, dst_addr, alias_addr, + src_port, 0, alias_port, + link_type); + } + return (lnk); +} + + +struct in_addr +FindOriginalAddress(struct libalias *la, struct in_addr alias_addr) +{ + struct alias_link *lnk; + + lnk = FindLinkIn(la, la->nullAddress, alias_addr, + 0, 0, LINK_ADDR, 0); + if (lnk == NULL) { + la->newDefaultLink = 1; + if (la->targetAddress.s_addr == INADDR_ANY) + return (alias_addr); + else if (la->targetAddress.s_addr == INADDR_NONE) + return (la->aliasAddress.s_addr != INADDR_ANY) ? + la->aliasAddress : alias_addr; + else + return (la->targetAddress); + } else { + if (lnk->server != NULL) { /* LSNAT link */ + struct in_addr src_addr; + + src_addr = lnk->server->addr; + lnk->server = lnk->server->next; + return (src_addr); + } else if (lnk->src_addr.s_addr == INADDR_ANY) + return (la->aliasAddress.s_addr != INADDR_ANY) ? + la->aliasAddress : alias_addr; + else + return (lnk->src_addr); + } +} + + +struct in_addr +FindAliasAddress(struct libalias *la, struct in_addr original_addr) +{ + struct alias_link *lnk; + + lnk = FindLinkOut(la, original_addr, la->nullAddress, + 0, 0, LINK_ADDR, 0); + if (lnk == NULL) { + return (la->aliasAddress.s_addr != INADDR_ANY) ? + la->aliasAddress : original_addr; + } else { + if (lnk->alias_addr.s_addr == INADDR_ANY) + return (la->aliasAddress.s_addr != INADDR_ANY) ? + la->aliasAddress : original_addr; + else + return (lnk->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() + SetProtocolFlags(), GetProtocolFlags() + SetDestCallId() +*/ + + +void +SetFragmentAddr(struct alias_link *lnk, struct in_addr src_addr) +{ + lnk->data.frag_addr = src_addr; +} + + +void +GetFragmentAddr(struct alias_link *lnk, struct in_addr *src_addr) +{ + *src_addr = lnk->data.frag_addr; +} + + +void +SetFragmentPtr(struct alias_link *lnk, char *fptr) +{ + lnk->data.frag_ptr = fptr; +} + + +void +GetFragmentPtr(struct alias_link *lnk, char **fptr) +{ + *fptr = lnk->data.frag_ptr; +} + + +void +SetStateIn(struct alias_link *lnk, int state) +{ + /* TCP input state */ + switch (state) { + case ALIAS_TCP_STATE_DISCONNECTED: + if (lnk->data.tcp->state.out != ALIAS_TCP_STATE_CONNECTED) + lnk->expire_time = TCP_EXPIRE_DEAD; + else + lnk->expire_time = TCP_EXPIRE_SINGLEDEAD; + break; + case ALIAS_TCP_STATE_CONNECTED: + if (lnk->data.tcp->state.out == ALIAS_TCP_STATE_CONNECTED) + lnk->expire_time = TCP_EXPIRE_CONNECTED; + break; + default: + abort(); + } + lnk->data.tcp->state.in = state; +} + + +void +SetStateOut(struct alias_link *lnk, int state) +{ + /* TCP output state */ + switch (state) { + case ALIAS_TCP_STATE_DISCONNECTED: + if (lnk->data.tcp->state.in != ALIAS_TCP_STATE_CONNECTED) + lnk->expire_time = TCP_EXPIRE_DEAD; + else + lnk->expire_time = TCP_EXPIRE_SINGLEDEAD; + break; + case ALIAS_TCP_STATE_CONNECTED: + if (lnk->data.tcp->state.in == ALIAS_TCP_STATE_CONNECTED) + lnk->expire_time = TCP_EXPIRE_CONNECTED; + break; + default: + abort(); + } + lnk->data.tcp->state.out = state; +} + + +int +GetStateIn(struct alias_link *lnk) +{ + /* TCP input state */ + return (lnk->data.tcp->state.in); +} + + +int +GetStateOut(struct alias_link *lnk) +{ + /* TCP output state */ + return (lnk->data.tcp->state.out); +} + + +struct in_addr +GetOriginalAddress(struct alias_link *lnk) +{ + if (lnk->src_addr.s_addr == INADDR_ANY) + return (lnk->la->aliasAddress); + else + return (lnk->src_addr); +} + + +struct in_addr +GetDestAddress(struct alias_link *lnk) +{ + return (lnk->dst_addr); +} + + +struct in_addr +GetAliasAddress(struct alias_link *lnk) +{ + if (lnk->alias_addr.s_addr == INADDR_ANY) + return (lnk->la->aliasAddress); + else + return (lnk->alias_addr); +} + + +struct in_addr +GetDefaultAliasAddress(struct libalias *la) +{ + return (la->aliasAddress); +} + + +void +SetDefaultAliasAddress(struct libalias *la, struct in_addr alias_addr) +{ + la->aliasAddress = alias_addr; +} + + +u_short +GetOriginalPort(struct alias_link *lnk) +{ + return (lnk->src_port); +} + + +u_short +GetAliasPort(struct alias_link *lnk) +{ + return (lnk->alias_port); +} + +#ifndef NO_FW_PUNCH +static u_short +GetDestPort(struct alias_link *lnk) +{ + return (lnk->dst_port); +} + +#endif + +void +SetAckModified(struct alias_link *lnk) +{ +/* Indicate that ACK numbers have been modified in a TCP connection */ + lnk->data.tcp->state.ack_modified = 1; +} + + +struct in_addr +GetProxyAddress(struct alias_link *lnk) +{ + return (lnk->proxy_addr); +} + + +void +SetProxyAddress(struct alias_link *lnk, struct in_addr addr) +{ + lnk->proxy_addr = addr; +} + + +u_short +GetProxyPort(struct alias_link *lnk) +{ + return (lnk->proxy_port); +} + + +void +SetProxyPort(struct alias_link *lnk, u_short port) +{ + lnk->proxy_port = port; +} + + +int +GetAckModified(struct alias_link *lnk) +{ +/* See if ACK numbers have been modified */ + return (lnk->data.tcp->state.ack_modified); +} + + +int +GetDeltaAckIn(struct ip *pip, struct alias_link *lnk) +{ +/* +Find out how much the ACK number has been altered for an incoming +TCP packet. To do this, a circular list of 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 = ip_next(pip); + 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 = lnk->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 *lnk) +{ +/* +Find out how much the sequence number has been altered for an outgoing +TCP packet. To do this, a circular list of 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 = ip_next(pip); + 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 = lnk->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 *lnk, 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 = ip_next(pip); + + 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 = lnk->data.tcp->state.index; + lnk->data.tcp->ack[i] = x; + + i++; + if (i == N_LINK_TCP_DATA) + lnk->data.tcp->state.index = 0; + else + lnk->data.tcp->state.index = i; +} + +void +SetExpire(struct alias_link *lnk, int expire) +{ + if (expire == 0) { + lnk->flags &= ~LINK_PERMANENT; + DeleteLink(lnk); + } else if (expire == -1) { + lnk->flags |= LINK_PERMANENT; + } else if (expire > 0) { + lnk->expire_time = expire; + } else { +#ifdef DEBUG + fprintf(stderr, "PacketAlias/SetExpire(): "); + fprintf(stderr, "error in expire parameter\n"); +#endif + } +} + +void +ClearCheckNewLink(struct libalias *la) +{ + la->newDefaultLink = 0; +} + +void +SetProtocolFlags(struct alias_link *lnk, int pflags) +{ + + lnk->pflags = pflags;; +} + +int +GetProtocolFlags(struct alias_link *lnk) +{ + + return (lnk->pflags); +} + +void +SetDestCallId(struct alias_link *lnk, u_int16_t cid) +{ + struct libalias *la = lnk->la; + + la->deleteAllLinks = 1; + lnk = ReLink(lnk, lnk->src_addr, lnk->dst_addr, lnk->alias_addr, + lnk->src_port, cid, lnk->alias_port, lnk->link_type); + la->deleteAllLinks = 0; +} + + +/* Miscellaneous Functions + + HouseKeeping() + InitPacketAliasLog() + UninitPacketAliasLog() +*/ + +/* + Whenever an outgoing or incoming packet is handled, HouseKeeping() + is called to find and remove timed-out aliasing links. Logic exists + to sweep through the entire table and linked list structure + every 60 seconds. + + (prototype in alias_local.h) +*/ + +void +HouseKeeping(struct libalias *la) +{ + 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); + la->timeStamp = tv.tv_sec; + + /* Compute number of spokes (output table link chains) to cover */ + n100 = LINK_TABLE_OUT_SIZE * 100 + la->houseKeepingResidual; + n100 *= la->timeStamp - la->lastCleanupTime; + n100 /= ALIAS_CLEANUP_INTERVAL_SECS; + + n = n100 / 100; + + /* Handle different cases */ + if (n > ALIAS_CLEANUP_MAX_SPOKES) { + n = ALIAS_CLEANUP_MAX_SPOKES; + la->lastCleanupTime = la->timeStamp; + la->houseKeepingResidual = 0; + + for (i = 0; i < n; i++) + IncrementalCleanup(la); + } else if (n > 0) { + la->lastCleanupTime = la->timeStamp; + la->houseKeepingResidual = n100 - 100 * n; + + for (i = 0; i < n; i++) + IncrementalCleanup(la); + } else if (n < 0) { +#ifdef DEBUG + fprintf(stderr, "PacketAlias/HouseKeeping(): "); + fprintf(stderr, "something unexpected in time values\n"); +#endif + la->lastCleanupTime = la->timeStamp; + la->houseKeepingResidual = 0; + } +} + + +/* Init the log file and enable logging */ +static void +InitPacketAliasLog(struct libalias *la) +{ + if ((~la->packetAliasMode & PKT_ALIAS_LOG) + && (la->monitorFile = fopen("/var/log/alias.log", "w"))) { + la->packetAliasMode |= PKT_ALIAS_LOG; + fprintf(la->monitorFile, + "PacketAlias/InitPacketAliasLog: Packet alias logging enabled.\n"); + } +} + + +/* Close the log-file and disable logging. */ +static void +UninitPacketAliasLog(struct libalias *la) +{ + if (la->monitorFile) { + fclose(la->monitorFile); + la->monitorFile = NULL; + } + la->packetAliasMode &= ~PKT_ALIAS_LOG; +} + + + + + + +/* Outside world interfaces + +-- "outside world" means other than alias*.c routines -- + + PacketAliasRedirectPort() + PacketAliasAddServer() + PacketAliasRedirectProto() + PacketAliasRedirectAddr() + PacketAliasRedirectDynamic() + PacketAliasRedirectDelete() + PacketAliasSetAddress() + PacketAliasInit() + PacketAliasUninit() + PacketAliasSetMode() + +(prototypes in alias.h) +*/ + +/* Redirection from a specific public addr:port to a + private addr:port */ +struct alias_link * +LibAliasRedirectPort(struct libalias *la, 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 *lnk; + + switch (proto) { + case IPPROTO_UDP: + link_type = LINK_UDP; + break; + case IPPROTO_TCP: + link_type = LINK_TCP; + break; + default: +#ifdef DEBUG + fprintf(stderr, "PacketAliasRedirectPort(): "); + fprintf(stderr, "only TCP and UDP protocols allowed\n"); +#endif + return (NULL); + } + + lnk = AddLink(la, src_addr, dst_addr, alias_addr, + src_port, dst_port, alias_port, + link_type); + + if (lnk != NULL) { + lnk->flags |= LINK_PERMANENT; + } +#ifdef DEBUG + else { + fprintf(stderr, "PacketAliasRedirectPort(): " + "call to AddLink() failed\n"); + } +#endif + + return (lnk); +} + +/* Add server to the pool of servers */ +int +LibAliasAddServer(struct libalias *la, struct alias_link *lnk, struct in_addr addr, u_short port) +{ + struct server *server; + + (void)la; + + server = malloc(sizeof(struct server)); + + if (server != NULL) { + struct server *head; + + server->addr = addr; + server->port = port; + + head = lnk->server; + if (head == NULL) + server->next = server; + else { + struct server *s; + + for (s = head; s->next != head; s = s->next); + s->next = server; + server->next = head; + } + lnk->server = server; + return (0); + } else + return (-1); +} + +/* Redirect packets of a given IP protocol from a specific + public address to a private address */ +struct alias_link * +LibAliasRedirectProto(struct libalias *la, struct in_addr src_addr, + struct in_addr dst_addr, + struct in_addr alias_addr, + u_char proto) +{ + struct alias_link *lnk; + + lnk = AddLink(la, src_addr, dst_addr, alias_addr, + NO_SRC_PORT, NO_DEST_PORT, 0, + proto); + + if (lnk != NULL) { + lnk->flags |= LINK_PERMANENT; + } +#ifdef DEBUG + else { + fprintf(stderr, "PacketAliasRedirectProto(): " + "call to AddLink() failed\n"); + } +#endif + + return (lnk); +} + +/* Static address translation */ +struct alias_link * +LibAliasRedirectAddr(struct libalias *la, struct in_addr src_addr, + struct in_addr alias_addr) +{ + struct alias_link *lnk; + + lnk = AddLink(la, src_addr, la->nullAddress, alias_addr, + 0, 0, 0, + LINK_ADDR); + + if (lnk != NULL) { + lnk->flags |= LINK_PERMANENT; + } +#ifdef DEBUG + else { + fprintf(stderr, "PacketAliasRedirectAddr(): " + "call to AddLink() failed\n"); + } +#endif + + return (lnk); +} + + +/* Mark the aliasing link dynamic */ +int +LibAliasRedirectDynamic(struct libalias *la, struct alias_link *lnk) +{ + + (void)la; + + if (lnk->flags & LINK_PARTIALLY_SPECIFIED) + return (-1); + else { + lnk->flags &= ~LINK_PERMANENT; + return (0); + } +} + + +void +LibAliasRedirectDelete(struct libalias *la, struct alias_link *lnk) +{ +/* This is a dangerous function to put in the API, + because an invalid pointer can crash the program. */ + + la->deleteAllLinks = 1; + DeleteLink(lnk); + la->deleteAllLinks = 0; +} + + +void +LibAliasSetAddress(struct libalias *la, struct in_addr addr) +{ + if (la->packetAliasMode & PKT_ALIAS_RESET_ON_ADDR_CHANGE + && la->aliasAddress.s_addr != addr.s_addr) + CleanupAliasData(la); + + la->aliasAddress = addr; +} + + +void +LibAliasSetTarget(struct libalias *la, struct in_addr target_addr) +{ + la->targetAddress = target_addr; +} + +static void +finishoff(void) +{ + + while (!LIST_EMPTY(&instancehead)) + LibAliasUninit(LIST_FIRST(&instancehead)); +} + +struct libalias * +LibAliasInit(struct libalias *la) +{ + int i; + struct timeval tv; + struct timezone tz; + + if (la == NULL) { + la = calloc(sizeof *la, 1); + if (la == NULL) + return (la); + if (LIST_EMPTY(&instancehead)) + atexit(finishoff); + LIST_INSERT_HEAD(&instancehead, la, instancelist); + + gettimeofday(&tv, &tz); + la->timeStamp = tv.tv_sec; + la->lastCleanupTime = tv.tv_sec; + la->houseKeepingResidual = 0; + + for (i = 0; i < LINK_TABLE_OUT_SIZE; i++) + LIST_INIT(&la->linkTableOut[i]); + for (i = 0; i < LINK_TABLE_IN_SIZE; i++) + LIST_INIT(&la->linkTableIn[i]); + + } else { + la->deleteAllLinks = 1; + CleanupAliasData(la); + la->deleteAllLinks = 0; + } + + la->aliasAddress.s_addr = INADDR_ANY; + la->targetAddress.s_addr = INADDR_ANY; + + la->icmpLinkCount = 0; + la->udpLinkCount = 0; + la->tcpLinkCount = 0; + la->pptpLinkCount = 0; + la->protoLinkCount = 0; + la->fragmentIdLinkCount = 0; + la->fragmentPtrLinkCount = 0; + la->sockCount = 0; + + la->cleanupIndex = 0; + + la->packetAliasMode = PKT_ALIAS_SAME_PORTS + | PKT_ALIAS_USE_SOCKETS + | PKT_ALIAS_RESET_ON_ADDR_CHANGE; +#ifndef NO_FW_PUNCH + la->fireWallFD = -1; +#endif + return (la); +} + +void +LibAliasUninit(struct libalias *la) +{ + la->deleteAllLinks = 1; + CleanupAliasData(la); + la->deleteAllLinks = 0; + UninitPacketAliasLog(la); +#ifndef NO_FW_PUNCH + UninitPunchFW(la); +#endif + LIST_REMOVE(la, instancelist); + free(la); +} + +/* Change mode for some operations */ +unsigned int +LibAliasSetMode( + struct libalias *la, + 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(la); /* Do the enable */ + } else +/* _Disable_ logging? */ + if (~flags & mask & PKT_ALIAS_LOG) { + UninitPacketAliasLog(la); + } +#ifndef NO_FW_PUNCH +/* Start punching holes in the firewall? */ + if (flags & mask & PKT_ALIAS_PUNCH_FW) { + InitPunchFW(la); + } else +/* Stop punching holes in the firewall? */ + if (~flags & mask & PKT_ALIAS_PUNCH_FW) { + UninitPunchFW(la); + } +#endif + +/* Other flags can be set/cleared without special action */ + la->packetAliasMode = (flags & mask) | (la->packetAliasMode & ~mask); + return (la->packetAliasMode); +} + + +int +LibAliasCheckNewLink(struct libalias *la) +{ + return (la->newDefaultLink); +} + + +#ifndef NO_FW_PUNCH + +/***************** + Code to support firewall punching. This shouldn't really be in this + file, but making variables global is evil too. + ****************/ + +#ifndef IPFW2 +#define IPFW2 1 /* use new ipfw code */ +#endif + +/* Firewall include files */ +#include <net/if.h> +#include <netinet/ip_fw.h> +#include <string.h> +#include <err.h> + +#if IPFW2 /* support for new firewall code */ +/* + * helper function, updates the pointer to cmd with the length + * of the current command, and also cleans up the first word of + * the new command in case it has been clobbered before. + */ +static ipfw_insn * +next_cmd(ipfw_insn * cmd) +{ + cmd += F_LEN(cmd); + bzero(cmd, sizeof(*cmd)); + return (cmd); +} + +/* + * A function to fill simple commands of size 1. + * Existing flags are preserved. + */ +static ipfw_insn * +fill_cmd(ipfw_insn * cmd, enum ipfw_opcodes opcode, int size, + int flags, u_int16_t arg) +{ + cmd->opcode = opcode; + cmd->len = ((cmd->len | flags) & (F_NOT | F_OR)) | (size & F_LEN_MASK); + cmd->arg1 = arg; + return next_cmd(cmd); +} + +static ipfw_insn * +fill_ip(ipfw_insn * cmd1, enum ipfw_opcodes opcode, u_int32_t addr) +{ + ipfw_insn_ip *cmd = (ipfw_insn_ip *) cmd1; + + cmd->addr.s_addr = addr; + return fill_cmd(cmd1, opcode, F_INSN_SIZE(ipfw_insn_u32), 0, 0); +} + +static ipfw_insn * +fill_one_port(ipfw_insn * cmd1, enum ipfw_opcodes opcode, u_int16_t port) +{ + ipfw_insn_u16 *cmd = (ipfw_insn_u16 *) cmd1; + + cmd->ports[0] = cmd->ports[1] = port; + return fill_cmd(cmd1, opcode, F_INSN_SIZE(ipfw_insn_u16), 0, 0); +} + +static int +fill_rule(void *buf, int bufsize, int rulenum, + enum ipfw_opcodes action, int proto, + struct in_addr sa, u_int16_t sp, struct in_addr da, u_int16_t dp) +{ + struct ip_fw *rule = (struct ip_fw *)buf; + ipfw_insn *cmd = (ipfw_insn *) rule->cmd; + + bzero(buf, bufsize); + rule->rulenum = rulenum; + + cmd = fill_cmd(cmd, O_PROTO, F_INSN_SIZE(ipfw_insn), 0, proto); + cmd = fill_ip(cmd, O_IP_SRC, sa.s_addr); + cmd = fill_one_port(cmd, O_IP_SRCPORT, sp); + cmd = fill_ip(cmd, O_IP_DST, da.s_addr); + cmd = fill_one_port(cmd, O_IP_DSTPORT, dp); + + rule->act_ofs = (u_int32_t *) cmd - (u_int32_t *) rule->cmd; + cmd = fill_cmd(cmd, action, F_INSN_SIZE(ipfw_insn), 0, 0); + + rule->cmd_len = (u_int32_t *) cmd - (u_int32_t *) rule->cmd; + + return ((char *)cmd - (char *)buf); +} + +#endif /* IPFW2 */ + +static void ClearAllFWHoles(struct libalias *la); + + +#define fw_setfield(la, field, num) \ +do { \ + (field)[(num) - la->fireWallBaseNum] = 1; \ +} /*lint -save -e717 */ while(0)/* lint -restore */ + +#define fw_clrfield(la, field, num) \ +do { \ + (field)[(num) - la->fireWallBaseNum] = 0; \ +} /*lint -save -e717 */ while(0)/* lint -restore */ + +#define fw_tstfield(la, field, num) ((field)[(num) - la->fireWallBaseNum]) + +static void +InitPunchFW(struct libalias *la) +{ + + la->fireWallField = malloc(la->fireWallNumNums); + if (la->fireWallField) { + memset(la->fireWallField, 0, la->fireWallNumNums); + if (la->fireWallFD < 0) { + la->fireWallFD = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); + } + ClearAllFWHoles(la); + la->fireWallActiveNum = la->fireWallBaseNum; + } +} + +static void +UninitPunchFW(struct libalias *la) +{ + ClearAllFWHoles(la); + if (la->fireWallFD >= 0) + close(la->fireWallFD); + la->fireWallFD = -1; + if (la->fireWallField) + free(la->fireWallField); + la->fireWallField = NULL; + la->packetAliasMode &= ~PKT_ALIAS_PUNCH_FW; +} + +/* Make a certain link go through the firewall */ +void +PunchFWHole(struct alias_link *lnk) +{ + struct libalias *la; + int r; /* Result code */ + struct ip_fw rule; /* On-the-fly built rule */ + int fwhole; /* Where to punch hole */ + + la = lnk->la; + +/* Don't do anything unless we are asked to */ + if (!(la->packetAliasMode & PKT_ALIAS_PUNCH_FW) || + la->fireWallFD < 0 || + lnk->link_type != LINK_TCP) + return; + + memset(&rule, 0, sizeof rule); + +/** Build rule **/ + + /* Find empty slot */ + for (fwhole = la->fireWallActiveNum; + fwhole < la->fireWallBaseNum + la->fireWallNumNums && + fw_tstfield(la, la->fireWallField, fwhole); + fwhole++); + if (fwhole == la->fireWallBaseNum + la->fireWallNumNums) { + for (fwhole = la->fireWallBaseNum; + fwhole < la->fireWallActiveNum && + fw_tstfield(la, la->fireWallField, fwhole); + fwhole++); + if (fwhole == la->fireWallActiveNum) { + /* No rule point empty - we can't punch more holes. */ + la->fireWallActiveNum = la->fireWallBaseNum; +#ifdef DEBUG + fprintf(stderr, "libalias: Unable to create firewall hole!\n"); +#endif + return; + } + } + /* Start next search at next position */ + la->fireWallActiveNum = fwhole + 1; + + /* + * generate two rules of the form + * + * add fwhole accept tcp from OAddr OPort to DAddr DPort add fwhole + * accept tcp from DAddr DPort to OAddr OPort + */ +#if IPFW2 + if (GetOriginalPort(lnk) != 0 && GetDestPort(lnk) != 0) { + u_int32_t rulebuf[255]; + int i; + + i = fill_rule(rulebuf, sizeof(rulebuf), fwhole, + O_ACCEPT, IPPROTO_TCP, + GetOriginalAddress(lnk), ntohs(GetOriginalPort(lnk)), + GetDestAddress(lnk), ntohs(GetDestPort(lnk))); + r = setsockopt(la->fireWallFD, IPPROTO_IP, IP_FW_ADD, rulebuf, i); + if (r) + err(1, "alias punch inbound(1) setsockopt(IP_FW_ADD)"); + + i = fill_rule(rulebuf, sizeof(rulebuf), fwhole, + O_ACCEPT, IPPROTO_TCP, + GetDestAddress(lnk), ntohs(GetDestPort(lnk)), + GetOriginalAddress(lnk), ntohs(GetOriginalPort(lnk))); + r = setsockopt(la->fireWallFD, IPPROTO_IP, IP_FW_ADD, rulebuf, i); + if (r) + err(1, "alias punch inbound(2) setsockopt(IP_FW_ADD)"); + } +#else /* !IPFW2, old code to generate ipfw rule */ + + /* Build generic part of the two rules */ + rule.fw_number = fwhole; + IP_FW_SETNSRCP(&rule, 1); /* Number of source ports. */ + IP_FW_SETNDSTP(&rule, 1); /* Number of destination ports. */ + rule.fw_flg = IP_FW_F_ACCEPT | IP_FW_F_IN | IP_FW_F_OUT; + rule.fw_prot = IPPROTO_TCP; + rule.fw_smsk.s_addr = INADDR_BROADCAST; + rule.fw_dmsk.s_addr = INADDR_BROADCAST; + + /* Build and apply specific part of the rules */ + rule.fw_src = GetOriginalAddress(lnk); + rule.fw_dst = GetDestAddress(lnk); + rule.fw_uar.fw_pts[0] = ntohs(GetOriginalPort(lnk)); + rule.fw_uar.fw_pts[1] = ntohs(GetDestPort(lnk)); + + /* + * Skip non-bound links - XXX should not be strictly necessary, but + * seems to leave hole if not done. Leak of non-bound links? (Code + * should be left even if the problem is fixed - it is a clear + * optimization) + */ + if (rule.fw_uar.fw_pts[0] != 0 && rule.fw_uar.fw_pts[1] != 0) { + r = setsockopt(fireWallFD, IPPROTO_IP, IP_FW_ADD, &rule, sizeof rule); +#ifdef DEBUG + if (r) + err(1, "alias punch inbound(1) setsockopt(IP_FW_ADD)"); +#endif + rule.fw_src = GetDestAddress(lnk); + rule.fw_dst = GetOriginalAddress(lnk); + rule.fw_uar.fw_pts[0] = ntohs(GetDestPort(lnk)); + rule.fw_uar.fw_pts[1] = ntohs(GetOriginalPort(lnk)); + r = setsockopt(fireWallFD, IPPROTO_IP, IP_FW_ADD, &rule, sizeof rule); +#ifdef DEBUG + if (r) + err(1, "alias punch inbound(2) setsockopt(IP_FW_ADD)"); +#endif + } +#endif /* !IPFW2 */ +/* Indicate hole applied */ + lnk->data.tcp->fwhole = fwhole; + fw_setfield(la, la->fireWallField, fwhole); +} + +/* Remove a hole in a firewall associated with a particular alias + lnk. Calling this too often is harmless. */ +static void +ClearFWHole(struct alias_link *lnk) +{ + + struct libalias *la; + + la = lnk->la; + if (lnk->link_type == LINK_TCP) { + int fwhole = lnk->data.tcp->fwhole; /* Where is the firewall + * hole? */ + struct ip_fw rule; + + if (fwhole < 0) + return; + + memset(&rule, 0, sizeof rule); /* useless for ipfw2 */ +#if IPFW2 + while (!setsockopt(la->fireWallFD, IPPROTO_IP, IP_FW_DEL, + &fwhole, sizeof fwhole)); +#else /* !IPFW2 */ + rule.fw_number = fwhole; + while (!setsockopt(fireWallFD, IPPROTO_IP, IP_FW_DEL, + &rule, sizeof rule)); +#endif /* !IPFW2 */ + fw_clrfield(la, la->fireWallField, fwhole); + lnk->data.tcp->fwhole = -1; + } +} + +/* Clear out the entire range dedicated to firewall holes. */ +static void +ClearAllFWHoles(struct libalias *la) +{ + struct ip_fw rule; /* On-the-fly built rule */ + int i; + + if (la->fireWallFD < 0) + return; + + memset(&rule, 0, sizeof rule); + for (i = la->fireWallBaseNum; i < la->fireWallBaseNum + la->fireWallNumNums; i++) { +#if IPFW2 + int r = i; + + while (!setsockopt(la->fireWallFD, IPPROTO_IP, IP_FW_DEL, &r, sizeof r)); +#else /* !IPFW2 */ + rule.fw_number = i; + while (!setsockopt(fireWallFD, IPPROTO_IP, IP_FW_DEL, &rule, sizeof rule)); +#endif /* !IPFW2 */ + } + /* XXX: third arg correct here ? /phk */ + memset(la->fireWallField, 0, la->fireWallNumNums); +} + +#endif + +void +LibAliasSetFWBase(struct libalias *la, unsigned int base, unsigned int num) +{ +#ifndef NO_FW_PUNCH + la->fireWallBaseNum = base; + la->fireWallNumNums = num; +#endif +} + +void +LibAliasSetSkinnyPort(struct libalias *la, unsigned int port) +{ + la->skinnyPort = port; +} diff --git a/lib/libalias/alias_ftp.c b/lib/libalias/alias_ftp.c new file mode 100644 index 0000000000000..b628528a0c1d3 --- /dev/null +++ b/lib/libalias/alias_ftp.c @@ -0,0 +1,675 @@ +/*- + * Copyright (c) 2001 Charles Mott <cm@linktel.net> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + Alias_ftp.c performs special processing for FTP sessions under + TCP. Specifically, when a PORT/EPRT command from the client + side or 227/229 reply from the server 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 message 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 message 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. + + + References: RFC 959, RFC 2428. + + 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) + Differential 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 + within the packet aliasing module. + + Version 3.1: May, 2000 (eds) + Add support for passive mode, alias the 227 replies. + + See HISTORY file for record of revisions. +*/ + +/* Includes */ +#include <ctype.h> +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <netinet/in_systm.h> +#include <netinet/in.h> +#include <netinet/ip.h> +#include <netinet/tcp.h> + +#include "alias_local.h" + +#define FTP_CONTROL_PORT_NUMBER 21 +#define MAX_MESSAGE_SIZE 128 + +/* FTP protocol flags. */ +#define WAIT_CRLF 0x01 + +enum ftp_message_type { + FTP_PORT_COMMAND, + FTP_EPRT_COMMAND, + FTP_227_REPLY, + FTP_229_REPLY, + FTP_UNKNOWN_MESSAGE +}; + +static int ParseFtpPortCommand(struct libalias *la, char *, int); +static int ParseFtpEprtCommand(struct libalias *la, char *, int); +static int ParseFtp227Reply(struct libalias *la, char *, int); +static int ParseFtp229Reply(struct libalias *la, char *, int); +static void NewFtpMessage(struct libalias *la, struct ip *, struct alias_link *, int, int); + +void +AliasHandleFtpOut( + struct libalias *la, + struct ip *pip, /* IP packet to examine/patch */ + struct alias_link *lnk, /* The link to go through (aliased port) */ + int maxpacketsize /* The maximum size this packet can grow to + (including headers) */ ) +{ + int hlen, tlen, dlen, pflags; + char *sptr; + struct tcphdr *tc; + int ftp_message_type; + +/* Calculate data length of TCP packet */ + tc = (struct tcphdr *)ip_next(pip); + hlen = (pip->ip_hl + tc->th_off) << 2; + tlen = ntohs(pip->ip_len); + dlen = tlen - hlen; + +/* Place string pointer and beginning of data */ + sptr = (char *)pip; + sptr += hlen; + +/* + * Check that data length is not too long and previous message was + * properly terminated with CRLF. + */ + pflags = GetProtocolFlags(lnk); + if (dlen <= MAX_MESSAGE_SIZE && !(pflags & WAIT_CRLF)) { + ftp_message_type = FTP_UNKNOWN_MESSAGE; + + if (ntohs(tc->th_dport) == FTP_CONTROL_PORT_NUMBER) { +/* + * When aliasing a client, check for the PORT/EPRT command. + */ + if (ParseFtpPortCommand(la, sptr, dlen)) + ftp_message_type = FTP_PORT_COMMAND; + else if (ParseFtpEprtCommand(la, sptr, dlen)) + ftp_message_type = FTP_EPRT_COMMAND; + } else { +/* + * When aliasing a server, check for the 227/229 reply. + */ + if (ParseFtp227Reply(la, sptr, dlen)) + ftp_message_type = FTP_227_REPLY; + else if (ParseFtp229Reply(la, sptr, dlen)) { + ftp_message_type = FTP_229_REPLY; + la->true_addr.s_addr = pip->ip_src.s_addr; + } + } + + if (ftp_message_type != FTP_UNKNOWN_MESSAGE) + NewFtpMessage(la, pip, lnk, maxpacketsize, ftp_message_type); + } +/* Track the msgs which are CRLF term'd for PORT/PASV FW breach */ + + if (dlen) { /* only if there's data */ + sptr = (char *)pip; /* start over at beginning */ + tlen = ntohs(pip->ip_len); /* recalc tlen, pkt may + * have grown */ + if (sptr[tlen - 2] == '\r' && sptr[tlen - 1] == '\n') + pflags &= ~WAIT_CRLF; + else + pflags |= WAIT_CRLF; + SetProtocolFlags(lnk, pflags); + } +} + +static int +ParseFtpPortCommand(struct libalias *la, char *sptr, int dlen) +{ + char ch; + int i, state; + u_int32_t addr; + u_short port; + u_int8_t octet; + + /* Format: "PORT A,D,D,R,PO,RT". */ + + /* Return if data length is too short. */ + if (dlen < 18) + return (0); + + addr = port = octet = 0; + state = -4; + for (i = 0; i < dlen; i++) { + ch = sptr[i]; + switch (state) { + case -4: + if (ch == 'P') + state++; + else + return (0); + break; + case -3: + if (ch == 'O') + state++; + else + return (0); + break; + case -2: + if (ch == 'R') + state++; + else + return (0); + break; + case -1: + if (ch == 'T') + state++; + else + return (0); + break; + + case 0: + if (isspace(ch)) + break; + else + state++; + case 1: + case 3: + case 5: + case 7: + case 9: + case 11: + if (isdigit(ch)) { + octet = ch - '0'; + state++; + } else + return (0); + break; + case 2: + case 4: + case 6: + case 8: + if (isdigit(ch)) + octet = 10 * octet + ch - '0'; + else if (ch == ',') { + addr = (addr << 8) + octet; + state++; + } else + return (0); + break; + case 10: + case 12: + if (isdigit(ch)) + octet = 10 * octet + ch - '0'; + else if (ch == ',' || state == 12) { + port = (port << 8) + octet; + state++; + } else + return (0); + break; + } + } + + if (state == 13) { + la->true_addr.s_addr = htonl(addr); + la->true_port = port; + return (1); + } else + return (0); +} + +static int +ParseFtpEprtCommand(struct libalias *la, char *sptr, int dlen) +{ + char ch, delim; + int i, state; + u_int32_t addr; + u_short port; + u_int8_t octet; + + /* Format: "EPRT |1|A.D.D.R|PORT|". */ + + /* Return if data length is too short. */ + if (dlen < 18) + return (0); + + addr = port = octet = 0; + delim = '|'; /* XXX gcc -Wuninitialized */ + state = -4; + for (i = 0; i < dlen; i++) { + ch = sptr[i]; + switch (state) { + case -4: + if (ch == 'E') + state++; + else + return (0); + break; + case -3: + if (ch == 'P') + state++; + else + return (0); + break; + case -2: + if (ch == 'R') + state++; + else + return (0); + break; + case -1: + if (ch == 'T') + state++; + else + return (0); + break; + + case 0: + if (!isspace(ch)) { + delim = ch; + state++; + } + break; + case 1: + if (ch == '1') /* IPv4 address */ + state++; + else + return (0); + break; + case 2: + if (ch == delim) + state++; + else + return (0); + break; + case 3: + case 5: + case 7: + case 9: + if (isdigit(ch)) { + octet = ch - '0'; + state++; + } else + return (0); + break; + case 4: + case 6: + case 8: + case 10: + if (isdigit(ch)) + octet = 10 * octet + ch - '0'; + else if (ch == '.' || state == 10) { + addr = (addr << 8) + octet; + state++; + } else + return (0); + break; + case 11: + if (isdigit(ch)) { + port = ch - '0'; + state++; + } else + return (0); + break; + case 12: + if (isdigit(ch)) + port = 10 * port + ch - '0'; + else if (ch == delim) + state++; + else + return (0); + break; + } + } + + if (state == 13) { + la->true_addr.s_addr = htonl(addr); + la->true_port = port; + return (1); + } else + return (0); +} + +static int +ParseFtp227Reply(struct libalias *la, char *sptr, int dlen) +{ + char ch; + int i, state; + u_int32_t addr; + u_short port; + u_int8_t octet; + + /* Format: "227 Entering Passive Mode (A,D,D,R,PO,RT)" */ + + /* Return if data length is too short. */ + if (dlen < 17) + return (0); + + addr = port = octet = 0; + + state = -3; + for (i = 0; i < dlen; i++) { + ch = sptr[i]; + switch (state) { + case -3: + if (ch == '2') + state++; + else + return (0); + break; + case -2: + if (ch == '2') + state++; + else + return (0); + break; + case -1: + if (ch == '7') + state++; + else + return (0); + break; + + case 0: + if (ch == '(') + state++; + break; + case 1: + case 3: + case 5: + case 7: + case 9: + case 11: + if (isdigit(ch)) { + octet = ch - '0'; + state++; + } else + return (0); + break; + case 2: + case 4: + case 6: + case 8: + if (isdigit(ch)) + octet = 10 * octet + ch - '0'; + else if (ch == ',') { + addr = (addr << 8) + octet; + state++; + } else + return (0); + break; + case 10: + case 12: + if (isdigit(ch)) + octet = 10 * octet + ch - '0'; + else if (ch == ',' || (state == 12 && ch == ')')) { + port = (port << 8) + octet; + state++; + } else + return (0); + break; + } + } + + if (state == 13) { + la->true_port = port; + la->true_addr.s_addr = htonl(addr); + return (1); + } else + return (0); +} + +static int +ParseFtp229Reply(struct libalias *la, char *sptr, int dlen) +{ + char ch, delim; + int i, state; + u_short port; + + /* Format: "229 Entering Extended Passive Mode (|||PORT|)" */ + + /* Return if data length is too short. */ + if (dlen < 11) + return (0); + + port = 0; + delim = '|'; /* XXX gcc -Wuninitialized */ + + state = -3; + for (i = 0; i < dlen; i++) { + ch = sptr[i]; + switch (state) { + case -3: + if (ch == '2') + state++; + else + return (0); + break; + case -2: + if (ch == '2') + state++; + else + return (0); + break; + case -1: + if (ch == '9') + state++; + else + return (0); + break; + + case 0: + if (ch == '(') + state++; + break; + case 1: + delim = ch; + state++; + break; + case 2: + case 3: + if (ch == delim) + state++; + else + return (0); + break; + case 4: + if (isdigit(ch)) { + port = ch - '0'; + state++; + } else + return (0); + break; + case 5: + if (isdigit(ch)) + port = 10 * port + ch - '0'; + else if (ch == delim) + state++; + else + return (0); + break; + case 6: + if (ch == ')') + state++; + else + return (0); + break; + } + } + + if (state == 7) { + la->true_port = port; + return (1); + } else + return (0); +} + +static void +NewFtpMessage(struct libalias *la, struct ip *pip, + struct alias_link *lnk, + int maxpacketsize, + int ftp_message_type) +{ + struct alias_link *ftp_lnk; + +/* Security checks. */ + if (pip->ip_src.s_addr != la->true_addr.s_addr) + return; + + if (la->true_port < IPPORT_RESERVED) + return; + +/* Establish link to address and port found in FTP control message. */ + ftp_lnk = FindUdpTcpOut(la, la->true_addr, GetDestAddress(lnk), + htons(la->true_port), 0, IPPROTO_TCP, 1); + + if (ftp_lnk != NULL) { + int slen, hlen, tlen, dlen; + struct tcphdr *tc; + +#ifndef NO_FW_PUNCH + /* Punch hole in firewall */ + PunchFWHole(ftp_lnk); +#endif + +/* Calculate data length of TCP packet */ + tc = (struct tcphdr *)ip_next(pip); + hlen = (pip->ip_hl + tc->th_off) << 2; + tlen = ntohs(pip->ip_len); + dlen = tlen - hlen; + +/* Create new FTP message. */ + { + char stemp[MAX_MESSAGE_SIZE + 1]; + 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(lnk); + ptr = (u_char *) & alias_address.s_addr; + a1 = *ptr++; + a2 = *ptr++; + a3 = *ptr++; + a4 = *ptr; + + alias_port = GetAliasPort(ftp_lnk); + + switch (ftp_message_type) { + case FTP_PORT_COMMAND: + case FTP_227_REPLY: + /* Decompose alias port into pair format. */ + ptr = (char *)&alias_port; + p1 = *ptr++; + p2 = *ptr; + + if (ftp_message_type == FTP_PORT_COMMAND) { + /* Generate PORT command string. */ + sprintf(stemp, "PORT %d,%d,%d,%d,%d,%d\r\n", + a1, a2, a3, a4, p1, p2); + } else { + /* Generate 227 reply string. */ + sprintf(stemp, + "227 Entering Passive Mode (%d,%d,%d,%d,%d,%d)\r\n", + a1, a2, a3, a4, p1, p2); + } + break; + case FTP_EPRT_COMMAND: + /* Generate EPRT command string. */ + sprintf(stemp, "EPRT |1|%d.%d.%d.%d|%d|\r\n", + a1, a2, a3, a4, ntohs(alias_port)); + break; + case FTP_229_REPLY: + /* Generate 229 reply string. */ + sprintf(stemp, "229 Entering Extended Passive Mode (|||%d|)\r\n", + ntohs(alias_port)); + break; + } + +/* Save string length for IP header modification */ + slen = strlen(stemp); + +/* Copy modified buffer into IP packet. */ + sptr = (char *)pip; + sptr += hlen; + strncpy(sptr, stemp, maxpacketsize - hlen); + } + +/* Save information regarding modified seq and ack numbers */ + { + int delta; + + SetAckModified(lnk); + delta = GetDeltaSeqOut(pip, lnk); + AddSeq(pip, lnk, 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 { +#ifdef DEBUG + fprintf(stderr, + "PacketAlias/HandleFtpOut: Cannot allocate FTP data port\n"); +#endif + } +} diff --git a/lib/libalias/alias_irc.c b/lib/libalias/alias_irc.c new file mode 100644 index 0000000000000..66106fa351126 --- /dev/null +++ b/lib/libalias/alias_irc.c @@ -0,0 +1,386 @@ +/*- + * Copyright (c) 2001 Charles Mott <cm@linktel.net> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* 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 + + 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 libalias *la, + struct ip *pip, /* IP packet to examine */ + struct alias_link *lnk, /* 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 *)ip_next(pip); + 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 < (int)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 + * :) */ + unsigned int copyat = i; /* Same */ + unsigned 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_lnk; + struct in_addr destaddr; + + + true_port = htons(org_port); + true_addr.s_addr = htonl(org_addr); + destaddr.s_addr = 0; + + /* Sanity/Security checking */ + if (!org_addr || !org_port || + pip->ip_src.s_addr != true_addr.s_addr || + org_port < IPPORT_RESERVED) + goto lBAD_CTCP; + + /* + * Steal the FTP_DATA_PORT - it doesn't really + * matter, and this would probably allow it through + * at least _some_ firewalls. + */ + dcc_lnk = FindUdpTcpOut(la, true_addr, destaddr, + true_port, 0, + IPPROTO_TCP, 1); + DBprintf(("Got a DCC link\n")); + if (dcc_lnk) { + struct in_addr alias_address; /* Address from aliasing */ + u_short alias_port; /* Port given by + * aliasing */ + int n; + +#ifndef NO_FW_PUNCH + /* Generate firewall hole as appropriate */ + PunchFWHole(dcc_lnk); +#endif + + alias_address = GetAliasAddress(lnk); + n = snprintf(&newpacket[iCopy], + sizeof(newpacket) - iCopy, + "%lu ", (u_long) htonl(alias_address.s_addr)); + if (n < 0) { + DBprintf(("DCC packet construct failure.\n")); + goto lBAD_CTCP; + } + if ((iCopy += n) >= sizeof(newpacket)) { /* Truncated/fit exactly + * - bad news */ + DBprintf(("DCC constructed packet overflow.\n")); + goto lBAD_CTCP; + } + alias_port = GetAliasPort(dcc_lnk); + n = snprintf(&newpacket[iCopy], + sizeof(newpacket) - iCopy, + "%u", htons(alias_port)); + if (n < 0) { + DBprintf(("DCC packet construct failure.\n")); + goto lBAD_CTCP; + } + iCopy += n; + /* + * 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(lnk); + delta = GetDeltaSeqOut(pip, lnk); + AddSeq(pip, lnk, 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 0000000000000..67947da818531 --- /dev/null +++ b/lib/libalias/alias_local.h @@ -0,0 +1,355 @@ +/*- + * Copyright (c) 2001 Charles Mott <cm@linktel.net> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * 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 also includes macros, globals and + * struct definitions shared by more than one alias*.c file. + * + * This include file 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 Eklund> + */ + +#ifndef _ALIAS_LOCAL_H_ +#define _ALIAS_LOCAL_H_ + +#include <sys/queue.h> + +/* Sizes of input and output link tables */ +#define LINK_TABLE_OUT_SIZE 101 +#define LINK_TABLE_IN_SIZE 4001 + +struct proxy_entry; + +struct libalias { + LIST_ENTRY(libalias) instancelist; + + int packetAliasMode; /* Mode flags */ + /* - documented in alias.h */ + + struct in_addr aliasAddress; /* Address written onto source */ + /* field of IP packet. */ + + struct in_addr targetAddress; /* IP address incoming packets */ + /* are sent to if no aliasing */ + /* link already exists */ + + struct in_addr nullAddress; /* Used as a dummy parameter for */ + /* some function calls */ + + LIST_HEAD (, alias_link) linkTableOut[LINK_TABLE_OUT_SIZE]; + /* Lookup table of pointers to */ + /* chains of link records. Each */ + + LIST_HEAD (, alias_link) linkTableIn[LINK_TABLE_IN_SIZE]; + /* link record is doubly indexed */ + /* into input and output lookup */ + /* tables. */ + + /* Link statistics */ + int icmpLinkCount; + int udpLinkCount; + int tcpLinkCount; + int pptpLinkCount; + int protoLinkCount; + int fragmentIdLinkCount; + int fragmentPtrLinkCount; + int sockCount; + + int cleanupIndex; /* Index to chain of link table */ + /* being inspected for old links */ + + int timeStamp; /* System time in seconds for */ + /* current packet */ + + int lastCleanupTime; /* Last time + * IncrementalCleanup() */ + /* was called */ + + int houseKeepingResidual; /* used by HouseKeeping() */ + + int deleteAllLinks; /* If equal to zero, DeleteLink() */ + /* will not remove permanent links */ + + FILE *monitorFile; /* File descriptor for link */ + /* statistics monitoring file */ + + int newDefaultLink; /* Indicates if a new aliasing */ + /* link has been created after a */ + /* call to PacketAliasIn/Out(). */ + +#ifndef NO_FW_PUNCH + int fireWallFD; /* File descriptor to be able to */ + /* control firewall. Opened by */ + /* PacketAliasSetMode on first */ + /* setting the PKT_ALIAS_PUNCH_FW */ + /* flag. */ + int fireWallBaseNum; /* The first firewall entry + * free for our use */ + int fireWallNumNums; /* How many entries can we + * use? */ + int fireWallActiveNum; /* Which entry did we last + * use? */ + char *fireWallField; /* bool array for entries */ +#endif + + unsigned int skinnyPort; /* TCP port used by the Skinny */ + /* protocol. */ + + struct proxy_entry *proxyList; + + struct in_addr true_addr; /* in network byte order. */ + u_short true_port; /* in host byte order. */ + +}; + +/* Macros */ + +/* + * 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) \ + do { \ + 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; \ + } \ + } while (0) + + +/* Prototypes */ + +/* General utilities */ +u_short IpChecksum(struct ip *_pip); +u_short TcpChecksum(struct ip *_pip); +void +DifferentialChecksum(u_short * _cksum, void * _new, void * _old, int _n); + +/* Internal data access */ +struct alias_link * +FindIcmpIn(struct libalias *la, struct in_addr _dst_addr, struct in_addr _alias_addr, + u_short _id_alias, int _create); +struct alias_link * +FindIcmpOut(struct libalias *la, struct in_addr _src_addr, struct in_addr _dst_addr, + u_short _id, int _create); +struct alias_link * +FindFragmentIn1(struct libalias *la, struct in_addr _dst_addr, struct in_addr _alias_addr, + u_short _ip_id); +struct alias_link * +FindFragmentIn2(struct libalias *la, struct in_addr _dst_addr, struct in_addr _alias_addr, + u_short _ip_id); +struct alias_link * + AddFragmentPtrLink(struct libalias *la, struct in_addr _dst_addr, u_short _ip_id); +struct alias_link * + FindFragmentPtr(struct libalias *la, struct in_addr _dst_addr, u_short _ip_id); +struct alias_link * +FindProtoIn(struct libalias *la, struct in_addr _dst_addr, struct in_addr _alias_addr, + u_char _proto); +struct alias_link * +FindProtoOut(struct libalias *la, struct in_addr _src_addr, struct in_addr _dst_addr, + u_char _proto); +struct alias_link * +FindUdpTcpIn(struct libalias *la, struct in_addr _dst_addr, struct in_addr _alias_addr, + u_short _dst_port, u_short _alias_port, u_char _proto, int _create); +struct alias_link * +FindUdpTcpOut(struct libalias *la, struct in_addr _src_addr, struct in_addr _dst_addr, + u_short _src_port, u_short _dst_port, u_char _proto, int _create); +struct alias_link * +AddPptp(struct libalias *la, struct in_addr _src_addr, struct in_addr _dst_addr, + struct in_addr _alias_addr, u_int16_t _src_call_id); +struct alias_link * +FindPptpOutByCallId(struct libalias *la, struct in_addr _src_addr, + struct in_addr _dst_addr, u_int16_t _src_call_id); +struct alias_link * +FindPptpInByCallId(struct libalias *la, struct in_addr _dst_addr, + struct in_addr _alias_addr, u_int16_t _dst_call_id); +struct alias_link * +FindPptpOutByPeerCallId(struct libalias *la, struct in_addr _src_addr, + struct in_addr _dst_addr, u_int16_t _dst_call_id); +struct alias_link * +FindPptpInByPeerCallId(struct libalias *la, struct in_addr _dst_addr, + struct in_addr _alias_addr, u_int16_t _alias_call_id); +struct alias_link * +FindRtspOut(struct libalias *la, struct in_addr _src_addr, struct in_addr _dst_addr, + u_short _src_port, u_short _alias_port, u_char _proto); +struct in_addr + FindOriginalAddress(struct libalias *la, struct in_addr _alias_addr); +struct in_addr + FindAliasAddress(struct libalias *la, struct in_addr _original_addr); + +/* External data access/modification */ +int +FindNewPortGroup(struct libalias *la, struct in_addr _dst_addr, struct in_addr _alias_addr, + u_short _src_port, u_short _dst_port, u_short _port_count, + u_char _proto, u_char _align); +void GetFragmentAddr(struct alias_link *_lnk, struct in_addr *_src_addr); +void SetFragmentAddr(struct alias_link *_lnk, struct in_addr _src_addr); +void GetFragmentPtr(struct alias_link *_lnk, char **_fptr); +void SetFragmentPtr(struct alias_link *_lnk, char *fptr); +void SetStateIn(struct alias_link *_lnk, int _state); +void SetStateOut(struct alias_link *_lnk, int _state); +int GetStateIn (struct alias_link *_lnk); +int GetStateOut(struct alias_link *_lnk); +struct in_addr + GetOriginalAddress(struct alias_link *_lnk); +struct in_addr + GetDestAddress(struct alias_link *_lnk); +struct in_addr + GetAliasAddress(struct alias_link *_lnk); +struct in_addr + GetDefaultAliasAddress(struct libalias *la); +void SetDefaultAliasAddress(struct libalias *la, struct in_addr _alias_addr); +u_short GetOriginalPort(struct alias_link *_lnk); +u_short GetAliasPort(struct alias_link *_lnk); +struct in_addr + GetProxyAddress(struct alias_link *_lnk); +void SetProxyAddress(struct alias_link *_lnk, struct in_addr _addr); +u_short GetProxyPort(struct alias_link *_lnk); +void SetProxyPort(struct alias_link *_lnk, u_short _port); +void SetAckModified(struct alias_link *_lnk); +int GetAckModified(struct alias_link *_lnk); +int GetDeltaAckIn(struct ip *_pip, struct alias_link *_lnk); +int GetDeltaSeqOut(struct ip *_pip, struct alias_link *_lnk); +void AddSeq (struct ip *_pip, struct alias_link *_lnk, int _delta); +void SetExpire (struct alias_link *_lnk, int _expire); +void ClearCheckNewLink(struct libalias *la); +void SetProtocolFlags(struct alias_link *_lnk, int _pflags); +int GetProtocolFlags(struct alias_link *_lnk); +void SetDestCallId(struct alias_link *_lnk, u_int16_t _cid); + +#ifndef NO_FW_PUNCH +void PunchFWHole(struct alias_link *_lnk); + +#endif + +/* Housekeeping function */ +void HouseKeeping(struct libalias *); + +/* Tcp specfic routines */ +/* lint -save -library Suppress flexelint warnings */ + +/* FTP routines */ +void +AliasHandleFtpOut(struct libalias *la, struct ip *_pip, struct alias_link *_lnk, + int _maxpacketsize); + +/* IRC routines */ +void +AliasHandleIrcOut(struct libalias *la, struct ip *_pip, struct alias_link *_lnk, + int _maxsize); + +/* RTSP routines */ +void +AliasHandleRtspOut(struct libalias *la, struct ip *_pip, struct alias_link *_lnk, + int _maxpacketsize); + +/* PPTP routines */ +void AliasHandlePptpOut(struct libalias *la, struct ip *_pip, struct alias_link *_lnk); +void AliasHandlePptpIn(struct libalias *la, struct ip *_pip, struct alias_link *_lnk); +int AliasHandlePptpGreOut(struct libalias *la, struct ip *_pip); +int AliasHandlePptpGreIn(struct libalias *la, struct ip *_pip); + +/* NetBIOS routines */ +int +AliasHandleUdpNbt(struct libalias *la, struct ip *_pip, struct alias_link *_lnk, + struct in_addr *_alias_address, u_short _alias_port); +int +AliasHandleUdpNbtNS(struct libalias *la, struct ip *_pip, struct alias_link *_lnk, + struct in_addr *_alias_address, u_short * _alias_port, + struct in_addr *_original_address, u_short * _original_port); + +/* CUSeeMe routines */ +void AliasHandleCUSeeMeOut(struct libalias *la, struct ip *_pip, struct alias_link *_lnk); +void AliasHandleCUSeeMeIn(struct libalias *la, struct ip *_pip, struct in_addr _original_addr); + +/* Skinny routines */ +void AliasHandleSkinny(struct libalias *la, struct ip *_pip, struct alias_link *_lnk); + +/* Transparent proxy routines */ +int +ProxyCheck(struct libalias *la, struct ip *_pip, struct in_addr *_proxy_server_addr, + u_short * _proxy_server_port); +void +ProxyModify(struct libalias *la, struct alias_link *_lnk, struct ip *_pip, + int _maxpacketsize, int _proxy_type); + +enum alias_tcp_state { + ALIAS_TCP_STATE_NOT_CONNECTED, + ALIAS_TCP_STATE_CONNECTED, + ALIAS_TCP_STATE_DISCONNECTED +}; + +#if defined(_NETINET_IP_H_) +static __inline void * +ip_next(struct ip *iphdr) +{ + char *p = (char *)iphdr; + return (&p[iphdr->ip_hl * 4]); +} +#endif + +#if defined(_NETINET_TCP_H_) +static __inline void * +tcp_next(struct tcphdr *tcphdr) +{ + char *p = (char *)tcphdr; + return (&p[tcphdr->th_off * 4]); +} +#endif + +#if defined(_NETINET_UDP_H_) +static __inline void * +udp_next(struct udphdr *udphdr) +{ + return ((void *)(udphdr + 1)); +} +#endif + +/*lint -restore */ + +#endif /* !_ALIAS_LOCAL_H_ */ diff --git a/lib/libalias/alias_nbt.c b/lib/libalias/alias_nbt.c new file mode 100644 index 0000000000000..b51abd1c5da56 --- /dev/null +++ b/lib/libalias/alias_nbt.c @@ -0,0 +1,722 @@ +/*- + * Written by Atsushi Murai <amurai@spec.co.jp> + * Copyright (c) 1998, System Planning and Engineering Co. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * TODO: + * oClean up. + * oConsidering for word alignment for other platform. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + alias_nbt.c performs special processing for NetBios over TCP/IP + sessions by UDP. + + Initial version: May, 1998 (Atsushi Murai <amurai@spec.co.jp>) + + See HISTORY file for record of revisions. +*/ + +/* Includes */ +#include <ctype.h> +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <netinet/in_systm.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netinet/ip.h> +#include <netinet/udp.h> +#include <netinet/tcp.h> + +#include "alias_local.h" + +typedef struct { + struct in_addr oldaddr; + u_short oldport; + struct in_addr newaddr; + u_short newport; + u_short *uh_sum; +} NBTArguments; + +typedef struct { + unsigned char type; + unsigned char flags; + u_short id; + struct in_addr source_ip; + u_short source_port; + u_short len; + u_short offset; +} NbtDataHeader; + +#define OpQuery 0 +#define OpUnknown 4 +#define OpRegist 5 +#define OpRelease 6 +#define OpWACK 7 +#define OpRefresh 8 +typedef struct { + u_short nametrid; + u_short dir: 1, opcode:4, nmflags:7, rcode:4; + u_short qdcount; + u_short ancount; + u_short nscount; + u_short arcount; +} NbtNSHeader; + +#define FMT_ERR 0x1 +#define SRV_ERR 0x2 +#define IMP_ERR 0x4 +#define RFS_ERR 0x5 +#define ACT_ERR 0x6 +#define CFT_ERR 0x7 + + +#ifdef DEBUG +static void +PrintRcode(u_char rcode) +{ + + switch (rcode) { + case FMT_ERR: + printf("\nFormat Error."); + case SRV_ERR: + printf("\nSever failure."); + case IMP_ERR: + printf("\nUnsupported request error.\n"); + case RFS_ERR: + printf("\nRefused error.\n"); + case ACT_ERR: + printf("\nActive error.\n"); + case CFT_ERR: + printf("\nName in conflict error.\n"); + default: + printf("\n?%c?=%0x\n", '?', rcode); + + } +} + +#endif + + +/* Handling Name field */ +static u_char * +AliasHandleName(u_char * p, char *pmax) +{ + + u_char *s; + u_char c; + int compress; + + /* Following length field */ + + if (p == NULL || (char *)p >= pmax) + return (NULL); + + if (*p & 0xc0) { + p = p + 2; + if ((char *)p > pmax) + return (NULL); + return ((u_char *) p); + } + while ((*p & 0x3f) != 0x00) { + s = p + 1; + if (*p == 0x20) + compress = 1; + else + compress = 0; + + /* Get next length field */ + p = (u_char *) (p + (*p & 0x3f) + 1); + if ((char *)p > pmax) { + p = NULL; + break; + } +#ifdef DEBUG + printf(":"); +#endif + while (s < p) { + if (compress == 1) { + c = (u_char) (((((*s & 0x0f) << 4) | (*(s + 1) & 0x0f)) - 0x11)); +#ifdef DEBUG + if (isprint(c)) + printf("%c", c); + else + printf("<0x%02x>", c); +#endif + s += 2; + } else { +#ifdef DEBUG + printf("%c", *s); +#endif + s++; + } + } +#ifdef DEBUG + printf(":"); +#endif + fflush(stdout); + } + + /* Set up to out of Name field */ + if (p == NULL || (char *)p >= pmax) + p = NULL; + else + p++; + return ((u_char *) p); +} + +/* + * NetBios Datagram Handler (IP/UDP) + */ +#define DGM_DIRECT_UNIQ 0x10 +#define DGM_DIRECT_GROUP 0x11 +#define DGM_BROADCAST 0x12 +#define DGM_ERROR 0x13 +#define DGM_QUERY 0x14 +#define DGM_POSITIVE_RES 0x15 +#define DGM_NEGATIVE_RES 0x16 + +int +AliasHandleUdpNbt( + struct libalias *la, + struct ip *pip, /* IP packet to examine/patch */ + struct alias_link *lnk, + struct in_addr *alias_address, + u_short alias_port +) +{ + struct udphdr *uh; + NbtDataHeader *ndh; + u_char *p = NULL; + char *pmax; + + (void)la; + (void)lnk; + + /* Calculate data length of UDP packet */ + uh = (struct udphdr *)ip_next(pip); + pmax = (char *)uh + ntohs(uh->uh_ulen); + + ndh = (NbtDataHeader *)udp_next(uh); + if ((char *)(ndh + 1) > pmax) + return (-1); +#ifdef DEBUG + printf("\nType=%02x,", ndh->type); +#endif + switch (ndh->type) { + case DGM_DIRECT_UNIQ: + case DGM_DIRECT_GROUP: + case DGM_BROADCAST: + p = (u_char *) ndh + 14; + p = AliasHandleName(p, pmax); /* Source Name */ + p = AliasHandleName(p, pmax); /* Destination Name */ + break; + case DGM_ERROR: + p = (u_char *) ndh + 11; + break; + case DGM_QUERY: + case DGM_POSITIVE_RES: + case DGM_NEGATIVE_RES: + p = (u_char *) ndh + 10; + p = AliasHandleName(p, pmax); /* Destination Name */ + break; + } + if (p == NULL || (char *)p > pmax) + p = NULL; +#ifdef DEBUG + printf("%s:%d-->", inet_ntoa(ndh->source_ip), ntohs(ndh->source_port)); +#endif + /* Doing an IP address and Port number Translation */ + if (uh->uh_sum != 0) { + int acc; + u_short *sptr; + + acc = ndh->source_port; + acc -= alias_port; + sptr = (u_short *) & (ndh->source_ip); + acc += *sptr++; + acc += *sptr; + sptr = (u_short *) alias_address; + acc -= *sptr++; + acc -= *sptr; + ADJUST_CHECKSUM(acc, uh->uh_sum); + } + ndh->source_ip = *alias_address; + ndh->source_port = alias_port; +#ifdef DEBUG + printf("%s:%d\n", inet_ntoa(ndh->source_ip), ntohs(ndh->source_port)); + fflush(stdout); +#endif + return ((p == NULL) ? -1 : 0); +} + +/* Question Section */ +#define QS_TYPE_NB 0x0020 +#define QS_TYPE_NBSTAT 0x0021 +#define QS_CLAS_IN 0x0001 +typedef struct { + u_short type; /* The type of Request */ + u_short class; /* The class of Request */ +} NBTNsQuestion; + +static u_char * +AliasHandleQuestion( + u_short count, + NBTNsQuestion * q, + char *pmax, + NBTArguments * nbtarg) +{ + + (void)nbtarg; + + while (count != 0) { + /* Name Filed */ + q = (NBTNsQuestion *) AliasHandleName((u_char *) q, pmax); + + if (q == NULL || (char *)(q + 1) > pmax) { + q = NULL; + break; + } + /* Type and Class filed */ + switch (ntohs(q->type)) { + case QS_TYPE_NB: + case QS_TYPE_NBSTAT: + q = q + 1; + break; + default: +#ifdef DEBUG + printf("\nUnknown Type on Question %0x\n", ntohs(q->type)); +#endif + break; + } + count--; + } + + /* Set up to out of Question Section */ + return ((u_char *) q); +} + +/* Resource Record */ +#define RR_TYPE_A 0x0001 +#define RR_TYPE_NS 0x0002 +#define RR_TYPE_NULL 0x000a +#define RR_TYPE_NB 0x0020 +#define RR_TYPE_NBSTAT 0x0021 +#define RR_CLAS_IN 0x0001 +#define SizeOfNsResource 8 +typedef struct { + u_short type; + u_short class; + unsigned int ttl; + u_short rdlen; +} NBTNsResource; + +#define SizeOfNsRNB 6 +typedef struct { + u_short g: 1 , ont:2, resv:13; + struct in_addr addr; +} NBTNsRNB; + +static u_char * +AliasHandleResourceNB( + NBTNsResource * q, + char *pmax, + NBTArguments * nbtarg) +{ + NBTNsRNB *nb; + u_short bcount; + + if (q == NULL || (char *)(q + 1) > pmax) + return (NULL); + /* Check out a length */ + bcount = ntohs(q->rdlen); + + /* Forward to Resource NB position */ + nb = (NBTNsRNB *) ((u_char *) q + SizeOfNsResource); + + /* Processing all in_addr array */ +#ifdef DEBUG + printf("NB rec[%s", inet_ntoa(nbtarg->oldaddr)); + printf("->%s, %dbytes] ", inet_ntoa(nbtarg->newaddr), bcount); +#endif + while (nb != NULL && bcount != 0) { + if ((char *)(nb + 1) > pmax) { + nb = NULL; + break; + } +#ifdef DEBUG + printf("<%s>", inet_ntoa(nb->addr)); +#endif + if (!bcmp(&nbtarg->oldaddr, &nb->addr, sizeof(struct in_addr))) { + if (*nbtarg->uh_sum != 0) { + int acc; + u_short *sptr; + + sptr = (u_short *) & (nb->addr); + acc = *sptr++; + acc += *sptr; + sptr = (u_short *) & (nbtarg->newaddr); + acc -= *sptr++; + acc -= *sptr; + ADJUST_CHECKSUM(acc, *nbtarg->uh_sum); + } + nb->addr = nbtarg->newaddr; +#ifdef DEBUG + printf("O"); +#endif + } +#ifdef DEBUG + else { + printf("."); + } +#endif + nb = (NBTNsRNB *) ((u_char *) nb + SizeOfNsRNB); + bcount -= SizeOfNsRNB; + } + if (nb == NULL || (char *)(nb + 1) > pmax) { + nb = NULL; + } + return ((u_char *) nb); +} + +#define SizeOfResourceA 6 +typedef struct { + struct in_addr addr; +} NBTNsResourceA; + +static u_char * +AliasHandleResourceA( + NBTNsResource * q, + char *pmax, + NBTArguments * nbtarg) +{ + NBTNsResourceA *a; + u_short bcount; + + if (q == NULL || (char *)(q + 1) > pmax) + return (NULL); + + /* Forward to Resource A position */ + a = (NBTNsResourceA *) ((u_char *) q + sizeof(NBTNsResource)); + + /* Check out of length */ + bcount = ntohs(q->rdlen); + + /* Processing all in_addr array */ +#ifdef DEBUG + printf("Arec [%s", inet_ntoa(nbtarg->oldaddr)); + printf("->%s]", inet_ntoa(nbtarg->newaddr)); +#endif + while (bcount != 0) { + if (a == NULL || (char *)(a + 1) > pmax) + return (NULL); +#ifdef DEBUG + printf("..%s", inet_ntoa(a->addr)); +#endif + if (!bcmp(&nbtarg->oldaddr, &a->addr, sizeof(struct in_addr))) { + if (*nbtarg->uh_sum != 0) { + int acc; + u_short *sptr; + + sptr = (u_short *) & (a->addr); /* Old */ + acc = *sptr++; + acc += *sptr; + sptr = (u_short *) & nbtarg->newaddr; /* New */ + acc -= *sptr++; + acc -= *sptr; + ADJUST_CHECKSUM(acc, *nbtarg->uh_sum); + } + a->addr = nbtarg->newaddr; + } + a++; /* XXXX */ + bcount -= SizeOfResourceA; + } + if (a == NULL || (char *)(a + 1) > pmax) + a = NULL; + return ((u_char *) a); +} + +typedef struct { + u_short opcode:4, flags:8, resv:4; +} NBTNsResourceNULL; + +static u_char * +AliasHandleResourceNULL( + NBTNsResource * q, + char *pmax, + NBTArguments * nbtarg) +{ + NBTNsResourceNULL *n; + u_short bcount; + + (void)nbtarg; + + if (q == NULL || (char *)(q + 1) > pmax) + return (NULL); + + /* Forward to Resource NULL position */ + n = (NBTNsResourceNULL *) ((u_char *) q + sizeof(NBTNsResource)); + + /* Check out of length */ + bcount = ntohs(q->rdlen); + + /* Processing all in_addr array */ + while (bcount != 0) { + if ((char *)(n + 1) > pmax) { + n = NULL; + break; + } + n++; + bcount -= sizeof(NBTNsResourceNULL); + } + if ((char *)(n + 1) > pmax) + n = NULL; + + return ((u_char *) n); +} + +static u_char * +AliasHandleResourceNS( + NBTNsResource * q, + char *pmax, + NBTArguments * nbtarg) +{ + NBTNsResourceNULL *n; + u_short bcount; + + (void)nbtarg; + + if (q == NULL || (char *)(q + 1) > pmax) + return (NULL); + + /* Forward to Resource NULL position */ + n = (NBTNsResourceNULL *) ((u_char *) q + sizeof(NBTNsResource)); + + /* Check out of length */ + bcount = ntohs(q->rdlen); + + /* Resource Record Name Filed */ + q = (NBTNsResource *) AliasHandleName((u_char *) n, pmax); /* XXX */ + + if (q == NULL || (char *)((u_char *) n + bcount) > pmax) + return (NULL); + else + return ((u_char *) n + bcount); +} + +typedef struct { + u_short numnames; +} NBTNsResourceNBSTAT; + +static u_char * +AliasHandleResourceNBSTAT( + NBTNsResource * q, + char *pmax, + NBTArguments * nbtarg) +{ + NBTNsResourceNBSTAT *n; + u_short bcount; + + (void)nbtarg; + + if (q == NULL || (char *)(q + 1) > pmax) + return (NULL); + + /* Forward to Resource NBSTAT position */ + n = (NBTNsResourceNBSTAT *) ((u_char *) q + sizeof(NBTNsResource)); + + /* Check out of length */ + bcount = ntohs(q->rdlen); + + if (q == NULL || (char *)((u_char *) n + bcount) > pmax) + return (NULL); + else + return ((u_char *) n + bcount); +} + +static u_char * +AliasHandleResource( + u_short count, + NBTNsResource * q, + char *pmax, + NBTArguments + * nbtarg) +{ + while (count != 0) { + /* Resource Record Name Filed */ + q = (NBTNsResource *) AliasHandleName((u_char *) q, pmax); + + if (q == NULL || (char *)(q + 1) > pmax) + break; +#ifdef DEBUG + printf("type=%02x, count=%d\n", ntohs(q->type), count); +#endif + + /* Type and Class filed */ + switch (ntohs(q->type)) { + case RR_TYPE_NB: + q = (NBTNsResource *) AliasHandleResourceNB( + q, + pmax, + nbtarg + ); + break; + case RR_TYPE_A: + q = (NBTNsResource *) AliasHandleResourceA( + q, + pmax, + nbtarg + ); + break; + case RR_TYPE_NS: + q = (NBTNsResource *) AliasHandleResourceNS( + q, + pmax, + nbtarg + ); + break; + case RR_TYPE_NULL: + q = (NBTNsResource *) AliasHandleResourceNULL( + q, + pmax, + nbtarg + ); + break; + case RR_TYPE_NBSTAT: + q = (NBTNsResource *) AliasHandleResourceNBSTAT( + q, + pmax, + nbtarg + ); + break; + default: +#ifdef DEBUG + printf( + "\nUnknown Type of Resource %0x\n", + ntohs(q->type) + ); +#endif + break; + } + count--; + } + fflush(stdout); + return ((u_char *) q); +} + +int +AliasHandleUdpNbtNS( + struct libalias *la, + struct ip *pip, /* IP packet to examine/patch */ + struct alias_link *lnk, + struct in_addr *alias_address, + u_short * alias_port, + struct in_addr *original_address, + u_short * original_port) +{ + struct udphdr *uh; + NbtNSHeader *nsh; + u_char *p; + char *pmax; + NBTArguments nbtarg; + + (void)la; + (void)lnk; + + /* Set up Common Parameter */ + nbtarg.oldaddr = *alias_address; + nbtarg.oldport = *alias_port; + nbtarg.newaddr = *original_address; + nbtarg.newport = *original_port; + + /* Calculate data length of UDP packet */ + uh = (struct udphdr *)ip_next(pip); + nbtarg.uh_sum = &(uh->uh_sum); + nsh = (NbtNSHeader *)udp_next(uh); + p = (u_char *) (nsh + 1); + pmax = (char *)uh + ntohs(uh->uh_ulen); + + if ((char *)(nsh + 1) > pmax) + return (-1); + +#ifdef DEBUG + printf(" [%s] ID=%02x, op=%01x, flag=%02x, rcode=%01x, qd=%04x" + ", an=%04x, ns=%04x, ar=%04x, [%d]-->", + nsh->dir ? "Response" : "Request", + nsh->nametrid, + nsh->opcode, + nsh->nmflags, + nsh->rcode, + ntohs(nsh->qdcount), + ntohs(nsh->ancount), + ntohs(nsh->nscount), + ntohs(nsh->arcount), + (u_char *) p - (u_char *) nsh + ); +#endif + + /* Question Entries */ + if (ntohs(nsh->qdcount) != 0) { + p = AliasHandleQuestion( + ntohs(nsh->qdcount), + (NBTNsQuestion *) p, + pmax, + &nbtarg + ); + } + /* Answer Resource Records */ + if (ntohs(nsh->ancount) != 0) { + p = AliasHandleResource( + ntohs(nsh->ancount), + (NBTNsResource *) p, + pmax, + &nbtarg + ); + } + /* Authority Resource Recodrs */ + if (ntohs(nsh->nscount) != 0) { + p = AliasHandleResource( + ntohs(nsh->nscount), + (NBTNsResource *) p, + pmax, + &nbtarg + ); + } + /* Additional Resource Recodrs */ + if (ntohs(nsh->arcount) != 0) { + p = AliasHandleResource( + ntohs(nsh->arcount), + (NBTNsResource *) p, + pmax, + &nbtarg + ); + } +#ifdef DEBUG + PrintRcode(nsh->rcode); +#endif + return ((p == NULL) ? -1 : 0); +} diff --git a/lib/libalias/alias_old.c b/lib/libalias/alias_old.c new file mode 100644 index 0000000000000..089ecef55aeed --- /dev/null +++ b/lib/libalias/alias_old.c @@ -0,0 +1,205 @@ +/*- + * Copyright (c) 2004 Poul-Henning Kamp <phk@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <stdlib.h> +#include <sys/types.h> +#include <netinet/in.h> +#include "alias.h" + +/* + * These functions are for backwards compatibility and because apps may + * be linked against shlib versions, they have to be actual functions, + * we cannot inline them. + */ + +static struct libalias *la; + +void +PacketAliasInit(void) +{ + + la = LibAliasInit(la); +} + +void +PacketAliasSetAddress(struct in_addr _addr) +{ + + LibAliasSetAddress(la, _addr); +} + +void +PacketAliasSetFWBase(unsigned int _base, unsigned int _num) +{ + + LibAliasSetFWBase(la, _base, _num); +} + +void +PacketAliasSetSkinnyPort(unsigned int _port) +{ + + LibAliasSetSkinnyPort(la, _port); +} + +unsigned int +PacketAliasSetMode(unsigned int _flags, unsigned int _mask) +{ + + return LibAliasSetMode(la, _flags, _mask); +} + +void +PacketAliasUninit(void) +{ + + LibAliasUninit(la); + la = NULL; +} + +int +PacketAliasIn(char *_ptr, int _maxpacketsize) +{ + return LibAliasIn(la, _ptr, _maxpacketsize); +} + +int +PacketAliasOut(char *_ptr, int _maxpacketsize) +{ + + return LibAliasOut(la, _ptr, _maxpacketsize); +} + +int +PacketUnaliasOut(char *_ptr, int _maxpacketsize) +{ + + return LibAliasUnaliasOut(la, _ptr, _maxpacketsize); +} + +int +PacketAliasAddServer(struct alias_link *_lnk, + struct in_addr _addr, unsigned short _port) +{ + + return LibAliasAddServer(la, _lnk, _addr, _port); +} + +struct alias_link * +PacketAliasRedirectAddr(struct in_addr _src_addr, + struct in_addr _alias_addr) +{ + + return LibAliasRedirectAddr(la, _src_addr, _alias_addr); +} + + +int +PacketAliasRedirectDynamic(struct alias_link *_lnk) +{ + + return LibAliasRedirectDynamic(la, _lnk); +} + +void +PacketAliasRedirectDelete(struct alias_link *_lnk) +{ + + LibAliasRedirectDelete(la, _lnk); +} + +struct alias_link * +PacketAliasRedirectPort(struct in_addr _src_addr, + unsigned short _src_port, struct in_addr _dst_addr, + unsigned short _dst_port, struct in_addr _alias_addr, + unsigned short _alias_port, unsigned char _proto) +{ + + return LibAliasRedirectPort(la, _src_addr, _src_port, _dst_addr, + _dst_port, _alias_addr, _alias_port, _proto); +} + +struct alias_link * +PacketAliasRedirectProto(struct in_addr _src_addr, + struct in_addr _dst_addr, struct in_addr _alias_addr, + unsigned char _proto) +{ + + return LibAliasRedirectProto(la, _src_addr, _dst_addr, _alias_addr, + _proto); +} + +void +PacketAliasFragmentIn(char *_ptr, char *_ptr_fragment) +{ + + LibAliasFragmentIn(la, _ptr, _ptr_fragment); +} + +char * +PacketAliasGetFragment(char *_ptr) +{ + + return LibAliasGetFragment(la, _ptr); +} + +int +PacketAliasSaveFragment(char *_ptr) +{ + return LibAliasSaveFragment(la, _ptr); +} + +int +PacketAliasCheckNewLink(void) +{ + + return LibAliasCheckNewLink(la); +} + +unsigned short +PacketAliasInternetChecksum(unsigned short *_ptr, int _nbytes) +{ + + return LibAliasInternetChecksum(la, _ptr, _nbytes); +} + +void +PacketAliasSetTarget(struct in_addr _target_addr) +{ + + LibAliasSetTarget(la, _target_addr); +} + +/* Transparent proxying routines. */ +int +PacketAliasProxyRule(const char *_cmd) +{ + + return LibAliasProxyRule(la, _cmd); +} diff --git a/lib/libalias/alias_pptp.c b/lib/libalias/alias_pptp.c new file mode 100644 index 0000000000000..05722b10813d7 --- /dev/null +++ b/lib/libalias/alias_pptp.c @@ -0,0 +1,373 @@ +/* + * alias_pptp.c + * + * Copyright (c) 2000 Whistle Communications, Inc. + * All rights reserved. + * + * Subject to the following obligations and disclaimer of warranty, use and + * redistribution of this software, in source or object code forms, with or + * without modifications are expressly permitted by Whistle Communications; + * provided, however, that: + * 1. Any and all reproductions of the source or object code must include the + * copyright notice above and the following disclaimer of warranties; and + * 2. No rights are granted, in any manner or form, to use Whistle + * Communications, Inc. trademarks, including the mark "WHISTLE + * COMMUNICATIONS" on advertising, endorsements, or otherwise except as + * such appears in the above copyright notice or in the software. + * + * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND + * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO + * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, + * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. + * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY + * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS + * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. + * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES + * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING + * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Erik Salander <erik@whistle.com> + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + Alias_pptp.c performs special processing for PPTP sessions under TCP. + Specifically, watch PPTP control messages and alias the Call ID or the + Peer's Call ID in the appropriate messages. Note, PPTP requires + "de-aliasing" of incoming packets, this is different than any other + TCP applications that are currently (ie. FTP, IRC and RTSP) aliased. + + For Call IDs encountered for the first time, a PPTP alias link is created. + The PPTP alias link uses the Call ID in place of the original port number. + An alias Call ID is created. + + For this routine to work, the PPTP control messages must fit entirely + into a single TCP packet. This is typically the case, but is not + required by the spec. + + Unlike some of the other TCP applications that are aliased (ie. FTP, + IRC and RTSP), the PPTP control messages that need to be aliased are + guaranteed to remain the same length. The aliased Call ID is a fixed + length field. + + Reference: RFC 2637 + + Initial version: May, 2000 (eds) + +*/ + +/* Includes */ +#include <sys/types.h> +#include <netinet/in_systm.h> +#include <netinet/in.h> +#include <netinet/ip.h> +#include <netinet/tcp.h> + +#include <stdio.h> + +#include "alias_local.h" + +/* + * PPTP definitions + */ + +struct grehdr { /* Enhanced GRE header. */ + u_int16_t gh_flags; /* Flags. */ + u_int16_t gh_protocol; /* Protocol type. */ + u_int16_t gh_length; /* Payload length. */ + u_int16_t gh_call_id; /* Call ID. */ + u_int32_t gh_seq_no; /* Sequence number (optional). */ + u_int32_t gh_ack_no; /* Acknowledgment number + * (optional). */ +}; +typedef struct grehdr GreHdr; + +/* The PPTP protocol ID used in the GRE 'proto' field. */ +#define PPTP_GRE_PROTO 0x880b + +/* Bits that must be set a certain way in all PPTP/GRE packets. */ +#define PPTP_INIT_VALUE ((0x2001 << 16) | PPTP_GRE_PROTO) +#define PPTP_INIT_MASK 0xef7fffff + +#define PPTP_MAGIC 0x1a2b3c4d +#define PPTP_CTRL_MSG_TYPE 1 + +enum { + PPTP_StartCtrlConnRequest = 1, + PPTP_StartCtrlConnReply = 2, + PPTP_StopCtrlConnRequest = 3, + PPTP_StopCtrlConnReply = 4, + PPTP_EchoRequest = 5, + PPTP_EchoReply = 6, + PPTP_OutCallRequest = 7, + PPTP_OutCallReply = 8, + PPTP_InCallRequest = 9, + PPTP_InCallReply = 10, + PPTP_InCallConn = 11, + PPTP_CallClearRequest = 12, + PPTP_CallDiscNotify = 13, + PPTP_WanErrorNotify = 14, + PPTP_SetLinkInfo = 15 +}; + + /* Message structures */ +struct pptpMsgHead { + u_int16_t length; /* total length */ + u_int16_t msgType;/* PPTP message type */ + u_int32_t magic; /* magic cookie */ + u_int16_t type; /* control message type */ + u_int16_t resv0; /* reserved */ +}; +typedef struct pptpMsgHead *PptpMsgHead; + +struct pptpCodes { + u_int8_t resCode;/* Result Code */ + u_int8_t errCode;/* Error Code */ +}; +typedef struct pptpCodes *PptpCode; + +struct pptpCallIds { + u_int16_t cid1; /* Call ID field #1 */ + u_int16_t cid2; /* Call ID field #2 */ +}; +typedef struct pptpCallIds *PptpCallId; + +static PptpCallId AliasVerifyPptp(struct ip *, u_int16_t *); + + +void +AliasHandlePptpOut(struct libalias *la, + struct ip *pip, /* IP packet to examine/patch */ + struct alias_link *lnk) +{ /* The PPTP control link */ + struct alias_link *pptp_lnk; + PptpCallId cptr; + PptpCode codes; + u_int16_t ctl_type; /* control message type */ + struct tcphdr *tc; + + /* Verify valid PPTP control message */ + if ((cptr = AliasVerifyPptp(pip, &ctl_type)) == NULL) + return; + + /* Modify certain PPTP messages */ + switch (ctl_type) { + case PPTP_OutCallRequest: + case PPTP_OutCallReply: + case PPTP_InCallRequest: + case PPTP_InCallReply: + /* + * Establish PPTP link for address and Call ID found in + * control message. + */ + pptp_lnk = AddPptp(la, GetOriginalAddress(lnk), GetDestAddress(lnk), + GetAliasAddress(lnk), cptr->cid1); + break; + case PPTP_CallClearRequest: + case PPTP_CallDiscNotify: + /* + * Find PPTP link for address and Call ID found in control + * message. + */ + pptp_lnk = FindPptpOutByCallId(la, GetOriginalAddress(lnk), + GetDestAddress(lnk), + cptr->cid1); + break; + default: + return; + } + + if (pptp_lnk != NULL) { + int accumulate = cptr->cid1; + + /* alias the Call Id */ + cptr->cid1 = GetAliasPort(pptp_lnk); + + /* Compute TCP checksum for revised packet */ + tc = (struct tcphdr *)ip_next(pip); + accumulate -= cptr->cid1; + ADJUST_CHECKSUM(accumulate, tc->th_sum); + + switch (ctl_type) { + case PPTP_OutCallReply: + case PPTP_InCallReply: + codes = (PptpCode) (cptr + 1); + if (codes->resCode == 1) /* Connection + * established, */ + SetDestCallId(pptp_lnk, /* note the Peer's Call + * ID. */ + cptr->cid2); + else + SetExpire(pptp_lnk, 0); /* Connection refused. */ + break; + case PPTP_CallDiscNotify: /* Connection closed. */ + SetExpire(pptp_lnk, 0); + break; + } + } +} + +void +AliasHandlePptpIn(struct libalias *la, + struct ip *pip, /* IP packet to examine/patch */ + struct alias_link *lnk) +{ /* The PPTP control link */ + struct alias_link *pptp_lnk; + PptpCallId cptr; + u_int16_t *pcall_id; + u_int16_t ctl_type; /* control message type */ + struct tcphdr *tc; + + /* Verify valid PPTP control message */ + if ((cptr = AliasVerifyPptp(pip, &ctl_type)) == NULL) + return; + + /* Modify certain PPTP messages */ + switch (ctl_type) { + case PPTP_InCallConn: + case PPTP_WanErrorNotify: + case PPTP_SetLinkInfo: + pcall_id = &cptr->cid1; + break; + case PPTP_OutCallReply: + case PPTP_InCallReply: + pcall_id = &cptr->cid2; + break; + case PPTP_CallDiscNotify: /* Connection closed. */ + pptp_lnk = FindPptpInByCallId(la, GetDestAddress(lnk), + GetAliasAddress(lnk), + cptr->cid1); + if (pptp_lnk != NULL) + SetExpire(pptp_lnk, 0); + return; + default: + return; + } + + /* Find PPTP link for address and Call ID found in PPTP Control Msg */ + pptp_lnk = FindPptpInByPeerCallId(la, GetDestAddress(lnk), + GetAliasAddress(lnk), + *pcall_id); + + if (pptp_lnk != NULL) { + int accumulate = *pcall_id; + + /* De-alias the Peer's Call Id. */ + *pcall_id = GetOriginalPort(pptp_lnk); + + /* Compute TCP checksum for modified packet */ + tc = (struct tcphdr *)ip_next(pip); + accumulate -= *pcall_id; + ADJUST_CHECKSUM(accumulate, tc->th_sum); + + if (ctl_type == PPTP_OutCallReply || ctl_type == PPTP_InCallReply) { + PptpCode codes = (PptpCode) (cptr + 1); + + if (codes->resCode == 1) /* Connection + * established, */ + SetDestCallId(pptp_lnk, /* note the Call ID. */ + cptr->cid1); + else + SetExpire(pptp_lnk, 0); /* Connection refused. */ + } + } +} + +static PptpCallId +AliasVerifyPptp(struct ip *pip, u_int16_t * ptype) +{ /* IP packet to examine/patch */ + int hlen, tlen, dlen; + PptpMsgHead hptr; + struct tcphdr *tc; + + /* Calculate some lengths */ + tc = (struct tcphdr *)ip_next(pip); + hlen = (pip->ip_hl + tc->th_off) << 2; + tlen = ntohs(pip->ip_len); + dlen = tlen - hlen; + + /* Verify data length */ + if (dlen < (int)(sizeof(struct pptpMsgHead) + sizeof(struct pptpCallIds))) + return (NULL); + + /* Move up to PPTP message header */ + hptr = (PptpMsgHead) tcp_next(tc); + + /* Return the control message type */ + *ptype = ntohs(hptr->type); + + /* Verify PPTP Control Message */ + if ((ntohs(hptr->msgType) != PPTP_CTRL_MSG_TYPE) || + (ntohl(hptr->magic) != PPTP_MAGIC)) + return (NULL); + + /* Verify data length. */ + if ((*ptype == PPTP_OutCallReply || *ptype == PPTP_InCallReply) && + (dlen < (int)(sizeof(struct pptpMsgHead) + sizeof(struct pptpCallIds) + + sizeof(struct pptpCodes)))) + return (NULL); + else + return (PptpCallId) (hptr + 1); +} + + +int +AliasHandlePptpGreOut(struct libalias *la, struct ip *pip) +{ + GreHdr *gr; + struct alias_link *lnk; + + gr = (GreHdr *) ip_next(pip); + + /* Check GRE header bits. */ + if ((ntohl(*((u_int32_t *) gr)) & PPTP_INIT_MASK) != PPTP_INIT_VALUE) + return (-1); + + lnk = FindPptpOutByPeerCallId(la, pip->ip_src, pip->ip_dst, gr->gh_call_id); + if (lnk != NULL) { + struct in_addr alias_addr = GetAliasAddress(lnk); + + /* Change source IP address. */ + DifferentialChecksum(&pip->ip_sum, + &alias_addr, &pip->ip_src, 2); + pip->ip_src = alias_addr; + } + return (0); +} + + +int +AliasHandlePptpGreIn(struct libalias *la, struct ip *pip) +{ + GreHdr *gr; + struct alias_link *lnk; + + gr = (GreHdr *) ip_next(pip); + + /* Check GRE header bits. */ + if ((ntohl(*((u_int32_t *) gr)) & PPTP_INIT_MASK) != PPTP_INIT_VALUE) + return (-1); + + lnk = FindPptpInByPeerCallId(la, pip->ip_src, pip->ip_dst, gr->gh_call_id); + if (lnk != NULL) { + struct in_addr src_addr = GetOriginalAddress(lnk); + + /* De-alias the Peer's Call Id. */ + gr->gh_call_id = GetOriginalPort(lnk); + + /* Restore original IP address. */ + DifferentialChecksum(&pip->ip_sum, + &src_addr, &pip->ip_dst, 2); + pip->ip_dst = src_addr; + } + return (0); +} diff --git a/lib/libalias/alias_proxy.c b/lib/libalias/alias_proxy.c new file mode 100644 index 0000000000000..e0e1c059ed68f --- /dev/null +++ b/lib/libalias/alias_proxy.c @@ -0,0 +1,812 @@ +/*- + * Copyright (c) 2001 Charles Mott <cm@linktel.net> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* file: alias_proxy.c + + This file encapsulates special operations related to transparent + proxy redirection. This is where packets with a particular destination, + usually tcp port 80, are redirected to a proxy server. + + When packets are proxied, the destination address and port are + modified. In certain cases, it is necessary to somehow encode + the original address/port info into the packet. Two methods are + presently supported: addition of a [DEST addr port] string at the + beginning of a tcp stream, or inclusion of an optional field + in the IP header. + + There is one public API function: + + PacketAliasProxyRule() -- Adds and deletes proxy + rules. + + Rules are stored in a linear linked list, so lookup efficiency + won't be too good for large lists. + + + Initial development: April, 1998 (cjm) +*/ + + +/* System includes */ +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <netdb.h> + +#include <sys/types.h> +#include <sys/socket.h> + +/* BSD IPV4 includes */ +#include <netinet/in_systm.h> +#include <netinet/in.h> +#include <netinet/ip.h> +#include <netinet/tcp.h> + +#include <arpa/inet.h> + +#include "alias_local.h" /* Functions used by alias*.c */ +#include "alias.h" /* Public API functions for libalias */ + + + +/* + Data structures + */ + +/* + * A linked list of arbitrary length, based on struct proxy_entry is + * used to store proxy rules. + */ +struct proxy_entry { + struct libalias *la; +#define PROXY_TYPE_ENCODE_NONE 1 +#define PROXY_TYPE_ENCODE_TCPSTREAM 2 +#define PROXY_TYPE_ENCODE_IPHDR 3 + int rule_index; + int proxy_type; + u_char proto; + u_short proxy_port; + u_short server_port; + + struct in_addr server_addr; + + struct in_addr src_addr; + struct in_addr src_mask; + + struct in_addr dst_addr; + struct in_addr dst_mask; + + struct proxy_entry *next; + struct proxy_entry *last; +}; + + + +/* + File scope variables +*/ + + + +/* Local (static) functions: + + IpMask() -- Utility function for creating IP + masks from integer (1-32) specification. + IpAddr() -- Utility function for converting string + to IP address + IpPort() -- Utility function for converting string + to port number + RuleAdd() -- Adds an element to the rule list. + RuleDelete() -- Removes an element from the rule list. + RuleNumberDelete() -- Removes all elements from the rule list + having a certain rule number. + ProxyEncodeTcpStream() -- Adds [DEST x.x.x.x xxxx] to the beginning + of a TCP stream. + ProxyEncodeIpHeader() -- Adds an IP option indicating the true + destination of a proxied IP packet +*/ + +static int IpMask(int, struct in_addr *); +static int IpAddr(char *, struct in_addr *); +static int IpPort(char *, int, int *); +static void RuleAdd(struct libalias *la, struct proxy_entry *); +static void RuleDelete(struct proxy_entry *); +static int RuleNumberDelete(struct libalias *la, int); +static void ProxyEncodeTcpStream(struct alias_link *, struct ip *, int); +static void ProxyEncodeIpHeader(struct ip *, int); + +static int +IpMask(int nbits, struct in_addr *mask) +{ + int i; + u_int imask; + + if (nbits < 0 || nbits > 32) + return (-1); + + imask = 0; + for (i = 0; i < nbits; i++) + imask = (imask >> 1) + 0x80000000; + mask->s_addr = htonl(imask); + + return (0); +} + +static int +IpAddr(char *s, struct in_addr *addr) +{ + if (inet_aton(s, addr) == 0) + return (-1); + else + return (0); +} + +static int +IpPort(char *s, int proto, int *port) +{ + int n; + + n = sscanf(s, "%d", port); + if (n != 1) { + struct servent *se; + + if (proto == IPPROTO_TCP) + se = getservbyname(s, "tcp"); + else if (proto == IPPROTO_UDP) + se = getservbyname(s, "udp"); + else + return (-1); + + if (se == NULL) + return (-1); + + *port = (u_int) ntohs(se->s_port); + } + return (0); +} + +void +RuleAdd(struct libalias *la, struct proxy_entry *entry) +{ + int rule_index; + struct proxy_entry *ptr; + struct proxy_entry *ptr_last; + + if (la->proxyList == NULL) { + la->proxyList = entry; + entry->last = NULL; + entry->next = NULL; + return; + } + entry->la = la; + + rule_index = entry->rule_index; + ptr = la->proxyList; + ptr_last = NULL; + while (ptr != NULL) { + if (ptr->rule_index >= rule_index) { + if (ptr_last == NULL) { + entry->next = la->proxyList; + entry->last = NULL; + la->proxyList->last = entry; + la->proxyList = entry; + return; + } + ptr_last->next = entry; + ptr->last = entry; + entry->last = ptr->last; + entry->next = ptr; + return; + } + ptr_last = ptr; + ptr = ptr->next; + } + + ptr_last->next = entry; + entry->last = ptr_last; + entry->next = NULL; +} + +static void +RuleDelete(struct proxy_entry *entry) +{ + struct libalias *la; + + la = entry->la; + if (entry->last != NULL) + entry->last->next = entry->next; + else + la->proxyList = entry->next; + + if (entry->next != NULL) + entry->next->last = entry->last; + + free(entry); +} + +static int +RuleNumberDelete(struct libalias *la, int rule_index) +{ + int err; + struct proxy_entry *ptr; + + err = -1; + ptr = la->proxyList; + while (ptr != NULL) { + struct proxy_entry *ptr_next; + + ptr_next = ptr->next; + if (ptr->rule_index == rule_index) { + err = 0; + RuleDelete(ptr); + } + ptr = ptr_next; + } + + return (err); +} + +static void +ProxyEncodeTcpStream(struct alias_link *lnk, + struct ip *pip, + int maxpacketsize) +{ + int slen; + char buffer[40]; + struct tcphdr *tc; + +/* Compute pointer to tcp header */ + tc = (struct tcphdr *)ip_next(pip); + +/* Don't modify if once already modified */ + + if (GetAckModified(lnk)) + return; + +/* Translate destination address and port to string form */ + snprintf(buffer, sizeof(buffer) - 2, "[DEST %s %d]", + inet_ntoa(GetProxyAddress(lnk)), (u_int) ntohs(GetProxyPort(lnk))); + +/* Pad string out to a multiple of two in length */ + slen = strlen(buffer); + switch (slen % 2) { + case 0: + strcat(buffer, " \n"); + slen += 2; + break; + case 1: + strcat(buffer, "\n"); + slen += 1; + } + +/* Check for packet overflow */ + if ((int)(ntohs(pip->ip_len) + strlen(buffer)) > maxpacketsize) + return; + +/* Shift existing TCP data and insert destination string */ + { + int dlen; + int hlen; + u_char *p; + + hlen = (pip->ip_hl + tc->th_off) << 2; + dlen = ntohs(pip->ip_len) - hlen; + +/* Modify first packet that has data in it */ + + if (dlen == 0) + return; + + p = (char *)pip; + p += hlen; + + memmove(p + slen, p, dlen); + memcpy(p, buffer, slen); + } + +/* Save information about modfied sequence number */ + { + int delta; + + SetAckModified(lnk); + delta = GetDeltaSeqOut(pip, lnk); + AddSeq(pip, lnk, delta + slen); + } + +/* Update IP header packet length and checksum */ + { + int accumulate; + + accumulate = pip->ip_len; + pip->ip_len = htons(ntohs(pip->ip_len) + slen); + accumulate -= pip->ip_len; + + ADJUST_CHECKSUM(accumulate, pip->ip_sum); + } + +/* Update TCP checksum, Use TcpChecksum since so many things have + already changed. */ + + tc->th_sum = 0; + tc->th_sum = TcpChecksum(pip); +} + +static void +ProxyEncodeIpHeader(struct ip *pip, + int maxpacketsize) +{ +#define OPTION_LEN_BYTES 8 +#define OPTION_LEN_INT16 4 +#define OPTION_LEN_INT32 2 + u_char option[OPTION_LEN_BYTES]; + +#ifdef DEBUG + fprintf(stdout, " ip cksum 1 = %x\n", (u_int) IpChecksum(pip)); + fprintf(stdout, "tcp cksum 1 = %x\n", (u_int) TcpChecksum(pip)); +#endif + + (void)maxpacketsize; + +/* Check to see that there is room to add an IP option */ + if (pip->ip_hl > (0x0f - OPTION_LEN_INT32)) + return; + +/* Build option and copy into packet */ + { + u_char *ptr; + struct tcphdr *tc; + + ptr = (u_char *) pip; + ptr += 20; + memcpy(ptr + OPTION_LEN_BYTES, ptr, ntohs(pip->ip_len) - 20); + + option[0] = 0x64; /* class: 3 (reserved), option 4 */ + option[1] = OPTION_LEN_BYTES; + + memcpy(&option[2], (u_char *) & pip->ip_dst, 4); + + tc = (struct tcphdr *)ip_next(pip); + memcpy(&option[6], (u_char *) & tc->th_sport, 2); + + memcpy(ptr, option, 8); + } + +/* Update checksum, header length and packet length */ + { + int i; + int accumulate; + u_short *sptr; + + sptr = (u_short *) option; + accumulate = 0; + for (i = 0; i < OPTION_LEN_INT16; i++) + accumulate -= *(sptr++); + + sptr = (u_short *) pip; + accumulate += *sptr; + pip->ip_hl += OPTION_LEN_INT32; + accumulate -= *sptr; + + accumulate += pip->ip_len; + pip->ip_len = htons(ntohs(pip->ip_len) + OPTION_LEN_BYTES); + accumulate -= pip->ip_len; + + ADJUST_CHECKSUM(accumulate, pip->ip_sum); + } +#undef OPTION_LEN_BYTES +#undef OPTION_LEN_INT16 +#undef OPTION_LEN_INT32 +#ifdef DEBUG + fprintf(stdout, " ip cksum 2 = %x\n", (u_int) IpChecksum(pip)); + fprintf(stdout, "tcp cksum 2 = %x\n", (u_int) TcpChecksum(pip)); +#endif +} + + +/* Functions by other packet alias source files + + ProxyCheck() -- Checks whether an outgoing packet should + be proxied. + ProxyModify() -- Encodes the original destination address/port + for a packet which is to be redirected to + a proxy server. +*/ + +int +ProxyCheck(struct libalias *la, struct ip *pip, + struct in_addr *proxy_server_addr, + u_short * proxy_server_port) +{ + u_short dst_port; + struct in_addr src_addr; + struct in_addr dst_addr; + struct proxy_entry *ptr; + + src_addr = pip->ip_src; + dst_addr = pip->ip_dst; + dst_port = ((struct tcphdr *)ip_next(pip)) + ->th_dport; + + ptr = la->proxyList; + while (ptr != NULL) { + u_short proxy_port; + + proxy_port = ptr->proxy_port; + if ((dst_port == proxy_port || proxy_port == 0) + && pip->ip_p == ptr->proto + && src_addr.s_addr != ptr->server_addr.s_addr) { + struct in_addr src_addr_masked; + struct in_addr dst_addr_masked; + + src_addr_masked.s_addr = src_addr.s_addr & ptr->src_mask.s_addr; + dst_addr_masked.s_addr = dst_addr.s_addr & ptr->dst_mask.s_addr; + + if ((src_addr_masked.s_addr == ptr->src_addr.s_addr) + && (dst_addr_masked.s_addr == ptr->dst_addr.s_addr)) { + if ((*proxy_server_port = ptr->server_port) == 0) + *proxy_server_port = dst_port; + *proxy_server_addr = ptr->server_addr; + return (ptr->proxy_type); + } + } + ptr = ptr->next; + } + + return (0); +} + +void +ProxyModify(struct libalias *la, struct alias_link *lnk, + struct ip *pip, + int maxpacketsize, + int proxy_type) +{ + + (void)la; + + switch (proxy_type) { + case PROXY_TYPE_ENCODE_IPHDR: + ProxyEncodeIpHeader(pip, maxpacketsize); + break; + + case PROXY_TYPE_ENCODE_TCPSTREAM: + ProxyEncodeTcpStream(lnk, pip, maxpacketsize); + break; + } +} + + +/* + Public API functions +*/ + +int +LibAliasProxyRule(struct libalias *la, const char *cmd) +{ +/* + * This function takes command strings of the form: + * + * server <addr>[:<port>] + * [port <port>] + * [rule n] + * [proto tcp|udp] + * [src <addr>[/n]] + * [dst <addr>[/n]] + * [type encode_tcp_stream|encode_ip_hdr|no_encode] + * + * delete <rule number> + * + * Subfields can be in arbitrary order. Port numbers and addresses + * must be in either numeric or symbolic form. An optional rule number + * is used to control the order in which rules are searched. If two + * rules have the same number, then search order cannot be guaranteed, + * and the rules should be disjoint. If no rule number is specified, + * then 0 is used, and group 0 rules are always checked before any + * others. + */ + int i, n, len; + int cmd_len; + int token_count; + int state; + char *token; + char buffer[256]; + char str_port[sizeof(buffer)]; + char str_server_port[sizeof(buffer)]; + char *res = buffer; + + int rule_index; + int proto; + int proxy_type; + int proxy_port; + int server_port; + struct in_addr server_addr; + struct in_addr src_addr, src_mask; + struct in_addr dst_addr, dst_mask; + struct proxy_entry *proxy_entry; + +/* Copy command line into a buffer */ + cmd += strspn(cmd, " \t"); + cmd_len = strlen(cmd); + if (cmd_len > (int)(sizeof(buffer) - 1)) + return (-1); + strcpy(buffer, cmd); + +/* Convert to lower case */ + len = strlen(buffer); + for (i = 0; i < len; i++) + buffer[i] = tolower((unsigned char)buffer[i]); + +/* Set default proxy type */ + +/* Set up default values */ + rule_index = 0; + proxy_type = PROXY_TYPE_ENCODE_NONE; + proto = IPPROTO_TCP; + proxy_port = 0; + server_addr.s_addr = 0; + server_port = 0; + src_addr.s_addr = 0; + IpMask(0, &src_mask); + dst_addr.s_addr = 0; + IpMask(0, &dst_mask); + + str_port[0] = 0; + str_server_port[0] = 0; + +/* Parse command string with state machine */ +#define STATE_READ_KEYWORD 0 +#define STATE_READ_TYPE 1 +#define STATE_READ_PORT 2 +#define STATE_READ_SERVER 3 +#define STATE_READ_RULE 4 +#define STATE_READ_DELETE 5 +#define STATE_READ_PROTO 6 +#define STATE_READ_SRC 7 +#define STATE_READ_DST 8 + state = STATE_READ_KEYWORD; + token = strsep(&res, " \t"); + token_count = 0; + while (token != NULL) { + token_count++; + switch (state) { + case STATE_READ_KEYWORD: + if (strcmp(token, "type") == 0) + state = STATE_READ_TYPE; + else if (strcmp(token, "port") == 0) + state = STATE_READ_PORT; + else if (strcmp(token, "server") == 0) + state = STATE_READ_SERVER; + else if (strcmp(token, "rule") == 0) + state = STATE_READ_RULE; + else if (strcmp(token, "delete") == 0) + state = STATE_READ_DELETE; + else if (strcmp(token, "proto") == 0) + state = STATE_READ_PROTO; + else if (strcmp(token, "src") == 0) + state = STATE_READ_SRC; + else if (strcmp(token, "dst") == 0) + state = STATE_READ_DST; + else + return (-1); + break; + + case STATE_READ_TYPE: + if (strcmp(token, "encode_ip_hdr") == 0) + proxy_type = PROXY_TYPE_ENCODE_IPHDR; + else if (strcmp(token, "encode_tcp_stream") == 0) + proxy_type = PROXY_TYPE_ENCODE_TCPSTREAM; + else if (strcmp(token, "no_encode") == 0) + proxy_type = PROXY_TYPE_ENCODE_NONE; + else + return (-1); + state = STATE_READ_KEYWORD; + break; + + case STATE_READ_PORT: + strcpy(str_port, token); + state = STATE_READ_KEYWORD; + break; + + case STATE_READ_SERVER: + { + int err; + char *p; + char s[sizeof(buffer)]; + + p = token; + while (*p != ':' && *p != 0) + p++; + + if (*p != ':') { + err = IpAddr(token, &server_addr); + if (err) + return (-1); + } else { + *p = ' '; + + n = sscanf(token, "%s %s", s, str_server_port); + if (n != 2) + return (-1); + + err = IpAddr(s, &server_addr); + if (err) + return (-1); + } + } + state = STATE_READ_KEYWORD; + break; + + case STATE_READ_RULE: + n = sscanf(token, "%d", &rule_index); + if (n != 1 || rule_index < 0) + return (-1); + state = STATE_READ_KEYWORD; + break; + + case STATE_READ_DELETE: + { + int err; + int rule_to_delete; + + if (token_count != 2) + return (-1); + + n = sscanf(token, "%d", &rule_to_delete); + if (n != 1) + return (-1); + err = RuleNumberDelete(la, rule_to_delete); + if (err) + return (-1); + return (0); + } + + case STATE_READ_PROTO: + if (strcmp(token, "tcp") == 0) + proto = IPPROTO_TCP; + else if (strcmp(token, "udp") == 0) + proto = IPPROTO_UDP; + else + return (-1); + state = STATE_READ_KEYWORD; + break; + + case STATE_READ_SRC: + case STATE_READ_DST: + { + int err; + char *p; + struct in_addr mask; + struct in_addr addr; + + p = token; + while (*p != '/' && *p != 0) + p++; + + if (*p != '/') { + IpMask(32, &mask); + err = IpAddr(token, &addr); + if (err) + return (-1); + } else { + int nbits; + char s[sizeof(buffer)]; + + *p = ' '; + n = sscanf(token, "%s %d", s, &nbits); + if (n != 2) + return (-1); + + err = IpAddr(s, &addr); + if (err) + return (-1); + + err = IpMask(nbits, &mask); + if (err) + return (-1); + } + + if (state == STATE_READ_SRC) { + src_addr = addr; + src_mask = mask; + } else { + dst_addr = addr; + dst_mask = mask; + } + } + state = STATE_READ_KEYWORD; + break; + + default: + return (-1); + break; + } + + do { + token = strsep(&res, " \t"); + } while (token != NULL && !*token); + } +#undef STATE_READ_KEYWORD +#undef STATE_READ_TYPE +#undef STATE_READ_PORT +#undef STATE_READ_SERVER +#undef STATE_READ_RULE +#undef STATE_READ_DELETE +#undef STATE_READ_PROTO +#undef STATE_READ_SRC +#undef STATE_READ_DST + +/* Convert port strings to numbers. This needs to be done after + the string is parsed, because the prototype might not be designated + before the ports (which might be symbolic entries in /etc/services) */ + + if (strlen(str_port) != 0) { + int err; + + err = IpPort(str_port, proto, &proxy_port); + if (err) + return (-1); + } else { + proxy_port = 0; + } + + if (strlen(str_server_port) != 0) { + int err; + + err = IpPort(str_server_port, proto, &server_port); + if (err) + return (-1); + } else { + server_port = 0; + } + +/* Check that at least the server address has been defined */ + if (server_addr.s_addr == 0) + return (-1); + +/* Add to linked list */ + proxy_entry = malloc(sizeof(struct proxy_entry)); + if (proxy_entry == NULL) + return (-1); + + proxy_entry->proxy_type = proxy_type; + proxy_entry->rule_index = rule_index; + proxy_entry->proto = proto; + proxy_entry->proxy_port = htons(proxy_port); + proxy_entry->server_port = htons(server_port); + proxy_entry->server_addr = server_addr; + proxy_entry->src_addr.s_addr = src_addr.s_addr & src_mask.s_addr; + proxy_entry->dst_addr.s_addr = dst_addr.s_addr & dst_mask.s_addr; + proxy_entry->src_mask = src_mask; + proxy_entry->dst_mask = dst_mask; + + RuleAdd(la, proxy_entry); + + return (0); +} diff --git a/lib/libalias/alias_skinny.c b/lib/libalias/alias_skinny.c new file mode 100644 index 0000000000000..d1e4a14693779 --- /dev/null +++ b/lib/libalias/alias_skinny.c @@ -0,0 +1,350 @@ +/*- + * alias_skinny.c + * + * Copyright (c) 2002, 2003 MarcusCom, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Author: Joe Marcus Clarke <marcus@FreeBSD.org> + * + * $FreeBSD$ + */ + +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in_systm.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netinet/ip.h> +#include <netinet/tcp.h> +#include <netinet/udp.h> +#include <unistd.h> + +#include "alias_local.h" + +/* + * alias_skinny.c handles the translation for the Cisco Skinny Station + * protocol. Skinny typically uses TCP port 2000 to set up calls between + * a Cisco Call Manager and a Cisco IP phone. When a phone comes on line, + * it first needs to register with the Call Manager. To do this it sends + * a registration message. This message contains the IP address of the + * IP phone. This message must then be translated to reflect our global + * IP address. Along with the registration message (and usually in the + * same packet), the phone sends an IP port message. This message indicates + * the TCP port over which it will communicate. + * + * When a call is placed from the phone, the Call Manager will send an + * Open Receive Channel message to the phone to let the caller know someone + * has answered. The phone then sends back an Open Receive Channel + * Acknowledgement. In this packet, the phone sends its IP address again, + * and the UDP port over which the voice traffic should flow. These values + * need translation. Right after the Open Receive Channel Acknowledgement, + * the Call Manager sends a Start Media Transmission message indicating the + * call is connected. This message contains the IP address and UDP port + * number of the remote (called) party. Once this message is translated, the + * call can commence. The called part sends the first UDP packet to the + * calling phone at the pre-arranged UDP port in the Open Receive Channel + * Acknowledgement. + * + * Skinny is a Cisco-proprietary protocol and is a trademark of Cisco Systems, + * Inc. All rights reserved. +*/ + +/* #define DEBUG 1 */ + +/* Message types that need translating */ +#define REG_MSG 0x00000001 +#define IP_PORT_MSG 0x00000002 +#define OPNRCVCH_ACK 0x00000022 +#define START_MEDIATX 0x0000008a + +struct skinny_header { + u_int32_t len; + u_int32_t reserved; + u_int32_t msgId; +}; + +struct RegisterMessage { + u_int32_t msgId; + char devName [16]; + u_int32_t uid; + u_int32_t instance; + u_int32_t ipAddr; + u_char devType; + u_int32_t maxStreams; +}; + +struct IpPortMessage { + u_int32_t msgId; + u_int32_t stationIpPort; /* Note: Skinny uses 32-bit port + * numbers */ +}; + +struct OpenReceiveChannelAck { + u_int32_t msgId; + u_int32_t status; + u_int32_t ipAddr; + u_int32_t port; + u_int32_t passThruPartyID; +}; + +struct StartMediaTransmission { + u_int32_t msgId; + u_int32_t conferenceID; + u_int32_t passThruPartyID; + u_int32_t remoteIpAddr; + u_int32_t remotePort; + u_int32_t MSPacket; + u_int32_t payloadCap; + u_int32_t precedence; + u_int32_t silenceSuppression; + u_short maxFramesPerPacket; + u_int32_t G723BitRate; +}; + +typedef enum { + ClientToServer = 0, + ServerToClient = 1 +} ConvDirection; + + +static int +alias_skinny_reg_msg(struct RegisterMessage *reg_msg, struct ip *pip, + struct tcphdr *tc, struct alias_link *lnk, + ConvDirection direction) +{ + (void)direction; + + reg_msg->ipAddr = (u_int32_t) GetAliasAddress(lnk).s_addr; + + tc->th_sum = 0; + tc->th_sum = TcpChecksum(pip); + + return (0); +} + +static int +alias_skinny_startmedia(struct StartMediaTransmission *start_media, + struct ip *pip, struct tcphdr *tc, + struct alias_link *lnk, u_int32_t localIpAddr, + ConvDirection direction) +{ + struct in_addr dst, src; + + (void)pip; + (void)tc; + (void)lnk; + (void)direction; + + dst.s_addr = start_media->remoteIpAddr; + src.s_addr = localIpAddr; + + /* + * XXX I should probably handle in bound global translations as + * well. + */ + + return (0); +} + +static int +alias_skinny_port_msg(struct IpPortMessage *port_msg, struct ip *pip, + struct tcphdr *tc, struct alias_link *lnk, + ConvDirection direction) +{ + (void)direction; + + port_msg->stationIpPort = (u_int32_t) ntohs(GetAliasPort(lnk)); + + tc->th_sum = 0; + tc->th_sum = TcpChecksum(pip); + + return (0); +} + +static int +alias_skinny_opnrcvch_ack(struct libalias *la, struct OpenReceiveChannelAck *opnrcvch_ack, + struct ip *pip, struct tcphdr *tc, + struct alias_link *lnk, u_int32_t * localIpAddr, + ConvDirection direction) +{ + struct in_addr null_addr; + struct alias_link *opnrcv_lnk; + u_int32_t localPort; + + (void)lnk; + (void)direction; + + *localIpAddr = (u_int32_t) opnrcvch_ack->ipAddr; + localPort = opnrcvch_ack->port; + + null_addr.s_addr = INADDR_ANY; + opnrcv_lnk = FindUdpTcpOut(la, pip->ip_src, null_addr, + htons((u_short) opnrcvch_ack->port), 0, + IPPROTO_UDP, 1); + opnrcvch_ack->ipAddr = (u_int32_t) GetAliasAddress(opnrcv_lnk).s_addr; + opnrcvch_ack->port = (u_int32_t) ntohs(GetAliasPort(opnrcv_lnk)); + + tc->th_sum = 0; + tc->th_sum = TcpChecksum(pip); + + return (0); +} + +void +AliasHandleSkinny(struct libalias *la, struct ip *pip, struct alias_link *lnk) +{ + size_t hlen, tlen, dlen; + struct tcphdr *tc; + u_int32_t msgId, t, len, lip; + struct skinny_header *sd; + size_t orig_len, skinny_hdr_len = sizeof(struct skinny_header); + ConvDirection direction; + + tc = (struct tcphdr *)ip_next(pip); + hlen = (pip->ip_hl + tc->th_off) << 2; + tlen = ntohs(pip->ip_len); + dlen = tlen - hlen; + + sd = (struct skinny_header *)tcp_next(tc); + + /* + * XXX This direction is reserved for future use. I still need to + * handle the scenario where the call manager is on the inside, and + * the calling phone is on the global outside. + */ + if (ntohs(tc->th_dport) == la->skinnyPort) { + direction = ClientToServer; + } else if (ntohs(tc->th_sport) == la->skinnyPort) { + direction = ServerToClient; + } else { +#ifdef DEBUG + fprintf(stderr, + "PacketAlias/Skinny: Invalid port number, not a Skinny packet\n"); +#endif + return; + } + + orig_len = dlen; + /* + * Skinny packets can contain many messages. We need to loop + * through the packet using len to determine message boundaries. + * This comes into play big time with port messages being in the + * same packet as register messages. Also, open receive channel + * acks are usually buried in a pakcet some 400 bytes long. + */ + while (dlen >= skinny_hdr_len) { + len = (sd->len); + msgId = (sd->msgId); + t = len; + + if (t > orig_len || t > dlen) { +#ifdef DEBUG + fprintf(stderr, + "PacketAlias/Skinny: Not a skinny packet, invalid length \n"); +#endif + return; + } + switch (msgId) { + case REG_MSG: { + struct RegisterMessage *reg_mesg; + + if (len < (int)sizeof(struct RegisterMessage)) { +#ifdef DEBUG + fprintf(stderr, + "PacketAlias/Skinny: Not a skinny packet, bad registration message\n"); +#endif + return; + } + reg_mesg = (struct RegisterMessage *)&sd->msgId; +#ifdef DEBUG + fprintf(stderr, + "PacketAlias/Skinny: Received a register message"); +#endif + alias_skinny_reg_msg(reg_mesg, pip, tc, lnk, direction); + break; + } + case IP_PORT_MSG: { + struct IpPortMessage *port_mesg; + + if (len < (int)sizeof(struct IpPortMessage)) { +#ifdef DEBUG + fprintf(stderr, + "PacketAlias/Skinny: Not a skinny packet, port message\n"); +#endif + return; + } +#ifdef DEBUG + fprintf(stderr, + "PacketAlias/Skinny: Received ipport message\n"); +#endif + port_mesg = (struct IpPortMessage *)&sd->msgId; + alias_skinny_port_msg(port_mesg, pip, tc, lnk, direction); + break; + } + case OPNRCVCH_ACK: { + struct OpenReceiveChannelAck *opnrcvchn_ack; + + if (len < (int)sizeof(struct OpenReceiveChannelAck)) { +#ifdef DEBUG + fprintf(stderr, + "PacketAlias/Skinny: Not a skinny packet, packet,OpnRcvChnAckMsg\n"); +#endif + return; + } +#ifdef DEBUG + fprintf(stderr, + "PacketAlias/Skinny: Received open rcv channel msg\n"); +#endif + opnrcvchn_ack = (struct OpenReceiveChannelAck *)&sd->msgId; + alias_skinny_opnrcvch_ack(la, opnrcvchn_ack, pip, tc, lnk, &lip, direction); + break; + } + case START_MEDIATX: { + struct StartMediaTransmission *startmedia_tx; + + if (len < (int)sizeof(struct StartMediaTransmission)) { +#ifdef DEBUG + fprintf(stderr, + "PacketAlias/Skinny: Not a skinny packet,StartMediaTx Message\n"); +#endif + return; + } +#ifdef DEBUG + fprintf(stderr, + "PacketAlias/Skinny: Received start media trans msg\n"); +#endif + startmedia_tx = (struct StartMediaTransmission *)&sd->msgId; + alias_skinny_startmedia(startmedia_tx, pip, tc, lnk, lip, direction); + break; + } + default: + break; + } + /* Place the pointer at the next message in the packet. */ + dlen -= len + (skinny_hdr_len - sizeof(msgId)); + sd = (struct skinny_header *)(((char *)&sd->msgId) + len); + } +} diff --git a/lib/libalias/alias_smedia.c b/lib/libalias/alias_smedia.c new file mode 100644 index 0000000000000..888937c9f2411 --- /dev/null +++ b/lib/libalias/alias_smedia.c @@ -0,0 +1,445 @@ +/* + * alias_smedia.c + * + * Copyright (c) 2000 Whistle Communications, Inc. + * All rights reserved. + * + * Subject to the following obligations and disclaimer of warranty, use and + * redistribution of this software, in source or object code forms, with or + * without modifications are expressly permitted by Whistle Communications; + * provided, however, that: + * 1. Any and all reproductions of the source or object code must include the + * copyright notice above and the following disclaimer of warranties; and + * 2. No rights are granted, in any manner or form, to use Whistle + * Communications, Inc. trademarks, including the mark "WHISTLE + * COMMUNICATIONS" on advertising, endorsements, or otherwise except as + * such appears in the above copyright notice or in the software. + * + * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND + * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO + * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, + * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. + * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY + * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS + * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. + * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES + * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING + * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Copyright (c) 2000 Junichi SATOH <junichi@astec.co.jp> + * <junichi@junichi.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Authors: Erik Salander <erik@whistle.com> + * Junichi SATOH <junichi@astec.co.jp> + * <junichi@junichi.org> + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + Alias_smedia.c is meant to contain the aliasing code for streaming media + protocols. It performs special processing for RSTP sessions under TCP. + Specifically, when a SETUP request is sent by a client, or a 200 reply + is sent by a server, it is intercepted and modified. The address is + changed to the gateway machine and an aliasing port is used. + + More specifically, the "client_port" configuration parameter is + parsed for SETUP requests. The "server_port" configuration parameter is + parsed for 200 replies eminating from a server. This is intended to handle + the unicast case. + + RTSP also allows a redirection of a stream to another client by using the + "destination" configuration parameter. The destination config parm would + indicate a different IP address. This function is NOT supported by the + RTSP translation code below. + + The RTSP multicast functions without any address translation intervention. + + For this routine to work, the SETUP/200 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 SETUP/200 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. + + Initial version: May, 2000 (eds) +*/ + +#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 <netinet/udp.h> + +#include "alias_local.h" + +#define RTSP_CONTROL_PORT_NUMBER_1 554 +#define RTSP_CONTROL_PORT_NUMBER_2 7070 +#define RTSP_PORT_GROUP 2 + +#define ISDIGIT(a) (((a) >= '0') && ((a) <= '9')) + +static int +search_string(char *data, int dlen, const char *search_str) +{ + int i, j, k; + int search_str_len; + + search_str_len = strlen(search_str); + for (i = 0; i < dlen - search_str_len; i++) { + for (j = i, k = 0; j < dlen - search_str_len; j++, k++) { + if (data[j] != search_str[k] && + data[j] != search_str[k] - ('a' - 'A')) { + break; + } + if (k == search_str_len - 1) { + return (j + 1); + } + } + } + return (-1); +} + +static int +alias_rtsp_out(struct libalias *la, struct ip *pip, + struct alias_link *lnk, + char *data, + const char *port_str) +{ + int hlen, tlen, dlen; + struct tcphdr *tc; + int i, j, pos, state, port_dlen, new_dlen, delta; + u_short p[2], new_len; + u_short sport, eport, base_port; + u_short salias = 0, ealias = 0, base_alias = 0; + const char *transport_str = "transport:"; + char newdata[2048], *port_data, *port_newdata, stemp[80]; + int links_created = 0, pkt_updated = 0; + struct alias_link *rtsp_lnk = NULL; + struct in_addr null_addr; + + /* Calculate data length of TCP packet */ + tc = (struct tcphdr *)ip_next(pip); + hlen = (pip->ip_hl + tc->th_off) << 2; + tlen = ntohs(pip->ip_len); + dlen = tlen - hlen; + + /* Find keyword, "Transport: " */ + pos = search_string(data, dlen, transport_str); + if (pos < 0) { + return (-1); + } + port_data = data + pos; + port_dlen = dlen - pos; + + memcpy(newdata, data, pos); + port_newdata = newdata + pos; + + while (port_dlen > (int)strlen(port_str)) { + /* Find keyword, appropriate port string */ + pos = search_string(port_data, port_dlen, port_str); + if (pos < 0) { + break; + } + memcpy(port_newdata, port_data, pos + 1); + port_newdata += (pos + 1); + + p[0] = p[1] = 0; + sport = eport = 0; + state = 0; + for (i = pos; i < port_dlen; i++) { + switch (state) { + case 0: + if (port_data[i] == '=') { + state++; + } + break; + case 1: + if (ISDIGIT(port_data[i])) { + p[0] = p[0] * 10 + port_data[i] - '0'; + } else { + if (port_data[i] == ';') { + state = 3; + } + if (port_data[i] == '-') { + state++; + } + } + break; + case 2: + if (ISDIGIT(port_data[i])) { + p[1] = p[1] * 10 + port_data[i] - '0'; + } else { + state++; + } + break; + case 3: + base_port = p[0]; + sport = htons(p[0]); + eport = htons(p[1]); + + if (!links_created) { + + links_created = 1; + /* + * Find an even numbered port + * number base that satisfies the + * contiguous number of ports we + * need + */ + null_addr.s_addr = 0; + if (0 == (salias = FindNewPortGroup(la, null_addr, + FindAliasAddress(la, pip->ip_src), + sport, 0, + RTSP_PORT_GROUP, + IPPROTO_UDP, 1))) { +#ifdef DEBUG + fprintf(stderr, + "PacketAlias/RTSP: Cannot find contiguous RTSP data ports\n"); +#endif + } else { + + base_alias = ntohs(salias); + for (j = 0; j < RTSP_PORT_GROUP; j++) { + /* + * Establish link + * to port found in + * RTSP packet + */ + rtsp_lnk = FindRtspOut(la, GetOriginalAddress(lnk), null_addr, + htons(base_port + j), htons(base_alias + j), + IPPROTO_UDP); + if (rtsp_lnk != NULL) { +#ifndef NO_FW_PUNCH + /* + * Punch + * hole in + * firewall + */ + PunchFWHole(rtsp_lnk); +#endif + } else { +#ifdef DEBUG + fprintf(stderr, + "PacketAlias/RTSP: Cannot allocate RTSP data ports\n"); +#endif + break; + } + } + } + ealias = htons(base_alias + (RTSP_PORT_GROUP - 1)); + } + if (salias && rtsp_lnk) { + + pkt_updated = 1; + + /* Copy into IP packet */ + sprintf(stemp, "%d", ntohs(salias)); + memcpy(port_newdata, stemp, strlen(stemp)); + port_newdata += strlen(stemp); + + if (eport != 0) { + *port_newdata = '-'; + port_newdata++; + + /* Copy into IP packet */ + sprintf(stemp, "%d", ntohs(ealias)); + memcpy(port_newdata, stemp, strlen(stemp)); + port_newdata += strlen(stemp); + } + *port_newdata = ';'; + port_newdata++; + } + state++; + break; + } + if (state > 3) { + break; + } + } + port_data += i; + port_dlen -= i; + } + + if (!pkt_updated) + return (-1); + + memcpy(port_newdata, port_data, port_dlen); + port_newdata += port_dlen; + *port_newdata = '\0'; + + /* Create new packet */ + new_dlen = port_newdata - newdata; + memcpy(data, newdata, new_dlen); + + SetAckModified(lnk); + delta = GetDeltaSeqOut(pip, lnk); + AddSeq(pip, lnk, delta + new_dlen - dlen); + + new_len = htons(hlen + new_dlen); + DifferentialChecksum(&pip->ip_sum, + &new_len, + &pip->ip_len, + 1); + pip->ip_len = new_len; + + tc->th_sum = 0; + tc->th_sum = TcpChecksum(pip); + + return (0); +} + +/* Support the protocol used by early versions of RealPlayer */ + +static int +alias_pna_out(struct libalias *la, struct ip *pip, + struct alias_link *lnk, + char *data, + int dlen) +{ + struct alias_link *pna_links; + u_short msg_id, msg_len; + char *work; + u_short alias_port, port; + struct tcphdr *tc; + + work = data; + work += 5; + while (work + 4 < data + dlen) { + memcpy(&msg_id, work, 2); + work += 2; + memcpy(&msg_len, work, 2); + work += 2; + if (ntohs(msg_id) == 0) { + /* end of options */ + return (0); + } + if ((ntohs(msg_id) == 1) || (ntohs(msg_id) == 7)) { + memcpy(&port, work, 2); + pna_links = FindUdpTcpOut(la, pip->ip_src, GetDestAddress(lnk), + port, 0, IPPROTO_UDP, 1); + if (pna_links != NULL) { +#ifndef NO_FW_PUNCH + /* Punch hole in firewall */ + PunchFWHole(pna_links); +#endif + tc = (struct tcphdr *)ip_next(pip); + alias_port = GetAliasPort(pna_links); + memcpy(work, &alias_port, 2); + + /* Compute TCP checksum for revised packet */ + tc->th_sum = 0; + tc->th_sum = TcpChecksum(pip); + } + } + work += ntohs(msg_len); + } + + return (0); +} + +void +AliasHandleRtspOut(struct libalias *la, struct ip *pip, struct alias_link *lnk, int maxpacketsize) +{ + int hlen, tlen, dlen; + struct tcphdr *tc; + char *data; + const char *setup = "SETUP", *pna = "PNA", *str200 = "200"; + const char *okstr = "OK", *client_port_str = "client_port"; + const char *server_port_str = "server_port"; + int i, parseOk; + + (void)maxpacketsize; + + tc = (struct tcphdr *)ip_next(pip); + hlen = (pip->ip_hl + tc->th_off) << 2; + tlen = ntohs(pip->ip_len); + dlen = tlen - hlen; + + data = (char *)pip; + data += hlen; + + /* When aliasing a client, check for the SETUP request */ + if ((ntohs(tc->th_dport) == RTSP_CONTROL_PORT_NUMBER_1) || + (ntohs(tc->th_dport) == RTSP_CONTROL_PORT_NUMBER_2)) { + + if (dlen >= (int)strlen(setup)) { + if (memcmp(data, setup, strlen(setup)) == 0) { + alias_rtsp_out(la, pip, lnk, data, client_port_str); + return; + } + } + if (dlen >= (int)strlen(pna)) { + if (memcmp(data, pna, strlen(pna)) == 0) { + alias_pna_out(la, pip, lnk, data, dlen); + } + } + } else { + + /* + * When aliasing a server, check for the 200 reply + * Accomodate varying number of blanks between 200 & OK + */ + + if (dlen >= (int)strlen(str200)) { + + for (parseOk = 0, i = 0; + i <= dlen - (int)strlen(str200); + i++) { + if (memcmp(&data[i], str200, strlen(str200)) == 0) { + parseOk = 1; + break; + } + } + if (parseOk) { + + i += strlen(str200); /* skip string found */ + while (data[i] == ' ') /* skip blank(s) */ + i++; + + if ((dlen - i) >= (int)strlen(okstr)) { + + if (memcmp(&data[i], okstr, strlen(okstr)) == 0) + alias_rtsp_out(la, pip, lnk, data, server_port_str); + + } + } + } + } +} diff --git a/lib/libalias/alias_util.c b/lib/libalias/alias_util.c new file mode 100644 index 0000000000000..687168e830b72 --- /dev/null +++ b/lib/libalias/alias_util.c @@ -0,0 +1,164 @@ +/*- + * Copyright (c) 2001 Charles Mott <cm@linktel.net> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + + +/* + Alias_util.c 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 word is filled with the proper value, +then these routines will give a result of zero (useful for testing +purposes); +*/ + +#include <stdio.h> +#include <sys/types.h> +#include <netinet/in_systm.h> +#include <netinet/in.h> +#include <netinet/ip.h> +#include <netinet/tcp.h> + +#include "alias.h" +#include "alias_local.h" + +u_short +LibAliasInternetChecksum(struct libalias *la, u_short * ptr, int nbytes) +{ + int sum, oddbyte; + + (void)la; + + sum = 0; + while (nbytes > 1) { + sum += *ptr++; + nbytes -= 2; + } + if (nbytes == 1) { + oddbyte = 0; + ((u_char *) & oddbyte)[0] = *(u_char *) ptr; + ((u_char *) & oddbyte)[1] = 0; + sum += oddbyte; + } + sum = (sum >> 16) + (sum & 0xffff); + sum += (sum >> 16); + return (~sum); +} + +u_short +IpChecksum(struct ip *pip) +{ + return (PacketAliasInternetChecksum((u_short *) pip, + (pip->ip_hl << 2))); + +} + +u_short +TcpChecksum(struct ip *pip) +{ + u_short *ptr; + struct tcphdr *tc; + int nhdr, ntcp, nbytes; + int sum, oddbyte; + + nhdr = pip->ip_hl << 2; + ntcp = ntohs(pip->ip_len) - nhdr; + + tc = (struct tcphdr *)ip_next(pip); + 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)[0] = *(u_char *) ptr; + ((u_char *) & oddbyte)[1] = 0; + 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, void *newp, void *oldp, int n) +{ + int i; + int accumulate; + u_short *new = newp; + u_short *old = oldp; + + accumulate = *cksum; + for (i = 0; i < n; i++) { + accumulate -= *new++; + accumulate += *old++; + } + + if (accumulate < 0) { + accumulate = -accumulate; + accumulate = (accumulate >> 16) + (accumulate & 0xffff); + accumulate += accumulate >> 16; + *cksum = (u_short) ~ accumulate; + } else { + accumulate = (accumulate >> 16) + (accumulate & 0xffff); + accumulate += accumulate >> 16; + *cksum = (u_short) accumulate; + } +} diff --git a/lib/libalias/libalias.3 b/lib/libalias/libalias.3 new file mode 100644 index 0000000000000..bb77e30384d36 --- /dev/null +++ b/lib/libalias/libalias.3 @@ -0,0 +1,1018 @@ +.\"- +.\" Copyright (c) 2001 Charles Mott <cm@linktel.net> +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd January 17, 2004 +.Dt LIBALIAS 3 +.Os +.Sh NAME +.Nm libalias +.Nd packet aliasing library for masquerading and network address translation +.Sh SYNOPSIS +.In sys/types.h +.In netinet/in.h +.In alias.h +.Pp +Function prototypes are given in the main body of the text. +.Sh DESCRIPTION +The +.Nm +library is a collection of functions for aliasing and de-aliasing of IP +packets, intended for masquerading and network address translation (NAT). +.Sh INTRODUCTION +This library is a moderately portable set of functions designed to assist +in the process of IP masquerading and network address translation. +Outgoing packets from a local network with unregistered IP addresses can +be aliased to appear as if they came from an accessible IP address. +Incoming packets are then de-aliased so that they are sent to the correct +machine on the local network. +.Pp +A certain amount of flexibility is built into the packet aliasing engine. +In the simplest mode of operation, a many-to-one address mapping takes +place between local network and the packet aliasing host. +This is known as IP masquerading. +In addition, one-to-one mappings between local and public addresses can +also be implemented, which is known as static NAT. +In between these extremes, different groups of private addresses can be +linked to different public addresses, comprising several distinct +many-to-one mappings. +Also, a given public address and port can be statically redirected to a +private address/port. +.Pp +The packet aliasing engine was designed to operate in user space outside +of the kernel, without any access to private kernel data structure, but +the source code can also be ported to a kernel environment. +.Sh INITIALIZATION AND CONTROL +One special function, +.Fn LibAliasInit , +must always be called before any packet handling may be performed and +the returned instance pointer passed to all the other functions. +Normally, the +.Fn LibAliasSetAddress +function is called afterwards, to set the default aliasing address. +In addition, the operating mode of the packet aliasing engine can be +customized by calling +.Fn LibAliasSetMode . +.Pp +.Ft "struct libalias *" +.Fn LibAliasInit "struct libalias *" +.Bd -ragged -offset indent +This function is used to initialize +internal data structures. +When called the first time, a +.Dv NULL +pointer should be passed as an argument. +The following mode bits are always set after calling +.Fn LibAliasInit . +See the description of +.Fn LibAliasSetMode +below for the meaning of these mode bits. +.Pp +.Bl -item -offset indent -compact +.It +.Dv PKT_ALIAS_SAME_PORTS +.It +.Dv PKT_ALIAS_USE_SOCKETS +.It +.Dv PKT_ALIAS_RESET_ON_ADDR_CHANGE +.El +.Pp +This function will always return the packet aliasing engine to the same +initial state. +The +.Fn LibAliasSetAddress +function is normally called afterwards, and any desired changes from the +default mode bits listed above require a call to +.Fn LibAliasSetMode . +.Pp +It is mandatory that this function be called at the beginning of a program +prior to any packet handling. +.Ed +.Pp +.Ft void +.Fn LibAliasUninit "struct libalias *" +.Bd -ragged -offset indent +This function has no return value and is used to clear any +resources attached to internal data structures. +.Pp +This functions should be called when a program stops using the aliasing +engine; it does, amongst other things, clear out any firewall holes. +To provide backwards compatibility and extra security, it is added to +the +.Xr atexit 3 +chain by +.Fn LibAliasInit . +.Ed +.Pp +.Ft void +.Fn LibAliasSetAddress "struct libalias *" "struct in_addr addr" +.Bd -ragged -offset indent +This function sets the source address to which outgoing packets from the +local area network are aliased. +All outgoing packets are re-mapped to this address unless overridden by a +static address mapping established by +.Fn LibAliasRedirectAddr . +If this function is not called, and no static rules match, an outgoing +packet retains its source address. +.Pp +If the +.Dv PKT_ALIAS_RESET_ON_ADDR_CHANGE +mode bit is set (the default mode of operation), then the internal aliasing +link tables will be reset any time the aliasing address changes. +This is useful for interfaces such as +.Xr ppp 8 , +where the IP +address may or may not change on successive dial-up attempts. +.Pp +If the +.Dv PKT_ALIAS_RESET_ON_ADDR_CHANGE +mode bit is set to zero, this function can also be used to dynamically change +the aliasing address on a packet to packet basis (it is a low overhead call). +.Pp +It is mandatory that this function be called prior to any packet handling. +.Ed +.Pp +.Ft unsigned int +.Fn LibAliasSetMode "struct libalias *" "unsigned int flags" "unsigned int mask" +.Bd -ragged -offset indent +This function sets or clears mode bits +according to the value of +.Fa flags . +Only bits marked in +.Fa mask +are affected. +The following mode bits are defined in +.In alias.h : +.Bl -tag -width indent +.It Dv PKT_ALIAS_LOG +Enables logging into +.Pa /var/log/alias.log . +Each time an aliasing link is created or deleted, the log file is appended +with the current number of ICMP, TCP and UDP links. +Mainly useful for debugging when the log file is viewed continuously with +.Xr tail 1 . +.It Dv PKT_ALIAS_DENY_INCOMING +If this mode bit is set, all incoming packets associated with new TCP +connections or new UDP transactions will be marked for being ignored +.Fn ( LibAliasIn +returns +.Dv PKT_ALIAS_IGNORED +code) +by the calling program. +Response packets to connections or transactions initiated from the packet +aliasing host or local network will be unaffected. +This mode bit is useful for implementing a one-way firewall. +.It Dv PKT_ALIAS_SAME_PORTS +If this mode bit is set, the packet aliasing engine will attempt to leave +the alias port numbers unchanged from the actual local port numbers. +This can be done as long as the quintuple (proto, alias addr, alias port, +remote addr, remote port) is unique. +If a conflict exists, a new aliasing port number is chosen even if this +mode bit is set. +.It Dv PKT_ALIAS_USE_SOCKETS +This bit should be set when the packet aliasing host originates network +traffic as well as forwards it. +When the packet aliasing host is waiting for a connection from an unknown +host address or unknown port number (e.g.\& an FTP data connection), this +mode bit specifies that a socket be allocated as a place holder to prevent +port conflicts. +Once a connection is established, usually within a minute or so, the socket +is closed. +.It Dv PKT_ALIAS_UNREGISTERED_ONLY +If this mode bit is set, traffic on the local network which does not +originate from unregistered address spaces will be ignored. +Standard Class A, B and C unregistered addresses are: +.Bd -literal -offset indent +10.0.0.0 -> 10.255.255.255 (Class A subnet) +172.16.0.0 -> 172.31.255.255 (Class B subnets) +192.168.0.0 -> 192.168.255.255 (Class C subnets) +.Ed +.Pp +This option is useful in the case that packet aliasing host has both +registered and unregistered subnets on different interfaces. +The registered subnet is fully accessible to the outside world, so traffic +from it does not need to be passed through the packet aliasing engine. +.It Dv PKT_ALIAS_RESET_ON_ADDR_CHANGE +When this mode bit is set and +.Fn LibAliasSetAddress +is called to change the aliasing address, the internal link table of the +packet aliasing engine will be cleared. +This operating mode is useful for +.Xr ppp 8 +links where the interface address can sometimes change or remain the same +between dial-up attempts. +If this mode bit is not set, the link table will never be reset in the event +of an address change. +.It Dv PKT_ALIAS_PUNCH_FW +This option makes +.Nm +`punch holes' in an +.Xr ipfirewall 4 +based firewall for FTP/IRC DCC connections. +The holes punched are bound by from/to IP address and port; it will not be +possible to use a hole for another connection. +A hole is removed when the connection that uses it dies. +To cater to unexpected death of a program using +.Nm +(e.g.\& kill -9), +changing the state of the flag will clear the entire firewall range +allocated for holes. +This will also happen on the initial call to +.Fn LibAliasSetFWBase . +This call must happen prior to setting this flag. +.It Dv PKT_ALIAS_REVERSE +This option makes +.Nm +reverse the way it handles incoming and outgoing packets, allowing it +to be fed with data that passes through the internal interface rather +than the external one. +.It Dv PKT_ALIAS_PROXY_ONLY +This option tells +.Nm +to obey transparent proxy rules only. +Normal packet aliasing is not performed. +See +.Fn LibAliasProxyRule +below for details. +.El +.Ed +.Pp +.Ft void +.Fn LibAliasSetFWBase "struct libalias *" "unsigned int base" "unsigned int num" +.Bd -ragged -offset indent +Set firewall range allocated for punching firewall holes (with the +.Dv PKT_ALIAS_PUNCH_FW +flag). +The range will be cleared for all rules on initialization. +.Ed +.Pp +.Ft void +.Fn LibAliasSkinnyPort "struct libalias *" "unsigned int port" +.Bd -ragged -offset indent +Set the TCP port used by the Skinny Station protocol. +Skinny is used by Cisco IP phones to communicate with +Cisco Call Managers to set up voice over IP calls. +If this is not set, Skinny aliasing will not be done. +The typical port used by Skinny is 2000. +.Ed +.Sh PACKET HANDLING +The packet handling functions are used to modify incoming (remote to local) +and outgoing (local to remote) packets. +The calling program is responsible for receiving and sending packets via +network interfaces. +.Pp +Along with +.Fn LibAliasInit +and +.Fn LibAliasSetAddress , +the two packet handling functions, +.Fn LibAliasIn +and +.Fn LibAliasOut , +comprise minimal set of functions needed for a basic IP masquerading +implementation. +.Pp +.Ft int +.Fn LibAliasIn "struct libalias *" "char *buffer" "int maxpacketsize" +.Bd -ragged -offset indent +An incoming packet coming from a remote machine to the local network is +de-aliased by this function. +The IP packet is pointed to by +.Fa buffer , +and +.Fa maxpacketsize +indicates the size of the data structure containing the packet and should +be at least as large as the actual packet size. +.Pp +Return codes: +.Bl -tag -width indent +.It Dv PKT_ALIAS_OK +The packet aliasing process was successful. +.It Dv PKT_ALIAS_IGNORED +The packet was ignored and not de-aliased. +This can happen if the protocol is unrecognized, possibly an ICMP message +type is not handled or if incoming packets for new connections are being +ignored (if +.Dv PKT_ALIAS_DENY_INCOMING +mode bit was set by +.Fn LibAliasSetMode ) . +.It Dv PKT_ALIAS_UNRESOLVED_FRAGMENT +This is returned when a fragment cannot be resolved because the header +fragment has not been sent yet. +In this situation, fragments must be saved with +.Fn LibAliasSaveFragment +until a header fragment is found. +.It Dv PKT_ALIAS_FOUND_HEADER_FRAGMENT +The packet aliasing process was successful, and a header fragment was found. +This is a signal to retrieve any unresolved fragments with +.Fn LibAliasGetFragment +and de-alias them with +.Fn LibAliasFragmentIn . +.It Dv PKT_ALIAS_ERROR +An internal error within the packet aliasing engine occurred. +.El +.Ed +.Pp +.Ft int +.Fn LibAliasOut "struct libalias *" "char *buffer" "int maxpacketsize" +.Bd -ragged -offset indent +An outgoing packet coming from the local network to a remote machine is +aliased by this function. +The IP packet is pointed to by +.Fa buffer , +and +.Fa maxpacketsize +indicates the maximum packet size permissible should the packet length be +changed. +IP encoding protocols place address and port information in the encapsulated +data stream which has to be modified and can account for changes in packet +length. +Well known examples of such protocols are FTP and IRC DCC. +.Pp +Return codes: +.Bl -tag -width indent +.It Dv PKT_ALIAS_OK +The packet aliasing process was successful. +.It Dv PKT_ALIAS_IGNORED +The packet was ignored and not aliased. +This can happen if the protocol is unrecognized, or possibly an ICMP message +type is not handled. +.It Dv PKT_ALIAS_ERROR +An internal error within the packet aliasing engine occurred. +.El +.Ed +.Sh PORT AND ADDRESS REDIRECTION +The functions described in this section allow machines on the local network +to be accessible in some degree to new incoming connections from the external +network. +Individual ports can be re-mapped or static network address translations can +be designated. +.Pp +.Ft struct alias_link * +.Fo LibAliasRedirectPort +.Fa "struct libalias *" +.Fa "struct in_addr local_addr" +.Fa "u_short local_port" +.Fa "struct in_addr remote_addr" +.Fa "u_short remote_port" +.Fa "struct in_addr alias_addr" +.Fa "u_short alias_port" +.Fa "u_char proto" +.Fc +.Bd -ragged -offset indent +This function specifies that traffic from a given remote address/port to +an alias address/port be redirected to a specified local address/port. +The parameter +.Fa proto +can be either +.Dv IPPROTO_TCP +or +.Dv IPPROTO_UDP , +as defined in +.In netinet/in.h . +.Pp +If +.Fa local_addr +or +.Fa alias_addr +is zero, this indicates that the packet aliasing address as established +by +.Fn LibAliasSetAddress +is to be used. +Even if +.Fn LibAliasSetAddress +is called to change the address after +.Fn LibAliasRedirectPort +is called, a zero reference will track this change. +.Pp +If the link is further set up to operate for a load sharing, then +.Fa local_addr +and +.Fa local_port +are ignored, and are selected dynamically from the server pool, as described in +.Fn LibAliasAddServer +below. +.Pp +If +.Fa remote_addr +is zero, this indicates to redirect packets from any remote address. +Likewise, if +.Fa remote_port +is zero, this indicates to redirect packets originating from any remote +port number. +Almost always, the remote port specification will be zero, but non-zero +remote addresses can sometimes be useful for firewalling. +If two calls to +.Fn LibAliasRedirectPort +overlap in their address/port specifications, then the most recent call +will have precedence. +.Pp +This function returns a pointer which can subsequently be used by +.Fn LibAliasRedirectDelete . +If +.Dv NULL +is returned, then the function call did not complete successfully. +.Pp +All port numbers should be in network address byte order, so it is necessary +to use +.Xr htons 3 +to convert these parameters from internally readable numbers to network byte +order. +Addresses are also in network byte order, which is implicit in the use of the +.Fa struct in_addr +data type. +.Ed +.Pp +.Ft struct alias_link * +.Fo LibAliasRedirectAddr +.Fa "struct libalias *" +.Fa "struct in_addr local_addr" +.Fa "struct in_addr alias_addr" +.Fc +.Bd -ragged -offset indent +This function designates that all incoming traffic to +.Fa alias_addr +be redirected to +.Fa local_addr . +Similarly, all outgoing traffic from +.Fa local_addr +is aliased to +.Fa alias_addr . +.Pp +If +.Fa local_addr +or +.Fa alias_addr +is zero, this indicates that the packet aliasing address as established by +.Fn LibAliasSetAddress +is to be used. +Even if +.Fn LibAliasSetAddress +is called to change the address after +.Fn LibAliasRedirectAddr +is called, a zero reference will track this change. +.Pp +If the link is further set up to operate for a load sharing, then +.Fa local_addr +is ignored, and is selected dynamically from the server pool, as described in +.Fn LibAliasAddServer +below. +.Pp +If subsequent calls to +.Fn LibAliasRedirectAddr +use the same aliasing address, all new incoming traffic to this aliasing +address will be redirected to the local address made in the last function +call. +New traffic generated by any of the local machines, designated in the +several function calls, will be aliased to the same address. +Consider the following example: +.Bd -literal -offset indent +LibAliasRedirectAddr(la, inet_aton("192.168.0.2"), + inet_aton("141.221.254.101")); +LibAliasRedirectAddr(la, inet_aton("192.168.0.3"), + inet_aton("141.221.254.101")); +LibAliasRedirectAddr(la, inet_aton("192.168.0.4"), + inet_aton("141.221.254.101")); +.Ed +.Pp +Any outgoing connections such as +.Xr telnet 1 +or +.Xr ftp 1 +from 192.168.0.2, 192.168.0.3 and 192.168.0.4 will appear to come from +141.221.254.101. +Any incoming connections to 141.221.254.101 will be directed to 192.168.0.4. +.Pp +Any calls to +.Fn LibAliasRedirectPort +will have precedence over address mappings designated by +.Fn LibAliasRedirectAddr . +.Pp +This function returns a pointer which can subsequently be used by +.Fn LibAliasRedirectDelete . +If +.Dv NULL +is returned, then the function call did not complete successfully. +.Ed +.Pp +.Ft int +.Fo LibAliasAddServer +.Fa "struct libalias *" +.Fa "struct alias_link *link" +.Fa "struct in_addr addr" +.Fa "u_short port" +.Fc +.Bd -ragged -offset indent +This function sets the +.Fa link +up for Load Sharing using IP Network Address Translation (RFC 2391, LSNAT). +LSNAT operates as follows. +A client attempts to access a server by using the server virtual address. +The LSNAT router transparently redirects the request to one of the hosts +in server pool, selected using a real-time load sharing algorithm. +Multiple sessions may be initiated from the same client, and each session +could be directed to a different host based on load balance across server +pool hosts at the time. +If load share is desired for just a few specific services, the configuration +on LSNAT could be defined to restrict load share for just the services +desired. +.Pp +Currently, only the simplest selection algorithm is implemented, where a +host is selected on a round-robin basis only, without regard to load on +the host. +.Pp +First, the +.Fa link +is created by either +.Fn LibAliasRedirectPort +or +.Fn LibAliasRedirectAddr . +Then, +.Fn LibAliasAddServer +is called multiple times to add entries to the +.Fa link Ns 's +server pool. +.Pp +For links created with +.Fn LibAliasRedirectAddr , +the +.Fa port +argument is ignored and could have any value, e.g.\& htons(~0). +.Pp +This function returns 0 on success, \-1 otherwise. +.Ed +.Pp +.Ft int +.Fn LibAliasRedirectDynamic "struct libalias *" "struct alias_link *link" +.Bd -ragged -offset indent +This function marks the specified static redirect rule entered by +.Fn LibAliasRedirectPort +as dynamic. +This can be used to e.g.\& dynamically redirect a single TCP connection, +after which the rule is removed. +Only fully specified links can be made dynamic. +(See the +.Sx STATIC AND DYNAMIC LINKS +and +.Sx PARTIALLY SPECIFIED ALIASING LINKS +sections below for a definition of static vs.\& dynamic, +and partially vs.\& fully specified links.) +.Pp +This function returns 0 on success, \-1 otherwise. +.Ed +.Pp +.Ft void +.Fn LibAliasRedirectDelete "struct libalias *" "struct alias_link *link" +.Bd -ragged -offset indent +This function will delete a specific static redirect rule entered by +.Fn LibAliasRedirectPort +or +.Fn LibAliasRedirectAddr . +The parameter +.Fa link +is the pointer returned by either of the redirection functions. +If an invalid pointer is passed to +.Fn LibAliasRedirectDelete , +then a program crash or unpredictable operation could result, so it is +necessary to be careful using this function. +.Ed +.Pp +.Ft int +.Fn LibAliasProxyRule "struct libalias *" "const char *cmd" +.Bd -ragged -offset indent +The passed +.Fa cmd +string consists of one or more pairs of words. +The first word in each pair is a token and the second is the value that +should be applied for that token. +Tokens and their argument types are as follows: +.Bl -tag -width indent +.It Cm type encode_ip_hdr | encode_tcp_stream | no_encode +In order to support transparent proxying, it is necessary to somehow +pass the original address and port information into the new destination +server. +If +.Cm encode_ip_hdr +is specified, the original destination address and port are passed +as an extra IP option. +If +.Cm encode_tcp_stream +is specified, the original destination address and port are passed +as the first piece of data in the TCP stream in the format +.Dq Li DEST Ar IP port . +.It Cm port Ar portnum +Only packets with the destination port +.Ar portnum +are proxied. +.It Cm server Ar host Ns Op : Ns Ar portnum +This specifies the +.Ar host +and +.Ar portnum +that the data is to be redirected to. +.Ar host +must be an IP address rather than a DNS host name. +If +.Ar portnum +is not specified, the destination port number is not changed. +.Pp +The +.Ar server +specification is mandatory unless the +.Cm delete +command is being used. +.It Cm rule Ar index +Normally, each call to +.Fn LibAliasProxyRule +inserts the next rule at the start of a linear list of rules. +If an +.Ar index +is specified, the new rule will be checked after all rules with lower +indices. +Calls to +.Fn LibAliasProxyRule +that do not specify a rule are assigned rule 0. +.It Cm delete Ar index +This token and its argument MUST NOT be used with any other tokens. +When used, all existing rules with the given +.Ar index +are deleted. +.It Cm proto tcp | udp +If specified, only packets of the given protocol type are matched. +.It Cm src Ar IP Ns Op / Ns Ar bits +If specified, only packets with a source address matching the given +.Ar IP +are matched. +If +.Ar bits +is also specified, then the first +.Ar bits +bits of +.Ar IP +are taken as a network specification, and all IP addresses from that +network will be matched. +.It Cm dst Ar IP Ns Op / Ns Ar bits +If specified, only packets with a destination address matching the given +.Ar IP +are matched. +If +.Ar bits +is also specified, then the first +.Ar bits +bits of +.Ar IP +are taken as a network specification, and all IP addresses from that +network will be matched. +.El +.Pp +This function is usually used to redirect outgoing connections for +internal machines that are not permitted certain types of internet +access, or to restrict access to certain external machines. +.Ed +.Pp +.Ft struct alias_link * +.Fo LibAliasRedirectProto +.Fa "struct libalias *" +.Fa "struct in_addr local_addr" +.Fa "struct in_addr remote_addr" +.Fa "struct in_addr alias_addr" +.Fa "u_char proto" +.Fc +.Bd -ragged -offset indent +This function specifies that any IP packet with protocol number of +.Fa proto +from a given remote address to an alias address be +redirected to a specified local address. +.Pp +If +.Fa local_addr +or +.Fa alias_addr +is zero, this indicates that the packet aliasing address as established +by +.Fn LibAliasSetAddress +is to be used. +Even if +.Fn LibAliasSetAddress +is called to change the address after +.Fn LibAliasRedirectProto +is called, a zero reference will track this change. +.Pp +If +.Fa remote_addr +is zero, this indicates to redirect packets from any remote address. +Non-zero remote addresses can sometimes be useful for firewalling. +.Pp +If two calls to +.Fn LibAliasRedirectProto +overlap in their address specifications, then the most recent call +will have precedence. +.Pp +This function returns a pointer which can subsequently be used by +.Fn LibAliasRedirectDelete . +If +.Dv NULL +is returned, then the function call did not complete successfully. +.Ed +.Sh FRAGMENT HANDLING +The functions in this section are used to deal with incoming fragments. +.Pp +Outgoing fragments are handled within +.Fn LibAliasOut +by changing the address according to any applicable mapping set by +.Fn LibAliasRedirectAddr , +or the default aliasing address set by +.Fn LibAliasSetAddress . +.Pp +Incoming fragments are handled in one of two ways. +If the header of a fragmented IP packet has already been seen, then all +subsequent fragments will be re-mapped in the same manner the header +fragment was. +Fragments which arrive before the header are saved and then retrieved +once the header fragment has been resolved. +.Pp +.Ft int +.Fn LibAliasSaveFragment "struct libalias *" "char *ptr" +.Bd -ragged -offset indent +When +.Fn LibAliasIn +returns +.Dv PKT_ALIAS_UNRESOLVED_FRAGMENT , +this function can be used to save the pointer to the unresolved fragment. +.Pp +It is implicitly assumed that +.Fa ptr +points to a block of memory allocated by +.Xr malloc 3 . +If the fragment is never resolved, the packet aliasing engine will +automatically free the memory after a timeout period. +[Eventually this function should be modified so that a callback function +for freeing memory is passed as an argument.] +.Pp +This function returns +.Dv PKT_ALIAS_OK +if it was successful and +.Dv PKT_ALIAS_ERROR +if there was an error. +.Ed +.Pp +.Ft char * +.Fn LibAliasGetFragment "struct libalias *" "char *buffer" +.Bd -ragged -offset indent +This function can be used to retrieve fragment pointers saved by +.Fn LibAliasSaveFragment . +The IP header fragment pointed to by +.Fa buffer +is the header fragment indicated when +.Fn LibAliasIn +returns +.Dv PKT_ALIAS_FOUND_HEADER_FRAGMENT . +Once a fragment pointer is retrieved, it becomes the calling program's +responsibility to free the dynamically allocated memory for the fragment. +.Pp +The +.Fn LibAliasGetFragment +function can be called sequentially until there are no more fragments +available, at which time it returns +.Dv NULL . +.Ed +.Pp +.Ft void +.Fn LibAliasFragmentIn "struct libalias *" "char *header" "char *fragment" +.Bd -ragged -offset indent +When a fragment is retrieved with +.Fn LibAliasGetFragment , +it can then be de-aliased with a call to +.Fn LibAliasFragmentIn . +The +.Fa header +argument is the pointer to a header fragment used as a template, and +.Fa fragment +is the pointer to the packet to be de-aliased. +.Ed +.Sh MISCELLANEOUS FUNCTIONS +.Ft void +.Fn LibAliasSetTarget "struct libalias *" "struct in_addr addr" +.Bd -ragged -offset indent +When an incoming packet not associated with any pre-existing aliasing link +arrives at the host machine, it will be sent to the address indicated by a +call to +.Fn LibAliasSetTarget . +.Pp +If this function is called with an +.Dv INADDR_NONE +address argument, then all new incoming packets go to the address set by +.Fn LibAliasSetAddress . +.Pp +If this function is not called, or is called with an +.Dv INADDR_ANY +address argument, then all new incoming packets go to the address specified +in the packet. +This allows external machines to talk directly to internal machines if they +can route packets to the machine in question. +.Ed +.Pp +.Ft int +.Fn LibAliasCheckNewLink void +.Bd -ragged -offset indent +This function returns a non-zero value when a new aliasing link is created. +In circumstances where incoming traffic is being sequentially sent to +different local servers, this function can be used to trigger when +.Fn LibAliasSetTarget +is called to change the default target address. +.Ed +.Pp +.Ft u_short +.Fn LibAliasInternetChecksum "struct libalias *" "u_short *buffer" "int nbytes" +.Bd -ragged -offset indent +This is a utility function that does not seem to be available elsewhere and +is included as a convenience. +It computes the internet checksum, which is used in both IP and +protocol-specific headers (TCP, UDP, ICMP). +.Pp +The +.Fa buffer +argument points to the data block to be checksummed, and +.Fa nbytes +is the number of bytes. +The 16-bit checksum field should be zeroed before computing the checksum. +.Pp +Checksums can also be verified by operating on a block of data including +its checksum. +If the checksum is valid, +.Fn LibAliasInternetChecksum +will return zero. +.Ed +.Pp +.Ft int +.Fn LibAliasUnaliasOut "struct libalias *" "char *buffer" "int maxpacketsize" +.Bd -ragged -offset indent +An outgoing packet, which has already been aliased, +has its private address/port information restored by this function. +The IP packet is pointed to by +.Fa buffer , +and +.Fa maxpacketsize +is provided for error checking purposes. +This function can be used if an already-aliased packet needs to have its +original IP header restored for further processing (e.g.\& logging). +.Ed +.Sh BUGS +PPTP aliasing does not work when more than one internal client +connects to the same external server at the same time, because +PPTP requires a single TCP control connection to be established +between any two IP addresses. +.Sh AUTHORS +.An Charles Mott Aq cm@linktel.net , +versions 1.0 - 1.8, 2.0 - 2.4. +.An Eivind Eklund Aq eivind@FreeBSD.org , +versions 1.8b, 1.9 and 2.5. +Added IRC DCC support as well as contributing a number of architectural +improvements; added the firewall bypass for FTP/IRC DCC. +.An Erik Salander Aq erik@whistle.com +added support for PPTP and RTSP. +.An Junichi Satoh Aq junichi@junichi.org +added support for RTSP/PNA. +.An Ruslan Ermilov Aq ru@FreeBSD.org +added support for PPTP and LSNAT as well as general hacking. +.Sh ACKNOWLEDGMENTS +Listed below, in approximate chronological order, are individuals who +have provided valuable comments and/or debugging assistance. +.Pp +.Bd -ragged -offset indent +.An -split +.An Gary Roberts +.An Tom Torrance +.An Reto Burkhalter +.An Martin Renters +.An Brian Somers +.An Paul Traina +.An Ari Suutari +.An Dave Remien +.An J. Fortes +.An Andrzej Bialecki +.An Gordon Burditt +.Ed +.Sh CONCEPTUAL BACKGROUND +This section is intended for those who are planning to modify the source +code or want to create somewhat esoteric applications using the packet +aliasing functions. +.Pp +The conceptual framework under which the packet aliasing engine operates +is described here. +Central to the discussion is the idea of an +.Em aliasing link +which describes the relationship for a given packet transaction between +the local machine, aliased identity and remote machine. +It is discussed how such links come into existence and are destroyed. +.Ss ALIASING LINKS +There is a notion of an +.Em aliasing link , +which is a 7-tuple describing a specific translation: +.Bd -literal -offset indent +(local addr, local port, alias addr, alias port, + remote addr, remote port, protocol) +.Ed +.Pp +Outgoing packets have the local address and port number replaced with the +alias address and port number. +Incoming packets undergo the reverse process. +The packet aliasing engine attempts to match packets against an internal +table of aliasing links to determine how to modify a given IP packet. +Both the IP header and protocol dependent headers are modified as necessary. +Aliasing links are created and deleted as necessary according to network +traffic. +.Pp +Protocols can be TCP, UDP or even ICMP in certain circumstances. +(Some types of ICMP packets can be aliased according to sequence or ID +number which acts as an equivalent port number for identifying how +individual packets should be handled.) +.Pp +Each aliasing link must have a unique combination of the following five +quantities: alias address/port, remote address/port and protocol. +This ensures that several machines on a local network can share the +same aliasing IP address. +In cases where conflicts might arise, the aliasing port is chosen so that +uniqueness is maintained. +.Ss STATIC AND DYNAMIC LINKS +Aliasing links can either be static or dynamic. +Static links persist indefinitely and represent fixed rules for translating +IP packets. +Dynamic links come into existence for a specific TCP connection or UDP +transaction or ICMP ECHO sequence. +For the case of TCP, the connection can be monitored to see when the +associated aliasing link should be deleted. +Aliasing links for UDP transactions (and ICMP ECHO and TIMESTAMP requests) +work on a simple timeout rule. +When no activity is observed on a dynamic link for a certain amount of time +it is automatically deleted. +Timeout rules also apply to TCP connections which do not open or close +properly. +.Ss PARTIALLY SPECIFIED ALIASING LINKS +Aliasing links can be partially specified, meaning that the remote address +and/or remote port are unknown. +In this case, when a packet matching the incomplete specification is found, +a fully specified dynamic link is created. +If the original partially specified link is dynamic, it will be deleted +after the fully specified link is created, otherwise it will persist. +.Pp +For instance, a partially specified link might be +.Bd -literal -offset indent +(192.168.0.4, 23, 204.228.203.215, 8066, 0, 0, tcp) +.Ed +.Pp +The zeros denote unspecified components for the remote address and port. +If this link were static it would have the effect of redirecting all +incoming traffic from port 8066 of 204.228.203.215 to port 23 (telnet) +of machine 192.168.0.4 on the local network. +Each individual telnet connection would initiate the creation of a distinct +dynamic link. +.Ss DYNAMIC LINK CREATION +In addition to aliasing links, there are also address mappings that can be +stored within the internal data table of the packet aliasing mechanism. +.Bd -literal -offset indent +(local addr, alias addr) +.Ed +.Pp +Address mappings are searched when creating new dynamic links. +.Pp +All outgoing packets from the local network automatically create a dynamic +link if they do not match an already existing fully specified link. +If an address mapping exists for the outgoing packet, this determines +the alias address to be used. +If no mapping exists, then a default address, usually the address of the +packet aliasing host, is used. +If necessary, this default address can be changed as often as each individual +packet arrives. +.Pp +The aliasing port number is determined such that the new dynamic link does +not conflict with any existing links. +In the default operating mode, the packet aliasing engine attempts to set +the aliasing port equal to the local port number. +If this results in a conflict, then port numbers are randomly chosen until +a unique aliasing link can be established. +In an alternate operating mode, the first choice of an aliasing port is also +random and unrelated to the local port number. |
