diff options
Diffstat (limited to 'freebsd/sys/netinet/libalias/alias.c')
-rw-r--r-- | freebsd/sys/netinet/libalias/alias.c | 1793 |
1 files changed, 1793 insertions, 0 deletions
diff --git a/freebsd/sys/netinet/libalias/alias.c b/freebsd/sys/netinet/libalias/alias.c new file mode 100644 index 00000000..e5c5138d --- /dev/null +++ b/freebsd/sys/netinet/libalias/alias.c @@ -0,0 +1,1793 @@ +#include <freebsd/machine/rtems-bsd-config.h> + +/*- + * 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 <freebsd/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. +*/ + +#ifdef _KERNEL +#include <freebsd/sys/param.h> +#include <freebsd/sys/systm.h> +#include <freebsd/sys/mbuf.h> +#include <freebsd/sys/sysctl.h> +#else +#include <freebsd/sys/types.h> +#include <freebsd/stdlib.h> +#include <freebsd/stdio.h> +#include <freebsd/ctype.h> +#include <freebsd/dlfcn.h> +#include <freebsd/errno.h> +#include <freebsd/string.h> +#endif + +#include <freebsd/netinet/in_systm.h> +#include <freebsd/netinet/in.h> +#include <freebsd/netinet/ip.h> +#include <freebsd/netinet/ip_icmp.h> +#include <freebsd/netinet/tcp.h> +#include <freebsd/netinet/udp.h> + +#ifdef _KERNEL +#include <freebsd/netinet/libalias/alias.h> +#include <freebsd/netinet/libalias/alias_local.h> +#include <freebsd/netinet/libalias/alias_mod.h> +#else +#include <freebsd/err.h> +#include <freebsd/local/alias.h> +#include <freebsd/local/alias_local.h> +#include <freebsd/local/alias_mod.h> +#endif + +/* + * Define libalias SYSCTL Node + */ +#ifdef SYSCTL_NODE + +SYSCTL_DECL(_net_inet); +SYSCTL_DECL(_net_inet_ip); +SYSCTL_NODE(_net_inet_ip, OID_AUTO, alias, CTLFLAG_RW, NULL, "Libalias sysctl API"); + +#endif + +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(u_char, struct alias_link *); + +static void TcpMonitorOut(u_char, struct alias_link *); + + +static void +TcpMonitorIn(u_char th_flags, struct alias_link *lnk) +{ + + switch (GetStateIn(lnk)) { + case ALIAS_TCP_STATE_NOT_CONNECTED: + if (th_flags & TH_RST) + SetStateIn(lnk, ALIAS_TCP_STATE_DISCONNECTED); + else if (th_flags & TH_SYN) + SetStateIn(lnk, ALIAS_TCP_STATE_CONNECTED); + break; + case ALIAS_TCP_STATE_CONNECTED: + if (th_flags & (TH_FIN | TH_RST)) + SetStateIn(lnk, ALIAS_TCP_STATE_DISCONNECTED); + break; + } +} + +static void +TcpMonitorOut(u_char th_flags, struct alias_link *lnk) +{ + + switch (GetStateOut(lnk)) { + case ALIAS_TCP_STATE_NOT_CONNECTED: + if (th_flags & TH_RST) + SetStateOut(lnk, ALIAS_TCP_STATE_DISCONNECTED); + else if (th_flags & TH_SYN) + SetStateOut(lnk, ALIAS_TCP_STATE_CONNECTED); + break; + case ALIAS_TCP_STATE_CONNECTED: + if (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 *la, struct in_addr ip_src, + struct in_addr *ip_dst, u_char ip_p, u_short *ip_sum); +static int ProtoAliasOut(struct libalias *la, struct in_addr *ip_src, + struct in_addr ip_dst, u_char ip_p, u_short *ip_sum, + int create); + +static int UdpAliasIn(struct libalias *, struct ip *); +static int UdpAliasOut(struct libalias *, struct ip *, int, 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) +{ + + LIBALIAS_LOCK_ASSERT(la); +/* + 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) +{ + + LIBALIAS_LOCK_ASSERT(la); +/* + 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; + + LIBALIAS_LOCK_ASSERT(la); +/* 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; + + LIBALIAS_LOCK_ASSERT(la); + 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; + + LIBALIAS_LOCK_ASSERT(la); + 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; + + LIBALIAS_LOCK_ASSERT(la); + (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 in_addr ip_src, + struct in_addr *ip_dst, u_char ip_p, u_short *ip_sum) +{ +/* + 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; + + LIBALIAS_LOCK_ASSERT(la); +/* Return if proxy-only mode is enabled */ + if (la->packetAliasMode & PKT_ALIAS_PROXY_ONLY) + return (PKT_ALIAS_OK); + + lnk = FindProtoIn(la, ip_src, *ip_dst, ip_p); + if (lnk != NULL) { + struct in_addr original_address; + + original_address = GetOriginalAddress(lnk); + +/* Restore original IP address */ + DifferentialChecksum(ip_sum, + &original_address, ip_dst, 2); + *ip_dst = original_address; + + return (PKT_ALIAS_OK); + } + return (PKT_ALIAS_IGNORED); +} + +static int +ProtoAliasOut(struct libalias *la, struct in_addr *ip_src, + struct in_addr ip_dst, u_char ip_p, u_short *ip_sum, 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; + + LIBALIAS_LOCK_ASSERT(la); + (void)create; + +/* Return if proxy-only mode is enabled */ + if (la->packetAliasMode & PKT_ALIAS_PROXY_ONLY) + return (PKT_ALIAS_OK); + + lnk = FindProtoOut(la, *ip_src, ip_dst, ip_p); + if (lnk != NULL) { + struct in_addr alias_address; + + alias_address = GetAliasAddress(lnk); + +/* Change source address */ + DifferentialChecksum(ip_sum, + &alias_address, ip_src, 2); + *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; + + LIBALIAS_LOCK_ASSERT(la); + + ud = (struct udphdr *)ip_next(pip); + + lnk = FindUdpTcpIn(la, pip->ip_src, pip->ip_dst, + ud->uh_sport, ud->uh_dport, + IPPROTO_UDP, !(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; + int error; + struct alias_data ad = { + .lnk = lnk, + .oaddr = &original_address, + .aaddr = &alias_address, + .aport = &alias_port, + .sport = &ud->uh_sport, + .dport = &ud->uh_dport, + .maxpktsize = 0 + }; + + alias_address = GetAliasAddress(lnk); + original_address = GetOriginalAddress(lnk); + proxy_address = GetProxyAddress(lnk); + alias_port = ud->uh_dport; + ud->uh_dport = GetOriginalPort(lnk); + proxy_port = GetProxyPort(lnk); + + /* Walk out chain. */ + error = find_handler(IN, UDP, la, pip, &ad); + /* If we cannot figure out the packet, ignore it. */ + if (error < 0) + return (PKT_ALIAS_IGNORED); + +/* 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); + +/* If this is a proxy packet, modify checksum because of source change.*/ + if (proxy_port != 0) { + accumulate += ud->uh_sport; + accumulate -= proxy_port; + } + + if (proxy_address.s_addr != 0) { + accumulate += twowords(&pip->ip_src); + accumulate -= twowords(&proxy_address); + } + + ADJUST_CHECKSUM(accumulate, ud->uh_sum); + } +/* XXX: Could the two if's below be concatenated to one ? */ +/* Restore source port and/or address in case of proxying*/ + + if (proxy_port != 0) + ud->uh_sport = proxy_port; + + if (proxy_address.s_addr != 0) { + DifferentialChecksum(&pip->ip_sum, + &proxy_address, &pip->ip_src, 2); + pip->ip_src = proxy_address; + } + +/* 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 +UdpAliasOut(struct libalias *la, struct ip *pip, int maxpacketsize, int create) +{ + struct udphdr *ud; + struct alias_link *lnk; + struct in_addr dest_address; + struct in_addr proxy_server_address; + u_short dest_port; + u_short proxy_server_port; + int proxy_type; + int error; + + LIBALIAS_LOCK_ASSERT(la); + +/* Return if proxy-only mode is enabled and not proxyrule found.*/ + ud = (struct udphdr *)ip_next(pip); + proxy_type = ProxyCheck(la, &proxy_server_address, + &proxy_server_port, pip->ip_src, pip->ip_dst, + ud->uh_dport, pip->ip_p); + 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 = ud->uh_dport; + dest_address = pip->ip_dst; + + if (proxy_type != 0) { + int accumulate; + + accumulate = twowords(&pip->ip_dst); + accumulate -= twowords(&proxy_server_address); + + ADJUST_CHECKSUM(accumulate, pip->ip_sum); + + if (ud->uh_sum != 0) { + accumulate = twowords(&pip->ip_dst); + accumulate -= twowords(&proxy_server_address); + accumulate += ud->uh_dport; + accumulate -= proxy_server_port; + ADJUST_CHECKSUM(accumulate, ud->uh_sum); + } + pip->ip_dst = proxy_server_address; + ud->uh_dport = proxy_server_port; + } + 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; + struct alias_data ad = { + .lnk = lnk, + .oaddr = NULL, + .aaddr = &alias_address, + .aport = &alias_port, + .sport = &ud->uh_sport, + .dport = &ud->uh_dport, + .maxpktsize = 0 + }; + +/* 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); + ud = (struct udphdr *)ip_next(pip); + } + + alias_address = GetAliasAddress(lnk); + alias_port = GetAliasPort(lnk); + + /* Walk out chain. */ + error = find_handler(OUT, UDP, la, pip, &ad); + +/* 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; + + LIBALIAS_LOCK_ASSERT(la); + 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, error; + + /* + * The init of MANY vars is a bit below, but aliashandlepptpin + * seems to need the destination port that came within the + * packet and not the original one looks below [*]. + */ + + struct alias_data ad = { + .lnk = lnk, + .oaddr = NULL, + .aaddr = NULL, + .aport = NULL, + .sport = &tc->th_sport, + .dport = &tc->th_dport, + .maxpktsize = 0 + }; + + /* Walk out chain. */ + error = find_handler(IN, TCP, la, pip, &ad); + + 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); + + /* + * Look above, if anyone is going to add find_handler AFTER + * this aliashandlepptpin/point, please redo alias_data too. + * Uncommenting the piece here below should be enough. + */ +#if 0 + struct alias_data ad = { + .lnk = lnk, + .oaddr = &original_address, + .aaddr = &alias_address, + .aport = &alias_port, + .sport = &ud->uh_sport, + .dport = &ud->uh_dport, + .maxpktsize = 0 + }; + + /* Walk out chain. */ + error = find_handler(la, pip, &ad); + if (error == EHDNOF) + printf("Protocol handler not found\n"); +#endif + +/* 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; + + tc = (struct tcphdr *)ip_next(pip); + delta = GetDeltaAckIn(tc->th_ack, 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 */ + tc = (struct tcphdr *)ip_next(pip); + TcpMonitorIn(tc->th_flags, 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, error; + 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; + + LIBALIAS_LOCK_ASSERT(la); + tc = (struct tcphdr *)ip_next(pip); + + if (create) + proxy_type = ProxyCheck(la, &proxy_server_address, + &proxy_server_port, pip->ip_src, pip->ip_dst, + tc->th_dport, pip->ip_p); + else + proxy_type = 0; + + 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; + struct alias_data ad = { + .lnk = lnk, + .oaddr = NULL, + .aaddr = &alias_address, + .aport = &alias_port, + .sport = &tc->th_sport, + .dport = &tc->th_dport, + .maxpktsize = maxpacketsize + }; + +/* 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 */ + tc = (struct tcphdr *)ip_next(pip); + TcpMonitorOut(tc->th_flags, lnk); + + /* Walk out chain. */ + error = find_handler(OUT, TCP, la, pip, &ad); + +/* 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; + + tc = (struct tcphdr *)ip_next(pip); + delta = GetDeltaSeqOut(tc->th_seq, 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 *la, struct in_addr ip_src, + struct in_addr *ip_dst, u_short ip_id, u_short *ip_sum); +static int FragmentOut(struct libalias *, struct in_addr *ip_src, + u_short *ip_sum); + +static int +FragmentIn(struct libalias *la, struct in_addr ip_src, struct in_addr *ip_dst, + u_short ip_id, u_short *ip_sum) +{ + struct alias_link *lnk; + + LIBALIAS_LOCK_ASSERT(la); + lnk = FindFragmentIn2(la, ip_src, *ip_dst, ip_id); + if (lnk != NULL) { + struct in_addr original_address; + + GetFragmentAddr(lnk, &original_address); + DifferentialChecksum(ip_sum, + &original_address, ip_dst, 2); + *ip_dst = original_address; + + return (PKT_ALIAS_OK); + } + return (PKT_ALIAS_UNRESOLVED_FRAGMENT); +} + +static int +FragmentOut(struct libalias *la, struct in_addr *ip_src, u_short *ip_sum) +{ + struct in_addr alias_address; + + LIBALIAS_LOCK_ASSERT(la); + alias_address = FindAliasAddress(la, *ip_src); + DifferentialChecksum(ip_sum, + &alias_address, ip_src, 2); + *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; + + LIBALIAS_LOCK(la); + 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; + } + LIBALIAS_UNLOCK(la); + return (iresult); +} + +char * +LibAliasGetFragment(struct libalias *la, char *ptr) +{ + struct alias_link *lnk; + char *fptr; + struct ip *pip; + + LIBALIAS_LOCK(la); + 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 */ + } else + fptr = NULL; + + LIBALIAS_UNLOCK(la); + return (fptr); +} + +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; + + LIBALIAS_LOCK(la); + (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; + LIBALIAS_UNLOCK(la); +} + +/* Local prototypes */ +static int +LibAliasOutLocked(struct libalias *la, char *ptr, + int maxpacketsize, int create); +static int +LibAliasInLocked(struct libalias *la, char *ptr, + int maxpacketsize); + +int +LibAliasIn(struct libalias *la, char *ptr, int maxpacketsize) +{ + int res; + + LIBALIAS_LOCK(la); + res = LibAliasInLocked(la, ptr, maxpacketsize); + LIBALIAS_UNLOCK(la); + return (res); +} + +static int +LibAliasInLocked(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 = LibAliasOutLocked(la, ptr, maxpacketsize, 1); + la->packetAliasMode |= PKT_ALIAS_REVERSE; + goto getout; + } + 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) { + iresult = PKT_ALIAS_IGNORED; + goto getout; + } + + 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; +#ifdef _KERNEL + case IPPROTO_SCTP: + iresult = SctpAlias(la, pip, SN_TO_LOCAL); + break; +#endif + case IPPROTO_GRE: { + int error; + struct alias_data ad = { + .lnk = NULL, + .oaddr = NULL, + .aaddr = NULL, + .aport = NULL, + .sport = NULL, + .dport = NULL, + .maxpktsize = 0 + }; + + /* Walk out chain. */ + error = find_handler(IN, IP, la, pip, &ad); + if (error == 0) + iresult = PKT_ALIAS_OK; + else + iresult = ProtoAliasIn(la, pip->ip_src, + &pip->ip_dst, pip->ip_p, &pip->ip_sum); + } + break; + default: + iresult = ProtoAliasIn(la, pip->ip_src, &pip->ip_dst, + pip->ip_p, &pip->ip_sum); + 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->ip_src, &pip->ip_dst, pip->ip_id, + &pip->ip_sum); + } + +getout: + 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, int maxpacketsize) +{ + int res; + + LIBALIAS_LOCK(la); + res = LibAliasOutLocked(la, ptr, maxpacketsize, 1); + LIBALIAS_UNLOCK(la); + return (res); +} + +int +LibAliasOutTry(struct libalias *la, char *ptr, int maxpacketsize, int create) +{ + int res; + + LIBALIAS_LOCK(la); + res = LibAliasOutLocked(la, ptr, maxpacketsize, create); + LIBALIAS_UNLOCK(la); + return (res); +} + +static int +LibAliasOutLocked(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 = LibAliasInLocked(la, ptr, maxpacketsize); + la->packetAliasMode |= PKT_ALIAS_REVERSE; + goto getout; + } + HouseKeeping(la); + ClearCheckNewLink(la); + pip = (struct ip *)ptr; + + /* Defense against mangled packets */ + if (ntohs(pip->ip_len) > maxpacketsize + || (pip->ip_hl << 2) > maxpacketsize) { + iresult = PKT_ALIAS_IGNORED; + goto getout; + } + + 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, maxpacketsize, create); + break; + case IPPROTO_TCP: + iresult = TcpAliasOut(la, pip, maxpacketsize, create); + break; +#ifdef _KERNEL + case IPPROTO_SCTP: + iresult = SctpAlias(la, pip, SN_TO_GLOBAL); + break; +#endif + case IPPROTO_GRE: { + int error; + struct alias_data ad = { + .lnk = NULL, + .oaddr = NULL, + .aaddr = NULL, + .aport = NULL, + .sport = NULL, + .dport = NULL, + .maxpktsize = 0 + }; + /* Walk out chain. */ + error = find_handler(OUT, IP, la, pip, &ad); + if (error == 0) + iresult = PKT_ALIAS_OK; + else + iresult = ProtoAliasOut(la, &pip->ip_src, + pip->ip_dst, pip->ip_p, &pip->ip_sum, create); + } + break; + default: + iresult = ProtoAliasOut(la, &pip->ip_src, + pip->ip_dst, pip->ip_p, &pip->ip_sum, create); + break; + } + } else { + iresult = FragmentOut(la, &pip->ip_src, &pip->ip_sum); + } + + SetDefaultAliasAddress(la, addr_save); +getout: + 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; + + LIBALIAS_LOCK(la); + pip = (struct ip *)ptr; + + /* Defense against mangled packets */ + if (ntohs(pip->ip_len) > maxpacketsize + || (pip->ip_hl << 2) > maxpacketsize) + goto getout; + + 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; + } + } +getout: + LIBALIAS_UNLOCK(la); + return (iresult); + +} + +#ifndef _KERNEL + +int +LibAliasRefreshModules(void) +{ + char buf[256], conf[] = "/etc/libalias.conf"; + FILE *fd; + int i, len; + + fd = fopen(conf, "r"); + if (fd == NULL) + err(1, "fopen(%s)", conf); + + LibAliasUnLoadAllModule(); + + for (;;) { + fgets(buf, 256, fd); + if (feof(fd)) + break; + len = strlen(buf); + if (len > 1) { + for (i = 0; i < len; i++) + if (!isspace(buf[i])) + break; + if (buf[i] == '#') + continue; + buf[len - 1] = '\0'; + LibAliasLoadModule(buf); + } + } + fclose(fd); + return (0); +} + +int +LibAliasLoadModule(char *path) +{ + struct dll *t; + void *handle; + struct proto_handler *m; + const char *error; + moduledata_t *p; + + handle = dlopen (path, RTLD_LAZY); + if (!handle) { + fprintf(stderr, "%s\n", dlerror()); + return (EINVAL); + } + + p = dlsym(handle, "alias_mod"); + if ((error = dlerror()) != NULL) { + fprintf(stderr, "%s\n", dlerror()); + return (EINVAL); + } + + t = malloc(sizeof(struct dll)); + if (t == NULL) + return (ENOMEM); + strncpy(t->name, p->name, DLL_LEN); + t->handle = handle; + if (attach_dll(t) == EEXIST) { + free(t); + fprintf(stderr, "dll conflict\n"); + return (EEXIST); + } + + m = dlsym(t->handle, "handlers"); + if ((error = dlerror()) != NULL) { + fprintf(stderr, "%s\n", error); + return (EINVAL); + } + + LibAliasAttachHandlers(m); + return (0); +} + +int +LibAliasUnLoadAllModule(void) +{ + struct dll *t; + struct proto_handler *p; + + /* Unload all modules then reload everything. */ + while ((p = first_handler()) != NULL) { + detach_handler(p); + } + while ((t = walk_dll_chain()) != NULL) { + dlclose(t->handle); + free(t); + } + return (1); +} + +#endif + +#ifdef _KERNEL +/* + * m_megapullup() - this function is a big hack. + * Thankfully, it's only used in ng_nat and ipfw+nat. + * + * It allocates an mbuf with cluster and copies the specified part of the chain + * into cluster, so that it is all contiguous and can be accessed via a plain + * (char *) pointer. This is required, because libalias doesn't know how to + * handle mbuf chains. + * + * On success, m_megapullup returns an mbuf (possibly with cluster) containing + * the input packet, on failure NULL. The input packet is always consumed. + */ +struct mbuf * +m_megapullup(struct mbuf *m, int len) { + struct mbuf *mcl; + + if (len > m->m_pkthdr.len) + goto bad; + + /* Do not reallocate packet if it is sequentional, + * writable and has some extra space for expansion. + * XXX: Constant 100bytes is completely empirical. */ +#define RESERVE 100 + if (m->m_next == NULL && M_WRITABLE(m) && M_TRAILINGSPACE(m) >= RESERVE) + return (m); + + if (len <= MCLBYTES - RESERVE) { + mcl = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); + } else if (len < MJUM16BYTES) { + int size; + if (len <= MJUMPAGESIZE - RESERVE) { + size = MJUMPAGESIZE; + } else if (len <= MJUM9BYTES - RESERVE) { + size = MJUM9BYTES; + } else { + size = MJUM16BYTES; + }; + mcl = m_getjcl(M_DONTWAIT, MT_DATA, M_PKTHDR, size); + } else { + goto bad; + } + if (mcl == NULL) + goto bad; + + m_move_pkthdr(mcl, m); + m_copydata(m, 0, len, mtod(mcl, caddr_t)); + mcl->m_len = mcl->m_pkthdr.len = len; + m_freem(m); + + return (mcl); +bad: + m_freem(m); + return (NULL); +} +#endif |