diff --git a/lib/libalias/alias.c b/lib/libalias/alias.c index 326b8c0338ad..027c7e4ee4c8 100644 --- a/lib/libalias/alias.c +++ b/lib/libalias/alias.c @@ -1,1421 +1,1421 @@ /*- * Copyright (c) 2001 Charles Mott * 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 __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.) + 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.) + 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. + - 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.) + 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. + 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). + - 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). + - 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. + - 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 #include #include #include #include #include #include #include #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) { u_short *s = p; return (s[0] + s[1]); } /* 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 *link) { struct tcphdr *tc; tc = (struct tcphdr *)((char *)pip + (pip->ip_hl << 2)); switch (GetStateIn(link)) { case ALIAS_TCP_STATE_NOT_CONNECTED: if (tc->th_flags & TH_RST) SetStateIn(link, ALIAS_TCP_STATE_DISCONNECTED); else if (tc->th_flags & TH_SYN) SetStateIn(link, ALIAS_TCP_STATE_CONNECTED); break; case ALIAS_TCP_STATE_CONNECTED: if (tc->th_flags & (TH_FIN | TH_RST)) SetStateIn(link, ALIAS_TCP_STATE_DISCONNECTED); break; } } static void TcpMonitorOut(struct ip *pip, struct alias_link *link) { struct tcphdr *tc; tc = (struct tcphdr *)((char *)pip + (pip->ip_hl << 2)); switch (GetStateOut(link)) { case ALIAS_TCP_STATE_NOT_CONNECTED: if (tc->th_flags & TH_RST) SetStateOut(link, ALIAS_TCP_STATE_DISCONNECTED); else if (tc->th_flags & TH_SYN) SetStateOut(link, ALIAS_TCP_STATE_CONNECTED); break; case ALIAS_TCP_STATE_CONNECTED: if (tc->th_flags & (TH_FIN | TH_RST)) SetStateOut(link, 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 *); 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 *link; struct icmp *ic; ic = (struct icmp *)((char *)pip + (pip->ip_hl << 2)); /* Get source address from ICMP data field and restore original data */ link = FindIcmpIn(la, pip->ip_src, pip->ip_dst, ic->icmp_id, 1); if (link != NULL) { u_short original_id; int accumulate; original_id = GetOriginalPort(link); /* Adjust ICMP checksum */ accumulate = ic->icmp_id; accumulate -= original_id; ADJUST_CHECKSUM(accumulate, ic->icmp_cksum); /* Put original sequence number back in */ ic->icmp_id = original_id; /* Put original address back into IP header */ { struct in_addr original_address; original_address = GetOriginalAddress(link); DifferentialChecksum(&pip->ip_sum, &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 *link; ic = (struct icmp *)((char *)pip + (pip->ip_hl << 2)); ip = &ic->icmp_ip; ud = (struct udphdr *)((char *)ip + (ip->ip_hl << 2)); tc = (struct tcphdr *)ud; ic2 = (struct icmp *)ud; if (ip->ip_p == IPPROTO_UDP) link = FindUdpTcpIn(la, ip->ip_dst, ip->ip_src, ud->uh_dport, ud->uh_sport, IPPROTO_UDP, 0); else if (ip->ip_p == IPPROTO_TCP) link = 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) link = FindIcmpIn(la, ip->ip_dst, ip->ip_src, ic2->icmp_id, 0); else link = NULL; } else link = NULL; if (link != 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(link); original_port = GetOriginalPort(link); /* 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(link); original_id = GetOriginalPort(link); /* 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 *)((char *)pip + (pip->ip_hl << 2)); iresult = PKT_ALIAS_IGNORED; switch (ic->icmp_type) { case ICMP_ECHOREPLY: case ICMP_TSTAMPREPLY: if (ic->icmp_code == 0) { iresult = IcmpAliasIn1(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) { /* Alias outgoing echo and timestamp requests. De-alias outgoing echo and timestamp replies. */ struct alias_link *link; struct icmp *ic; ic = (struct icmp *)((char *)pip + (pip->ip_hl << 2)); /* Save overwritten data for when echo packet returns */ link = FindIcmpOut(la, pip->ip_src, pip->ip_dst, ic->icmp_id, 1); if (link != NULL) { u_short alias_id; int accumulate; alias_id = GetAliasPort(link); /* Since data field is being modified, adjust ICMP checksum */ accumulate = ic->icmp_id; accumulate -= alias_id; ADJUST_CHECKSUM(accumulate, ic->icmp_cksum); /* Alias sequence number */ ic->icmp_id = alias_id; /* Change source address */ { struct in_addr alias_address; alias_address = GetAliasAddress(link); DifferentialChecksum(&pip->ip_sum, &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 *link; ic = (struct icmp *)((char *)pip + (pip->ip_hl << 2)); ip = &ic->icmp_ip; ud = (struct udphdr *)((char *)ip + (ip->ip_hl << 2)); tc = (struct tcphdr *)ud; ic2 = (struct icmp *)ud; if (ip->ip_p == IPPROTO_UDP) link = FindUdpTcpOut(la, ip->ip_dst, ip->ip_src, ud->uh_dport, ud->uh_sport, IPPROTO_UDP, 0); else if (ip->ip_p == IPPROTO_TCP) link = 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) link = FindIcmpOut(la, ip->ip_dst, ip->ip_src, ic2->icmp_id, 0); else link = NULL; } else link = NULL; if (link != 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(link); alias_port = GetAliasPort(link); /* 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(link); alias_id = GetAliasPort(link); /* 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; /* Return if proxy-only mode is enabled */ if (la->packetAliasMode & PKT_ALIAS_PROXY_ONLY) return PKT_ALIAS_OK; ic = (struct icmp *)((char *)pip + (pip->ip_hl << 2)); iresult = PKT_ALIAS_IGNORED; switch (ic->icmp_type) { case ICMP_ECHO: case ICMP_TSTAMP: if (ic->icmp_code == 0) { iresult = IcmpAliasOut1(la, pip); } 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); } 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 *link; /* Return if proxy-only mode is enabled */ if (la->packetAliasMode & PKT_ALIAS_PROXY_ONLY) return PKT_ALIAS_OK; link = FindProtoIn(la, pip->ip_src, pip->ip_dst, pip->ip_p); if (link != NULL) { struct in_addr original_address; original_address = GetOriginalAddress(link); /* 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 *link; /* Return if proxy-only mode is enabled */ if (la->packetAliasMode & PKT_ALIAS_PROXY_ONLY) return PKT_ALIAS_OK; link = FindProtoOut(la, pip->ip_src, pip->ip_dst, pip->ip_p); if (link != NULL) { struct in_addr alias_address; alias_address = GetAliasAddress(link); /* 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 *link; /* Return if proxy-only mode is enabled */ if (la->packetAliasMode & PKT_ALIAS_PROXY_ONLY) return PKT_ALIAS_OK; ud = (struct udphdr *)((char *)pip + (pip->ip_hl << 2)); link = FindUdpTcpIn(la, pip->ip_src, pip->ip_dst, ud->uh_sport, ud->uh_dport, IPPROTO_UDP, 1); if (link != NULL) { struct in_addr alias_address; struct in_addr original_address; u_short alias_port; int accumulate; int r = 0; alias_address = GetAliasAddress(link); original_address = GetOriginalAddress(link); alias_port = ud->uh_dport; ud->uh_dport = GetOriginalPort(link); /* 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, link, &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, link, &alias_address, &alias_port, &original_address, &ud->uh_dport); /* If UDP checksum is not zero, then adjust since destination port */ /* is being unaliased and destination 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 *link; /* Return if proxy-only mode is enabled */ if (la->packetAliasMode & PKT_ALIAS_PROXY_ONLY) return PKT_ALIAS_OK; ud = (struct udphdr *)((char *)pip + (pip->ip_hl << 2)); link = FindUdpTcpOut(la, pip->ip_src, pip->ip_dst, ud->uh_sport, ud->uh_dport, IPPROTO_UDP, create); if (link != NULL) { u_short alias_port; struct in_addr alias_address; alias_address = GetAliasAddress(link); alias_port = GetAliasPort(link); /* Special processing for IP encoding protocols */ if (ntohs(ud->uh_dport) == CUSEEME_PORT_NUMBER) AliasHandleCUSeeMeOut(la, pip, link); /* 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, link, &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, link, &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 *link; tc = (struct tcphdr *)((char *)pip + (pip->ip_hl << 2)); link = FindUdpTcpIn(la, pip->ip_src, pip->ip_dst, tc->th_sport, tc->th_dport, IPPROTO_TCP, !(la->packetAliasMode & PKT_ALIAS_PROXY_ONLY)); if (link != 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, link); else if (la->skinnyPort != 0 && (ntohs(tc->th_dport) == la->skinnyPort || ntohs(tc->th_sport) == la->skinnyPort)) AliasHandleSkinny(la, pip, link); alias_address = GetAliasAddress(link); original_address = GetOriginalAddress(link); proxy_address = GetProxyAddress(link); alias_port = tc->th_dport; tc->th_dport = GetOriginalPort(link); proxy_port = GetProxyPort(link); /* 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(link) == 1) { int delta; delta = GetDeltaAckIn(pip, link); 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, link); 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 *link; tc = (struct tcphdr *)((char *)pip + (pip->ip_hl << 2)); 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); } link = FindUdpTcpOut(la, pip->ip_src, pip->ip_dst, tc->th_sport, tc->th_dport, IPPROTO_TCP, create); if (link == NULL) return (PKT_ALIAS_IGNORED); if (link != 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(link, dest_port); SetProxyAddress(link, dest_address); ProxyModify(la, link, pip, maxpacketsize, proxy_type); tc = (struct tcphdr *)((char *)pip + (pip->ip_hl << 2)); } /* Get alias address and port */ alias_port = GetAliasPort(link); alias_address = GetAliasAddress(link); /* Monitor TCP connection state */ TcpMonitorOut(pip, link); /* 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, link, maxpacketsize); else if (ntohs(tc->th_dport) == IRC_CONTROL_PORT_NUMBER_1 || ntohs(tc->th_dport) == IRC_CONTROL_PORT_NUMBER_2) AliasHandleIrcOut(la, pip, link, 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, link, maxpacketsize); else if (ntohs(tc->th_dport) == PPTP_CONTROL_PORT_NUMBER || ntohs(tc->th_sport) == PPTP_CONTROL_PORT_NUMBER) AliasHandlePptpOut(la, pip, link); else if (la->skinnyPort != 0 && (ntohs(tc->th_sport) == la->skinnyPort || ntohs(tc->th_dport) == la->skinnyPort)) AliasHandleSkinny(la, pip, link); /* 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(link) == 1) { int delta; delta = GetDeltaSeqOut(pip, link); 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 *link; link = FindFragmentIn2(la, pip->ip_src, pip->ip_dst, pip->ip_id); if (link != NULL) { struct in_addr original_address; GetFragmentAddr(link, &original_address); DifferentialChecksum(&pip->ip_sum, &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() + PacketAliasSaveFragment() + PacketAliasGetFragment() + PacketAliasFragmentIn() + PacketAliasIn() + PacketAliasOut() + PacketUnaliasOut() (prototypes in alias.h) */ int LibAliasSaveFragment(struct libalias *la, char *ptr) { int iresult; struct alias_link *link; struct ip *pip; pip = (struct ip *)ptr; link = AddFragmentPtrLink(la, pip->ip_src, pip->ip_id); iresult = PKT_ALIAS_ERROR; if (link != NULL) { SetFragmentPtr(link, ptr); iresult = PKT_ALIAS_OK; } return (iresult); } char * LibAliasGetFragment(struct libalias *la, char *ptr) { struct alias_link *link; char *fptr; struct ip *pip; pip = (struct ip *)ptr; link = FindFragmentPtr(la, pip->ip_src, pip->ip_id); if (link != NULL) { GetFragmentPtr(link, &fptr); SetFragmentPtr(link, NULL); SetExpire(link, 0); /* Deletes link */ return (fptr); } else { return (NULL); } } void 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; 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 *link; link = FindFragmentIn1(la, pip->ip_src, alias_addr, pip->ip_id); if (link != NULL) { iresult = PKT_ALIAS_FOUND_HEADER_FRAGMENT; SetFragmentAddr(link, pip->ip_dst); } else { iresult = PKT_ALIAS_ERROR; } } } else { iresult = FragmentIn(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 *link; 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 *)((char *)pip + (pip->ip_hl << 2)); tc = (struct tcphdr *)ud; ic = (struct icmp *)ud; /* Find a link */ if (pip->ip_p == IPPROTO_UDP) link = FindUdpTcpIn(la, pip->ip_dst, pip->ip_src, ud->uh_dport, ud->uh_sport, IPPROTO_UDP, 0); else if (pip->ip_p == IPPROTO_TCP) link = FindUdpTcpIn(la, pip->ip_dst, pip->ip_src, tc->th_dport, tc->th_sport, IPPROTO_TCP, 0); else if (pip->ip_p == IPPROTO_ICMP) link = FindIcmpIn(la, pip->ip_dst, pip->ip_src, ic->icmp_id, 0); else link = NULL; /* Change it from an aliased packet to an unaliased packet */ if (link != 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(link); original_port = GetOriginalPort(link); /* 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(link); original_id = GetOriginalPort(link); /* 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_db.c b/lib/libalias/alias_db.c index 039db039311d..31f669d3d594 100644 --- a/lib/libalias/alias_db.c +++ b/lib/libalias/alias_db.c @@ -1,2784 +1,2784 @@ /*- * Copyright (c) 2001 Charles Mott * 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 __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. + Facility for handling incoming links added. Version 1.6: September 18, 1996 (cjm) - ICMP data handling simplified. + 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. + 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) + 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) + 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) + 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) + 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) + Removed K&R style function headers + and general cleanup. (ee) - Added packetAliasMode to replace compiler #defines's (ee) + Added packetAliasMode to replace compiler #defines's (ee) - Allocates sockets for partially specified - ports if ALIAS_USE_SOCKETS defined. (cjm) + 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) + 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: + 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.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 + (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) + 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) + 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) + 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). + 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. + 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. + 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 FindNewPortGroup to get contiguous range of port values. - Added QueryUdpTcpIn and QueryUdpTcpOut to look for an aliasing + Added QueryUdpTcpIn and QueryUdpTcpOut to look for an aliasing link but not actually add one. - Added FindRtspOut, which is closely derived from FindUdpTcpOut, + 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 #include #include #include #include #include #include #include /* BSD network include files */ #include #include #include #include #include #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) + 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 + incoming packets StartPointOut() -- link table initial search point for - outgoing packets + 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 *la); static void UninitPunchFW(struct libalias *la); static void ClearFWHole(struct alias_link *link); #endif /* Log file control */ static void InitPacketAliasLog(struct libalias *la); static void UninitPacketAliasLog(struct libalias *la); 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 *link, int alias_port_param) { int i; int max_trials; u_short port_sys; u_short port_net; /* Description of alias_port_param for GetNewPort(). When this parameter is zero or positive, it precisely specifies the port number. GetNewPort() will return this number without check that it is in use. 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 = link->src_port; port_sys = ntohs(port_net); } else { /* First trial and all subsequent are random. */ port_sys = random() & ALIAS_PORT_MASK; port_sys += ALIAS_PORT_BASE; port_net = htons(port_sys); } } else if (alias_port_param >= 0 && alias_port_param < 0x10000) { link->alias_port = (u_short) alias_port_param; return (0); } else { #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, link->dst_addr, link->alias_addr, link->dst_port, port_net, link->link_type, 0); if (search_result == NULL) go_ahead = 1; else if (!(link->flags & LINK_PARTIALLY_SPECIFIED) && (search_result->flags & LINK_PARTIALLY_SPECIFIED)) go_ahead = 1; else go_ahead = 0; if (go_ahead) { if ((la->packetAliasMode & PKT_ALIAS_USE_SOCKETS) && (link->flags & LINK_PARTIALLY_SPECIFIED) && ((link->link_type == LINK_TCP) || (link->link_type == LINK_UDP))) { if (GetSocket(la, port_net, &link->sockfd, link->link_type)) { link->alias_port = port_net; return (0); } } else { link->alias_port = port_net; return (0); } } port_sys = random() & ALIAS_PORT_MASK; port_sys += ALIAS_PORT_BASE; port_net = htons(port_sys); } #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 *link; int i, icount; icount = 0; for (i = 0; i < LINK_TABLE_OUT_SIZE; i++) { link = LIST_FIRST(&la->linkTableOut[i]); while (link != NULL) { struct alias_link *link_next; link_next = LIST_NEXT(link, list_out); icount++; DeleteLink(link); link = link_next; } } la->cleanupIndex = 0; } static void IncrementalCleanup(struct libalias *la) { int icount; struct alias_link *link; icount = 0; link = LIST_FIRST(&la->linkTableOut[la->cleanupIndex++]); while (link != NULL) { int idelta; struct alias_link *link_next; link_next = LIST_NEXT(link, list_out); idelta = la->timeStamp - link->timestamp; switch (link->link_type) { case LINK_TCP: if (idelta > link->expire_time) { struct tcp_dat *tcp_aux; tcp_aux = link->data.tcp; if (tcp_aux->state.in != ALIAS_TCP_STATE_CONNECTED || tcp_aux->state.out != ALIAS_TCP_STATE_CONNECTED) { DeleteLink(link); icount++; } } break; default: if (idelta > link->expire_time) { DeleteLink(link); icount++; } break; } link = link_next; } if (la->cleanupIndex == LINK_TABLE_OUT_SIZE) la->cleanupIndex = 0; } static void DeleteLink(struct alias_link *link) { struct libalias *la = link->la; /* Don't do anything if the link is marked permanent */ if (la->deleteAllLinks == 0 && link->flags & LINK_PERMANENT) return; #ifndef NO_FW_PUNCH /* Delete associated firewall hole, if any */ ClearFWHole(link); #endif /* Free memory allocated for LSNAT server pool */ if (link->server != NULL) { struct server *head, *curr, *next; head = curr = link->server; do { next = curr->next; free(curr); } while ((curr = next) != head); } /* Adjust output table pointers */ LIST_REMOVE(link, list_out); /* Adjust input table pointers */ LIST_REMOVE(link, list_in); /* Close socket, if one has been allocated */ if (link->sockfd != -1) { la->sockCount--; close(link->sockfd); } /* Link-type dependent cleanup */ switch (link->link_type) { case LINK_ICMP: la->icmpLinkCount--; break; case LINK_UDP: la->udpLinkCount--; break; case LINK_TCP: la->tcpLinkCount--; free(link->data.tcp); break; case LINK_PPTP: la->pptpLinkCount--; break; case LINK_FRAGMENT_ID: la->fragmentIdLinkCount--; break; case LINK_FRAGMENT_PTR: la->fragmentPtrLinkCount--; if (link->data.frag_ptr != NULL) free(link->data.frag_ptr); break; case LINK_ADDR: break; default: la->protoLinkCount--; break; } /* Free memory */ free(link); /* 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 *link; link = malloc(sizeof(struct alias_link)); if (link != NULL) { /* Basic initialization */ link->la = la; link->src_addr = src_addr; link->dst_addr = dst_addr; link->alias_addr = alias_addr; link->proxy_addr.s_addr = INADDR_ANY; link->src_port = src_port; link->dst_port = dst_port; link->proxy_port = 0; link->server = NULL; link->link_type = link_type; link->sockfd = -1; link->flags = 0; link->pflags = 0; link->timestamp = la->timeStamp; /* Expiration time */ switch (link_type) { case LINK_ICMP: link->expire_time = ICMP_EXPIRE_TIME; break; case LINK_UDP: link->expire_time = UDP_EXPIRE_TIME; break; case LINK_TCP: link->expire_time = TCP_EXPIRE_INITIAL; break; case LINK_PPTP: link->flags |= LINK_PERMANENT; /* no timeout. */ break; case LINK_FRAGMENT_ID: link->expire_time = FRAGMENT_ID_EXPIRE_TIME; break; case LINK_FRAGMENT_PTR: link->expire_time = FRAGMENT_PTR_EXPIRE_TIME; break; case LINK_ADDR: break; default: link->expire_time = PROTO_EXPIRE_TIME; break; } /* Determine alias flags */ if (dst_addr.s_addr == INADDR_ANY) link->flags |= LINK_UNKNOWN_DEST_ADDR; if (dst_port == 0) link->flags |= LINK_UNKNOWN_DEST_PORT; /* Determine alias port */ if (GetNewPort(la, link, alias_port_param) != 0) { free(link); 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; link->data.tcp = aux_tcp; } else { #ifdef DEBUG fprintf(stderr, "PacketAlias/AddLink: "); fprintf(stderr, " cannot allocate auxiliary TCP data\n"); #endif free(link); 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], link, list_out); /* Set up pointers for input lookup table */ start_point = StartPointIn(alias_addr, link->alias_port, link_type); LIST_INSERT_HEAD(&la->linkTableIn[start_point], link, 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 (link); } static struct alias_link * ReLink(struct alias_link *old_link, struct in_addr src_addr, struct in_addr dst_addr, struct in_addr alias_addr, u_short src_port, u_short dst_port, int alias_port_param, /* if less than zero, alias */ int link_type) { /* port will be automatically *//* chosen. * If greater than */ struct alias_link *new_link; /* zero, equal to alias port */ struct libalias *la = old_link->la; new_link = AddLink(la, src_addr, dst_addr, alias_addr, src_port, dst_port, alias_port_param, link_type); #ifndef NO_FW_PUNCH if (new_link != NULL && old_link->link_type == LINK_TCP && old_link->data.tcp->fwhole > 0) { PunchFWHole(new_link); } #endif DeleteLink(old_link); return new_link; } 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 *link; i = StartPointOut(src_addr, dst_addr, src_port, dst_port, link_type); LIST_FOREACH(link, &la->linkTableOut[i], list_out) { if (link->src_addr.s_addr == src_addr.s_addr && link->server == NULL && link->dst_addr.s_addr == dst_addr.s_addr && link->dst_port == dst_port && link->src_port == src_port && link->link_type == link_type) { link->timestamp = la->timeStamp; break; } } /* Search for partially specified links. */ if (link == NULL && replace_partial_links) { if (dst_port != 0 && dst_addr.s_addr != INADDR_ANY) { link = _FindLinkOut(la, src_addr, dst_addr, src_port, 0, link_type, 0); if (link == NULL) link = _FindLinkOut(la, src_addr, la->nullAddress, src_port, dst_port, link_type, 0); } if (link == NULL && (dst_port != 0 || dst_addr.s_addr != INADDR_ANY)) { link = _FindLinkOut(la, src_addr, la->nullAddress, src_port, 0, link_type, 0); } if (link != NULL) { link = ReLink(link, src_addr, dst_addr, link->alias_addr, src_port, dst_port, link->alias_port, link_type); } } return (link); } 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 *link; link = _FindLinkOut(la, src_addr, dst_addr, src_port, dst_port, link_type, replace_partial_links); if (link == 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) { link = _FindLinkOut(la, la->nullAddress, dst_addr, src_port, dst_port, link_type, replace_partial_links); } } return (link); } 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 *link; struct alias_link *link_fully_specified; struct alias_link *link_unknown_all; struct alias_link *link_unknown_dst_addr; struct alias_link *link_unknown_dst_port; /* Initialize pointers */ link_fully_specified = NULL; link_unknown_all = NULL; link_unknown_dst_addr = NULL; link_unknown_dst_port = NULL; /* If either the dest addr or port is unknown, the search loop will have to know about this. */ flags_in = 0; if (dst_addr.s_addr == 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(link, &la->linkTableIn[start_point], list_in) { int flags; flags = flags_in | link->flags; if (!(flags & LINK_PARTIALLY_SPECIFIED)) { if (link->alias_addr.s_addr == alias_addr.s_addr && link->alias_port == alias_port && link->dst_addr.s_addr == dst_addr.s_addr && link->dst_port == dst_port && link->link_type == link_type) { link_fully_specified = link; break; } } else if ((flags & LINK_UNKNOWN_DEST_ADDR) && (flags & LINK_UNKNOWN_DEST_PORT)) { if (link->alias_addr.s_addr == alias_addr.s_addr && link->alias_port == alias_port && link->link_type == link_type) { if (link_unknown_all == NULL) link_unknown_all = link; } } else if (flags & LINK_UNKNOWN_DEST_ADDR) { if (link->alias_addr.s_addr == alias_addr.s_addr && link->alias_port == alias_port && link->link_type == link_type && link->dst_port == dst_port) { if (link_unknown_dst_addr == NULL) link_unknown_dst_addr = link; } } else if (flags & LINK_UNKNOWN_DEST_PORT) { if (link->alias_addr.s_addr == alias_addr.s_addr && link->alias_port == alias_port && link->link_type == link_type && link->dst_addr.s_addr == dst_addr.s_addr) { if (link_unknown_dst_port == NULL) link_unknown_dst_port = link; } } } if (link_fully_specified != NULL) { link_fully_specified->timestamp = la->timeStamp; link = link_fully_specified; } else if (link_unknown_dst_port != NULL) link = link_unknown_dst_port; else if (link_unknown_dst_addr != NULL) link = link_unknown_dst_addr; else if (link_unknown_all != NULL) link = link_unknown_all; else return (NULL); if (replace_partial_links && (link->flags & LINK_PARTIALLY_SPECIFIED || link->server != NULL)) { struct in_addr src_addr; u_short src_port; if (link->server != NULL) { /* LSNAT link */ src_addr = link->server->addr; src_port = link->server->port; link->server = link->server->next; } else { src_addr = link->src_addr; src_port = link->src_port; } link = ReLink(link, src_addr, dst_addr, alias_addr, src_port, dst_port, alias_port, link_type); } return (link); } 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 *link; link = _FindLinkIn(la, dst_addr, alias_addr, dst_port, alias_port, link_type, replace_partial_links); if (link == 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) { link = _FindLinkIn(la, dst_addr, la->nullAddress, dst_port, alias_port, link_type, replace_partial_links); } } return (link); } /* 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 *link; link = FindLinkIn(la, dst_addr, alias_addr, NO_DEST_PORT, id_alias, LINK_ICMP, 0); if (link == NULL && create && !(la->packetAliasMode & PKT_ALIAS_DENY_INCOMING)) { struct in_addr target_addr; target_addr = FindOriginalAddress(la, alias_addr); link = AddLink(la, target_addr, dst_addr, alias_addr, id_alias, NO_DEST_PORT, id_alias, LINK_ICMP); } return (link); } 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 *link; link = FindLinkOut(la, src_addr, dst_addr, id, NO_DEST_PORT, LINK_ICMP, 0); if (link == NULL && create) { struct in_addr alias_addr; alias_addr = FindAliasAddress(la, src_addr); link = AddLink(la, src_addr, dst_addr, alias_addr, id, NO_DEST_PORT, GET_ALIAS_ID, LINK_ICMP); } return (link); } struct alias_link * FindFragmentIn1(struct libalias *la, struct in_addr dst_addr, struct in_addr alias_addr, u_short ip_id) { struct alias_link *link; link = FindLinkIn(la, dst_addr, alias_addr, NO_DEST_PORT, ip_id, LINK_FRAGMENT_ID, 0); if (link == NULL) { link = AddLink(la, la->nullAddress, dst_addr, alias_addr, NO_SRC_PORT, NO_DEST_PORT, ip_id, LINK_FRAGMENT_ID); } return (link); } 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 *link; link = FindLinkIn(la, dst_addr, alias_addr, NO_DEST_PORT, 0, proto, 1); if (link == NULL && !(la->packetAliasMode & PKT_ALIAS_DENY_INCOMING)) { struct in_addr target_addr; target_addr = FindOriginalAddress(la, alias_addr); link = AddLink(la, target_addr, dst_addr, alias_addr, NO_SRC_PORT, NO_DEST_PORT, 0, proto); } return (link); } struct alias_link * FindProtoOut(struct libalias *la, struct in_addr src_addr, struct in_addr dst_addr, u_char proto) { struct alias_link *link; link = FindLinkOut(la, src_addr, dst_addr, NO_SRC_PORT, NO_DEST_PORT, proto, 1); if (link == NULL) { struct in_addr alias_addr; alias_addr = FindAliasAddress(la, src_addr); link = AddLink(la, src_addr, dst_addr, alias_addr, NO_SRC_PORT, NO_DEST_PORT, 0, proto); } return (link); } 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 *link; switch (proto) { case IPPROTO_UDP: link_type = LINK_UDP; break; case IPPROTO_TCP: link_type = LINK_TCP; break; default: return NULL; break; } link = FindLinkIn(la, dst_addr, alias_addr, dst_port, alias_port, link_type, create); if (link == NULL && create && !(la->packetAliasMode & PKT_ALIAS_DENY_INCOMING)) { struct in_addr target_addr; target_addr = FindOriginalAddress(la, alias_addr); link = AddLink(la, target_addr, dst_addr, alias_addr, alias_port, dst_port, alias_port, link_type); } return (link); } 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 *link; switch (proto) { case IPPROTO_UDP: link_type = LINK_UDP; break; case IPPROTO_TCP: link_type = LINK_TCP; break; default: return NULL; break; } link = FindLinkOut(la, src_addr, dst_addr, src_port, dst_port, link_type, create); if (link == NULL && create) { struct in_addr alias_addr; alias_addr = FindAliasAddress(la, src_addr); link = AddLink(la, src_addr, dst_addr, alias_addr, src_port, dst_port, GET_ALIAS_PORT, link_type); } return (link); } 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 *link; link = AddLink(la, src_addr, dst_addr, alias_addr, src_call_id, 0, GET_ALIAS_PORT, LINK_PPTP); return (link); } 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 *link; i = StartPointOut(src_addr, dst_addr, 0, 0, LINK_PPTP); LIST_FOREACH(link, &la->linkTableOut[i], list_out) if (link->link_type == LINK_PPTP && link->src_addr.s_addr == src_addr.s_addr && link->dst_addr.s_addr == dst_addr.s_addr && link->src_port == src_call_id) break; return (link); } 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 *link; i = StartPointOut(src_addr, dst_addr, 0, 0, LINK_PPTP); LIST_FOREACH(link, &la->linkTableOut[i], list_out) if (link->link_type == LINK_PPTP && link->src_addr.s_addr == src_addr.s_addr && link->dst_addr.s_addr == dst_addr.s_addr && link->dst_port == dst_call_id) break; return (link); } 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 *link; i = StartPointIn(alias_addr, 0, LINK_PPTP); LIST_FOREACH(link, &la->linkTableIn[i], list_in) if (link->link_type == LINK_PPTP && link->dst_addr.s_addr == dst_addr.s_addr && link->alias_addr.s_addr == alias_addr.s_addr && link->dst_port == dst_call_id) break; return (link); } 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 *link; link = FindLinkIn(la, dst_addr, alias_addr, 0 /* any */ , alias_call_id, LINK_PPTP, 0); return (link); } 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 *link; switch (proto) { case IPPROTO_UDP: link_type = LINK_UDP; break; case IPPROTO_TCP: link_type = LINK_TCP; break; default: return NULL; break; } link = FindLinkOut(la, src_addr, dst_addr, src_port, 0, link_type, 1); if (link == NULL) { struct in_addr alias_addr; alias_addr = FindAliasAddress(la, src_addr); link = AddLink(la, src_addr, dst_addr, alias_addr, src_port, 0, alias_port, link_type); } return (link); } struct in_addr FindOriginalAddress(struct libalias *la, struct in_addr alias_addr) { struct alias_link *link; link = FindLinkIn(la, la->nullAddress, alias_addr, 0, 0, LINK_ADDR, 0); if (link == 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 (link->server != NULL) { /* LSNAT link */ struct in_addr src_addr; src_addr = link->server->addr; link->server = link->server->next; return (src_addr); } else if (link->src_addr.s_addr == INADDR_ANY) return (la->aliasAddress.s_addr != INADDR_ANY) ? la->aliasAddress : alias_addr; else return link->src_addr; } } struct in_addr FindAliasAddress(struct libalias *la, struct in_addr original_addr) { struct alias_link *link; link = FindLinkOut(la, original_addr, la->nullAddress, 0, 0, LINK_ADDR, 0); if (link == NULL) { return (la->aliasAddress.s_addr != INADDR_ANY) ? la->aliasAddress : original_addr; } else { if (link->alias_addr.s_addr == INADDR_ANY) return (la->aliasAddress.s_addr != INADDR_ANY) ? la->aliasAddress : original_addr; else return link->alias_addr; } } /* External routines for getting or changing link data (external to alias_db.c, but internal to alias*.c) SetFragmentData(), GetFragmentData() SetFragmentPtr(), GetFragmentPtr() SetStateIn(), SetStateOut(), GetStateIn(), GetStateOut() GetOriginalAddress(), GetDestAddress(), GetAliasAddress() GetOriginalPort(), GetAliasPort() SetAckModified(), GetAckModified() GetDeltaAckIn(), GetDeltaSeqOut(), AddSeq() SetProtocolFlags(), GetProtocolFlags() SetDestCallId() */ void SetFragmentAddr(struct alias_link *link, struct in_addr src_addr) { link->data.frag_addr = src_addr; } void GetFragmentAddr(struct alias_link *link, struct in_addr *src_addr) { *src_addr = link->data.frag_addr; } void SetFragmentPtr(struct alias_link *link, char *fptr) { link->data.frag_ptr = fptr; } void GetFragmentPtr(struct alias_link *link, char **fptr) { *fptr = link->data.frag_ptr; } void SetStateIn(struct alias_link *link, int state) { /* TCP input state */ switch (state) { case ALIAS_TCP_STATE_DISCONNECTED: if (link->data.tcp->state.out != ALIAS_TCP_STATE_CONNECTED) link->expire_time = TCP_EXPIRE_DEAD; else link->expire_time = TCP_EXPIRE_SINGLEDEAD; break; case ALIAS_TCP_STATE_CONNECTED: if (link->data.tcp->state.out == ALIAS_TCP_STATE_CONNECTED) link->expire_time = TCP_EXPIRE_CONNECTED; break; default: abort(); } link->data.tcp->state.in = state; } void SetStateOut(struct alias_link *link, int state) { /* TCP output state */ switch (state) { case ALIAS_TCP_STATE_DISCONNECTED: if (link->data.tcp->state.in != ALIAS_TCP_STATE_CONNECTED) link->expire_time = TCP_EXPIRE_DEAD; else link->expire_time = TCP_EXPIRE_SINGLEDEAD; break; case ALIAS_TCP_STATE_CONNECTED: if (link->data.tcp->state.in == ALIAS_TCP_STATE_CONNECTED) link->expire_time = TCP_EXPIRE_CONNECTED; break; default: abort(); } link->data.tcp->state.out = state; } int GetStateIn(struct alias_link *link) { /* TCP input state */ return link->data.tcp->state.in; } int GetStateOut(struct alias_link *link) { /* TCP output state */ return link->data.tcp->state.out; } struct in_addr GetOriginalAddress(struct alias_link *link) { if (link->src_addr.s_addr == INADDR_ANY) return link->la->aliasAddress; else return (link->src_addr); } struct in_addr GetDestAddress(struct alias_link *link) { return (link->dst_addr); } struct in_addr GetAliasAddress(struct alias_link *link) { if (link->alias_addr.s_addr == INADDR_ANY) return link->la->aliasAddress; else return link->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 *link) { return (link->src_port); } u_short GetAliasPort(struct alias_link *link) { return (link->alias_port); } #ifndef NO_FW_PUNCH static u_short GetDestPort(struct alias_link *link) { return (link->dst_port); } #endif void SetAckModified(struct alias_link *link) { /* Indicate that ACK numbers have been modified in a TCP connection */ link->data.tcp->state.ack_modified = 1; } struct in_addr GetProxyAddress(struct alias_link *link) { return link->proxy_addr; } void SetProxyAddress(struct alias_link *link, struct in_addr addr) { link->proxy_addr = addr; } u_short GetProxyPort(struct alias_link *link) { return link->proxy_port; } void SetProxyPort(struct alias_link *link, u_short port) { link->proxy_port = port; } int GetAckModified(struct alias_link *link) { /* See if ACK numbers have been modified */ return link->data.tcp->state.ack_modified; } int GetDeltaAckIn(struct ip *pip, struct alias_link *link) { /* Find out how much the ACK number has been altered for an incoming TCP packet. To do this, a circular list 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 = (struct tcphdr *)((char *)pip + (pip->ip_hl << 2)); ack = tc->th_ack; delta = 0; ack_diff_min = -1; for (i = 0; i < N_LINK_TCP_DATA; i++) { struct ack_data_record x; x = link->data.tcp->ack[i]; if (x.active == 1) { int ack_diff; ack_diff = SeqDiff(x.ack_new, ack); if (ack_diff >= 0) { if (ack_diff_min >= 0) { if (ack_diff < ack_diff_min) { delta = x.delta; ack_diff_min = ack_diff; } } else { delta = x.delta; ack_diff_min = ack_diff; } } } } return (delta); } int GetDeltaSeqOut(struct ip *pip, struct alias_link *link) { /* Find out how much the 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 = (struct tcphdr *)((char *)pip + (pip->ip_hl << 2)); seq = tc->th_seq; delta = 0; seq_diff_min = -1; for (i = 0; i < N_LINK_TCP_DATA; i++) { struct ack_data_record x; x = link->data.tcp->ack[i]; if (x.active == 1) { int seq_diff; seq_diff = SeqDiff(x.ack_old, seq); if (seq_diff >= 0) { if (seq_diff_min >= 0) { if (seq_diff < seq_diff_min) { delta = x.delta; seq_diff_min = seq_diff; } } else { delta = x.delta; seq_diff_min = seq_diff; } } } } return (delta); } void AddSeq(struct ip *pip, struct alias_link *link, int delta) { /* When a TCP packet has been altered in length, save this information in a circular list. If enough packets have been altered, then this list will begin to overwrite itself. */ struct tcphdr *tc; struct ack_data_record x; int hlen, tlen, dlen; int i; tc = (struct tcphdr *)((char *)pip + (pip->ip_hl << 2)); hlen = (pip->ip_hl + tc->th_off) << 2; tlen = ntohs(pip->ip_len); dlen = tlen - hlen; x.ack_old = htonl(ntohl(tc->th_seq) + dlen); x.ack_new = htonl(ntohl(tc->th_seq) + dlen + delta); x.delta = delta; x.active = 1; i = link->data.tcp->state.index; link->data.tcp->ack[i] = x; i++; if (i == N_LINK_TCP_DATA) link->data.tcp->state.index = 0; else link->data.tcp->state.index = i; } void SetExpire(struct alias_link *link, int expire) { if (expire == 0) { link->flags &= ~LINK_PERMANENT; DeleteLink(link); } else if (expire == -1) { link->flags |= LINK_PERMANENT; } else if (expire > 0) { link->expire_time = expire; } else { #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 *link, int pflags) { link->pflags = pflags;; } int GetProtocolFlags(struct alias_link *link) { return (link->pflags); } void SetDestCallId(struct alias_link *link, u_int16_t cid) { struct libalias *la = link->la; la->deleteAllLinks = 1; link = ReLink(link, link->src_addr, link->dst_addr, link->alias_addr, link->src_port, cid, link->alias_port, link->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 *link; 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; } link = AddLink(la, src_addr, dst_addr, alias_addr, src_port, dst_port, alias_port, link_type); if (link != NULL) { link->flags |= LINK_PERMANENT; } #ifdef DEBUG else { fprintf(stderr, "PacketAliasRedirectPort(): " "call to AddLink() failed\n"); } #endif return link; } /* Add server to the pool of servers */ int LibAliasAddServer(struct libalias *la, struct alias_link *link, struct in_addr addr, u_short port) { struct server *server; server = malloc(sizeof(struct server)); if (server != NULL) { struct server *head; server->addr = addr; server->port = port; head = link->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; } link->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 *link; link = AddLink(la, src_addr, dst_addr, alias_addr, NO_SRC_PORT, NO_DEST_PORT, 0, proto); if (link != NULL) { link->flags |= LINK_PERMANENT; } #ifdef DEBUG else { fprintf(stderr, "PacketAliasRedirectProto(): " "call to AddLink() failed\n"); } #endif return link; } /* Static address translation */ struct alias_link * LibAliasRedirectAddr(struct libalias *la, struct in_addr src_addr, struct in_addr alias_addr) { struct alias_link *link; link = AddLink(la, src_addr, la->nullAddress, alias_addr, 0, 0, 0, LINK_ADDR); if (link != NULL) { link->flags |= LINK_PERMANENT; } #ifdef DEBUG else { fprintf(stderr, "PacketAliasRedirectAddr(): " "call to AddLink() failed\n"); } #endif return link; } /* Mark the aliasing link dynamic */ int LibAliasRedirectDynamic(struct libalias *la, struct alias_link *link) { if (link->flags & LINK_PARTIALLY_SPECIFIED) return (-1); else { link->flags &= ~LINK_PERMANENT; return (0); } } void LibAliasRedirectDelete(struct libalias *la, struct alias_link *link) { /* This is a dangerous function to put in the API, because an invalid pointer can crash the program. */ la->deleteAllLinks = 1; DeleteLink(link); 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 #include #include #include #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 *link) { struct libalias *la; int r; /* Result code */ struct ip_fw rule; /* On-the-fly built rule */ int fwhole; /* Where to punch hole */ la = link->la; /* Don't do anything unless we are asked to */ if (!(la->packetAliasMode & PKT_ALIAS_PUNCH_FW) || la->fireWallFD < 0 || link->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(link) != 0 && GetDestPort(link) != 0) { u_int32_t rulebuf[255]; int i; i = fill_rule(rulebuf, sizeof(rulebuf), fwhole, O_ACCEPT, IPPROTO_TCP, GetOriginalAddress(link), ntohs(GetOriginalPort(link)), GetDestAddress(link), ntohs(GetDestPort(link))); 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(link), ntohs(GetDestPort(link)), GetOriginalAddress(link), ntohs(GetOriginalPort(link))); 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(link); rule.fw_dst = GetDestAddress(link); rule.fw_uar.fw_pts[0] = ntohs(GetOriginalPort(link)); rule.fw_uar.fw_pts[1] = ntohs(GetDestPort(link)); /* * Skip non-bound links - XXX should not be strictly necessary, but * seems to leave hole if not done. Leak of non-bound links? (Code * should be left even if the problem is fixed - it is a clear * optimization) */ if (rule.fw_uar.fw_pts[0] != 0 && rule.fw_uar.fw_pts[1] != 0) { r = setsockopt(fireWallFD, IPPROTO_IP, IP_FW_ADD, &rule, sizeof rule); #ifdef DEBUG if (r) err(1, "alias punch inbound(1) setsockopt(IP_FW_ADD)"); #endif rule.fw_src = GetDestAddress(link); rule.fw_dst = GetOriginalAddress(link); rule.fw_uar.fw_pts[0] = ntohs(GetDestPort(link)); rule.fw_uar.fw_pts[1] = ntohs(GetOriginalPort(link)); r = setsockopt(fireWallFD, IPPROTO_IP, IP_FW_ADD, &rule, sizeof rule); #ifdef DEBUG if (r) err(1, "alias punch inbound(2) setsockopt(IP_FW_ADD)"); #endif } #endif /* !IPFW2 */ /* Indicate hole applied */ link->data.tcp->fwhole = fwhole; fw_setfield(la, la->fireWallField, fwhole); } /* Remove a hole in a firewall associated with a particular alias link. Calling this too often is harmless. */ static void ClearFWHole(struct alias_link *link) { struct libalias *la; la = link->la; if (link->link_type == LINK_TCP) { int fwhole = link->data.tcp->fwhole; /* Where is the firewall * hole? */ struct ip_fw rule; if (fwhole < 0) return; memset(&rule, 0, sizeof rule); /* 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); link->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 index 91d49525bc75..2a7def7bc433 100644 --- a/lib/libalias/alias_ftp.c +++ b/lib/libalias/alias_ftp.c @@ -1,675 +1,675 @@ /*- * Copyright (c) 2001 Charles Mott * 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 __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. + 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. + 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. + 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 #include #include #include #include #include #include #include #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 *link, /* The link to go through (aliased port) */ int maxpacketsize /* The maximum size this packet can grow to - (including headers) */ ) + (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 *)((char *)pip + (pip->ip_hl << 2)); 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(link); 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, link, 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(link, 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 *link, int maxpacketsize, int ftp_message_type) { struct alias_link *ftp_link; /* 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_link = FindUdpTcpOut(la, la->true_addr, GetDestAddress(link), htons(la->true_port), 0, IPPROTO_TCP, 1); if (ftp_link != NULL) { int slen, hlen, tlen, dlen; struct tcphdr *tc; #ifndef NO_FW_PUNCH /* Punch hole in firewall */ PunchFWHole(ftp_link); #endif /* Calculate data length of TCP packet */ tc = (struct tcphdr *)((char *)pip + (pip->ip_hl << 2)); hlen = (pip->ip_hl + tc->th_off) << 2; tlen = ntohs(pip->ip_len); dlen = tlen - hlen; /* Create new 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(link); ptr = (u_char *) & alias_address.s_addr; a1 = *ptr++; a2 = *ptr++; a3 = *ptr++; a4 = *ptr; alias_port = GetAliasPort(ftp_link); 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(link); delta = GetDeltaSeqOut(pip, link); AddSeq(pip, link, delta + slen - dlen); } /* Revise IP header */ { u_short new_len; new_len = htons(hlen + slen); DifferentialChecksum(&pip->ip_sum, &new_len, &pip->ip_len, 1); pip->ip_len = new_len; } /* Compute TCP checksum for revised packet */ tc->th_sum = 0; tc->th_sum = TcpChecksum(pip); } else { #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 index 5785e54a7594..7b3d030d1a39 100644 --- a/lib/libalias/alias_irc.c +++ b/lib/libalias/alias_irc.c @@ -1,386 +1,386 @@ /*- * Copyright (c) 2001 Charles Mott * 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 __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 (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. + Version 2.1: May, 1997 (cjm) + Very minor changes to conform with + local/global/function naming conventions + withing the packet alising module. */ /* Includes */ #include #include #include #include #include #include #include #include #include #include "alias_local.h" /* Local defines */ #define DBprintf(a) void AliasHandleIrcOut(struct libalias *la, struct ip *pip, /* IP packet to examine */ struct alias_link *link, /* Which link are we on? */ int maxsize /* Maximum size of IP packet including * headers */ ) { int hlen, tlen, dlen; struct in_addr true_addr; u_short true_port; char *sptr; struct tcphdr *tc; int i; /* Iterator through the source */ /* Calculate data length of TCP packet */ tc = (struct tcphdr *)((char *)pip + (pip->ip_hl << 2)); hlen = (pip->ip_hl + tc->th_off) << 2; tlen = ntohs(pip->ip_len); dlen = tlen - hlen; /* * Return if data length is too short - assume an entire PRIVMSG in * each packet. */ if (dlen < sizeof(":A!a@n.n PRIVMSG A :aDCC 1 1a") - 1) return; /* Place string pointer at beginning of data */ sptr = (char *)pip; sptr += hlen; maxsize -= hlen; /* We're interested in maximum size of * data, not packet */ /* Search for a CTCP command [Note 1] */ for (i = 0; i < dlen; i++) { if (sptr[i] == '\001') goto lFOUND_CTCP; } return; /* No CTCP commands in */ /* Handle CTCP commands - the buffer may have to be copied */ lFOUND_CTCP: { char newpacket[65536]; /* Estimate of maximum packet size * :) */ int copyat = i; /* Same */ int iCopy = 0; /* How much data have we written to * copy-back string? */ unsigned long org_addr; /* Original IP address */ unsigned short org_port; /* Original source port * address */ lCTCP_START: if (i >= dlen || iCopy >= sizeof(newpacket)) goto lPACKET_DONE; newpacket[iCopy++] = sptr[i++]; /* Copy the CTCP start * character */ /* Start of a CTCP */ if (i + 4 >= dlen) /* Too short for DCC */ goto lBAD_CTCP; if (sptr[i + 0] != 'D') goto lBAD_CTCP; if (sptr[i + 1] != 'C') goto lBAD_CTCP; if (sptr[i + 2] != 'C') goto lBAD_CTCP; if (sptr[i + 3] != ' ') goto lBAD_CTCP; /* We have a DCC command - handle it! */ i += 4; /* Skip "DCC " */ if (iCopy + 4 > sizeof(newpacket)) goto lPACKET_DONE; newpacket[iCopy++] = 'D'; newpacket[iCopy++] = 'C'; newpacket[iCopy++] = 'C'; newpacket[iCopy++] = ' '; DBprintf(("Found DCC\n")); /* * Skip any extra spaces (should not occur according to * protocol, but DCC breaks CTCP protocol anyway */ while (sptr[i] == ' ') { if (++i >= dlen) { DBprintf(("DCC packet terminated in just spaces\n")); goto lPACKET_DONE; } } DBprintf(("Transferring command...\n")); while (sptr[i] != ' ') { newpacket[iCopy++] = sptr[i]; if (++i >= dlen || iCopy >= sizeof(newpacket)) { DBprintf(("DCC packet terminated during command\n")); goto lPACKET_DONE; } } /* Copy _one_ space */ if (i + 1 < dlen && iCopy < sizeof(newpacket)) newpacket[iCopy++] = sptr[i++]; DBprintf(("Done command - removing spaces\n")); /* * Skip any extra spaces (should not occur according to * protocol, but DCC breaks CTCP protocol anyway */ while (sptr[i] == ' ') { if (++i >= dlen) { DBprintf(("DCC packet terminated in just spaces (post-command)\n")); goto lPACKET_DONE; } } DBprintf(("Transferring filename...\n")); while (sptr[i] != ' ') { newpacket[iCopy++] = sptr[i]; if (++i >= dlen || iCopy >= sizeof(newpacket)) { DBprintf(("DCC packet terminated during filename\n")); goto lPACKET_DONE; } } /* Copy _one_ space */ if (i + 1 < dlen && iCopy < sizeof(newpacket)) newpacket[iCopy++] = sptr[i++]; DBprintf(("Done filename - removing spaces\n")); /* * Skip any extra spaces (should not occur according to * protocol, but DCC breaks CTCP protocol anyway */ while (sptr[i] == ' ') { if (++i >= dlen) { DBprintf(("DCC packet terminated in just spaces (post-filename)\n")); goto lPACKET_DONE; } } DBprintf(("Fetching IP address\n")); /* Fetch IP address */ org_addr = 0; while (i < dlen && isdigit(sptr[i])) { if (org_addr > ULONG_MAX / 10UL) { /* Terminate on overflow */ DBprintf(("DCC Address overflow (org_addr == 0x%08lx, next char %c\n", org_addr, sptr[i])); goto lBAD_CTCP; } org_addr *= 10; org_addr += sptr[i++] - '0'; } DBprintf(("Skipping space\n")); if (i + 1 >= dlen || sptr[i] != ' ') { DBprintf(("Overflow (%d >= %d) or bad character (%02x) terminating IP address\n", i + 1, dlen, sptr[i])); goto lBAD_CTCP; } /* * Skip any extra spaces (should not occur according to * protocol, but DCC breaks CTCP protocol anyway, so we * might as well play it safe */ while (sptr[i] == ' ') { if (++i >= dlen) { DBprintf(("Packet failure - space overflow.\n")); goto lPACKET_DONE; } } DBprintf(("Fetching port number\n")); /* Fetch source port */ org_port = 0; while (i < dlen && isdigit(sptr[i])) { if (org_port > 6554) { /* Terminate on overflow * (65536/10 rounded up */ DBprintf(("DCC: port number overflow\n")); goto lBAD_CTCP; } org_port *= 10; org_port += sptr[i++] - '0'; } /* Skip illegal addresses (or early termination) */ if (i >= dlen || (sptr[i] != '\001' && sptr[i] != ' ')) { DBprintf(("Bad port termination\n")); goto lBAD_CTCP; } DBprintf(("Got IP %lu and port %u\n", org_addr, (unsigned)org_port)); /* We've got the address and port - now alias it */ { struct alias_link *dcc_link; struct in_addr destaddr; true_port = htons(org_port); true_addr.s_addr = htonl(org_addr); destaddr.s_addr = 0; /* 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_link = FindUdpTcpOut(la, true_addr, destaddr, true_port, 0, IPPROTO_TCP, 1); DBprintf(("Got a DCC link\n")); if (dcc_link) { struct in_addr alias_address; /* Address from aliasing */ u_short alias_port; /* Port given by * aliasing */ int n; #ifndef NO_FW_PUNCH /* Generate firewall hole as appropriate */ PunchFWHole(dcc_link); #endif alias_address = GetAliasAddress(link); 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_link); 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(link); delta = GetDeltaSeqOut(pip, link); AddSeq(pip, link, delta + copyat + iCopy - dlen); } /* Revise IP header */ { u_short new_len; new_len = htons(hlen + iCopy + copyat); DifferentialChecksum(&pip->ip_sum, &new_len, &pip->ip_len, 1); pip->ip_len = new_len; } /* Compute TCP checksum for revised packet */ tc->th_sum = 0; tc->th_sum = TcpChecksum(pip); return; } } /* Notes: [Note 1] The initial search will most often fail; it could be replaced with a 32-bit specific search. Such a search would be done for 32-bit unsigned value V: V ^= 0x01010101; (Search is for null bytes) if( ((V-0x01010101)^V) & 0x80808080 ) { (found a null bytes which was a 01 byte) } To assert that the processor is 32-bits, do extern int ircdccar[32]; (32 bits) extern int ircdccar[CHAR_BIT*sizeof(unsigned int)]; which will generate a type-error on all but 32-bit machines. [Note 2] This routine really ought to be replaced with one that creates a transparent proxy on the aliasing host, to allow arbitary changes in the TCP stream. This should not be too difficult given this base; I (ee) will try to do this some time later. */ diff --git a/lib/libalias/alias_proxy.c b/lib/libalias/alias_proxy.c index cdbe32d1c0aa..ceb9119dac95 100644 --- a/lib/libalias/alias_proxy.c +++ b/lib/libalias/alias_proxy.c @@ -1,807 +1,807 @@ /*- * Copyright (c) 2001 Charles Mott * 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 __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. + 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 #include #include #include #include #include #include /* BSD IPV4 includes */ #include #include #include #include #include #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. + masks from integer (1-32) specification. IpAddr() -- Utility function for converting string - to IP address + to IP address IpPort() -- Utility function for converting string - to port number + 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. + having a certain rule number. ProxyEncodeTcpStream() -- Adds [DEST x.x.x.x xxxx] to the beginning - of a TCP stream. + of a TCP stream. ProxyEncodeIpHeader() -- Adds an IP option indicating the true - destination of a proxied IP packet + 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 *link, struct ip *pip, int maxpacketsize) { int slen; char buffer[40]; struct tcphdr *tc; /* Compute pointer to tcp header */ tc = (struct tcphdr *)((char *)pip + (pip->ip_hl << 2)); /* Don't modify if once already modified */ if (GetAckModified(link)) return; /* Translate destination address and port to string form */ snprintf(buffer, sizeof(buffer) - 2, "[DEST %s %d]", inet_ntoa(GetProxyAddress(link)), (u_int) ntohs(GetProxyPort(link))); /* 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 ((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(link); delta = GetDeltaSeqOut(pip, link); AddSeq(pip, link, 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 /* 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 *)((char *)pip + (pip->ip_hl << 2)); 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. + be proxied. ProxyModify() -- Encodes the original destination address/port - for a packet which is to be redirected to - a proxy server. + 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 *)((char *)pip + (pip->ip_hl << 2))) ->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 *link, struct ip *pip, int maxpacketsize, int proxy_type) { switch (proxy_type) { case PROXY_TYPE_ENCODE_IPHDR: ProxyEncodeIpHeader(pip, maxpacketsize); break; case PROXY_TYPE_ENCODE_TCPSTREAM: ProxyEncodeTcpStream(link, pip, maxpacketsize); break; } } /* Public API functions */ int LibAliasProxyRule(struct libalias *la, const char *cmd) { /* * This function takes command strings of the form: * * server [:] * [port ] * [rule n] * [proto tcp|udp] * [src [/n]] * [dst [/n]] * [type encode_tcp_stream|encode_ip_hdr|no_encode] * * delete * * 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 > (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_util.c b/lib/libalias/alias_util.c index bec0ba7dea68..574d4ed7d091 100644 --- a/lib/libalias/alias_util.c +++ b/lib/libalias/alias_util.c @@ -1,162 +1,162 @@ /*- * Copyright (c) 2001 Charles Mott * 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 __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. + 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 #include #include #include #include #include #include "alias.h" #include "alias_local.h" u_short LibAliasInternetChecksum(struct libalias *la, u_short * ptr, int nbytes) { int sum, oddbyte; sum = 0; while (nbytes > 1) { sum += *ptr++; nbytes -= 2; } if (nbytes == 1) { oddbyte = 0; ((u_char *) & oddbyte)[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 *)((char *)pip + nhdr); ptr = (u_short *) tc; /* Add up TCP header and data */ nbytes = ntcp; sum = 0; while (nbytes > 1) { sum += *ptr++; nbytes -= 2; } if (nbytes == 1) { oddbyte = 0; ((u_char *) & oddbyte)[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/sys/netinet/libalias/alias.c b/sys/netinet/libalias/alias.c index 326b8c0338ad..027c7e4ee4c8 100644 --- a/sys/netinet/libalias/alias.c +++ b/sys/netinet/libalias/alias.c @@ -1,1421 +1,1421 @@ /*- * Copyright (c) 2001 Charles Mott * 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 __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.) + 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.) + 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. + - 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.) + 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. + 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). + - 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). + - 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. + - 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 #include #include #include #include #include #include #include #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) { u_short *s = p; return (s[0] + s[1]); } /* 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 *link) { struct tcphdr *tc; tc = (struct tcphdr *)((char *)pip + (pip->ip_hl << 2)); switch (GetStateIn(link)) { case ALIAS_TCP_STATE_NOT_CONNECTED: if (tc->th_flags & TH_RST) SetStateIn(link, ALIAS_TCP_STATE_DISCONNECTED); else if (tc->th_flags & TH_SYN) SetStateIn(link, ALIAS_TCP_STATE_CONNECTED); break; case ALIAS_TCP_STATE_CONNECTED: if (tc->th_flags & (TH_FIN | TH_RST)) SetStateIn(link, ALIAS_TCP_STATE_DISCONNECTED); break; } } static void TcpMonitorOut(struct ip *pip, struct alias_link *link) { struct tcphdr *tc; tc = (struct tcphdr *)((char *)pip + (pip->ip_hl << 2)); switch (GetStateOut(link)) { case ALIAS_TCP_STATE_NOT_CONNECTED: if (tc->th_flags & TH_RST) SetStateOut(link, ALIAS_TCP_STATE_DISCONNECTED); else if (tc->th_flags & TH_SYN) SetStateOut(link, ALIAS_TCP_STATE_CONNECTED); break; case ALIAS_TCP_STATE_CONNECTED: if (tc->th_flags & (TH_FIN | TH_RST)) SetStateOut(link, 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 *); 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 *link; struct icmp *ic; ic = (struct icmp *)((char *)pip + (pip->ip_hl << 2)); /* Get source address from ICMP data field and restore original data */ link = FindIcmpIn(la, pip->ip_src, pip->ip_dst, ic->icmp_id, 1); if (link != NULL) { u_short original_id; int accumulate; original_id = GetOriginalPort(link); /* Adjust ICMP checksum */ accumulate = ic->icmp_id; accumulate -= original_id; ADJUST_CHECKSUM(accumulate, ic->icmp_cksum); /* Put original sequence number back in */ ic->icmp_id = original_id; /* Put original address back into IP header */ { struct in_addr original_address; original_address = GetOriginalAddress(link); DifferentialChecksum(&pip->ip_sum, &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 *link; ic = (struct icmp *)((char *)pip + (pip->ip_hl << 2)); ip = &ic->icmp_ip; ud = (struct udphdr *)((char *)ip + (ip->ip_hl << 2)); tc = (struct tcphdr *)ud; ic2 = (struct icmp *)ud; if (ip->ip_p == IPPROTO_UDP) link = FindUdpTcpIn(la, ip->ip_dst, ip->ip_src, ud->uh_dport, ud->uh_sport, IPPROTO_UDP, 0); else if (ip->ip_p == IPPROTO_TCP) link = 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) link = FindIcmpIn(la, ip->ip_dst, ip->ip_src, ic2->icmp_id, 0); else link = NULL; } else link = NULL; if (link != 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(link); original_port = GetOriginalPort(link); /* 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(link); original_id = GetOriginalPort(link); /* 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 *)((char *)pip + (pip->ip_hl << 2)); iresult = PKT_ALIAS_IGNORED; switch (ic->icmp_type) { case ICMP_ECHOREPLY: case ICMP_TSTAMPREPLY: if (ic->icmp_code == 0) { iresult = IcmpAliasIn1(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) { /* Alias outgoing echo and timestamp requests. De-alias outgoing echo and timestamp replies. */ struct alias_link *link; struct icmp *ic; ic = (struct icmp *)((char *)pip + (pip->ip_hl << 2)); /* Save overwritten data for when echo packet returns */ link = FindIcmpOut(la, pip->ip_src, pip->ip_dst, ic->icmp_id, 1); if (link != NULL) { u_short alias_id; int accumulate; alias_id = GetAliasPort(link); /* Since data field is being modified, adjust ICMP checksum */ accumulate = ic->icmp_id; accumulate -= alias_id; ADJUST_CHECKSUM(accumulate, ic->icmp_cksum); /* Alias sequence number */ ic->icmp_id = alias_id; /* Change source address */ { struct in_addr alias_address; alias_address = GetAliasAddress(link); DifferentialChecksum(&pip->ip_sum, &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 *link; ic = (struct icmp *)((char *)pip + (pip->ip_hl << 2)); ip = &ic->icmp_ip; ud = (struct udphdr *)((char *)ip + (ip->ip_hl << 2)); tc = (struct tcphdr *)ud; ic2 = (struct icmp *)ud; if (ip->ip_p == IPPROTO_UDP) link = FindUdpTcpOut(la, ip->ip_dst, ip->ip_src, ud->uh_dport, ud->uh_sport, IPPROTO_UDP, 0); else if (ip->ip_p == IPPROTO_TCP) link = 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) link = FindIcmpOut(la, ip->ip_dst, ip->ip_src, ic2->icmp_id, 0); else link = NULL; } else link = NULL; if (link != 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(link); alias_port = GetAliasPort(link); /* 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(link); alias_id = GetAliasPort(link); /* 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; /* Return if proxy-only mode is enabled */ if (la->packetAliasMode & PKT_ALIAS_PROXY_ONLY) return PKT_ALIAS_OK; ic = (struct icmp *)((char *)pip + (pip->ip_hl << 2)); iresult = PKT_ALIAS_IGNORED; switch (ic->icmp_type) { case ICMP_ECHO: case ICMP_TSTAMP: if (ic->icmp_code == 0) { iresult = IcmpAliasOut1(la, pip); } 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); } 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 *link; /* Return if proxy-only mode is enabled */ if (la->packetAliasMode & PKT_ALIAS_PROXY_ONLY) return PKT_ALIAS_OK; link = FindProtoIn(la, pip->ip_src, pip->ip_dst, pip->ip_p); if (link != NULL) { struct in_addr original_address; original_address = GetOriginalAddress(link); /* 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 *link; /* Return if proxy-only mode is enabled */ if (la->packetAliasMode & PKT_ALIAS_PROXY_ONLY) return PKT_ALIAS_OK; link = FindProtoOut(la, pip->ip_src, pip->ip_dst, pip->ip_p); if (link != NULL) { struct in_addr alias_address; alias_address = GetAliasAddress(link); /* 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 *link; /* Return if proxy-only mode is enabled */ if (la->packetAliasMode & PKT_ALIAS_PROXY_ONLY) return PKT_ALIAS_OK; ud = (struct udphdr *)((char *)pip + (pip->ip_hl << 2)); link = FindUdpTcpIn(la, pip->ip_src, pip->ip_dst, ud->uh_sport, ud->uh_dport, IPPROTO_UDP, 1); if (link != NULL) { struct in_addr alias_address; struct in_addr original_address; u_short alias_port; int accumulate; int r = 0; alias_address = GetAliasAddress(link); original_address = GetOriginalAddress(link); alias_port = ud->uh_dport; ud->uh_dport = GetOriginalPort(link); /* 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, link, &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, link, &alias_address, &alias_port, &original_address, &ud->uh_dport); /* If UDP checksum is not zero, then adjust since destination port */ /* is being unaliased and destination 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 *link; /* Return if proxy-only mode is enabled */ if (la->packetAliasMode & PKT_ALIAS_PROXY_ONLY) return PKT_ALIAS_OK; ud = (struct udphdr *)((char *)pip + (pip->ip_hl << 2)); link = FindUdpTcpOut(la, pip->ip_src, pip->ip_dst, ud->uh_sport, ud->uh_dport, IPPROTO_UDP, create); if (link != NULL) { u_short alias_port; struct in_addr alias_address; alias_address = GetAliasAddress(link); alias_port = GetAliasPort(link); /* Special processing for IP encoding protocols */ if (ntohs(ud->uh_dport) == CUSEEME_PORT_NUMBER) AliasHandleCUSeeMeOut(la, pip, link); /* 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, link, &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, link, &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 *link; tc = (struct tcphdr *)((char *)pip + (pip->ip_hl << 2)); link = FindUdpTcpIn(la, pip->ip_src, pip->ip_dst, tc->th_sport, tc->th_dport, IPPROTO_TCP, !(la->packetAliasMode & PKT_ALIAS_PROXY_ONLY)); if (link != 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, link); else if (la->skinnyPort != 0 && (ntohs(tc->th_dport) == la->skinnyPort || ntohs(tc->th_sport) == la->skinnyPort)) AliasHandleSkinny(la, pip, link); alias_address = GetAliasAddress(link); original_address = GetOriginalAddress(link); proxy_address = GetProxyAddress(link); alias_port = tc->th_dport; tc->th_dport = GetOriginalPort(link); proxy_port = GetProxyPort(link); /* 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(link) == 1) { int delta; delta = GetDeltaAckIn(pip, link); 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, link); 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 *link; tc = (struct tcphdr *)((char *)pip + (pip->ip_hl << 2)); 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); } link = FindUdpTcpOut(la, pip->ip_src, pip->ip_dst, tc->th_sport, tc->th_dport, IPPROTO_TCP, create); if (link == NULL) return (PKT_ALIAS_IGNORED); if (link != 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(link, dest_port); SetProxyAddress(link, dest_address); ProxyModify(la, link, pip, maxpacketsize, proxy_type); tc = (struct tcphdr *)((char *)pip + (pip->ip_hl << 2)); } /* Get alias address and port */ alias_port = GetAliasPort(link); alias_address = GetAliasAddress(link); /* Monitor TCP connection state */ TcpMonitorOut(pip, link); /* 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, link, maxpacketsize); else if (ntohs(tc->th_dport) == IRC_CONTROL_PORT_NUMBER_1 || ntohs(tc->th_dport) == IRC_CONTROL_PORT_NUMBER_2) AliasHandleIrcOut(la, pip, link, 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, link, maxpacketsize); else if (ntohs(tc->th_dport) == PPTP_CONTROL_PORT_NUMBER || ntohs(tc->th_sport) == PPTP_CONTROL_PORT_NUMBER) AliasHandlePptpOut(la, pip, link); else if (la->skinnyPort != 0 && (ntohs(tc->th_sport) == la->skinnyPort || ntohs(tc->th_dport) == la->skinnyPort)) AliasHandleSkinny(la, pip, link); /* 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(link) == 1) { int delta; delta = GetDeltaSeqOut(pip, link); 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 *link; link = FindFragmentIn2(la, pip->ip_src, pip->ip_dst, pip->ip_id); if (link != NULL) { struct in_addr original_address; GetFragmentAddr(link, &original_address); DifferentialChecksum(&pip->ip_sum, &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() + PacketAliasSaveFragment() + PacketAliasGetFragment() + PacketAliasFragmentIn() + PacketAliasIn() + PacketAliasOut() + PacketUnaliasOut() (prototypes in alias.h) */ int LibAliasSaveFragment(struct libalias *la, char *ptr) { int iresult; struct alias_link *link; struct ip *pip; pip = (struct ip *)ptr; link = AddFragmentPtrLink(la, pip->ip_src, pip->ip_id); iresult = PKT_ALIAS_ERROR; if (link != NULL) { SetFragmentPtr(link, ptr); iresult = PKT_ALIAS_OK; } return (iresult); } char * LibAliasGetFragment(struct libalias *la, char *ptr) { struct alias_link *link; char *fptr; struct ip *pip; pip = (struct ip *)ptr; link = FindFragmentPtr(la, pip->ip_src, pip->ip_id); if (link != NULL) { GetFragmentPtr(link, &fptr); SetFragmentPtr(link, NULL); SetExpire(link, 0); /* Deletes link */ return (fptr); } else { return (NULL); } } void 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; 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 *link; link = FindFragmentIn1(la, pip->ip_src, alias_addr, pip->ip_id); if (link != NULL) { iresult = PKT_ALIAS_FOUND_HEADER_FRAGMENT; SetFragmentAddr(link, pip->ip_dst); } else { iresult = PKT_ALIAS_ERROR; } } } else { iresult = FragmentIn(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 *link; 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 *)((char *)pip + (pip->ip_hl << 2)); tc = (struct tcphdr *)ud; ic = (struct icmp *)ud; /* Find a link */ if (pip->ip_p == IPPROTO_UDP) link = FindUdpTcpIn(la, pip->ip_dst, pip->ip_src, ud->uh_dport, ud->uh_sport, IPPROTO_UDP, 0); else if (pip->ip_p == IPPROTO_TCP) link = FindUdpTcpIn(la, pip->ip_dst, pip->ip_src, tc->th_dport, tc->th_sport, IPPROTO_TCP, 0); else if (pip->ip_p == IPPROTO_ICMP) link = FindIcmpIn(la, pip->ip_dst, pip->ip_src, ic->icmp_id, 0); else link = NULL; /* Change it from an aliased packet to an unaliased packet */ if (link != 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(link); original_port = GetOriginalPort(link); /* 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(link); original_id = GetOriginalPort(link); /* 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/sys/netinet/libalias/alias_db.c b/sys/netinet/libalias/alias_db.c index 039db039311d..31f669d3d594 100644 --- a/sys/netinet/libalias/alias_db.c +++ b/sys/netinet/libalias/alias_db.c @@ -1,2784 +1,2784 @@ /*- * Copyright (c) 2001 Charles Mott * 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 __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. + Facility for handling incoming links added. Version 1.6: September 18, 1996 (cjm) - ICMP data handling simplified. + 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. + 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) + 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) + 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) + 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) + 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) + Removed K&R style function headers + and general cleanup. (ee) - Added packetAliasMode to replace compiler #defines's (ee) + Added packetAliasMode to replace compiler #defines's (ee) - Allocates sockets for partially specified - ports if ALIAS_USE_SOCKETS defined. (cjm) + 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) + 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: + 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.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 + (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) + 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) + 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) + 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). + 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. + 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. + 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 FindNewPortGroup to get contiguous range of port values. - Added QueryUdpTcpIn and QueryUdpTcpOut to look for an aliasing + Added QueryUdpTcpIn and QueryUdpTcpOut to look for an aliasing link but not actually add one. - Added FindRtspOut, which is closely derived from FindUdpTcpOut, + 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 #include #include #include #include #include #include #include /* BSD network include files */ #include #include #include #include #include #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) + 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 + incoming packets StartPointOut() -- link table initial search point for - outgoing packets + 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 *la); static void UninitPunchFW(struct libalias *la); static void ClearFWHole(struct alias_link *link); #endif /* Log file control */ static void InitPacketAliasLog(struct libalias *la); static void UninitPacketAliasLog(struct libalias *la); 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 *link, int alias_port_param) { int i; int max_trials; u_short port_sys; u_short port_net; /* Description of alias_port_param for GetNewPort(). When this parameter is zero or positive, it precisely specifies the port number. GetNewPort() will return this number without check that it is in use. 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 = link->src_port; port_sys = ntohs(port_net); } else { /* First trial and all subsequent are random. */ port_sys = random() & ALIAS_PORT_MASK; port_sys += ALIAS_PORT_BASE; port_net = htons(port_sys); } } else if (alias_port_param >= 0 && alias_port_param < 0x10000) { link->alias_port = (u_short) alias_port_param; return (0); } else { #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, link->dst_addr, link->alias_addr, link->dst_port, port_net, link->link_type, 0); if (search_result == NULL) go_ahead = 1; else if (!(link->flags & LINK_PARTIALLY_SPECIFIED) && (search_result->flags & LINK_PARTIALLY_SPECIFIED)) go_ahead = 1; else go_ahead = 0; if (go_ahead) { if ((la->packetAliasMode & PKT_ALIAS_USE_SOCKETS) && (link->flags & LINK_PARTIALLY_SPECIFIED) && ((link->link_type == LINK_TCP) || (link->link_type == LINK_UDP))) { if (GetSocket(la, port_net, &link->sockfd, link->link_type)) { link->alias_port = port_net; return (0); } } else { link->alias_port = port_net; return (0); } } port_sys = random() & ALIAS_PORT_MASK; port_sys += ALIAS_PORT_BASE; port_net = htons(port_sys); } #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 *link; int i, icount; icount = 0; for (i = 0; i < LINK_TABLE_OUT_SIZE; i++) { link = LIST_FIRST(&la->linkTableOut[i]); while (link != NULL) { struct alias_link *link_next; link_next = LIST_NEXT(link, list_out); icount++; DeleteLink(link); link = link_next; } } la->cleanupIndex = 0; } static void IncrementalCleanup(struct libalias *la) { int icount; struct alias_link *link; icount = 0; link = LIST_FIRST(&la->linkTableOut[la->cleanupIndex++]); while (link != NULL) { int idelta; struct alias_link *link_next; link_next = LIST_NEXT(link, list_out); idelta = la->timeStamp - link->timestamp; switch (link->link_type) { case LINK_TCP: if (idelta > link->expire_time) { struct tcp_dat *tcp_aux; tcp_aux = link->data.tcp; if (tcp_aux->state.in != ALIAS_TCP_STATE_CONNECTED || tcp_aux->state.out != ALIAS_TCP_STATE_CONNECTED) { DeleteLink(link); icount++; } } break; default: if (idelta > link->expire_time) { DeleteLink(link); icount++; } break; } link = link_next; } if (la->cleanupIndex == LINK_TABLE_OUT_SIZE) la->cleanupIndex = 0; } static void DeleteLink(struct alias_link *link) { struct libalias *la = link->la; /* Don't do anything if the link is marked permanent */ if (la->deleteAllLinks == 0 && link->flags & LINK_PERMANENT) return; #ifndef NO_FW_PUNCH /* Delete associated firewall hole, if any */ ClearFWHole(link); #endif /* Free memory allocated for LSNAT server pool */ if (link->server != NULL) { struct server *head, *curr, *next; head = curr = link->server; do { next = curr->next; free(curr); } while ((curr = next) != head); } /* Adjust output table pointers */ LIST_REMOVE(link, list_out); /* Adjust input table pointers */ LIST_REMOVE(link, list_in); /* Close socket, if one has been allocated */ if (link->sockfd != -1) { la->sockCount--; close(link->sockfd); } /* Link-type dependent cleanup */ switch (link->link_type) { case LINK_ICMP: la->icmpLinkCount--; break; case LINK_UDP: la->udpLinkCount--; break; case LINK_TCP: la->tcpLinkCount--; free(link->data.tcp); break; case LINK_PPTP: la->pptpLinkCount--; break; case LINK_FRAGMENT_ID: la->fragmentIdLinkCount--; break; case LINK_FRAGMENT_PTR: la->fragmentPtrLinkCount--; if (link->data.frag_ptr != NULL) free(link->data.frag_ptr); break; case LINK_ADDR: break; default: la->protoLinkCount--; break; } /* Free memory */ free(link); /* 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 *link; link = malloc(sizeof(struct alias_link)); if (link != NULL) { /* Basic initialization */ link->la = la; link->src_addr = src_addr; link->dst_addr = dst_addr; link->alias_addr = alias_addr; link->proxy_addr.s_addr = INADDR_ANY; link->src_port = src_port; link->dst_port = dst_port; link->proxy_port = 0; link->server = NULL; link->link_type = link_type; link->sockfd = -1; link->flags = 0; link->pflags = 0; link->timestamp = la->timeStamp; /* Expiration time */ switch (link_type) { case LINK_ICMP: link->expire_time = ICMP_EXPIRE_TIME; break; case LINK_UDP: link->expire_time = UDP_EXPIRE_TIME; break; case LINK_TCP: link->expire_time = TCP_EXPIRE_INITIAL; break; case LINK_PPTP: link->flags |= LINK_PERMANENT; /* no timeout. */ break; case LINK_FRAGMENT_ID: link->expire_time = FRAGMENT_ID_EXPIRE_TIME; break; case LINK_FRAGMENT_PTR: link->expire_time = FRAGMENT_PTR_EXPIRE_TIME; break; case LINK_ADDR: break; default: link->expire_time = PROTO_EXPIRE_TIME; break; } /* Determine alias flags */ if (dst_addr.s_addr == INADDR_ANY) link->flags |= LINK_UNKNOWN_DEST_ADDR; if (dst_port == 0) link->flags |= LINK_UNKNOWN_DEST_PORT; /* Determine alias port */ if (GetNewPort(la, link, alias_port_param) != 0) { free(link); 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; link->data.tcp = aux_tcp; } else { #ifdef DEBUG fprintf(stderr, "PacketAlias/AddLink: "); fprintf(stderr, " cannot allocate auxiliary TCP data\n"); #endif free(link); 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], link, list_out); /* Set up pointers for input lookup table */ start_point = StartPointIn(alias_addr, link->alias_port, link_type); LIST_INSERT_HEAD(&la->linkTableIn[start_point], link, 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 (link); } static struct alias_link * ReLink(struct alias_link *old_link, struct in_addr src_addr, struct in_addr dst_addr, struct in_addr alias_addr, u_short src_port, u_short dst_port, int alias_port_param, /* if less than zero, alias */ int link_type) { /* port will be automatically *//* chosen. * If greater than */ struct alias_link *new_link; /* zero, equal to alias port */ struct libalias *la = old_link->la; new_link = AddLink(la, src_addr, dst_addr, alias_addr, src_port, dst_port, alias_port_param, link_type); #ifndef NO_FW_PUNCH if (new_link != NULL && old_link->link_type == LINK_TCP && old_link->data.tcp->fwhole > 0) { PunchFWHole(new_link); } #endif DeleteLink(old_link); return new_link; } 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 *link; i = StartPointOut(src_addr, dst_addr, src_port, dst_port, link_type); LIST_FOREACH(link, &la->linkTableOut[i], list_out) { if (link->src_addr.s_addr == src_addr.s_addr && link->server == NULL && link->dst_addr.s_addr == dst_addr.s_addr && link->dst_port == dst_port && link->src_port == src_port && link->link_type == link_type) { link->timestamp = la->timeStamp; break; } } /* Search for partially specified links. */ if (link == NULL && replace_partial_links) { if (dst_port != 0 && dst_addr.s_addr != INADDR_ANY) { link = _FindLinkOut(la, src_addr, dst_addr, src_port, 0, link_type, 0); if (link == NULL) link = _FindLinkOut(la, src_addr, la->nullAddress, src_port, dst_port, link_type, 0); } if (link == NULL && (dst_port != 0 || dst_addr.s_addr != INADDR_ANY)) { link = _FindLinkOut(la, src_addr, la->nullAddress, src_port, 0, link_type, 0); } if (link != NULL) { link = ReLink(link, src_addr, dst_addr, link->alias_addr, src_port, dst_port, link->alias_port, link_type); } } return (link); } 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 *link; link = _FindLinkOut(la, src_addr, dst_addr, src_port, dst_port, link_type, replace_partial_links); if (link == 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) { link = _FindLinkOut(la, la->nullAddress, dst_addr, src_port, dst_port, link_type, replace_partial_links); } } return (link); } 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 *link; struct alias_link *link_fully_specified; struct alias_link *link_unknown_all; struct alias_link *link_unknown_dst_addr; struct alias_link *link_unknown_dst_port; /* Initialize pointers */ link_fully_specified = NULL; link_unknown_all = NULL; link_unknown_dst_addr = NULL; link_unknown_dst_port = NULL; /* If either the dest addr or port is unknown, the search loop will have to know about this. */ flags_in = 0; if (dst_addr.s_addr == 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(link, &la->linkTableIn[start_point], list_in) { int flags; flags = flags_in | link->flags; if (!(flags & LINK_PARTIALLY_SPECIFIED)) { if (link->alias_addr.s_addr == alias_addr.s_addr && link->alias_port == alias_port && link->dst_addr.s_addr == dst_addr.s_addr && link->dst_port == dst_port && link->link_type == link_type) { link_fully_specified = link; break; } } else if ((flags & LINK_UNKNOWN_DEST_ADDR) && (flags & LINK_UNKNOWN_DEST_PORT)) { if (link->alias_addr.s_addr == alias_addr.s_addr && link->alias_port == alias_port && link->link_type == link_type) { if (link_unknown_all == NULL) link_unknown_all = link; } } else if (flags & LINK_UNKNOWN_DEST_ADDR) { if (link->alias_addr.s_addr == alias_addr.s_addr && link->alias_port == alias_port && link->link_type == link_type && link->dst_port == dst_port) { if (link_unknown_dst_addr == NULL) link_unknown_dst_addr = link; } } else if (flags & LINK_UNKNOWN_DEST_PORT) { if (link->alias_addr.s_addr == alias_addr.s_addr && link->alias_port == alias_port && link->link_type == link_type && link->dst_addr.s_addr == dst_addr.s_addr) { if (link_unknown_dst_port == NULL) link_unknown_dst_port = link; } } } if (link_fully_specified != NULL) { link_fully_specified->timestamp = la->timeStamp; link = link_fully_specified; } else if (link_unknown_dst_port != NULL) link = link_unknown_dst_port; else if (link_unknown_dst_addr != NULL) link = link_unknown_dst_addr; else if (link_unknown_all != NULL) link = link_unknown_all; else return (NULL); if (replace_partial_links && (link->flags & LINK_PARTIALLY_SPECIFIED || link->server != NULL)) { struct in_addr src_addr; u_short src_port; if (link->server != NULL) { /* LSNAT link */ src_addr = link->server->addr; src_port = link->server->port; link->server = link->server->next; } else { src_addr = link->src_addr; src_port = link->src_port; } link = ReLink(link, src_addr, dst_addr, alias_addr, src_port, dst_port, alias_port, link_type); } return (link); } 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 *link; link = _FindLinkIn(la, dst_addr, alias_addr, dst_port, alias_port, link_type, replace_partial_links); if (link == 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) { link = _FindLinkIn(la, dst_addr, la->nullAddress, dst_port, alias_port, link_type, replace_partial_links); } } return (link); } /* 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 *link; link = FindLinkIn(la, dst_addr, alias_addr, NO_DEST_PORT, id_alias, LINK_ICMP, 0); if (link == NULL && create && !(la->packetAliasMode & PKT_ALIAS_DENY_INCOMING)) { struct in_addr target_addr; target_addr = FindOriginalAddress(la, alias_addr); link = AddLink(la, target_addr, dst_addr, alias_addr, id_alias, NO_DEST_PORT, id_alias, LINK_ICMP); } return (link); } 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 *link; link = FindLinkOut(la, src_addr, dst_addr, id, NO_DEST_PORT, LINK_ICMP, 0); if (link == NULL && create) { struct in_addr alias_addr; alias_addr = FindAliasAddress(la, src_addr); link = AddLink(la, src_addr, dst_addr, alias_addr, id, NO_DEST_PORT, GET_ALIAS_ID, LINK_ICMP); } return (link); } struct alias_link * FindFragmentIn1(struct libalias *la, struct in_addr dst_addr, struct in_addr alias_addr, u_short ip_id) { struct alias_link *link; link = FindLinkIn(la, dst_addr, alias_addr, NO_DEST_PORT, ip_id, LINK_FRAGMENT_ID, 0); if (link == NULL) { link = AddLink(la, la->nullAddress, dst_addr, alias_addr, NO_SRC_PORT, NO_DEST_PORT, ip_id, LINK_FRAGMENT_ID); } return (link); } 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 *link; link = FindLinkIn(la, dst_addr, alias_addr, NO_DEST_PORT, 0, proto, 1); if (link == NULL && !(la->packetAliasMode & PKT_ALIAS_DENY_INCOMING)) { struct in_addr target_addr; target_addr = FindOriginalAddress(la, alias_addr); link = AddLink(la, target_addr, dst_addr, alias_addr, NO_SRC_PORT, NO_DEST_PORT, 0, proto); } return (link); } struct alias_link * FindProtoOut(struct libalias *la, struct in_addr src_addr, struct in_addr dst_addr, u_char proto) { struct alias_link *link; link = FindLinkOut(la, src_addr, dst_addr, NO_SRC_PORT, NO_DEST_PORT, proto, 1); if (link == NULL) { struct in_addr alias_addr; alias_addr = FindAliasAddress(la, src_addr); link = AddLink(la, src_addr, dst_addr, alias_addr, NO_SRC_PORT, NO_DEST_PORT, 0, proto); } return (link); } 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 *link; switch (proto) { case IPPROTO_UDP: link_type = LINK_UDP; break; case IPPROTO_TCP: link_type = LINK_TCP; break; default: return NULL; break; } link = FindLinkIn(la, dst_addr, alias_addr, dst_port, alias_port, link_type, create); if (link == NULL && create && !(la->packetAliasMode & PKT_ALIAS_DENY_INCOMING)) { struct in_addr target_addr; target_addr = FindOriginalAddress(la, alias_addr); link = AddLink(la, target_addr, dst_addr, alias_addr, alias_port, dst_port, alias_port, link_type); } return (link); } 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 *link; switch (proto) { case IPPROTO_UDP: link_type = LINK_UDP; break; case IPPROTO_TCP: link_type = LINK_TCP; break; default: return NULL; break; } link = FindLinkOut(la, src_addr, dst_addr, src_port, dst_port, link_type, create); if (link == NULL && create) { struct in_addr alias_addr; alias_addr = FindAliasAddress(la, src_addr); link = AddLink(la, src_addr, dst_addr, alias_addr, src_port, dst_port, GET_ALIAS_PORT, link_type); } return (link); } 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 *link; link = AddLink(la, src_addr, dst_addr, alias_addr, src_call_id, 0, GET_ALIAS_PORT, LINK_PPTP); return (link); } 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 *link; i = StartPointOut(src_addr, dst_addr, 0, 0, LINK_PPTP); LIST_FOREACH(link, &la->linkTableOut[i], list_out) if (link->link_type == LINK_PPTP && link->src_addr.s_addr == src_addr.s_addr && link->dst_addr.s_addr == dst_addr.s_addr && link->src_port == src_call_id) break; return (link); } 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 *link; i = StartPointOut(src_addr, dst_addr, 0, 0, LINK_PPTP); LIST_FOREACH(link, &la->linkTableOut[i], list_out) if (link->link_type == LINK_PPTP && link->src_addr.s_addr == src_addr.s_addr && link->dst_addr.s_addr == dst_addr.s_addr && link->dst_port == dst_call_id) break; return (link); } 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 *link; i = StartPointIn(alias_addr, 0, LINK_PPTP); LIST_FOREACH(link, &la->linkTableIn[i], list_in) if (link->link_type == LINK_PPTP && link->dst_addr.s_addr == dst_addr.s_addr && link->alias_addr.s_addr == alias_addr.s_addr && link->dst_port == dst_call_id) break; return (link); } 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 *link; link = FindLinkIn(la, dst_addr, alias_addr, 0 /* any */ , alias_call_id, LINK_PPTP, 0); return (link); } 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 *link; switch (proto) { case IPPROTO_UDP: link_type = LINK_UDP; break; case IPPROTO_TCP: link_type = LINK_TCP; break; default: return NULL; break; } link = FindLinkOut(la, src_addr, dst_addr, src_port, 0, link_type, 1); if (link == NULL) { struct in_addr alias_addr; alias_addr = FindAliasAddress(la, src_addr); link = AddLink(la, src_addr, dst_addr, alias_addr, src_port, 0, alias_port, link_type); } return (link); } struct in_addr FindOriginalAddress(struct libalias *la, struct in_addr alias_addr) { struct alias_link *link; link = FindLinkIn(la, la->nullAddress, alias_addr, 0, 0, LINK_ADDR, 0); if (link == 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 (link->server != NULL) { /* LSNAT link */ struct in_addr src_addr; src_addr = link->server->addr; link->server = link->server->next; return (src_addr); } else if (link->src_addr.s_addr == INADDR_ANY) return (la->aliasAddress.s_addr != INADDR_ANY) ? la->aliasAddress : alias_addr; else return link->src_addr; } } struct in_addr FindAliasAddress(struct libalias *la, struct in_addr original_addr) { struct alias_link *link; link = FindLinkOut(la, original_addr, la->nullAddress, 0, 0, LINK_ADDR, 0); if (link == NULL) { return (la->aliasAddress.s_addr != INADDR_ANY) ? la->aliasAddress : original_addr; } else { if (link->alias_addr.s_addr == INADDR_ANY) return (la->aliasAddress.s_addr != INADDR_ANY) ? la->aliasAddress : original_addr; else return link->alias_addr; } } /* External routines for getting or changing link data (external to alias_db.c, but internal to alias*.c) SetFragmentData(), GetFragmentData() SetFragmentPtr(), GetFragmentPtr() SetStateIn(), SetStateOut(), GetStateIn(), GetStateOut() GetOriginalAddress(), GetDestAddress(), GetAliasAddress() GetOriginalPort(), GetAliasPort() SetAckModified(), GetAckModified() GetDeltaAckIn(), GetDeltaSeqOut(), AddSeq() SetProtocolFlags(), GetProtocolFlags() SetDestCallId() */ void SetFragmentAddr(struct alias_link *link, struct in_addr src_addr) { link->data.frag_addr = src_addr; } void GetFragmentAddr(struct alias_link *link, struct in_addr *src_addr) { *src_addr = link->data.frag_addr; } void SetFragmentPtr(struct alias_link *link, char *fptr) { link->data.frag_ptr = fptr; } void GetFragmentPtr(struct alias_link *link, char **fptr) { *fptr = link->data.frag_ptr; } void SetStateIn(struct alias_link *link, int state) { /* TCP input state */ switch (state) { case ALIAS_TCP_STATE_DISCONNECTED: if (link->data.tcp->state.out != ALIAS_TCP_STATE_CONNECTED) link->expire_time = TCP_EXPIRE_DEAD; else link->expire_time = TCP_EXPIRE_SINGLEDEAD; break; case ALIAS_TCP_STATE_CONNECTED: if (link->data.tcp->state.out == ALIAS_TCP_STATE_CONNECTED) link->expire_time = TCP_EXPIRE_CONNECTED; break; default: abort(); } link->data.tcp->state.in = state; } void SetStateOut(struct alias_link *link, int state) { /* TCP output state */ switch (state) { case ALIAS_TCP_STATE_DISCONNECTED: if (link->data.tcp->state.in != ALIAS_TCP_STATE_CONNECTED) link->expire_time = TCP_EXPIRE_DEAD; else link->expire_time = TCP_EXPIRE_SINGLEDEAD; break; case ALIAS_TCP_STATE_CONNECTED: if (link->data.tcp->state.in == ALIAS_TCP_STATE_CONNECTED) link->expire_time = TCP_EXPIRE_CONNECTED; break; default: abort(); } link->data.tcp->state.out = state; } int GetStateIn(struct alias_link *link) { /* TCP input state */ return link->data.tcp->state.in; } int GetStateOut(struct alias_link *link) { /* TCP output state */ return link->data.tcp->state.out; } struct in_addr GetOriginalAddress(struct alias_link *link) { if (link->src_addr.s_addr == INADDR_ANY) return link->la->aliasAddress; else return (link->src_addr); } struct in_addr GetDestAddress(struct alias_link *link) { return (link->dst_addr); } struct in_addr GetAliasAddress(struct alias_link *link) { if (link->alias_addr.s_addr == INADDR_ANY) return link->la->aliasAddress; else return link->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 *link) { return (link->src_port); } u_short GetAliasPort(struct alias_link *link) { return (link->alias_port); } #ifndef NO_FW_PUNCH static u_short GetDestPort(struct alias_link *link) { return (link->dst_port); } #endif void SetAckModified(struct alias_link *link) { /* Indicate that ACK numbers have been modified in a TCP connection */ link->data.tcp->state.ack_modified = 1; } struct in_addr GetProxyAddress(struct alias_link *link) { return link->proxy_addr; } void SetProxyAddress(struct alias_link *link, struct in_addr addr) { link->proxy_addr = addr; } u_short GetProxyPort(struct alias_link *link) { return link->proxy_port; } void SetProxyPort(struct alias_link *link, u_short port) { link->proxy_port = port; } int GetAckModified(struct alias_link *link) { /* See if ACK numbers have been modified */ return link->data.tcp->state.ack_modified; } int GetDeltaAckIn(struct ip *pip, struct alias_link *link) { /* Find out how much the ACK number has been altered for an incoming TCP packet. To do this, a circular list 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 = (struct tcphdr *)((char *)pip + (pip->ip_hl << 2)); ack = tc->th_ack; delta = 0; ack_diff_min = -1; for (i = 0; i < N_LINK_TCP_DATA; i++) { struct ack_data_record x; x = link->data.tcp->ack[i]; if (x.active == 1) { int ack_diff; ack_diff = SeqDiff(x.ack_new, ack); if (ack_diff >= 0) { if (ack_diff_min >= 0) { if (ack_diff < ack_diff_min) { delta = x.delta; ack_diff_min = ack_diff; } } else { delta = x.delta; ack_diff_min = ack_diff; } } } } return (delta); } int GetDeltaSeqOut(struct ip *pip, struct alias_link *link) { /* Find out how much the 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 = (struct tcphdr *)((char *)pip + (pip->ip_hl << 2)); seq = tc->th_seq; delta = 0; seq_diff_min = -1; for (i = 0; i < N_LINK_TCP_DATA; i++) { struct ack_data_record x; x = link->data.tcp->ack[i]; if (x.active == 1) { int seq_diff; seq_diff = SeqDiff(x.ack_old, seq); if (seq_diff >= 0) { if (seq_diff_min >= 0) { if (seq_diff < seq_diff_min) { delta = x.delta; seq_diff_min = seq_diff; } } else { delta = x.delta; seq_diff_min = seq_diff; } } } } return (delta); } void AddSeq(struct ip *pip, struct alias_link *link, int delta) { /* When a TCP packet has been altered in length, save this information in a circular list. If enough packets have been altered, then this list will begin to overwrite itself. */ struct tcphdr *tc; struct ack_data_record x; int hlen, tlen, dlen; int i; tc = (struct tcphdr *)((char *)pip + (pip->ip_hl << 2)); hlen = (pip->ip_hl + tc->th_off) << 2; tlen = ntohs(pip->ip_len); dlen = tlen - hlen; x.ack_old = htonl(ntohl(tc->th_seq) + dlen); x.ack_new = htonl(ntohl(tc->th_seq) + dlen + delta); x.delta = delta; x.active = 1; i = link->data.tcp->state.index; link->data.tcp->ack[i] = x; i++; if (i == N_LINK_TCP_DATA) link->data.tcp->state.index = 0; else link->data.tcp->state.index = i; } void SetExpire(struct alias_link *link, int expire) { if (expire == 0) { link->flags &= ~LINK_PERMANENT; DeleteLink(link); } else if (expire == -1) { link->flags |= LINK_PERMANENT; } else if (expire > 0) { link->expire_time = expire; } else { #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 *link, int pflags) { link->pflags = pflags;; } int GetProtocolFlags(struct alias_link *link) { return (link->pflags); } void SetDestCallId(struct alias_link *link, u_int16_t cid) { struct libalias *la = link->la; la->deleteAllLinks = 1; link = ReLink(link, link->src_addr, link->dst_addr, link->alias_addr, link->src_port, cid, link->alias_port, link->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 *link; 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; } link = AddLink(la, src_addr, dst_addr, alias_addr, src_port, dst_port, alias_port, link_type); if (link != NULL) { link->flags |= LINK_PERMANENT; } #ifdef DEBUG else { fprintf(stderr, "PacketAliasRedirectPort(): " "call to AddLink() failed\n"); } #endif return link; } /* Add server to the pool of servers */ int LibAliasAddServer(struct libalias *la, struct alias_link *link, struct in_addr addr, u_short port) { struct server *server; server = malloc(sizeof(struct server)); if (server != NULL) { struct server *head; server->addr = addr; server->port = port; head = link->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; } link->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 *link; link = AddLink(la, src_addr, dst_addr, alias_addr, NO_SRC_PORT, NO_DEST_PORT, 0, proto); if (link != NULL) { link->flags |= LINK_PERMANENT; } #ifdef DEBUG else { fprintf(stderr, "PacketAliasRedirectProto(): " "call to AddLink() failed\n"); } #endif return link; } /* Static address translation */ struct alias_link * LibAliasRedirectAddr(struct libalias *la, struct in_addr src_addr, struct in_addr alias_addr) { struct alias_link *link; link = AddLink(la, src_addr, la->nullAddress, alias_addr, 0, 0, 0, LINK_ADDR); if (link != NULL) { link->flags |= LINK_PERMANENT; } #ifdef DEBUG else { fprintf(stderr, "PacketAliasRedirectAddr(): " "call to AddLink() failed\n"); } #endif return link; } /* Mark the aliasing link dynamic */ int LibAliasRedirectDynamic(struct libalias *la, struct alias_link *link) { if (link->flags & LINK_PARTIALLY_SPECIFIED) return (-1); else { link->flags &= ~LINK_PERMANENT; return (0); } } void LibAliasRedirectDelete(struct libalias *la, struct alias_link *link) { /* This is a dangerous function to put in the API, because an invalid pointer can crash the program. */ la->deleteAllLinks = 1; DeleteLink(link); 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 #include #include #include #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 *link) { struct libalias *la; int r; /* Result code */ struct ip_fw rule; /* On-the-fly built rule */ int fwhole; /* Where to punch hole */ la = link->la; /* Don't do anything unless we are asked to */ if (!(la->packetAliasMode & PKT_ALIAS_PUNCH_FW) || la->fireWallFD < 0 || link->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(link) != 0 && GetDestPort(link) != 0) { u_int32_t rulebuf[255]; int i; i = fill_rule(rulebuf, sizeof(rulebuf), fwhole, O_ACCEPT, IPPROTO_TCP, GetOriginalAddress(link), ntohs(GetOriginalPort(link)), GetDestAddress(link), ntohs(GetDestPort(link))); 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(link), ntohs(GetDestPort(link)), GetOriginalAddress(link), ntohs(GetOriginalPort(link))); 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(link); rule.fw_dst = GetDestAddress(link); rule.fw_uar.fw_pts[0] = ntohs(GetOriginalPort(link)); rule.fw_uar.fw_pts[1] = ntohs(GetDestPort(link)); /* * Skip non-bound links - XXX should not be strictly necessary, but * seems to leave hole if not done. Leak of non-bound links? (Code * should be left even if the problem is fixed - it is a clear * optimization) */ if (rule.fw_uar.fw_pts[0] != 0 && rule.fw_uar.fw_pts[1] != 0) { r = setsockopt(fireWallFD, IPPROTO_IP, IP_FW_ADD, &rule, sizeof rule); #ifdef DEBUG if (r) err(1, "alias punch inbound(1) setsockopt(IP_FW_ADD)"); #endif rule.fw_src = GetDestAddress(link); rule.fw_dst = GetOriginalAddress(link); rule.fw_uar.fw_pts[0] = ntohs(GetDestPort(link)); rule.fw_uar.fw_pts[1] = ntohs(GetOriginalPort(link)); r = setsockopt(fireWallFD, IPPROTO_IP, IP_FW_ADD, &rule, sizeof rule); #ifdef DEBUG if (r) err(1, "alias punch inbound(2) setsockopt(IP_FW_ADD)"); #endif } #endif /* !IPFW2 */ /* Indicate hole applied */ link->data.tcp->fwhole = fwhole; fw_setfield(la, la->fireWallField, fwhole); } /* Remove a hole in a firewall associated with a particular alias link. Calling this too often is harmless. */ static void ClearFWHole(struct alias_link *link) { struct libalias *la; la = link->la; if (link->link_type == LINK_TCP) { int fwhole = link->data.tcp->fwhole; /* Where is the firewall * hole? */ struct ip_fw rule; if (fwhole < 0) return; memset(&rule, 0, sizeof rule); /* 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); link->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/sys/netinet/libalias/alias_ftp.c b/sys/netinet/libalias/alias_ftp.c index 91d49525bc75..2a7def7bc433 100644 --- a/sys/netinet/libalias/alias_ftp.c +++ b/sys/netinet/libalias/alias_ftp.c @@ -1,675 +1,675 @@ /*- * Copyright (c) 2001 Charles Mott * 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 __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. + 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. + 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. + 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 #include #include #include #include #include #include #include #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 *link, /* The link to go through (aliased port) */ int maxpacketsize /* The maximum size this packet can grow to - (including headers) */ ) + (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 *)((char *)pip + (pip->ip_hl << 2)); 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(link); 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, link, 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(link, 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 *link, int maxpacketsize, int ftp_message_type) { struct alias_link *ftp_link; /* 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_link = FindUdpTcpOut(la, la->true_addr, GetDestAddress(link), htons(la->true_port), 0, IPPROTO_TCP, 1); if (ftp_link != NULL) { int slen, hlen, tlen, dlen; struct tcphdr *tc; #ifndef NO_FW_PUNCH /* Punch hole in firewall */ PunchFWHole(ftp_link); #endif /* Calculate data length of TCP packet */ tc = (struct tcphdr *)((char *)pip + (pip->ip_hl << 2)); hlen = (pip->ip_hl + tc->th_off) << 2; tlen = ntohs(pip->ip_len); dlen = tlen - hlen; /* Create new 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(link); ptr = (u_char *) & alias_address.s_addr; a1 = *ptr++; a2 = *ptr++; a3 = *ptr++; a4 = *ptr; alias_port = GetAliasPort(ftp_link); 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(link); delta = GetDeltaSeqOut(pip, link); AddSeq(pip, link, delta + slen - dlen); } /* Revise IP header */ { u_short new_len; new_len = htons(hlen + slen); DifferentialChecksum(&pip->ip_sum, &new_len, &pip->ip_len, 1); pip->ip_len = new_len; } /* Compute TCP checksum for revised packet */ tc->th_sum = 0; tc->th_sum = TcpChecksum(pip); } else { #ifdef DEBUG fprintf(stderr, "PacketAlias/HandleFtpOut: Cannot allocate FTP data port\n"); #endif } } diff --git a/sys/netinet/libalias/alias_irc.c b/sys/netinet/libalias/alias_irc.c index 5785e54a7594..7b3d030d1a39 100644 --- a/sys/netinet/libalias/alias_irc.c +++ b/sys/netinet/libalias/alias_irc.c @@ -1,386 +1,386 @@ /*- * Copyright (c) 2001 Charles Mott * 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 __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 (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. + Version 2.1: May, 1997 (cjm) + Very minor changes to conform with + local/global/function naming conventions + withing the packet alising module. */ /* Includes */ #include #include #include #include #include #include #include #include #include #include "alias_local.h" /* Local defines */ #define DBprintf(a) void AliasHandleIrcOut(struct libalias *la, struct ip *pip, /* IP packet to examine */ struct alias_link *link, /* Which link are we on? */ int maxsize /* Maximum size of IP packet including * headers */ ) { int hlen, tlen, dlen; struct in_addr true_addr; u_short true_port; char *sptr; struct tcphdr *tc; int i; /* Iterator through the source */ /* Calculate data length of TCP packet */ tc = (struct tcphdr *)((char *)pip + (pip->ip_hl << 2)); hlen = (pip->ip_hl + tc->th_off) << 2; tlen = ntohs(pip->ip_len); dlen = tlen - hlen; /* * Return if data length is too short - assume an entire PRIVMSG in * each packet. */ if (dlen < sizeof(":A!a@n.n PRIVMSG A :aDCC 1 1a") - 1) return; /* Place string pointer at beginning of data */ sptr = (char *)pip; sptr += hlen; maxsize -= hlen; /* We're interested in maximum size of * data, not packet */ /* Search for a CTCP command [Note 1] */ for (i = 0; i < dlen; i++) { if (sptr[i] == '\001') goto lFOUND_CTCP; } return; /* No CTCP commands in */ /* Handle CTCP commands - the buffer may have to be copied */ lFOUND_CTCP: { char newpacket[65536]; /* Estimate of maximum packet size * :) */ int copyat = i; /* Same */ int iCopy = 0; /* How much data have we written to * copy-back string? */ unsigned long org_addr; /* Original IP address */ unsigned short org_port; /* Original source port * address */ lCTCP_START: if (i >= dlen || iCopy >= sizeof(newpacket)) goto lPACKET_DONE; newpacket[iCopy++] = sptr[i++]; /* Copy the CTCP start * character */ /* Start of a CTCP */ if (i + 4 >= dlen) /* Too short for DCC */ goto lBAD_CTCP; if (sptr[i + 0] != 'D') goto lBAD_CTCP; if (sptr[i + 1] != 'C') goto lBAD_CTCP; if (sptr[i + 2] != 'C') goto lBAD_CTCP; if (sptr[i + 3] != ' ') goto lBAD_CTCP; /* We have a DCC command - handle it! */ i += 4; /* Skip "DCC " */ if (iCopy + 4 > sizeof(newpacket)) goto lPACKET_DONE; newpacket[iCopy++] = 'D'; newpacket[iCopy++] = 'C'; newpacket[iCopy++] = 'C'; newpacket[iCopy++] = ' '; DBprintf(("Found DCC\n")); /* * Skip any extra spaces (should not occur according to * protocol, but DCC breaks CTCP protocol anyway */ while (sptr[i] == ' ') { if (++i >= dlen) { DBprintf(("DCC packet terminated in just spaces\n")); goto lPACKET_DONE; } } DBprintf(("Transferring command...\n")); while (sptr[i] != ' ') { newpacket[iCopy++] = sptr[i]; if (++i >= dlen || iCopy >= sizeof(newpacket)) { DBprintf(("DCC packet terminated during command\n")); goto lPACKET_DONE; } } /* Copy _one_ space */ if (i + 1 < dlen && iCopy < sizeof(newpacket)) newpacket[iCopy++] = sptr[i++]; DBprintf(("Done command - removing spaces\n")); /* * Skip any extra spaces (should not occur according to * protocol, but DCC breaks CTCP protocol anyway */ while (sptr[i] == ' ') { if (++i >= dlen) { DBprintf(("DCC packet terminated in just spaces (post-command)\n")); goto lPACKET_DONE; } } DBprintf(("Transferring filename...\n")); while (sptr[i] != ' ') { newpacket[iCopy++] = sptr[i]; if (++i >= dlen || iCopy >= sizeof(newpacket)) { DBprintf(("DCC packet terminated during filename\n")); goto lPACKET_DONE; } } /* Copy _one_ space */ if (i + 1 < dlen && iCopy < sizeof(newpacket)) newpacket[iCopy++] = sptr[i++]; DBprintf(("Done filename - removing spaces\n")); /* * Skip any extra spaces (should not occur according to * protocol, but DCC breaks CTCP protocol anyway */ while (sptr[i] == ' ') { if (++i >= dlen) { DBprintf(("DCC packet terminated in just spaces (post-filename)\n")); goto lPACKET_DONE; } } DBprintf(("Fetching IP address\n")); /* Fetch IP address */ org_addr = 0; while (i < dlen && isdigit(sptr[i])) { if (org_addr > ULONG_MAX / 10UL) { /* Terminate on overflow */ DBprintf(("DCC Address overflow (org_addr == 0x%08lx, next char %c\n", org_addr, sptr[i])); goto lBAD_CTCP; } org_addr *= 10; org_addr += sptr[i++] - '0'; } DBprintf(("Skipping space\n")); if (i + 1 >= dlen || sptr[i] != ' ') { DBprintf(("Overflow (%d >= %d) or bad character (%02x) terminating IP address\n", i + 1, dlen, sptr[i])); goto lBAD_CTCP; } /* * Skip any extra spaces (should not occur according to * protocol, but DCC breaks CTCP protocol anyway, so we * might as well play it safe */ while (sptr[i] == ' ') { if (++i >= dlen) { DBprintf(("Packet failure - space overflow.\n")); goto lPACKET_DONE; } } DBprintf(("Fetching port number\n")); /* Fetch source port */ org_port = 0; while (i < dlen && isdigit(sptr[i])) { if (org_port > 6554) { /* Terminate on overflow * (65536/10 rounded up */ DBprintf(("DCC: port number overflow\n")); goto lBAD_CTCP; } org_port *= 10; org_port += sptr[i++] - '0'; } /* Skip illegal addresses (or early termination) */ if (i >= dlen || (sptr[i] != '\001' && sptr[i] != ' ')) { DBprintf(("Bad port termination\n")); goto lBAD_CTCP; } DBprintf(("Got IP %lu and port %u\n", org_addr, (unsigned)org_port)); /* We've got the address and port - now alias it */ { struct alias_link *dcc_link; struct in_addr destaddr; true_port = htons(org_port); true_addr.s_addr = htonl(org_addr); destaddr.s_addr = 0; /* 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_link = FindUdpTcpOut(la, true_addr, destaddr, true_port, 0, IPPROTO_TCP, 1); DBprintf(("Got a DCC link\n")); if (dcc_link) { struct in_addr alias_address; /* Address from aliasing */ u_short alias_port; /* Port given by * aliasing */ int n; #ifndef NO_FW_PUNCH /* Generate firewall hole as appropriate */ PunchFWHole(dcc_link); #endif alias_address = GetAliasAddress(link); 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_link); 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(link); delta = GetDeltaSeqOut(pip, link); AddSeq(pip, link, delta + copyat + iCopy - dlen); } /* Revise IP header */ { u_short new_len; new_len = htons(hlen + iCopy + copyat); DifferentialChecksum(&pip->ip_sum, &new_len, &pip->ip_len, 1); pip->ip_len = new_len; } /* Compute TCP checksum for revised packet */ tc->th_sum = 0; tc->th_sum = TcpChecksum(pip); return; } } /* Notes: [Note 1] The initial search will most often fail; it could be replaced with a 32-bit specific search. Such a search would be done for 32-bit unsigned value V: V ^= 0x01010101; (Search is for null bytes) if( ((V-0x01010101)^V) & 0x80808080 ) { (found a null bytes which was a 01 byte) } To assert that the processor is 32-bits, do extern int ircdccar[32]; (32 bits) extern int ircdccar[CHAR_BIT*sizeof(unsigned int)]; which will generate a type-error on all but 32-bit machines. [Note 2] This routine really ought to be replaced with one that creates a transparent proxy on the aliasing host, to allow arbitary changes in the TCP stream. This should not be too difficult given this base; I (ee) will try to do this some time later. */ diff --git a/sys/netinet/libalias/alias_proxy.c b/sys/netinet/libalias/alias_proxy.c index cdbe32d1c0aa..ceb9119dac95 100644 --- a/sys/netinet/libalias/alias_proxy.c +++ b/sys/netinet/libalias/alias_proxy.c @@ -1,807 +1,807 @@ /*- * Copyright (c) 2001 Charles Mott * 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 __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. + 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 #include #include #include #include #include #include /* BSD IPV4 includes */ #include #include #include #include #include #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. + masks from integer (1-32) specification. IpAddr() -- Utility function for converting string - to IP address + to IP address IpPort() -- Utility function for converting string - to port number + 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. + having a certain rule number. ProxyEncodeTcpStream() -- Adds [DEST x.x.x.x xxxx] to the beginning - of a TCP stream. + of a TCP stream. ProxyEncodeIpHeader() -- Adds an IP option indicating the true - destination of a proxied IP packet + 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 *link, struct ip *pip, int maxpacketsize) { int slen; char buffer[40]; struct tcphdr *tc; /* Compute pointer to tcp header */ tc = (struct tcphdr *)((char *)pip + (pip->ip_hl << 2)); /* Don't modify if once already modified */ if (GetAckModified(link)) return; /* Translate destination address and port to string form */ snprintf(buffer, sizeof(buffer) - 2, "[DEST %s %d]", inet_ntoa(GetProxyAddress(link)), (u_int) ntohs(GetProxyPort(link))); /* 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 ((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(link); delta = GetDeltaSeqOut(pip, link); AddSeq(pip, link, 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 /* 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 *)((char *)pip + (pip->ip_hl << 2)); 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. + be proxied. ProxyModify() -- Encodes the original destination address/port - for a packet which is to be redirected to - a proxy server. + 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 *)((char *)pip + (pip->ip_hl << 2))) ->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 *link, struct ip *pip, int maxpacketsize, int proxy_type) { switch (proxy_type) { case PROXY_TYPE_ENCODE_IPHDR: ProxyEncodeIpHeader(pip, maxpacketsize); break; case PROXY_TYPE_ENCODE_TCPSTREAM: ProxyEncodeTcpStream(link, pip, maxpacketsize); break; } } /* Public API functions */ int LibAliasProxyRule(struct libalias *la, const char *cmd) { /* * This function takes command strings of the form: * * server [:] * [port ] * [rule n] * [proto tcp|udp] * [src [/n]] * [dst [/n]] * [type encode_tcp_stream|encode_ip_hdr|no_encode] * * delete * * 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 > (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/sys/netinet/libalias/alias_util.c b/sys/netinet/libalias/alias_util.c index bec0ba7dea68..574d4ed7d091 100644 --- a/sys/netinet/libalias/alias_util.c +++ b/sys/netinet/libalias/alias_util.c @@ -1,162 +1,162 @@ /*- * Copyright (c) 2001 Charles Mott * 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 __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. + 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 #include #include #include #include #include #include "alias.h" #include "alias_local.h" u_short LibAliasInternetChecksum(struct libalias *la, u_short * ptr, int nbytes) { int sum, oddbyte; sum = 0; while (nbytes > 1) { sum += *ptr++; nbytes -= 2; } if (nbytes == 1) { oddbyte = 0; ((u_char *) & oddbyte)[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 *)((char *)pip + nhdr); ptr = (u_short *) tc; /* Add up TCP header and data */ nbytes = ntcp; sum = 0; while (nbytes > 1) { sum += *ptr++; nbytes -= 2; } if (nbytes == 1) { oddbyte = 0; ((u_char *) & oddbyte)[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; } }