Index: head/lib/libalias/alias.c =================================================================== --- head/lib/libalias/alias.c (revision 59725) +++ head/lib/libalias/alias.c (revision 59726) @@ -1,1428 +1,1422 @@ /* -*- mode: c; tab-width: 8; c-basic-indent: 4; -*- */ /* Alias.c provides supervisory control for the functions of the packet aliasing software. It consists of routines to monitor TCP connection state, protocol-specific aliasing routines, fragment handling and the following outside world functional interfaces: SaveFragmentPtr, GetFragmentPtr, FragmentAliasIn, PacketAliasIn and PacketAliasOut. The other C program files are briefly described. The data structure framework which holds information needed to translate packets is encapsulated in alias_db.c. Data is accessed by function calls, so other segments of the program need not know about the underlying data structures. Alias_ftp.c contains special code for modifying the ftp PORT command used to establish data connections, while alias_irc.c does the same for IRC DCC. Alias_util.c contains a few utility routines. This software is placed into the public domain with no restrictions on its distribution. Version 1.0 August, 1996 (cjm) Version 1.1 August 20, 1996 (cjm) PPP host accepts incoming connections for ports 0 to 1023. (Gary Roberts pointed out the need to handle incoming connections.) Version 1.2 September 7, 1996 (cjm) Fragment handling error in alias_db.c corrected. (Tom Torrance helped fix this problem.) Version 1.4 September 16, 1996 (cjm) - A more generalized method for handling incoming connections, without the 0-1023 restriction, is implemented in alias_db.c - Improved ICMP support in alias.c. Traceroute packet streams can now be correctly aliased. - TCP connection closing logic simplified in alias.c and now allows for additional 1 minute "grace period" after FIN or RST is observed. Version 1.5 September 17, 1996 (cjm) Corrected error in handling incoming UDP packets with 0 checksum. (Tom Torrance helped fix this problem.) Version 1.6 September 18, 1996 (cjm) Simplified ICMP aliasing scheme. Should now support traceroute from Win95 as well as FreeBSD. Version 1.7 January 9, 1997 (cjm) - Out-of-order fragment handling. - IP checksum error fixed for ftp transfers from aliasing host. - Integer return codes added to all aliasing/de-aliasing functions. - Some obsolete comments cleaned up. - Differential checksum computations for IP header (TCP, UDP and ICMP were already differential). Version 2.1 May 1997 (cjm) - Added support for outgoing ICMP error messages. - Added two functions PacketAliasIn2() and PacketAliasOut2() for dynamic address control (e.g. round-robin allocation of incoming packets). Version 2.2 July 1997 (cjm) - Rationalized API function names to begin with "PacketAlias..." - Eliminated PacketAliasIn2() and PacketAliasOut2() as poorly conceived. Version 2.3 Dec 1998 (dillon) - Major bounds checking additions, see FreeBSD/CVS See HISTORY file for additional revisions. $FreeBSD$ */ #include #include #include #include #include #include #include #ifndef IPPROTO_GRE #define IPPROTO_GRE 47 #define IPPROTO_ESP 50 #define IPPROTO_AH 51 #endif #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 /* 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(), IcmpAliasIn3() IcmpAliasOut(), IcmpAliasOut1(), IcmpAliasOut2(), IcmpAliasOut3() + 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 ip *); static int IcmpAliasIn2(struct ip *); static int IcmpAliasIn3(struct ip *); static int IcmpAliasIn (struct ip *); static int IcmpAliasOut1(struct ip *); static int IcmpAliasOut2(struct ip *); static int IcmpAliasOut3(struct ip *); static int IcmpAliasOut (struct ip *); +static int ProtoAliasIn(struct ip *); +static int ProtoAliasOut(struct ip *); + static int UdpAliasOut(struct ip *); static int UdpAliasIn (struct ip *); static int TcpAliasOut(struct ip *, int); static int TcpAliasIn (struct ip *); static int IcmpAliasIn1(struct ip *pip) { /* De-alias incoming echo and timestamp replies */ struct alias_link *link; struct icmp *ic; ic = (struct icmp *) ((char *) pip + (pip->ip_hl << 2)); /* Get source address from ICMP data field and restore original data */ link = FindIcmpIn(pip->ip_src, pip->ip_dst, ic->icmp_id); if (link != NULL) { u_short original_id; int accumulate; original_id = GetOriginalPort(link); /* Adjust ICMP checksum */ accumulate = ic->icmp_id; accumulate -= original_id; ADJUST_CHECKSUM(accumulate, ic->icmp_cksum) /* Put original sequence number back in */ ic->icmp_id = original_id; /* Put original address back into IP header */ { struct in_addr original_address; original_address = GetOriginalAddress(link); DifferentialChecksum(&pip->ip_sum, (u_short *) &original_address, (u_short *) &pip->ip_dst, 2); pip->ip_dst = original_address; } return(PKT_ALIAS_OK); } return(PKT_ALIAS_IGNORED); } static int IcmpAliasIn2(struct ip *pip) { /* Alias incoming ICMP error messages containing IP header and first 64 bits of datagram. */ struct ip *ip; struct icmp *ic, *ic2; struct udphdr *ud; struct tcphdr *tc; struct alias_link *link; ic = (struct icmp *) ((char *) pip + (pip->ip_hl << 2)); ip = (struct ip *) ic->icmp_data; ud = (struct udphdr *) ((char *) ip + (ip->ip_hl <<2)); tc = (struct tcphdr *) ud; ic2 = (struct icmp *) ud; if (ip->ip_p == IPPROTO_UDP) link = FindUdpTcpIn(ip->ip_dst, ip->ip_src, ud->uh_dport, ud->uh_sport, IPPROTO_UDP); else if (ip->ip_p == IPPROTO_TCP) link = FindUdpTcpIn(ip->ip_dst, ip->ip_src, tc->th_dport, tc->th_sport, IPPROTO_TCP); else if (ip->ip_p == IPPROTO_ICMP) { if (ic2->icmp_type == ICMP_ECHO || ic2->icmp_type == ICMP_TSTAMP) link = FindIcmpIn(ip->ip_dst, ip->ip_src, ic2->icmp_id); else link = NULL; } else link = NULL; if (link != NULL) { if (ip->ip_p == IPPROTO_UDP || ip->ip_p == IPPROTO_TCP) { u_short *sptr; int accumulate; struct in_addr original_address; u_short original_port; original_address = GetOriginalAddress(link); original_port = GetOriginalPort(link); /* Adjust ICMP checksum */ sptr = (u_short *) &(ip->ip_src); accumulate = *sptr++; accumulate += *sptr; sptr = (u_short *) &original_address; accumulate -= *sptr++; accumulate -= *sptr; accumulate += ud->uh_sport; accumulate -= original_port; ADJUST_CHECKSUM(accumulate, ic->icmp_cksum) /* Un-alias address in IP header */ DifferentialChecksum(&pip->ip_sum, (u_short *) &original_address, (u_short *) &pip->ip_dst, 2); pip->ip_dst = original_address; /* Un-alias address and port number of original IP packet fragment contained in ICMP data section */ ip->ip_src = original_address; ud->uh_sport = original_port; } else if (pip->ip_p == IPPROTO_ICMP) { u_short *sptr; int accumulate; struct in_addr original_address; u_short original_id; original_address = GetOriginalAddress(link); original_id = GetOriginalPort(link); /* Adjust ICMP checksum */ sptr = (u_short *) &(ip->ip_src); accumulate = *sptr++; accumulate += *sptr; sptr = (u_short *) &original_address; accumulate -= *sptr++; accumulate -= *sptr; accumulate += ic2->icmp_id; accumulate -= original_id; ADJUST_CHECKSUM(accumulate, ic->icmp_cksum) /* Un-alias address in IP header */ DifferentialChecksum(&pip->ip_sum, (u_short *) &original_address, (u_short *) &pip->ip_dst, 2); pip->ip_dst = original_address; /* Un-alias address of original IP packet and 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 IcmpAliasIn3(struct ip *pip) { struct in_addr original_address; original_address = FindOriginalAddress(pip->ip_dst); DifferentialChecksum(&pip->ip_sum, (u_short *) &original_address, (u_short *) &pip->ip_dst, 2); pip->ip_dst = original_address; return PKT_ALIAS_OK; } static int IcmpAliasIn(struct ip *pip) { int iresult; struct icmp *ic; /* Return if proxy-only mode is enabled */ if (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(pip); } break; case ICMP_UNREACH: case ICMP_SOURCEQUENCH: case ICMP_TIMXCEED: case ICMP_PARAMPROB: iresult = IcmpAliasIn2(pip); break; case ICMP_ECHO: case ICMP_TSTAMP: iresult = IcmpAliasIn3(pip); break; } return(iresult); } static int IcmpAliasOut1(struct ip *pip) { /* Alias ICMP echo and timestamp packets */ struct alias_link *link; struct icmp *ic; ic = (struct icmp *) ((char *) pip + (pip->ip_hl << 2)); /* Save overwritten data for when echo packet returns */ link = FindIcmpOut(pip->ip_src, pip->ip_dst, ic->icmp_id); if (link != NULL) { u_short alias_id; int accumulate; alias_id = GetAliasPort(link); /* Since data field is being modified, adjust ICMP checksum */ accumulate = ic->icmp_id; accumulate -= alias_id; ADJUST_CHECKSUM(accumulate, ic->icmp_cksum) /* Alias sequence number */ ic->icmp_id = alias_id; /* Change source address */ { struct in_addr alias_address; alias_address = GetAliasAddress(link); DifferentialChecksum(&pip->ip_sum, (u_short *) &alias_address, (u_short *) &pip->ip_src, 2); pip->ip_src = alias_address; } return(PKT_ALIAS_OK); } return(PKT_ALIAS_IGNORED); } static int IcmpAliasOut2(struct ip *pip) { /* Alias outgoing ICMP error messages containing IP header and first 64 bits of datagram. */ struct ip *ip; struct icmp *ic, *ic2; struct udphdr *ud; struct tcphdr *tc; struct alias_link *link; ic = (struct icmp *) ((char *) pip + (pip->ip_hl << 2)); ip = (struct ip *) ic->icmp_data; ud = (struct udphdr *) ((char *) ip + (ip->ip_hl <<2)); tc = (struct tcphdr *) ud; ic2 = (struct icmp *) ud; if (ip->ip_p == IPPROTO_UDP) link = FindUdpTcpOut(ip->ip_dst, ip->ip_src, ud->uh_dport, ud->uh_sport, IPPROTO_UDP); else if (ip->ip_p == IPPROTO_TCP) link = FindUdpTcpOut(ip->ip_dst, ip->ip_src, tc->th_dport, tc->th_sport, IPPROTO_TCP); else if (ip->ip_p == IPPROTO_ICMP) { if (ic2->icmp_type == ICMP_ECHO || ic2->icmp_type == ICMP_TSTAMP) link = FindIcmpOut(ip->ip_dst, ip->ip_src, ic2->icmp_id); else link = NULL; } else link = NULL; if (link != NULL) { if (ip->ip_p == IPPROTO_UDP || ip->ip_p == IPPROTO_TCP) { u_short *sptr; int accumulate; struct in_addr alias_address; u_short alias_port; alias_address = GetAliasAddress(link); alias_port = GetAliasPort(link); /* Adjust ICMP checksum */ sptr = (u_short *) &(ip->ip_dst); accumulate = *sptr++; accumulate += *sptr; sptr = (u_short *) &alias_address; accumulate -= *sptr++; accumulate -= *sptr; accumulate += ud->uh_dport; accumulate -= alias_port; ADJUST_CHECKSUM(accumulate, ic->icmp_cksum) /* Alias address in IP header */ DifferentialChecksum(&pip->ip_sum, (u_short *) &alias_address, (u_short *) &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 (pip->ip_p == IPPROTO_ICMP) { u_short *sptr; int accumulate; struct in_addr alias_address; u_short alias_id; alias_address = GetAliasAddress(link); alias_id = GetAliasPort(link); /* Adjust ICMP checksum */ sptr = (u_short *) &(ip->ip_dst); accumulate = *sptr++; accumulate += *sptr; sptr = (u_short *) &alias_address; accumulate -= *sptr++; accumulate -= *sptr; accumulate += ic2->icmp_id; accumulate -= alias_id; ADJUST_CHECKSUM(accumulate, ic->icmp_cksum) /* Alias address in IP header */ DifferentialChecksum(&pip->ip_sum, (u_short *) &alias_address, (u_short *) &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 IcmpAliasOut3(struct ip *pip) { /* Handle outgoing echo and timestamp replies. The only thing which is done in this case is to alias the source IP address of the packet. */ struct in_addr alias_addr; alias_addr = FindAliasAddress(pip->ip_src); DifferentialChecksum(&pip->ip_sum, (u_short *) &alias_addr, (u_short *) &pip->ip_src, 2); pip->ip_src = alias_addr; return PKT_ALIAS_OK; } static int IcmpAliasOut(struct ip *pip) { int iresult; struct icmp *ic; /* Return if proxy-only mode is enabled */ if (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(pip); } break; case ICMP_UNREACH: case ICMP_SOURCEQUENCH: case ICMP_TIMXCEED: case ICMP_PARAMPROB: iresult = IcmpAliasOut2(pip); break; case ICMP_ECHOREPLY: case ICMP_TSTAMPREPLY: iresult = IcmpAliasOut3(pip); } return(iresult); } static int -PptpAliasIn(struct ip *pip) +ProtoAliasIn(struct ip *pip) { /* - Handle incoming PPTP packets. The + 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 (packetAliasMode & PKT_ALIAS_PROXY_ONLY) return PKT_ALIAS_OK; - if (packetAliasMode & PKT_ALIAS_DENY_PPTP) - return PKT_ALIAS_IGNORED; - - link = FindPptpIn(pip->ip_src, pip->ip_dst); + link = FindProtoIn(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, (u_short *) &original_address, (u_short *) &pip->ip_dst, 2); pip->ip_dst = original_address; return(PKT_ALIAS_OK); } return(PKT_ALIAS_IGNORED); } static int -PptpAliasOut(struct ip *pip) +ProtoAliasOut(struct ip *pip) { /* - Handle outgoing PPTP packets. The + 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 (packetAliasMode & PKT_ALIAS_PROXY_ONLY) return PKT_ALIAS_OK; - if (packetAliasMode & PKT_ALIAS_DENY_PPTP) - return PKT_ALIAS_IGNORED; - - link = FindPptpOut(pip->ip_src, pip->ip_dst); + link = FindProtoOut(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, (u_short *) &alias_address, (u_short *) &pip->ip_src, 2); pip->ip_src = alias_address; return(PKT_ALIAS_OK); } return(PKT_ALIAS_IGNORED); } static int UdpAliasIn(struct ip *pip) { struct udphdr *ud; struct alias_link *link; /* Return if proxy-only mode is enabled */ if (packetAliasMode & PKT_ALIAS_PROXY_ONLY) return PKT_ALIAS_OK; ud = (struct udphdr *) ((char *) pip + (pip->ip_hl << 2)); link = FindUdpTcpIn(pip->ip_src, pip->ip_dst, ud->uh_sport, ud->uh_dport, IPPROTO_UDP); if (link != NULL) { struct in_addr alias_address; struct in_addr original_address; u_short alias_port; int accumulate; u_short *sptr; int r = 0; alias_address = GetAliasAddress(link); original_address = GetOriginalAddress(link); alias_port = ud->uh_dport; ud->uh_dport = GetOriginalPort(link); /* If NETBIOS Datagram, It should be alias address in UDP Data, too */ if (ntohs(ud->uh_dport) == NETBIOS_DGM_PORT_NUMBER || ntohs(ud->uh_sport) == NETBIOS_DGM_PORT_NUMBER ) { r = AliasHandleUdpNbt(pip, link, &original_address, ud->uh_dport); } else if (ntohs(ud->uh_dport) == NETBIOS_NS_PORT_NUMBER || ntohs(ud->uh_sport) == NETBIOS_NS_PORT_NUMBER ) { r = AliasHandleUdpNbtNS(pip, link, &alias_address, &alias_port, &original_address, &ud->uh_dport ); } if (ntohs(ud->uh_dport) == CUSEEME_PORT_NUMBER) AliasHandleCUSeeMeIn(pip, original_address); /* If UDP checksum is not zero, then adjust since destination port */ /* is being unaliased and destination port is being altered. */ if (ud->uh_sum != 0) { accumulate = alias_port; accumulate -= ud->uh_dport; sptr = (u_short *) &alias_address; accumulate += *sptr++; accumulate += *sptr; sptr = (u_short *) &original_address; accumulate -= *sptr++; accumulate -= *sptr; ADJUST_CHECKSUM(accumulate, ud->uh_sum) } /* Restore original IP address */ DifferentialChecksum(&pip->ip_sum, (u_short *) &original_address, (u_short *) &pip->ip_dst, 2); pip->ip_dst = original_address; /* * 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 ip *pip) { struct udphdr *ud; struct alias_link *link; /* Return if proxy-only mode is enabled */ if (packetAliasMode & PKT_ALIAS_PROXY_ONLY) return PKT_ALIAS_OK; ud = (struct udphdr *) ((char *) pip + (pip->ip_hl << 2)); link = FindUdpTcpOut(pip->ip_src, pip->ip_dst, ud->uh_sport, ud->uh_dport, IPPROTO_UDP); if (link != NULL) { u_short alias_port; struct in_addr alias_address; alias_address = GetAliasAddress(link); alias_port = GetAliasPort(link); if (ntohs(ud->uh_dport) == CUSEEME_PORT_NUMBER) AliasHandleCUSeeMeOut(pip, link); /* If NETBIOS Datagram, It should be alias address in UDP Data, too */ if (ntohs(ud->uh_dport) == NETBIOS_DGM_PORT_NUMBER || ntohs(ud->uh_sport) == NETBIOS_DGM_PORT_NUMBER ) { AliasHandleUdpNbt(pip, link, &alias_address, alias_port); } else if (ntohs(ud->uh_dport) == NETBIOS_NS_PORT_NUMBER || ntohs(ud->uh_sport) == NETBIOS_NS_PORT_NUMBER ) { AliasHandleUdpNbtNS(pip, link, &pip->ip_src, &ud->uh_sport, &alias_address, &alias_port); } /* If UDP checksum is not zero, adjust since source port is */ /* being aliased and source address is being altered */ if (ud->uh_sum != 0) { int accumulate; u_short *sptr; accumulate = ud->uh_sport; accumulate -= alias_port; sptr = (u_short *) &(pip->ip_src); accumulate += *sptr++; accumulate += *sptr; sptr = (u_short *) &alias_address; accumulate -= *sptr++; accumulate -= *sptr; ADJUST_CHECKSUM(accumulate, ud->uh_sum) } /* Put alias port in UDP header */ ud->uh_sport = alias_port; /* Change source address */ DifferentialChecksum(&pip->ip_sum, (u_short *) &alias_address, (u_short *) &pip->ip_src, 2); pip->ip_src = alias_address; return(PKT_ALIAS_OK); } return(PKT_ALIAS_IGNORED); } static int TcpAliasIn(struct ip *pip) { struct tcphdr *tc; struct alias_link *link; tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2)); link = FindUdpTcpIn(pip->ip_src, pip->ip_dst, tc->th_sport, tc->th_dport, IPPROTO_TCP); if (link != NULL) { struct in_addr alias_address; struct in_addr original_address; struct in_addr proxy_address; u_short alias_port; u_short proxy_port; int accumulate; u_short *sptr; 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; sptr = (u_short *) &alias_address; accumulate += *sptr++; accumulate += *sptr; sptr = (u_short *) &original_address; accumulate -= *sptr++; accumulate -= *sptr; /* 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; sptr = (u_short *) &pip->ip_src; accumulate += *sptr++; accumulate += *sptr; sptr = (u_short *) &proxy_address; accumulate -= *sptr++; accumulate -= *sptr; } /* See if ACK number needs to be modified */ if (GetAckModified(link) == 1) { int delta; delta = GetDeltaAckIn(pip, link); if (delta != 0) { sptr = (u_short *) &tc->th_ack; accumulate += *sptr++; accumulate += *sptr; tc->th_ack = htonl(ntohl(tc->th_ack) - delta); sptr = (u_short *) &tc->th_ack; accumulate -= *sptr++; accumulate -= *sptr; } } ADJUST_CHECKSUM(accumulate, tc->th_sum); /* Restore original IP address */ sptr = (u_short *) &pip->ip_dst; accumulate = *sptr++; accumulate += *sptr; pip->ip_dst = original_address; sptr = (u_short *) &pip->ip_dst; accumulate -= *sptr++; accumulate -= *sptr; /* If this is a transparent proxy packet, then modify the source address */ if (proxy_address.s_addr != 0) { sptr = (u_short *) &pip->ip_src; accumulate += *sptr++; accumulate += *sptr; pip->ip_src = proxy_address; sptr = (u_short *) &pip->ip_src; accumulate -= *sptr++; accumulate -= *sptr; } 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 ip *pip, int maxpacketsize) { 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(pip, &proxy_server_address, &proxy_server_port); if (proxy_type == 0 && (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; u_short *sptr; accumulate = tc->th_dport; tc->th_dport = proxy_server_port; accumulate -= tc->th_dport; sptr = (u_short *) &(pip->ip_dst); accumulate += *sptr++; accumulate += *sptr; sptr = (u_short *) &proxy_server_address; accumulate -= *sptr++; accumulate -= *sptr; ADJUST_CHECKSUM(accumulate, tc->th_sum); sptr = (u_short *) &(pip->ip_dst); accumulate = *sptr++; accumulate += *sptr; pip->ip_dst = proxy_server_address; sptr = (u_short *) &(pip->ip_dst); accumulate -= *sptr++; accumulate -= *sptr; ADJUST_CHECKSUM(accumulate, pip->ip_sum); } link = FindUdpTcpOut(pip->ip_src, pip->ip_dst, tc->th_sport, tc->th_dport, IPPROTO_TCP); if (link !=NULL) { u_short alias_port; struct in_addr alias_address; int accumulate; u_short *sptr; /* Save original destination address, if this is a proxy packet. Also modify packet to include destination encoding. */ if (proxy_type != 0) { SetProxyPort(link, dest_port); SetProxyAddress(link, dest_address); ProxyModify(link, pip, maxpacketsize, proxy_type); } /* 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(pip, link, maxpacketsize); if (ntohs(tc->th_dport) == IRC_CONTROL_PORT_NUMBER_1 || ntohs(tc->th_dport) == IRC_CONTROL_PORT_NUMBER_2) AliasHandleIrcOut(pip, link, maxpacketsize); /* Adjust TCP checksum since source port is being aliased */ /* and source address is being altered */ accumulate = tc->th_sport; tc->th_sport = alias_port; accumulate -= tc->th_sport; sptr = (u_short *) &(pip->ip_src); accumulate += *sptr++; accumulate += *sptr; sptr = (u_short *) &alias_address; accumulate -= *sptr++; accumulate -= *sptr; /* Modify sequence number if necessary */ if (GetAckModified(link) == 1) { int delta; delta = GetDeltaSeqOut(pip, link); if (delta != 0) { sptr = (u_short *) &tc->th_seq; accumulate += *sptr++; accumulate += *sptr; tc->th_seq = htonl(ntohl(tc->th_seq) + delta); sptr = (u_short *) &tc->th_seq; accumulate -= *sptr++; accumulate -= *sptr; } } ADJUST_CHECKSUM(accumulate, tc->th_sum) /* Change source address */ sptr = (u_short *) &(pip->ip_src); accumulate = *sptr++; accumulate += *sptr; pip->ip_src = alias_address; sptr = (u_short *) &(pip->ip_src); accumulate -= *sptr++; accumulate -= *sptr; 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 ip *); static int FragmentOut(struct ip *); static int FragmentIn(struct ip *pip) { struct alias_link *link; link = FindFragmentIn2(pip->ip_src, pip->ip_dst, pip->ip_id); if (link != NULL) { struct in_addr original_address; GetFragmentAddr(link, &original_address); DifferentialChecksum(&pip->ip_sum, (u_short *) &original_address, (u_short *) &pip->ip_dst, 2); pip->ip_dst = original_address; return(PKT_ALIAS_OK); } return(PKT_ALIAS_UNRESOLVED_FRAGMENT); } static int FragmentOut(struct ip *pip) { struct in_addr alias_address; alias_address = FindAliasAddress(pip->ip_src); DifferentialChecksum(&pip->ip_sum, (u_short *) &alias_address, (u_short *) &pip->ip_src, 2); pip->ip_src = alias_address; return(PKT_ALIAS_OK); } /* Outside World Access PacketAliasSaveFragment() PacketAliasGetFragment() PacketAliasFragmentIn() PacketAliasIn() PacketAliasOut() (prototypes in alias.h) */ int PacketAliasSaveFragment(char *ptr) { int iresult; struct alias_link *link; struct ip *pip; pip = (struct ip *) ptr; link = AddFragmentPtrLink(pip->ip_src, pip->ip_id); iresult = PKT_ALIAS_ERROR; if (link != NULL) { SetFragmentPtr(link, ptr); iresult = PKT_ALIAS_OK; } return(iresult); } char * PacketAliasGetFragment(char *ptr) { struct alias_link *link; char *fptr; struct ip *pip; pip = (struct ip *) ptr; link = FindFragmentPtr(pip->ip_src, pip->ip_id); if (link != NULL) { GetFragmentPtr(link, &fptr); SetFragmentPtr(link, NULL); SetExpire(link, 0); /* Deletes link */ return(fptr); } else { return(NULL); } } void PacketAliasFragmentIn(char *ptr, /* Points to correctly de-aliased header fragment */ char *ptr_fragment /* Points to fragment which must be de-aliased */ ) { struct ip *pip; struct ip *fpip; pip = (struct ip *) ptr; fpip = (struct ip *) ptr_fragment; DifferentialChecksum(&fpip->ip_sum, (u_short *) &pip->ip_dst, (u_short *) &fpip->ip_dst, 2); fpip->ip_dst = pip->ip_dst; } int PacketAliasIn(char *ptr, int maxpacketsize) { struct in_addr alias_addr; struct ip *pip; int iresult; if (packetAliasMode & PKT_ALIAS_REVERSE) { packetAliasMode &= ~PKT_ALIAS_REVERSE; iresult = PacketAliasOut(ptr, maxpacketsize); packetAliasMode |= PKT_ALIAS_REVERSE; return iresult; } HouseKeeping(); ClearCheckNewLink(); pip = (struct ip *) ptr; alias_addr = pip->ip_dst; /* Defense against mangled packets */ if (ntohs(pip->ip_len) > maxpacketsize || (pip->ip_hl<<2) > maxpacketsize) return PKT_ALIAS_IGNORED; iresult = PKT_ALIAS_IGNORED; if ( (ntohs(pip->ip_off) & IP_OFFMASK) == 0 ) { switch (pip->ip_p) { case IPPROTO_ICMP: iresult = IcmpAliasIn(pip); break; case IPPROTO_UDP: iresult = UdpAliasIn(pip); break; case IPPROTO_TCP: iresult = TcpAliasIn(pip); break; - case IPPROTO_GRE: - case IPPROTO_ESP: - case IPPROTO_AH: - iresult = PptpAliasIn(pip); + default: + iresult = ProtoAliasIn(pip); break; } if (ntohs(pip->ip_off) & IP_MF) { struct alias_link *link; link = FindFragmentIn1(pip->ip_src, alias_addr, pip->ip_id); if (link != NULL) { iresult = PKT_ALIAS_FOUND_HEADER_FRAGMENT; SetFragmentAddr(link, pip->ip_dst); } else { iresult = PKT_ALIAS_ERROR; } } } else { iresult = FragmentIn(pip); } return(iresult); } /* Unregistered address ranges */ /* 10.0.0.0 -> 10.255.255.255 */ #define UNREG_ADDR_A_LOWER 0x0a000000 #define UNREG_ADDR_A_UPPER 0x0affffff /* 172.16.0.0 -> 172.31.255.255 */ #define UNREG_ADDR_B_LOWER 0xac100000 #define UNREG_ADDR_B_UPPER 0xac1fffff /* 192.168.0.0 -> 192.168.255.255 */ #define UNREG_ADDR_C_LOWER 0xc0a80000 #define UNREG_ADDR_C_UPPER 0xc0a8ffff int PacketAliasOut(char *ptr, /* valid IP packet */ int maxpacketsize /* How much the packet data may grow (FTP and IRC inline changes) */ ) { int iresult; struct in_addr addr_save; struct ip *pip; if (packetAliasMode & PKT_ALIAS_REVERSE) { packetAliasMode &= ~PKT_ALIAS_REVERSE; iresult = PacketAliasIn(ptr, maxpacketsize); packetAliasMode |= PKT_ALIAS_REVERSE; return iresult; } HouseKeeping(); ClearCheckNewLink(); pip = (struct ip *) ptr; /* Defense against mangled packets */ if (ntohs(pip->ip_len) > maxpacketsize || (pip->ip_hl<<2) > maxpacketsize) return PKT_ALIAS_IGNORED; addr_save = GetDefaultAliasAddress(); if (packetAliasMode & PKT_ALIAS_UNREGISTERED_ONLY) { 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(pip->ip_src); } } iresult = PKT_ALIAS_IGNORED; if ((ntohs(pip->ip_off) & IP_OFFMASK) == 0) { switch (pip->ip_p) { case IPPROTO_ICMP: iresult = IcmpAliasOut(pip); break; case IPPROTO_UDP: iresult = UdpAliasOut(pip); break; case IPPROTO_TCP: iresult = TcpAliasOut(pip, maxpacketsize); break; - case IPPROTO_GRE: - case IPPROTO_ESP: - case IPPROTO_AH: - iresult = PptpAliasOut(pip); + default: + iresult = ProtoAliasOut(pip); break; } } else { iresult = FragmentOut(pip); } SetDefaultAliasAddress(addr_save); return(iresult); } Index: head/lib/libalias/alias.h =================================================================== --- head/lib/libalias/alias.h (revision 59725) +++ head/lib/libalias/alias.h (revision 59726) @@ -1,172 +1,171 @@ /*lint -save -library Flexelint comment for external headers */ /* Alias.h defines the outside world interfaces for the packet aliasing software. This software is placed into the public domain with no restrictions on its distribution. $FreeBSD$ */ #ifndef _ALIAS_H_ #define _ALIAS_H_ /* Alias link representative (incomplete struct) */ struct alias_link; /* External interfaces (API) to packet aliasing engine */ /* Initialization and Control */ extern void PacketAliasInit(void); extern void PacketAliasUninit(void); extern void PacketAliasSetAddress(struct in_addr); extern unsigned int PacketAliasSetMode(unsigned int, unsigned int); #ifndef NO_FW_PUNCH extern void PacketAliasSetFWBase(unsigned int, unsigned int); #endif /* Packet Handling */ extern int PacketAliasIn(char *, int maxpacketsize); extern int PacketAliasOut(char *, int maxpacketsize); /* Port and Address Redirection */ extern struct alias_link * PacketAliasRedirectPort(struct in_addr, u_short, struct in_addr, u_short, struct in_addr, u_short, u_char); extern int PacketAliasAddServer(struct alias_link *link, struct in_addr addr, u_short port); extern int PacketAliasPptp(struct in_addr); extern struct alias_link * - PacketAliasRedirectPptp(struct in_addr, struct in_addr, struct in_addr); + PacketAliasRedirectProto(struct in_addr, + struct in_addr, + struct in_addr, + u_char); extern struct alias_link * PacketAliasRedirectAddr(struct in_addr, struct in_addr); extern void PacketAliasRedirectDelete(struct alias_link *); /* Fragment Handling */ extern int PacketAliasSaveFragment(char *); extern char * PacketAliasGetFragment(char *); extern void PacketAliasFragmentIn(char *, char *); /* Miscellaneous Functions */ extern void PacketAliasSetTarget(struct in_addr addr); extern int PacketAliasCheckNewLink(void); extern u_short PacketAliasInternetChecksum(u_short *, int); /* Transparent Proxying */ extern int PacketAliasProxyRule(const char *); /********************** Mode flags ********************/ /* Set these flags using PacketAliasSetMode() */ /* If PKT_ALIAS_LOG is set, a message will be printed to /var/log/alias.log every time a link is created or deleted. This is useful for debugging */ #define PKT_ALIAS_LOG 0x01 /* If PKT_ALIAS_DENY_INCOMING is set, then incoming connections (e.g. to ftp, telnet or web servers will be prevented by the aliasing mechanism. */ #define PKT_ALIAS_DENY_INCOMING 0x02 /* If PKT_ALIAS_SAME_PORTS is set, packets will be attempted sent from the same port as they originated on. This allows e.g. rsh to work *99% of the time*, but _not_ 100%. (It will be slightly flakey instead of not working at all.) This mode bit is set by PacketAliasInit(), so it is a default mode of operation. */ #define PKT_ALIAS_SAME_PORTS 0x04 /* If PKT_ALIAS_USE_SOCKETS is set, then when partially specified links (e.g. destination port and/or address is zero), the packet aliasing engine will attempt to allocate a socket for the aliasing port it chooses. This will avoid interference with the host machine. Fully specified links do not require this. This bit is set after a call to PacketAliasInit(), so it is a default mode of operation. */ #define PKT_ALIAS_USE_SOCKETS 0x08 /* If PKT_ALIAS_UNREGISTERED_ONLY is set, then only packets with unregistered source addresses will be aliased. Private addresses are those in the following ranges: 10.0.0.0 -> 10.255.255.255 172.16.0.0 -> 172.31.255.255 192.168.0.0 -> 192.168.255.255 */ #define PKT_ALIAS_UNREGISTERED_ONLY 0x10 /* If PKT_ALIAS_RESET_ON_ADDR_CHANGE is set, then the table of dynamic aliasing links will be reset whenever PacketAliasSetAddress() changes the default aliasing address. If the default aliasing address is left unchanged by this function call, then the table of dynamic aliasing links will be left intact. This bit is set after a call to PacketAliasInit(). */ #define PKT_ALIAS_RESET_ON_ADDR_CHANGE 0x20 #ifndef NO_FW_PUNCH /* If PKT_ALIAS_PUNCH_FW is set, active FTP and IRC DCC connections will create a 'hole' in the firewall to allow the transfers to work. Where (IPFW "line-numbers") the hole is created is controlled by PacketAliasSetFWBase(base, size). The hole will be attached to that particular alias_link, so when the link goes away so do the hole. */ #define PKT_ALIAS_PUNCH_FW 0x100 #endif /* If PKT_ALIAS_PROXY_ONLY is set, then NAT will be disabled and only transparent proxying performed */ #define PKT_ALIAS_PROXY_ONLY 0x40 /* If PKT_ALIAS_REVERSE is set, the actions of PacketAliasIn() and PacketAliasOut() are reversed */ #define PKT_ALIAS_REVERSE 0x80 - -/* If PKT_ALIAS_DENY_PPTP is set, then PPTP sessions will be - prevented by the aliasing engine. */ -#define PKT_ALIAS_DENY_PPTP 0x200 /* Return Codes */ #define PKT_ALIAS_ERROR -1 #define PKT_ALIAS_OK 1 #define PKT_ALIAS_IGNORED 2 #define PKT_ALIAS_UNRESOLVED_FRAGMENT 3 #define PKT_ALIAS_FOUND_HEADER_FRAGMENT 4 #endif /*lint -restore */ Index: head/lib/libalias/alias_db.c =================================================================== --- head/lib/libalias/alias_db.c (revision 59725) +++ head/lib/libalias/alias_db.c (revision 59726) @@ -1,2536 +1,2539 @@ /* -*- mode: c; tab-width: 8; c-basic-indent: 4; -*- Alias_db.c encapsulates all data structures used for storing packet aliasing data. Other parts of the aliasing software access data through functions provided in this file. Data storage is based on the notion of a "link", which is established for ICMP echo/reply packets, UDP datagrams and TCP stream connections. A link stores the original source and destination addresses. For UDP and TCP, it also stores source and destination port numbers, as well as an alias port number. Links are also used to store information about fragments. There is a facility for sweeping through and deleting old links as new packets are sent through. A simple timeout is used for ICMP and UDP links. TCP links are left alone unless there is an incomplete connection, in which case the link can be deleted after a certain amount of time. This software is placed into the public domain with no restrictions on its distribution. Initial version: August, 1996 (cjm) Version 1.4: September 16, 1996 (cjm) Facility for handling incoming links added. Version 1.6: September 18, 1996 (cjm) ICMP data handling simplified. Version 1.7: January 9, 1997 (cjm) Fragment handling simplified. Saves pointers for unresolved fragments. Permits links for unspecified remote ports or unspecified remote addresses. Fixed bug which did not properly zero port table entries after a link was deleted. Cleaned up some obsolete comments. Version 1.8: January 14, 1997 (cjm) Fixed data type error in StartPoint(). (This error did not exist prior to v1.7 and was discovered and fixed by Ari Suutari) Version 1.9: February 1, 1997 Optionally, connections initiated from packet aliasing host machine will will not have their port number aliased unless it conflicts with an aliasing port already being used. (cjm) All options earlier being #ifdef'ed are now available through a new interface, SetPacketAliasMode(). This allows run time control (which is now available in PPP+pktAlias through the 'alias' keyword). (ee) Added ability to create an alias port without either destination address or port specified. port type = ALIAS_PORT_UNKNOWN_DEST_ALL (ee) Removed K&R style function headers and general cleanup. (ee) Added packetAliasMode to replace compiler #defines's (ee) Allocates sockets for partially specified ports if ALIAS_USE_SOCKETS defined. (cjm) Version 2.0: March, 1997 SetAliasAddress() will now clean up alias links if the aliasing address is changed. (cjm) PacketAliasPermanentLink() function added to support permanent links. (J. Fortes suggested the need for this.) Examples: (192.168.0.1, port 23) <-> alias port 6002, unknown dest addr/port (192.168.0.2, port 21) <-> alias port 3604, known dest addr unknown dest port These permanent links allow for incoming connections to machines on the local network. They can be given with a user-chosen amount of specificity, with increasing specificity meaning more security. (cjm) Quite a bit of rework to the basic engine. The portTable[] array, which kept track of which ports were in use was replaced by a table/linked list structure. (cjm) SetExpire() function added. (cjm) DeleteLink() no longer frees memory association with a pointer to a fragment (this bug was first recognized by E. Eklund in v1.9). Version 2.1: May, 1997 (cjm) Packet aliasing engine reworked so that it can handle multiple external addresses rather than just a single host address. PacketAliasRedirectPort() and PacketAliasRedirectAddr() added to the API. The first function is a more generalized version of PacketAliasPermanentLink(). The second function implements static network address translation. See HISTORY file for additional revisions. $FreeBSD$ */ /* System include files */ #include #include #include #include #include #include #include /* BSD network include files */ #include #include #include #include #include #include "alias.h" #include "alias_local.h" /* Constants (note: constants are also defined near relevant functions or structs) */ /* Sizes of input and output link tables */ #define LINK_TABLE_OUT_SIZE 101 #define LINK_TABLE_IN_SIZE 4001 /* Parameters used for cleanup of expired links */ #define ALIAS_CLEANUP_INTERVAL_SECS 60 #define ALIAS_CLEANUP_MAX_SPOKES 30 /* Timeouts (in seconds) for different link types */ #define ICMP_EXPIRE_TIME 60 #define UDP_EXPIRE_TIME 60 -#define PPTP_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 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, PPTP, frag */ + int link_type; /* Type of link: TCP, UDP, ICMP, proto, frag */ /* values for link_type */ -#define LINK_ICMP 1 -#define LINK_UDP 2 -#define LINK_TCP 3 -#define LINK_FRAGMENT_ID 4 -#define LINK_FRAGMENT_PTR 5 -#define LINK_ADDR 6 -#define LINK_PPTP 7 +#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) int flags; /* indicates special characteristics */ /* flag bits */ #define LINK_UNKNOWN_DEST_PORT 0x01 #define LINK_UNKNOWN_DEST_ADDR 0x02 #define LINK_PERMANENT 0x04 #define LINK_PARTIALLY_SPECIFIED 0x03 /* logical-or of first two bits */ #define LINK_UNFIREWALLED 0x08 int timestamp; /* Time link was last accessed */ int expire_time; /* Expire time for link */ int sockfd; /* socket descriptor */ u_int start_point_out; /* Index number in output lookup table */ u_int start_point_in; struct alias_link *next_out; /* Linked list pointers for input and */ struct alias_link *last_out; /* output tables */ struct alias_link *next_in; /* . */ struct alias_link *last_in; /* . */ union /* Auxiliary data */ { char *frag_ptr; struct in_addr frag_addr; struct tcp_dat *tcp; } data; }; /* Global Variables The global variables listed here are only accessed from within alias_db.c and so are prefixed with the static designation. */ int packetAliasMode; /* Mode flags */ /* - documented in alias.h */ static struct in_addr aliasAddress; /* Address written onto source */ /* field of IP packet. */ static struct in_addr targetAddress; /* IP address incoming packets */ /* are sent to if no aliasing */ /* link already exists */ static struct in_addr nullAddress; /* Used as a dummy parameter for */ /* some function calls */ static struct alias_link * linkTableOut[LINK_TABLE_OUT_SIZE]; /* Lookup table of pointers to */ /* chains of link records. Each */ static struct alias_link * /* link record is doubly indexed */ linkTableIn[LINK_TABLE_IN_SIZE]; /* into input and output lookup */ /* tables. */ static int icmpLinkCount; /* Link statistics */ static int udpLinkCount; static int tcpLinkCount; -static int pptpLinkCount; +static int protoLinkCount; static int fragmentIdLinkCount; static int fragmentPtrLinkCount; static int sockCount; static int cleanupIndex; /* Index to chain of link table */ /* being inspected for old links */ static int timeStamp; /* System time in seconds for */ /* current packet */ static int lastCleanupTime; /* Last time IncrementalCleanup() */ /* was called */ static int houseKeepingResidual; /* used by HouseKeeping() */ static int deleteAllLinks; /* If equal to zero, DeleteLink() */ /* will not remove permanent links */ static FILE *monitorFile; /* File descriptor for link */ /* statistics monitoring file */ static int newDefaultLink; /* Indicates if a new aliasing */ /* link has been created after a */ /* call to PacketAliasIn/Out(). */ #ifndef NO_FW_PUNCH static int fireWallFD = -1; /* File descriptor to be able to */ /* control firewall. Opened by */ /* PacketAliasSetMode on first */ /* setting the PKT_ALIAS_PUNCH_FW */ /* flag. */ #endif /* Internal utility routines (used only in alias_db.c) Lookup table starting points: StartPointIn() -- link table initial search point for incoming packets StartPointOut() -- link table initial search point for outgoing packets Miscellaneous: SeqDiff() -- difference between two TCP sequences ShowAliasStats() -- send alias statistics to a monitor file */ /* Local prototypes */ static u_int StartPointIn(struct in_addr, u_short, int); static u_int StartPointOut(struct in_addr, struct in_addr, u_short, u_short, int); static int SeqDiff(u_long, u_long); static void ShowAliasStats(void); #ifndef NO_FW_PUNCH /* Firewall control */ static void InitPunchFW(void); static void UninitPunchFW(void); static void ClearFWHole(struct alias_link *link); #endif /* Log file control */ static void InitPacketAliasLog(void); static void UninitPacketAliasLog(void); static u_int StartPointIn(struct in_addr alias_addr, u_short alias_port, int link_type) { u_int n; n = alias_addr.s_addr; n += alias_port; n += link_type; return(n % LINK_TABLE_IN_SIZE); } static u_int StartPointOut(struct in_addr src_addr, struct in_addr dst_addr, u_short src_port, u_short dst_port, int link_type) { u_int n; n = src_addr.s_addr; n += dst_addr.s_addr; n += src_port; n += dst_port; n += link_type; return(n % LINK_TABLE_OUT_SIZE); } static int SeqDiff(u_long x, u_long y) { /* Return the difference between two TCP sequence numbers */ /* This function is encapsulated in case there are any unusual arithmetic conditions that need to be considered. */ return (ntohl(y) - ntohl(x)); } static void ShowAliasStats(void) { /* Used for debugging */ if (monitorFile) { - fprintf(monitorFile, "icmp=%d, udp=%d, tcp=%d, pptp=%d, frag_id=%d frag_ptr=%d", + fprintf(monitorFile, "icmp=%d, udp=%d, tcp=%d, proto=%d, frag_id=%d frag_ptr=%d", icmpLinkCount, udpLinkCount, tcpLinkCount, - pptpLinkCount, + protoLinkCount, fragmentIdLinkCount, fragmentPtrLinkCount); fprintf(monitorFile, " / tot=%d (sock=%d)\n", icmpLinkCount + udpLinkCount + tcpLinkCount - + pptpLinkCount + + protoLinkCount + fragmentIdLinkCount + fragmentPtrLinkCount, sockCount); fflush(monitorFile); } } /* Internal routines for finding, deleting and adding links Port Allocation: GetNewPort() -- find and reserve new alias port number GetSocket() -- try to allocate a socket for a given port Link creation and deletion: CleanupAliasData() - remove all link chains from lookup table IncrementalCleanup() - look for stale links in a single chain DeleteLink() - remove link AddLink() - add link ReLink() - change link Link search: FindLinkOut() - find link for outgoing packets FindLinkIn() - find link for incoming packets */ /* Local prototypes */ static int GetNewPort(struct alias_link *, int); static u_short GetSocket(u_short, int *, int); static void CleanupAliasData(void); static void IncrementalCleanup(void); static void DeleteLink(struct alias_link *); static struct alias_link * AddLink(struct in_addr, struct in_addr, struct in_addr, u_short, u_short, int, int); static struct alias_link * ReLink(struct alias_link *, struct in_addr, struct in_addr, struct in_addr, u_short, u_short, int, int); static struct alias_link * FindLinkOut(struct in_addr, struct in_addr, u_short, u_short, int, int); static struct alias_link * FindLinkIn(struct in_addr, struct in_addr, u_short, u_short, int, int); #define ALIAS_PORT_BASE 0x08000 #define ALIAS_PORT_MASK 0x07fff #define GET_NEW_PORT_MAX_ATTEMPTS 20 #define GET_ALIAS_PORT -1 #define GET_ALIAS_ID GET_ALIAS_PORT /* GetNewPort() allocates port numbers. Note that if a port number is already in use, that does not mean that it cannot be used by another link concurrently. This is because GetNewPort() looks for unused triplets: (dest addr, dest port, alias port). */ static int GetNewPort(struct alias_link *link, int alias_port_param) { int i; int max_trials; u_short port_sys; u_short port_net; /* Description of alias_port_param for GetNewPort(). When this parameter is zero or positive, it precisely specifies the port number. GetNewPort() will return this number without check that it is in use. When this parameter is -1, it indicates to get a randomly selected port number. */ if (alias_port_param == GET_ALIAS_PORT) { /* * The aliasing port is automatically selected * by one of two methods below: */ max_trials = GET_NEW_PORT_MAX_ATTEMPTS; if (packetAliasMode & PKT_ALIAS_SAME_PORTS) { /* * When the 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; idst_addr, link->alias_addr, link->dst_port, port_net, link->link_type, 0); if (search_result == NULL) go_ahead = 1; else if (!(link->flags & LINK_PARTIALLY_SPECIFIED) && (search_result->flags & LINK_PARTIALLY_SPECIFIED)) go_ahead = 1; else go_ahead = 0; if (go_ahead) { if ((packetAliasMode & PKT_ALIAS_USE_SOCKETS) && (link->flags & LINK_PARTIALLY_SPECIFIED)) { if (GetSocket(port_net, &link->sockfd, link->link_type)) { link->alias_port = port_net; return(0); } } else { link->alias_port = port_net; return(0); } } port_sys = random() & ALIAS_PORT_MASK; port_sys += ALIAS_PORT_BASE; port_net = htons(port_sys); } #ifdef DEBUG fprintf(stderr, "PacketAlias/GetnewPort(): "); fprintf(stderr, "could not find free port\n"); #endif return(-1); } static u_short GetSocket(u_short port_net, int *sockfd, int link_type) { int err; int sock; struct sockaddr_in sock_addr; if (link_type == LINK_TCP) sock = socket(AF_INET, SOCK_STREAM, 0); else if (link_type == LINK_UDP) sock = socket(AF_INET, SOCK_DGRAM, 0); else { #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) { sockCount++; *sockfd = sock; return(1); } else { close(sock); return(0); } } static void CleanupAliasData(void) { struct alias_link *link; int i, icount; icount = 0; for (i=0; inext_out; icount++; DeleteLink(link); link = link_next; } } cleanupIndex =0; } static void IncrementalCleanup(void) { int icount; struct alias_link *link; icount = 0; link = linkTableOut[cleanupIndex++]; while (link != NULL) { int idelta; struct alias_link *link_next; link_next = link->next_out; idelta = timeStamp - link->timestamp; switch (link->link_type) { - case LINK_ICMP: - case LINK_UDP: - case LINK_FRAGMENT_ID: - case LINK_FRAGMENT_PTR: - case LINK_PPTP: - if (idelta > link->expire_time) - { - DeleteLink(link); - icount++; - } - break; case LINK_TCP: if (idelta > link->expire_time) { struct tcp_dat *tcp_aux; tcp_aux = link->data.tcp; if (tcp_aux->state.in != ALIAS_TCP_STATE_CONNECTED || tcp_aux->state.out != ALIAS_TCP_STATE_CONNECTED) { DeleteLink(link); icount++; } } break; + default: + if (idelta > link->expire_time) + { + DeleteLink(link); + icount++; + } + break; } link = link_next; } if (cleanupIndex == LINK_TABLE_OUT_SIZE) cleanupIndex = 0; } void DeleteLink(struct alias_link *link) { struct alias_link *link_last; struct alias_link *link_next; /* Don't do anything if the link is marked permanent */ if (deleteAllLinks == 0 && link->flags & LINK_PERMANENT) return; #ifndef NO_FW_PUNCH /* Delete 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 */ link_last = link->last_out; link_next = link->next_out; if (link_last != NULL) link_last->next_out = link_next; else linkTableOut[link->start_point_out] = link_next; if (link_next != NULL) link_next->last_out = link_last; /* Adjust input table pointers */ link_last = link->last_in; link_next = link->next_in; if (link_last != NULL) link_last->next_in = link_next; else linkTableIn[link->start_point_in] = link_next; if (link_next != NULL) link_next->last_in = link_last; /* Close socket, if one has been allocated */ if (link->sockfd != -1) { sockCount--; close(link->sockfd); } /* Link-type dependent cleanup */ switch(link->link_type) { case LINK_ICMP: icmpLinkCount--; break; case LINK_UDP: udpLinkCount--; break; case LINK_TCP: tcpLinkCount--; if (link->data.tcp != NULL) free(link->data.tcp); break; - case LINK_PPTP: - pptpLinkCount--; - break; case LINK_FRAGMENT_ID: fragmentIdLinkCount--; break; case LINK_FRAGMENT_PTR: fragmentPtrLinkCount--; if (link->data.frag_ptr != NULL) free(link->data.frag_ptr); break; + case LINK_ADDR: + break; + default: + protoLinkCount--; + break; } /* Free memory */ free(link); /* Write statistics, if logging enabled */ if (packetAliasMode & PKT_ALIAS_LOG) { ShowAliasStats(); } } static struct alias_link * AddLink(struct in_addr src_addr, struct in_addr dst_addr, struct in_addr alias_addr, u_short src_port, u_short dst_port, int alias_port_param, /* if less than zero, alias */ int link_type) /* port will be automatically */ { /* chosen. If greater than */ u_int start_point; /* zero, equal to alias port */ struct alias_link *link; struct alias_link *first_link; link = malloc(sizeof(struct alias_link)); if (link != NULL) { /* Basic initialization */ 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->timestamp = timeStamp; /* Expiration time */ switch (link_type) { case LINK_ICMP: link->expire_time = ICMP_EXPIRE_TIME; break; case LINK_UDP: link->expire_time = UDP_EXPIRE_TIME; break; case LINK_TCP: link->expire_time = TCP_EXPIRE_INITIAL; break; - case LINK_PPTP: - link->expire_time = PPTP_EXPIRE_TIME; - break; case LINK_FRAGMENT_ID: link->expire_time = FRAGMENT_ID_EXPIRE_TIME; break; case LINK_FRAGMENT_PTR: link->expire_time = FRAGMENT_PTR_EXPIRE_TIME; break; + 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(link, alias_port_param) != 0) { free(link); return(NULL); } /* Set up pointers for output lookup table */ start_point = StartPointOut(src_addr, dst_addr, src_port, dst_port, link_type); first_link = linkTableOut[start_point]; link->last_out = NULL; link->next_out = first_link; link->start_point_out = start_point; if (first_link != NULL) first_link->last_out = link; linkTableOut[start_point] = link; /* Set up pointers for input lookup table */ start_point = StartPointIn(alias_addr, link->alias_port, link_type); first_link = linkTableIn[start_point]; link->last_in = NULL; link->next_in = first_link; link->start_point_in = start_point; if (first_link != NULL) first_link->last_in = link; linkTableIn[start_point] = link; /* Link-type dependent initialization */ switch(link_type) { struct tcp_dat *aux_tcp; case LINK_ICMP: icmpLinkCount++; break; case LINK_UDP: udpLinkCount++; break; case LINK_TCP: aux_tcp = malloc(sizeof(struct tcp_dat)); link->data.tcp = aux_tcp; if (aux_tcp != NULL) { int i; tcpLinkCount++; aux_tcp->state.in = ALIAS_TCP_STATE_NOT_CONNECTED; aux_tcp->state.out = ALIAS_TCP_STATE_NOT_CONNECTED; aux_tcp->state.index = 0; aux_tcp->state.ack_modified = 0; for (i=0; iack[i].active = 0; aux_tcp->fwhole = -1; } else { #ifdef DEBUG fprintf(stderr, "PacketAlias/AddLink: "); fprintf(stderr, " cannot allocate auxiliary TCP data\n"); #endif } break; - case LINK_PPTP: - pptpLinkCount++; - break; case LINK_FRAGMENT_ID: fragmentIdLinkCount++; break; case LINK_FRAGMENT_PTR: fragmentPtrLinkCount++; break; + case LINK_ADDR: + break; + default: + protoLinkCount++; + break; } } else { #ifdef DEBUG fprintf(stderr, "PacketAlias/AddLink(): "); fprintf(stderr, "malloc() call failed.\n"); #endif } if (packetAliasMode & PKT_ALIAS_LOG) { ShowAliasStats(); } return(link); } static struct alias_link * ReLink(struct alias_link *old_link, struct in_addr src_addr, struct in_addr dst_addr, struct in_addr alias_addr, u_short src_port, u_short dst_port, int alias_port_param, /* if less than zero, alias */ int link_type) /* port will be automatically */ { /* chosen. If greater than */ struct alias_link *new_link; /* zero, equal to alias port */ new_link = AddLink(src_addr, dst_addr, alias_addr, src_port, dst_port, alias_port_param, link_type); #ifndef NO_FW_PUNCH if (new_link != NULL && old_link->link_type == LINK_TCP && old_link->data.tcp && old_link->data.tcp->fwhole > 0) { PunchFWHole(new_link); } #endif DeleteLink(old_link); return new_link; } static struct alias_link * _FindLinkOut(struct in_addr src_addr, struct in_addr dst_addr, u_short src_port, u_short dst_port, int link_type, int replace_partial_links) { u_int i; struct alias_link *link; i = StartPointOut(src_addr, dst_addr, src_port, dst_port, link_type); link = linkTableOut[i]; while (link != NULL) { if (link->src_addr.s_addr == src_addr.s_addr && link->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 = timeStamp; break; } link = link->next_out; } /* Search for partially specified links. */ if (link == NULL && replace_partial_links) { if (dst_port != 0 && dst_addr.s_addr != INADDR_ANY) { link = _FindLinkOut(src_addr, dst_addr, src_port, 0, link_type, 0); if (link == NULL) link = _FindLinkOut(src_addr, nullAddress, src_port, dst_port, link_type, 0); } if (link == NULL && (dst_port != 0 || dst_addr.s_addr != INADDR_ANY)) { link = _FindLinkOut(src_addr, 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 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(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 (aliasAddress.s_addr != 0 && src_addr.s_addr == aliasAddress.s_addr) { link = _FindLinkOut(nullAddress, dst_addr, src_port, dst_port, link_type, replace_partial_links); } } return(link); } static struct alias_link * _FindLinkIn(struct in_addr dst_addr, struct in_addr alias_addr, u_short dst_port, u_short alias_port, int link_type, int replace_partial_links) { int flags_in; u_int start_point; struct alias_link *link; struct alias_link *link_fully_specified; struct alias_link *link_unknown_all; struct alias_link *link_unknown_dst_addr; struct alias_link *link_unknown_dst_port; /* Initialize pointers */ link_fully_specified = NULL; link_unknown_all = NULL; link_unknown_dst_addr = NULL; link_unknown_dst_port = NULL; /* If either the dest addr or port is unknown, the search loop will have to know about this. */ flags_in = 0; if (dst_addr.s_addr == 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); link = linkTableIn[start_point]; while (link != NULL) { int flags; flags = flags_in | link->flags; if (!(flags & LINK_PARTIALLY_SPECIFIED)) { if (link->alias_addr.s_addr == alias_addr.s_addr && link->alias_port == alias_port && link->dst_addr.s_addr == dst_addr.s_addr && link->dst_port == dst_port && link->link_type == link_type) { link_fully_specified = link; break; } } else if ((flags & LINK_UNKNOWN_DEST_ADDR) && (flags & LINK_UNKNOWN_DEST_PORT)) { if (link->alias_addr.s_addr == alias_addr.s_addr && link->alias_port == alias_port && link->link_type == link_type) { if (link_unknown_all == NULL) link_unknown_all = link; } } else if (flags & LINK_UNKNOWN_DEST_ADDR) { if (link->alias_addr.s_addr == alias_addr.s_addr && link->alias_port == alias_port && link->link_type == link_type && link->dst_port == dst_port) { if (link_unknown_dst_addr == NULL) link_unknown_dst_addr = link; } } else if (flags & LINK_UNKNOWN_DEST_PORT) { if (link->alias_addr.s_addr == alias_addr.s_addr && link->alias_port == alias_port && link->link_type == link_type && link->dst_addr.s_addr == dst_addr.s_addr) { if (link_unknown_dst_port == NULL) link_unknown_dst_port = link; } } link = link->next_in; } if (link_fully_specified != NULL) { link_fully_specified->timestamp = 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 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(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 (aliasAddress.s_addr != 0 && alias_addr.s_addr == aliasAddress.s_addr) { link = _FindLinkIn(dst_addr, 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() - FindPptpIn(), FindPptpOut() + FindProtoIn(), FindProtoOut() FindUdpTcpIn(), FindUdpTcpOut() FindOriginalAddress(), FindAliasAddress() (prototypes in alias_local.h) */ struct alias_link * FindIcmpIn(struct in_addr dst_addr, struct in_addr alias_addr, u_short id_alias) { return FindLinkIn(dst_addr, alias_addr, NO_DEST_PORT, id_alias, LINK_ICMP, 0); } struct alias_link * FindIcmpOut(struct in_addr src_addr, struct in_addr dst_addr, u_short id) { struct alias_link * link; link = FindLinkOut(src_addr, dst_addr, id, NO_DEST_PORT, LINK_ICMP, 0); if (link == NULL) { struct in_addr alias_addr; alias_addr = FindAliasAddress(src_addr); link = AddLink(src_addr, dst_addr, alias_addr, id, NO_DEST_PORT, GET_ALIAS_ID, LINK_ICMP); } return(link); } struct alias_link * FindFragmentIn1(struct in_addr dst_addr, struct in_addr alias_addr, u_short ip_id) { struct alias_link *link; link = FindLinkIn(dst_addr, alias_addr, NO_DEST_PORT, ip_id, LINK_FRAGMENT_ID, 0); if (link == NULL) { link = AddLink(nullAddress, dst_addr, alias_addr, NO_SRC_PORT, NO_DEST_PORT, ip_id, LINK_FRAGMENT_ID); } return(link); } struct alias_link * FindFragmentIn2(struct in_addr dst_addr, /* Doesn't add a link if one */ struct in_addr alias_addr, /* is not found. */ u_short ip_id) { return FindLinkIn(dst_addr, alias_addr, NO_DEST_PORT, ip_id, LINK_FRAGMENT_ID, 0); } struct alias_link * AddFragmentPtrLink(struct in_addr dst_addr, u_short ip_id) { return AddLink(nullAddress, dst_addr, nullAddress, NO_SRC_PORT, NO_DEST_PORT, ip_id, LINK_FRAGMENT_PTR); } struct alias_link * FindFragmentPtr(struct in_addr dst_addr, u_short ip_id) { return FindLinkIn(dst_addr, nullAddress, NO_DEST_PORT, ip_id, LINK_FRAGMENT_PTR, 0); } struct alias_link * -FindPptpIn(struct in_addr dst_addr, - struct in_addr alias_addr) +FindProtoIn(struct in_addr dst_addr, + struct in_addr alias_addr, + u_char proto) { struct alias_link *link; link = FindLinkIn(dst_addr, alias_addr, NO_DEST_PORT, 0, - LINK_PPTP, 1); + proto, 1); if (link == NULL && !(packetAliasMode & PKT_ALIAS_DENY_INCOMING)) { struct in_addr target_addr; target_addr = FindOriginalAddress(alias_addr); link = AddLink(target_addr, dst_addr, alias_addr, NO_SRC_PORT, NO_DEST_PORT, 0, - LINK_PPTP); + proto); } return (link); } struct alias_link * -FindPptpOut(struct in_addr src_addr, - struct in_addr dst_addr) +FindProtoOut(struct in_addr src_addr, + struct in_addr dst_addr, + u_char proto) { struct alias_link *link; link = FindLinkOut(src_addr, dst_addr, NO_SRC_PORT, NO_DEST_PORT, - LINK_PPTP, 1); + proto, 1); if (link == NULL) { struct in_addr alias_addr; alias_addr = FindAliasAddress(src_addr); link = AddLink(src_addr, dst_addr, alias_addr, NO_SRC_PORT, NO_DEST_PORT, 0, - LINK_PPTP); + proto); } return (link); } struct alias_link * FindUdpTcpIn(struct in_addr dst_addr, struct in_addr alias_addr, u_short dst_port, u_short alias_port, u_char proto) { int link_type; struct alias_link *link; switch (proto) { case IPPROTO_UDP: link_type = LINK_UDP; break; case IPPROTO_TCP: link_type = LINK_TCP; break; default: return NULL; break; } link = FindLinkIn(dst_addr, alias_addr, dst_port, alias_port, link_type, 1); if (!(packetAliasMode & PKT_ALIAS_DENY_INCOMING) && !(packetAliasMode & PKT_ALIAS_PROXY_ONLY) && link == NULL) { struct in_addr target_addr; target_addr = FindOriginalAddress(alias_addr); link = AddLink(target_addr, dst_addr, alias_addr, alias_port, dst_port, alias_port, link_type); } return(link); } struct alias_link * FindUdpTcpOut(struct in_addr src_addr, struct in_addr dst_addr, u_short src_port, u_short dst_port, u_char proto) { int link_type; struct alias_link *link; switch (proto) { case IPPROTO_UDP: link_type = LINK_UDP; break; case IPPROTO_TCP: link_type = LINK_TCP; break; default: return NULL; break; } link = FindLinkOut(src_addr, dst_addr, src_port, dst_port, link_type, 1); if (link == NULL) { struct in_addr alias_addr; alias_addr = FindAliasAddress(src_addr); link = AddLink(src_addr, dst_addr, alias_addr, src_port, dst_port, GET_ALIAS_PORT, link_type); } return(link); } struct in_addr FindOriginalAddress(struct in_addr alias_addr) { struct alias_link *link; link = FindLinkIn(nullAddress, alias_addr, 0, 0, LINK_ADDR, 0); if (link == NULL) { newDefaultLink = 1; if (targetAddress.s_addr == INADDR_ANY) return alias_addr; else if (targetAddress.s_addr == INADDR_NONE) return aliasAddress; else return 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 aliasAddress; else return link->src_addr; } } struct in_addr FindAliasAddress(struct in_addr original_addr) { struct alias_link *link; link = FindLinkOut(original_addr, nullAddress, 0, 0, LINK_ADDR, 0); if (link == NULL) { return aliasAddress; } else { if (link->alias_addr.s_addr == INADDR_ANY) return aliasAddress; else return link->alias_addr; } } /* External routines for getting or changing link data (external to alias_db.c, but internal to alias*.c) SetFragmentData(), GetFragmentData() SetFragmentPtr(), GetFragmentPtr() SetStateIn(), SetStateOut(), GetStateIn(), GetStateOut() GetOriginalAddress(), GetDestAddress(), GetAliasAddress() GetOriginalPort(), GetAliasPort() SetAckModified(), GetAckModified() GetDeltaAckIn(), GetDeltaSeqOut(), AddSeq() */ void SetFragmentAddr(struct alias_link *link, struct in_addr src_addr) { link->data.frag_addr = src_addr; } void GetFragmentAddr(struct alias_link *link, struct in_addr *src_addr) { *src_addr = link->data.frag_addr; } void SetFragmentPtr(struct alias_link *link, char *fptr) { link->data.frag_ptr = fptr; } void GetFragmentPtr(struct alias_link *link, char **fptr) { *fptr = link->data.frag_ptr; } void SetStateIn(struct alias_link *link, int state) { /* TCP input state */ switch (state) { case ALIAS_TCP_STATE_DISCONNECTED: if (link->data.tcp->state.out != ALIAS_TCP_STATE_CONNECTED) link->expire_time = TCP_EXPIRE_DEAD; else link->expire_time = TCP_EXPIRE_SINGLEDEAD; 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 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 aliasAddress; else return link->alias_addr; } struct in_addr GetDefaultAliasAddress() { return aliasAddress; } void SetDefaultAliasAddress(struct in_addr alias_addr) { aliasAddress = alias_addr; } u_short GetOriginalPort(struct alias_link *link) { return(link->src_port); } u_short GetAliasPort(struct alias_link *link) { return(link->alias_port); } #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; idata.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; idata.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(void) { newDefaultLink = 0; } /* Miscellaneous Functions HouseKeeping() InitPacketAliasLog() UninitPacketAliasLog() */ /* Whenever an outgoing or incoming packet is handled, HouseKeeping() is called to find and remove timed-out aliasing links. Logic exists to sweep through the entire table and linked list structure every 60 seconds. (prototype in alias_local.h) */ void HouseKeeping(void) { int i, n, n100; struct timeval tv; struct timezone tz; /* * Save system time (seconds) in global variable timeStamp for * use by other functions. This is done so as not to unnecessarily * waste timeline by making system calls. */ gettimeofday(&tv, &tz); timeStamp = tv.tv_sec; /* Compute number of spokes (output table link chains) to cover */ n100 = LINK_TABLE_OUT_SIZE * 100 + houseKeepingResidual; n100 *= timeStamp - lastCleanupTime; n100 /= ALIAS_CLEANUP_INTERVAL_SECS; n = n100/100; /* Handle different cases */ if (n > ALIAS_CLEANUP_MAX_SPOKES) { n = ALIAS_CLEANUP_MAX_SPOKES; lastCleanupTime = timeStamp; houseKeepingResidual = 0; for (i=0; i 0) { lastCleanupTime = timeStamp; houseKeepingResidual = n100 - 100*n; for (i=0; iflags |= LINK_PERMANENT; } #ifdef DEBUG else { fprintf(stderr, "PacketAliasRedirectPort(): " "call to AddLink() failed\n"); } #endif return link; } /* Add server to the pool of servers */ int PacketAliasAddServer(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); } /* Translate PPTP packets to a machine on the inside - * XXX This function is made obsolete by PacketAliasRedirectPptp(). + * XXX This function is made obsolete by PacketAliasRedirectProto(). */ int PacketAliasPptp(struct in_addr src_addr) { - if (src_addr.s_addr == INADDR_NONE) - packetAliasMode |= PKT_ALIAS_DENY_PPTP; - else - (void)PacketAliasRedirectPptp(src_addr, nullAddress, nullAddress); + if (src_addr.s_addr != INADDR_NONE) + (void)PacketAliasRedirectProto(src_addr, nullAddress, nullAddress, + IPPROTO_GRE); return 1; } -/* Redirect PPTP packets from a specific +/* Redirect packets of a given IP protocol from a specific public address to a private address */ struct alias_link * -PacketAliasRedirectPptp(struct in_addr src_addr, - struct in_addr dst_addr, - struct in_addr alias_addr) +PacketAliasRedirectProto(struct in_addr src_addr, + struct in_addr dst_addr, + struct in_addr alias_addr, + u_char proto) { struct alias_link *link; link = AddLink(src_addr, dst_addr, alias_addr, NO_SRC_PORT, NO_DEST_PORT, 0, - LINK_PPTP); + proto); if (link != NULL) { link->flags |= LINK_PERMANENT; } #ifdef DEBUG else { - fprintf(stderr, "PacketAliasRedirectPptp(): " + fprintf(stderr, "PacketAliasRedirectProto(): " "call to AddLink() failed\n"); } #endif return link; } /* Static address translation */ struct alias_link * PacketAliasRedirectAddr(struct in_addr src_addr, struct in_addr alias_addr) { struct alias_link *link; link = AddLink(src_addr, nullAddress, alias_addr, 0, 0, 0, LINK_ADDR); if (link != NULL) { link->flags |= LINK_PERMANENT; } #ifdef DEBUG else { fprintf(stderr, "PacketAliasRedirectAddr(): " "call to AddLink() failed\n"); } #endif return link; } void PacketAliasRedirectDelete(struct alias_link *link) { /* This is a dangerous function to put in the API, because an invalid pointer can crash the program. */ deleteAllLinks = 1; DeleteLink(link); deleteAllLinks = 0; } void PacketAliasSetAddress(struct in_addr addr) { if (packetAliasMode & PKT_ALIAS_RESET_ON_ADDR_CHANGE && aliasAddress.s_addr != addr.s_addr) CleanupAliasData(); aliasAddress = addr; } void PacketAliasSetTarget(struct in_addr target_addr) { targetAddress = target_addr; } void PacketAliasInit(void) { int i; struct timeval tv; struct timezone tz; static int firstCall = 1; if (firstCall == 1) { gettimeofday(&tv, &tz); timeStamp = tv.tv_sec; lastCleanupTime = tv.tv_sec; houseKeepingResidual = 0; for (i=0; i #include #include #include #include static void ClearAllFWHoles(void); static int fireWallBaseNum; /* The first firewall entry free for our use */ static int fireWallNumNums; /* How many entries can we use? */ static int fireWallActiveNum; /* Which entry did we last use? */ static char *fireWallField; /* bool array for entries */ #define fw_setfield(field, num) \ do { \ (field)[num] = 1; \ } /*lint -save -e717 */ while(0) /*lint -restore */ #define fw_clrfield(field, num) \ do { \ (field)[num] = 0; \ } /*lint -save -e717 */ while(0) /*lint -restore */ #define fw_tstfield(field, num) ((field)[num]) void PacketAliasSetFWBase(unsigned int base, unsigned int num) { fireWallBaseNum = base; fireWallNumNums = num; } static void InitPunchFW(void) { fireWallField = malloc(fireWallNumNums); if (fireWallField) { memset(fireWallField, 0, fireWallNumNums); if (fireWallFD < 0) { fireWallFD = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); } ClearAllFWHoles(); fireWallActiveNum = fireWallBaseNum; } } static void UninitPunchFW(void) { ClearAllFWHoles(); if (fireWallFD >= 0) close(fireWallFD); fireWallFD = -1; if (fireWallField) free(fireWallField); fireWallField = NULL; packetAliasMode &= ~PKT_ALIAS_PUNCH_FW; } /* Make a certain link go through the firewall */ void PunchFWHole(struct alias_link *link) { int r; /* Result code */ struct ip_fw rule; /* On-the-fly built rule */ int fwhole; /* Where to punch hole */ /* Don't do anything unless we are asked to */ if ( !(packetAliasMode & PKT_ALIAS_PUNCH_FW) || fireWallFD < 0 || link->link_type != LINK_TCP || !link->data.tcp) return; memset(&rule, 0, sizeof rule); /** Build rule **/ /* Find empty slot */ for (fwhole = fireWallActiveNum; fwhole < fireWallBaseNum + fireWallNumNums && fw_tstfield(fireWallField, fwhole); fwhole++) ; if (fwhole >= fireWallBaseNum + fireWallNumNums || fw_tstfield(fireWallField, fwhole)) { for (fwhole = fireWallBaseNum; fwhole < fireWallActiveNum && fw_tstfield(fireWallField, fwhole); fwhole++) ; if (fwhole == fireWallActiveNum) { /* No rule point empty - we can't punch more holes. */ fireWallActiveNum = fireWallBaseNum; #ifdef DEBUG fprintf(stderr, "libalias: Unable to create firewall hole!\n"); #endif return; } } /* Start next search at next position */ fireWallActiveNum = fwhole+1; /* Build generic part of the two rules */ rule.fw_number = fwhole; rule.fw_nports = 1; /* Number of source ports; dest ports follow */ rule.fw_flg = IP_FW_F_ACCEPT; rule.fw_prot = IPPROTO_TCP; rule.fw_smsk.s_addr = INADDR_BROADCAST; rule.fw_dmsk.s_addr = INADDR_BROADCAST; /* Build and apply specific part of the rules */ rule.fw_src = GetOriginalAddress(link); rule.fw_dst = GetDestAddress(link); rule.fw_uar.fw_pts[0] = ntohs(GetOriginalPort(link)); rule.fw_uar.fw_pts[1] = ntohs(GetDestPort(link)); /* Skip non-bound links - XXX should not be strictly necessary, but seems to leave hole if not done. Leak of non-bound links? (Code should be left even if the problem is fixed - it is a clear optimization) */ if (rule.fw_uar.fw_pts[0] != 0 && rule.fw_uar.fw_pts[1] != 0) { r = setsockopt(fireWallFD, IPPROTO_IP, IP_FW_ADD, &rule, sizeof rule); #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 } /* Indicate hole applied */ link->data.tcp->fwhole = fwhole; fw_setfield(fireWallField, fwhole); } /* Remove a hole in a firewall associated with a particular alias link. Calling this too often is harmless. */ static void ClearFWHole(struct alias_link *link) { if (link->link_type == LINK_TCP && link->data.tcp) { int fwhole = link->data.tcp->fwhole; /* Where is the firewall hole? */ struct ip_fw rule; if (fwhole < 0) return; memset(&rule, 0, sizeof rule); rule.fw_number = fwhole; while (!setsockopt(fireWallFD, IPPROTO_IP, IP_FW_DEL, &rule, sizeof rule)) ; fw_clrfield(fireWallField, fwhole); link->data.tcp->fwhole = -1; } } /* Clear out the entire range dedicated to firewall holes. */ static void ClearAllFWHoles(void) { struct ip_fw rule; /* On-the-fly built rule */ int i; if (fireWallFD < 0) return; memset(&rule, 0, sizeof rule); for (i = fireWallBaseNum; i < fireWallBaseNum + fireWallNumNums; i++) { rule.fw_number = i; while (!setsockopt(fireWallFD, IPPROTO_IP, IP_FW_DEL, &rule, sizeof rule)) ; } memset(fireWallField, 0, fireWallNumNums); } #endif Index: head/lib/libalias/alias_local.h =================================================================== --- head/lib/libalias/alias_local.h (revision 59725) +++ head/lib/libalias/alias_local.h (revision 59726) @@ -1,179 +1,179 @@ /* -*- mode: c; tab-width: 3; c-basic-offset: 3; -*- Alias_local.h contains the function prototypes for alias.c, alias_db.c, alias_util.c and alias_ftp.c, alias_irc.c (as well as any future add-ons). It also includes macros, globals and struct definitions shared by more than one alias*.c file. This include file is intended to be used only within the aliasing software. Outside world interfaces are defined in alias.h This software is placed into the public domain with no restrictions on its distribution. Initial version: August, 1996 (cjm) $FreeBSD$ */ #ifndef ALIAS_LOCAL_H #define ALIAS_LOCAL_H #ifndef NULL #define NULL 0 #endif /* Macros */ /* The following macro is used to update an internet checksum. "delta" is a 32-bit accumulation of all the changes to the checksum (adding in new 16-bit words and subtracting out old words), and "cksum" is the checksum value to be updated. */ #define ADJUST_CHECKSUM(acc, cksum) { \ acc += cksum; \ if (acc < 0) \ { \ acc = -acc; \ acc = (acc >> 16) + (acc & 0xffff); \ acc += acc >> 16; \ cksum = (u_short) ~acc; \ } \ else \ { \ acc = (acc >> 16) + (acc & 0xffff); \ acc += acc >> 16; \ cksum = (u_short) acc; \ } \ } /* Globals */ extern int packetAliasMode; /* Structs */ struct alias_link; /* Incomplete structure */ /* Prototypes */ /* General utilities */ u_short IpChecksum(struct ip *); u_short TcpChecksum(struct ip *); void DifferentialChecksum(u_short *, u_short *, u_short *, int); /* Internal data access */ struct alias_link * FindIcmpIn(struct in_addr, struct in_addr, u_short); struct alias_link * FindIcmpOut(struct in_addr, struct in_addr, u_short); struct alias_link * FindFragmentIn1(struct in_addr, struct in_addr, u_short); struct alias_link * FindFragmentIn2(struct in_addr, struct in_addr, u_short); struct alias_link * AddFragmentPtrLink(struct in_addr, u_short); struct alias_link * FindFragmentPtr(struct in_addr, u_short); struct alias_link * -FindPptpIn(struct in_addr, struct in_addr); +FindProtoIn(struct in_addr, struct in_addr, u_char); struct alias_link * -FindPptpOut(struct in_addr, struct in_addr); +FindProtoOut(struct in_addr, struct in_addr, u_char); struct alias_link * FindUdpTcpIn (struct in_addr, struct in_addr, u_short, u_short, u_char); struct alias_link * FindUdpTcpOut(struct in_addr, struct in_addr, u_short, u_short, u_char); struct in_addr FindOriginalAddress(struct in_addr); struct in_addr FindAliasAddress(struct in_addr); /* External data access/modification */ void GetFragmentAddr(struct alias_link *, struct in_addr *); void SetFragmentAddr(struct alias_link *, struct in_addr); void GetFragmentPtr(struct alias_link *, char **); void SetFragmentPtr(struct alias_link *, char *); void SetStateIn(struct alias_link *, int); void SetStateOut(struct alias_link *, int); int GetStateIn(struct alias_link *); int GetStateOut(struct alias_link *); struct in_addr GetOriginalAddress(struct alias_link *); struct in_addr GetDestAddress(struct alias_link *); struct in_addr GetAliasAddress(struct alias_link *); struct in_addr GetDefaultAliasAddress(void); void SetDefaultAliasAddress(struct in_addr); u_short GetOriginalPort(struct alias_link *); u_short GetAliasPort(struct alias_link *); struct in_addr GetProxyAddress(struct alias_link *); void SetProxyAddress(struct alias_link *, struct in_addr); u_short GetProxyPort(struct alias_link *); void SetProxyPort(struct alias_link *, u_short); void SetAckModified(struct alias_link *); int GetAckModified(struct alias_link *); int GetDeltaAckIn(struct ip *, struct alias_link *); int GetDeltaSeqOut(struct ip *, struct alias_link *); void AddSeq(struct ip *, struct alias_link *, int); void SetExpire(struct alias_link *, int); void ClearCheckNewLink(void); #ifndef NO_FW_PUNCH void PunchFWHole(struct alias_link *); #endif /* Housekeeping function */ void HouseKeeping(void); /* Tcp specfic routines */ /*lint -save -library Suppress flexelint warnings */ /* FTP routines */ void AliasHandleFtpOut(struct ip *, struct alias_link *, int); /* IRC routines */ void AliasHandleIrcOut(struct ip *, struct alias_link *, int); /* NetBIOS routines */ int AliasHandleUdpNbt(struct ip *, struct alias_link *, struct in_addr *, u_short); int AliasHandleUdpNbtNS(struct ip *, struct alias_link *, struct in_addr *, u_short *, struct in_addr *, u_short *); /* CUSeeMe routines */ void AliasHandleCUSeeMeOut(struct ip *, struct alias_link *); void AliasHandleCUSeeMeIn(struct ip *, struct in_addr); /* Transparent proxy routines */ int ProxyCheck(struct ip *, struct in_addr *, u_short *); void ProxyModify(struct alias_link *, struct ip *, int, int); enum alias_tcp_state { ALIAS_TCP_STATE_NOT_CONNECTED, ALIAS_TCP_STATE_CONNECTED, ALIAS_TCP_STATE_DISCONNECTED }; /*lint -restore */ #endif /* defined(ALIAS_LOCAL_H) */ Index: head/lib/libalias/libalias.3 =================================================================== --- head/lib/libalias/libalias.3 (revision 59725) +++ head/lib/libalias/libalias.3 (revision 59726) @@ -1,984 +1,965 @@ .\" $FreeBSD$ .\" .Dd April 13, 2000 .Dt LIBALIAS 3 .Os FreeBSD .Sh NAME .Nm libalias .Nd packet aliasing library for masquerading and network address translation .Sh SYNOPSIS .Fd #include .Fd #include .Fd #include .Pp Function prototypes are given in the main body of the text. .Sh DESCRIPTION The .Nm library is a collection of functions for aliasing and de-aliasing of IP packets, intended for masquerading and network address translation (NAT). .Sh INTRODUCTION This library is a moderately portable set of functions designed to assist in the process of IP masquerading and network address translation. Outgoing packets from a local network with unregistered IP addresses can be aliased to appear as if they came from an accessible IP address. Incoming packets are then de-aliased so that they are sent to the correct machine on the local network. .Pp A certain amount of flexibility is built into the packet aliasing engine. In the simplest mode of operation, a many-to-one address mapping takes place between local network and the packet aliasing host. This is known as IP masquerading. In addition, one-to-one mappings between local and public addresses can also be implemented, which is known as static NAT. In between these extremes, different groups of private addresses can be linked to different public addresses, comprising several distinct many-to-one mappings. Also, a given public address and port can be statically redirected to a private address/port. .Pp The packet aliasing engine was designed to operate in user space outside of the kernel, without any access to private kernel data structure, but the source code can also be ported to a kernel environment. .Sh INITIALIZATION AND CONTROL Two special functions, .Fn PacketAliasInit and .Fn PacketAliasSetAddress , must always be called before any packet handling may be performed. In addition, the operating mode of the packet aliasing engine can be customized by calling .Fn PacketAliasSetMode . .Pp .Ft void .Fn PacketAliasInit void .Bd -ragged -offset indent This function has no arguments or return value and is used to initialize internal data structures. The following mode bits are always set after calling .Fn PacketAliasInit . See the description of .Fn PacketAliasSetMode below for the meaning of these mode bits. .Pp .Bl -item -offset indent -compact .It .Dv PKT_ALIAS_SAME_PORTS .It .Dv PKT_ALIAS_USE_SOCKETS .It .Dv PKT_ALIAS_RESET_ON_ADDR_CHANGE .El .Pp This function will always return the packet aliasing engine to the same initial state. .Fn PacketAliasSetAddress must be called afterwards, and any desired changes from the default mode bits listed above require a call to .Fn PacketAliasSetMode . .Pp It is mandatory that this function be called at the beginning of a program prior to any packet handling. .Ed .Pp .Ft void .Fn PacketAliasUninit void .Bd -ragged -offset indent This function has no arguments or return value and is used to clear any resources attached to internal data structures. .Pp This functions should be called when a program stops using the aliasing engine; it does, amongst other things, clear out any firewall holes. To provide backwards compatibility and extra security, it is added to the .Xr atexit 3 chain by .Fn PacketAliasInit . Calling it multiple times is harmless. .Ed .Pp .Ft void .Fn PacketAliasSetAddress "struct in_addr addr" .Bd -ragged -offset indent This function sets the source address to which outgoing packets from the local area network are aliased. All outgoing packets are re-mapped to this address unless overridden by a static address mapping established by .Fn PacketAliasRedirectAddr . .Pp If the .Dv PKT_ALIAS_RESET_ON_ADDR_CHANGE mode bit is set (the default mode of operation), then the internal aliasing link tables will be reset any time the aliasing address changes. This is useful for interfaces such as .Xr ppp 8 , where the IP address may or may not change on successive dial-up attempts. .Pp If the .Dv PKT_ALIAS_RESET_ON_ADDR_CHANGE mode bit is set to zero, this function can also be used to dynamically change the aliasing address on a packet to packet basis (it is a low overhead call). .Pp It is mandatory that this function be called prior to any packet handling. .Ed .Pp .Ft unsigned int .Fn PacketAliasSetMode "unsigned int flags" "unsigned int mask" .Bd -ragged -offset indent This function sets or clears mode bits according to the value of .Fa flags . Only bits marked in .Fa mask are affected. The following mode bits are defined in .Aq Pa alias.h : .Bl -tag -width indent .It Dv PKT_ALIAS_LOG Enables logging into .Pa /var/log/alias.log . Each time an aliasing link is created or deleted, the log file is appended with the current number of ICMP, TCP and UDP links. Mainly useful for debugging when the log file is viewed continuously with .Xr tail 1 . .It Dv PKT_ALIAS_DENY_INCOMING If this mode bit is set, all incoming packets associated with new TCP connections or new UDP transactions will be marked for being ignored .Po .Fn PacketAliasIn returns .Dv PKT_ALIAS_IGNORED code .Pc by the calling program. Response packets to connections or transactions initiated from the packet aliasing host or local network will be unaffected. This mode bit is useful for implementing a one-way firewall. .It Dv PKT_ALIAS_SAME_PORTS If this mode bit is set, the packet aliasing engine will attempt to leave the alias port numbers unchanged from the actual local port numbers. This can be done as long as the quintuple (proto, alias addr, alias port, remote addr, remote port) is unique. If a conflict exists, a new aliasing port number is chosen even if this mode bit is set. .It Dv PKT_ALIAS_USE_SOCKETS This bit should be set when the packet aliasing host originates network traffic as well as forwards it. When the packet aliasing host is waiting for a connection from an unknown host address or unknown port number (e.g. an FTP data connection), this mode bit specifies that a socket be allocated as a place holder to prevent port conflicts. Once a connection is established, usually within a minute or so, the socket is closed. .It Dv PKT_ALIAS_UNREGISTERED_ONLY If this mode bit is set, traffic on the local network which does not originate from unregistered address spaces will be ignored. Standard Class A, B and C unregistered addresses are: .Bd -literal -offset indent 10.0.0.0 -> 10.255.255.255 (Class A subnet) 172.16.0.0 -> 172.31.255.255 (Class B subnets) 192.168.0.0 -> 192.168.255.255 (Class C subnets) .Ed .Pp This option is useful in the case that packet aliasing host has both registered and unregistered subnets on different interfaces. The registered subnet is fully accessible to the outside world, so traffic from it does not need to be passed through the packet aliasing engine. .It Dv PKT_ALIAS_RESET_ON_ADDR_CHANGE When this mode bit is set and .Fn PacketAliasSetAddress is called to change the aliasing address, the internal link table of the packet aliasing engine will be cleared. This operating mode is useful for .Xr ppp 8 links where the interface address can sometimes change or remain the same between dial-up attempts. If this mode bit is not set, the link table will never be reset in the event of an address change. .It Dv PKT_ALIAS_PUNCH_FW This option makes .Nm `punch holes' in an .Xr ipfirewall 4 based firewall for FTP/IRC DCC connections. The holes punched are bound by from/to IP address and port; it will not be possible to use a hole for another connection. A hole is removed when the connection that uses it dies. To cater to unexpected death of a program using .Nm (e.g. kill -9), changing the state of the flag will clear the entire firewall range allocated for holes. This will also happen on the initial call to .Fn PacketAliasSetFWBase . This call must happen prior to setting this flag. .It Dv PKT_ALIAS_REVERSE This option makes .Nm reverse the way it handles incoming and outgoing packets, allowing it to be fed with data that passes through the internal interface rather than the external one. .It Dv PKT_ALIAS_PROXY_ONLY This option tells .Nm to obey transparent proxy rules only. Normal packet aliasing is not performed. See .Fn PacketAliasProxyRule below for details. -.It Dv PKT_ALIAS_DENY_PPTP -If this mode bit is set, all PPTP packets will be marked for being ignored -(both -.Fn PacketAliasIn -and -.Fn PacketAliasOut -return -.Dv PKT_ALIAS_IGNORED -code). .El .Ed .Pp .Ft void .Fn PacketAliasSetFWBase "unsigned int base" "unsigned int num" .Bd -ragged -offset indent Set firewall range allocated for punching firewall holes (with the .Dv PKT_ALIAS_PUNCH_FW flag). The range will be cleared for all rules on initialization. .Ed .Sh PACKET HANDLING The packet handling functions are used to modify incoming (remote to local) and outgoing (local to remote) packets. The calling program is responsible for receiving and sending packets via network interfaces. .Pp Along with .Fn PacketAliasInit and .Fn PacketAliasSetAddress , the two packet handling functions, .Fn PacketAliasIn and .Fn PacketAliasOut , comprise minimal set of functions needed for a basic IP masquerading implementation. .Pp .Ft int .Fn PacketAliasIn "char *buffer" "int maxpacketsize" .Bd -ragged -offset indent An incoming packet coming from a remote machine to the local network is de-aliased by this function. The IP packet is pointed to by .Fa buffer , and .Fa maxpacketsize indicates the size of the data structure containing the packet and should be at least as large as the actual packet size. .Pp Return codes: .Bl -tag -width indent .It Dv PKT_ALIAS_OK The packet aliasing process was successful. .It Dv PKT_ALIAS_IGNORED The packet was ignored and not de-aliased. This can happen if the protocol is unrecognized, possibly an ICMP message type is not handled or if incoming packets for new connections are being ignored (if .Dv PKT_ALIAS_DENY_INCOMING mode bit was set by .Fn PacketAliasSetMode ) . .It Dv PKT_ALIAS_UNRESOLVED_FRAGMENT This is returned when a fragment cannot be resolved because the header fragment has not been sent yet. In this situation, fragments must be saved with .Fn PacketAliasSaveFragment until a header fragment is found. .It Dv PKT_ALIAS_FOUND_HEADER_FRAGMENT The packet aliasing process was successful, and a header fragment was found. This is a signal to retrieve any unresolved fragments with .Fn PacketAliasGetFragment and de-alias them with .Fn PacketAliasFragmentIn . .It Dv PKT_ALIAS_ERROR An internal error within the packet aliasing engine occurred. .El .Ed .Pp .Ft int .Fn PacketAliasOut "char *buffer" "int maxpacketsize" .Bd -ragged -offset indent An outgoing packet coming from the local network to a remote machine is aliased by this function. The IP packet is pointed to by .Fa buffer , and .Fa maxpacketsize indicates the maximum packet size permissible should the packet length be changed. IP encoding protocols place address and port information in the encapsulated data stream which has to be modified and can account for changes in packet length. Well known examples of such protocols are FTP and IRC DCC. .Pp Return codes: .Bl -tag -width indent .It Dv PKT_ALIAS_OK The packet aliasing process was successful. .It Dv PKT_ALIAS_IGNORED The packet was ignored and not aliased. This can happen if the protocol is unrecognized, or possibly an ICMP message type is not handled. .It Dv PKT_ALIAS_ERROR An internal error within the packet aliasing engine occurred. .El .Ed .Sh PORT AND ADDRESS REDIRECTION The functions described in this section allow machines on the local network to be accessible in some degree to new incoming connections from the external network. Individual ports can be re-mapped or static network address translations can be designated. .Pp .Ft struct alias_link * .Fo PacketAliasRedirectPort .Fa "struct in_addr local_addr" .Fa "u_short local_port" .Fa "struct in_addr remote_addr" .Fa "u_short remote_port" .Fa "struct in_addr alias_addr" .Fa "u_short alias_port" .Fa "u_char proto" .Fc .Bd -ragged -offset indent This function specifies that traffic from a given remote address/port to an alias address/port be redirected to a specified local address/port. The parameter .Fa proto can be either .Dv IPPROTO_TCP or .Dv IPPROTO_UDP , as defined in .Aq Pa netinet/in.h . .Pp If .Fa local_addr or .Fa alias_addr is zero, this indicates that the packet aliasing address as established by .Fn PacketAliasSetAddress is to be used. Even if .Fn PacketAliasSetAddress is called to change the address after .Fn PacketAliasRedirectPort is called, a zero reference will track this change. .Pp If the link is further set up to operate for a load sharing, then .Fa local_addr and .Fa local_port are ignored, and are selected dynamically from the server pool, as described in .Fn PacketAliasAddServer below. .Pp If .Fa remote_addr is zero, this indicates to redirect packets from any remote address. Likewise, if .Fa remote_port is zero, this indicates to redirect packets originating from any remote port number. Almost always, the remote port specification will be zero, but non-zero remote addresses can sometimes be useful for firewalling. If two calls to .Fn PacketAliasRedirectPort overlap in their address/port specifications, then the most recent call will have precedence. .Pp This function returns a pointer which can subsequently be used by .Fn PacketAliasRedirectDelete . If .Dv NULL is returned, then the function call did not complete successfully. .Pp All port numbers should be in network address byte order, so it is necessary to use .Xr htons 3 to convert these parameters from internally readable numbers to network byte order. Addresses are also in network byte order, which is implicit in the use of the .Fa struct in_addr data type. .Ed .Pp .Ft struct alias_link * .Fo PacketAliasRedirectAddr .Fa "struct in_addr local_addr" .Fa "struct in_addr alias_addr" .Fc .Bd -ragged -offset indent This function designates that all incoming traffic to .Fa alias_addr be redirected to .Fa local_addr . Similarly, all outgoing traffic from .Fa local_addr is aliased to .Fa alias_addr . .Pp If .Fa local_addr or .Fa alias_addr is zero, this indicates that the packet aliasing address as established by .Fn PacketAliasSetAddress is to be used. Even if .Fn PacketAliasSetAddress is called to change the address after .Fn PacketAliasRedirectAddr is called, a zero reference will track this change. .Pp If the link is further set up to operate for a load sharing, then .Fa local_addr is ignored, and is selected dynamically from the server pool, as described in .Fn PacketAliasAddServer below. .Pp If subsequent calls to .Fn PacketAliasRedirectAddr use the same aliasing address, all new incoming traffic to this aliasing address will be redirected to the local address made in the last function call. New traffic generated by any of the local machines, designated in the several function calls, will be aliased to the same address. Consider the following example: .Bd -literal -offset indent PacketAliasRedirectAddr(inet_aton("192.168.0.2"), inet_aton("141.221.254.101")); PacketAliasRedirectAddr(inet_aton("192.168.0.3"), inet_aton("141.221.254.101")); PacketAliasRedirectAddr(inet_aton("192.168.0.4"), inet_aton("141.221.254.101")); .Ed .Pp Any outgoing connections such as .Xr telnet 1 or .Xr ftp 1 from 192.168.0.2, 192.168.0.3 and 192.168.0.4 will appear to come from 141.221.254.101. Any incoming connections to 141.221.254.101 will be directed to 192.168.0.4. .Pp Any calls to .Fn PacketAliasRedirectPort will have precedence over address mappings designated by .Fn PacketAliasRedirectAddr . .Pp This function returns a pointer which can subsequently be used by .Fn PacketAliasRedirectDelete . If .Dv NULL is returned, then the function call did not complete successfully. .Ed .Pp .Ft int .Fo PacketAliasAddServer .Fa "struct alias_link *link" .Fa "struct in_addr addr" .Fa "u_short port" .Fc .Bd -ragged -offset indent This function sets the .Fa link up for Load Sharing using IP Network Address Translation (RFC 2391, LSNAT). LSNAT operates as follows. A client attempts to access a server by using the server virtual address. The LSNAT router transparently redirects the request to one of the hosts in server pool, selected using a real-time load sharing algorithm. Multiple sessions may be initiated from the same client, and each session could be directed to a different host based on load balance across server pool hosts at the time. If load share is desired for just a few specific services, the configuration on LSNAT could be defined to restrict load share for just the services desired. .Pp Currently, only the simplest selection algorithm is implemented, where a host is selected on a round-robin basis only, without regard to load on the host. .Pp First, the .Fa link is created by either .Fn PacketAliasRedirectPort or .Fn PacketAliasRedirectAddr . Then, .Fn PacketAliasAddServer is called multiple times to add entries to the .Fa link Ns 's server pool. .Pp For links created with .Fn PacketAliasRedirectAddr , the .Fa port argument is ignored and could have any value, e.g. htons(~0). .Pp This function returns 0 on success, -1 otherwise. .Ed .Pp .Ft void .Fn PacketAliasRedirectDelete "struct alias_link *link" .Bd -ragged -offset indent This function will delete a specific static redirect rule entered by .Fn PacketAliasRedirectPort or .Fn PacketAliasRedirectAddr . The parameter .Fa link is the pointer returned by either of the redirection functions. If an invalid pointer is passed to .Fn PacketAliasRedirectDelete , then a program crash or unpredictable operation could result, so it is necessary to be careful using this function. .Ed .Pp .Ft int .Fn PacketAliasProxyRule "const char *cmd" .Bd -ragged -offset indent The passed .Fa cmd string consists of one or more pairs of words. The first word in each pair is a token and the second is the value that should be applied for that token. Tokens and their argument types are as follows: .Bl -tag -width indent .It Cm type encode_ip_hdr | encode_tcp_stream | no_encode In order to support transparent proxying, it is necessary to somehow pass the original address and port information into the new destination server. If .Cm encode_ip_hdr is specified, the original address and port is passed as an extra IP option. If .Cm encode_tcp_stream is specified, the original address and port is passed as the first piece of data in the TCP stream in the format .Dq DEST Ar IP port . .It Cm port Ar portnum Only packets with the destination port .Ar portnum are proxied. .It Cm server Ar host Ns Xo .Op : Ns Ar portnum .Xc This specifies the .Ar host and .Ar portnum that the data is to be redirected to. .Ar host must be an IP address rather than a DNS host name. If .Ar portnum is not specified, the destination port number is not changed. .Pp The .Ar server specification is mandatory unless the .Cm delete command is being used. .It Cm rule Ar index Normally, each call to .Fn PacketAliasProxyRule inserts the next rule at the start of a linear list of rules. If an .Ar index is specified, the new rule will be checked after all rules with lower indices. Calls to .Fn PacketAliasProxyRule that do not specify a rule are assigned rule 0. .It Cm delete Ar index This token and its argument MUST NOT be used with any other tokens. When used, all existing rules with the given .Ar index are deleted. .It Cm proto tcp | udp If specified, only packets of the given protocol type are matched. .It Cm src Ar IP Ns Xo .Op / Ns Ar bits .Xc If specified, only packets with a source address matching the given .Ar IP are matched. If .Ar bits is also specified, then the first .Ar bits bits of .Ar IP are taken as a network specification, and all IP addresses from that network will be matched. .It Cm dst Ar IP Ns Xo .Op / Ns Ar bits .Xc If specified, only packets with a destination address matching the given .Ar IP are matched. If .Ar bits is also specified, then the first .Ar bits bits of .Ar IP are taken as a network specification, and all IP addresses from that network will be matched. .El .Pp This function is usually used to redirect outgoing connections for internal machines that are not permitted certain types of internet access, or to restrict access to certain external machines. .Ed .Pp .Ft struct alias_link * -.Fo PacketAliasRedirectPptp +.Fo PacketAliasRedirectProto .Fa "struct in_addr local_addr" .Fa "struct in_addr remote_addr" .Fa "struct in_addr alias_addr" +.Fa "u_char proto" .Fc .Bd -ragged -offset indent -This function specifies that any Point to Point Tunneling Protocol -(PPTP) traffic from a given remote address to an alias address be +This function specifies that any IP packet with protocol number of +.Fa proto +from a given remote address to an alias address be redirected to a specified local address. -Currently supported PPTP protocols include: .Pp -.Bl -tag -width "IPPROTO_GRE" -compact -.It IPPROTO_GRE -Generic Routing Encapsulation (RFC 1702) -.It IPPROTO_ESP -IP Encapsulating Security Payload (RFC 1827) -.It IPPROTO_AH -IP Authentication Header (RFC 1826) -.El -.Pp If .Fa local_addr or .Fa alias_addr is zero, this indicates that the packet aliasing address as established by .Fn PacketAliasSetAddress is to be used. Even if .Fn PacketAliasSetAddress is called to change the address after -.Fn PacketAliasRedirectPptp +.Fn PacketAliasRedirectProto is called, a zero reference will track this change. .Pp If .Fa remote_addr -is zero, this indicates to redirect PPTP packets from any remote address. +is zero, this indicates to redirect packets from any remote address. Non-zero remote addresses can sometimes be useful for firewalling. .Pp If two calls to -.Fn PacketAliasRedirectPptp +.Fn PacketAliasRedirectProto overlap in their address specifications, then the most recent call will have precedence. .Pp This function returns a pointer which can subsequently be used by .Fn PacketAliasRedirectDelete . If .Dv NULL is returned, then the function call did not complete successfully. .Ed .Pp .Ft int .Fn PacketAliasPptp "struct in_addr addr" .Bd -ragged -offset indent This function causes any PPTP packets to be aliased using .Fa addr rather than the address set via .Fn PacketAliasSetAddress . This allows the uses of the PPTP on a single machine on the internal network. .Pp If the passed address is .Dv INADDR_NONE , then PPTP aliasing is disabled. .Pp .Bf -symbolic This function is made obsolete by -.Fn PacketAliasRedirectPptp -and -.Dv PKT_ALIAS_DENY_PPTP -mode bit, and is provided only for backward compatibility. +.Fn PacketAliasRedirectProto , +and is provided only for backward compatibility. .Ef .Ed .Sh FRAGMENT HANDLING The functions in this section are used to deal with incoming fragments. .Pp Outgoing fragments are handled within .Fn PacketAliasOut by changing the address according to any applicable mapping set by .Fn PacketAliasRedirectAddr , or the default aliasing address set by .Fn PacketAliasSetAddress . .Pp Incoming fragments are handled in one of two ways. If the header of a fragmented IP packet has already been seen, then all subsequent fragments will be re-mapped in the same manner the header fragment was. Fragments which arrive before the header are saved and then retrieved once the header fragment has been resolved. .Pp .Ft int .Fn PacketAliasSaveFragment "char *ptr" .Bd -ragged -offset indent When .Fn PacketAliasIn returns .Dv PKT_ALIAS_UNRESOLVED_FRAGMENT , this function can be used to save the pointer to the unresolved fragment. .Pp It is implicitly assumed that .Fa ptr points to a block of memory allocated by .Xr malloc 3 . If the fragment is never resolved, the packet aliasing engine will automatically free the memory after a timeout period. [Eventually this function should be modified so that a callback function for freeing memory is passed as an argument.] .Pp This function returns .Dv PKT_ALIAS_OK if it was successful and .Dv PKT_ALIAS_ERROR if there was an error. .Ed .Pp .Ft char * .Fn PacketAliasGetFragment "char *buffer" .Bd -ragged -offset indent This function can be used to retrieve fragment pointers saved by .Fn PacketAliasSaveFragment . The IP header fragment pointed to by .Fa buffer is the header fragment indicated when .Fn PacketAliasIn returns .Dv PKT_ALIAS_FOUND_HEADER_FRAGMENT . Once a fragment pointer is retrieved, it becomes the calling program's responsibility to free the dynamically allocated memory for the fragment. .Pp .Fn PacketAliasGetFragment can be called sequentially until there are no more fragments available, at which time it returns .Dv NULL . .Ed .Pp .Ft void .Fn PacketAliasFragmentIn "char *header" "char *fragment" .Bd -ragged -offset indent When a fragment is retrieved with .Fn PacketAliasGetFragment , it can then be de-aliased with a call to .Fn PacketAliasFragmentIn . The .Fa header argument is the pointer to a header fragment used as a template, and .Fa fragment is the pointer to the packet to be de-aliased. .Ed .Sh MISCELLANEOUS FUNCTIONS .Ft void .Fn PacketAliasSetTarget "struct in_addr addr" .Bd -ragged -offset indent When an incoming packet not associated with any pre-existing aliasing link arrives at the host machine, it will be sent to the address indicated by a call to .Fn PacketAliasSetTarget . .Pp If this function is not called, or is called with an .Dv INADDR_NONE address argument, then all new incoming packets go to the address set by .Fn PacketAliasSetAddress . .Pp If this function is called with an .Dv INADDR_ANY address argument, then all new incoming packets go to the address specified in the packet. This allows external machines to talk directly to internal machines if they can route packets to the machine in question. .Ed .Pp .Ft int .Fn PacketAliasCheckNewLink void .Bd -ragged -offset indent This function returns a non-zero value when a new aliasing link is created. In circumstances where incoming traffic is being sequentially sent to different local servers, this function can be used to trigger when .Fn PacketAliasSetTarget is called to change the default target address. .Ed .Pp .Ft u_short .Fn PacketAliasInternetChecksum "u_short *buffer" "int nbytes" .Bd -ragged -offset indent This is a utility function that does not seem to be available elsewhere and is included as a convenience. It computes the internet checksum, which is used in both IP and protocol-specific headers (TCP, UDP, ICMP). .Pp The .Fa buffer argument points to the data block to be checksummed, and .Fa nbytes is the number of bytes. The 16-bit checksum field should be zeroed before computing the checksum. .Pp Checksums can also be verified by operating on a block of data including its checksum. If the checksum is valid, .Fn PacketAliasInternetChecksum will return zero. .Ed .Sh AUTHORS .An Charles Mott Aq cmott@scientech.com , versions 1.0 - 1.8, 2.0 - 2.4. .An Eivind Eklund Aq eivind@FreeBSD.org , versions 1.8b, 1.9 and 2.5. Added IRC DCC support as well as contributing a number of architectural improvements; added the firewall bypass for FTP/IRC DCC. .Sh ACKNOWLEDGMENTS Listed below, in approximate chronological order, are individuals who have provided valuable comments and/or debugging assistance. .Pp .Bl -item -offset indent -compact .It Gary Roberts .It Tom Torrance .It Reto Burkhalter .It Martin Renters .It Brian Somers .It Paul Traina .It Ari Suutari .It Dave Remien .It J. Fortes .It Andrzej Bialecki .It Gordon Burditt .El .Sh CONCEPTUAL BACKGROUND This section is intended for those who are planning to modify the source code or want to create somewhat esoteric applications using the packet aliasing functions. .Pp The conceptual framework under which the packet aliasing engine operates is described here. Central to the discussion is the idea of an .Em aliasing link which describes the relationship for a given packet transaction between the local machine, aliased identity and remote machine. It is discussed how such links come into existence and are destroyed. .Ss ALIASING LINKS There is a notion of an .Em aliasing link , which is a 7-tuple describing a specific translation: .Bd -literal -offset indent (local addr, local port, alias addr, alias port, remote addr, remote port, protocol) .Ed .Pp Outgoing packets have the local address and port number replaced with the alias address and port number. Incoming packets undergo the reverse process. The packet aliasing engine attempts to match packets against an internal table of aliasing links to determine how to modify a given IP packet. Both the IP header and protocol dependent headers are modified as necessary. Aliasing links are created and deleted as necessary according to network traffic. .Pp Protocols can be TCP, UDP or even ICMP in certain circumstances. (Some types of ICMP packets can be aliased according to sequence or ID number which acts as an equivalent port number for identifying how individual packets should be handled.) .Pp Each aliasing link must have a unique combination of the following five quantities: alias address/port, remote address/port and protocol. This ensures that several machines on a local network can share the same aliasing IP address. In cases where conflicts might arise, the aliasing port is chosen so that uniqueness is maintained. .Ss STATIC AND DYNAMIC LINKS Aliasing links can either be static or dynamic. Static links persist indefinitely and represent fixed rules for translating IP packets. Dynamic links come into existence for a specific TCP connection or UDP transaction or ICMP ECHO sequence. For the case of TCP, the connection can be monitored to see when the associated aliasing link should be deleted. Aliasing links for UDP transactions (and ICMP ECHO and TIMESTAMP requests) work on a simple timeout rule. When no activity is observed on a dynamic link for a certain amount of time it is automatically deleted. Timeout rules also apply to TCP connections which do not open or close properly. .Ss PARTIALLY SPECIFIED ALIASING LINKS Aliasing links can be partially specified, meaning that the remote address and/or remote port are unknown. In this case, when a packet matching the incomplete specification is found, a fully specified dynamic link is created. If the original partially specified link is dynamic, it will be deleted after the fully specified link is created, otherwise it will persist. .Pp For instance, a partially specified link might be .Bd -literal -offset indent (192.168.0.4, 23, 204.228.203.215, 8066, 0, 0, tcp) .Ed .Pp The zeros denote unspecified components for the remote address and port. If this link were static it would have the effect of redirecting all incoming traffic from port 8066 of 204.228.203.215 to port 23 (telnet) of machine 192.168.0.4 on the local network. Each individual telnet connection would initiate the creation of a distinct dynamic link. .Ss DYNAMIC LINK CREATION In addition to aliasing links, there are also address mappings that can be stored within the internal data table of the packet aliasing mechanism. .Bd -literal -offset indent (local addr, alias addr) .Ed .Pp Address mappings are searched when creating new dynamic links. .Pp All outgoing packets from the local network automatically create a dynamic link if they do not match an already existing fully specified link. If an address mapping exists for the outgoing packet, this determines the alias address to be used. If no mapping exists, then a default address, usually the address of the packet aliasing host, is used. If necessary, this default address can be changed as often as each individual packet arrives. .Pp The aliasing port number is determined such that the new dynamic link does not conflict with any existing links. In the default operating mode, the packet aliasing engine attempts to set the aliasing port equal to the local port number. If this results in a conflict, then port numbers are randomly chosen until a unique aliasing link can be established. In an alternate operating mode, the first choice of an aliasing port is also random and unrelated to the local port number. Index: head/sys/netinet/libalias/alias.c =================================================================== --- head/sys/netinet/libalias/alias.c (revision 59725) +++ head/sys/netinet/libalias/alias.c (revision 59726) @@ -1,1428 +1,1422 @@ /* -*- mode: c; tab-width: 8; c-basic-indent: 4; -*- */ /* Alias.c provides supervisory control for the functions of the packet aliasing software. It consists of routines to monitor TCP connection state, protocol-specific aliasing routines, fragment handling and the following outside world functional interfaces: SaveFragmentPtr, GetFragmentPtr, FragmentAliasIn, PacketAliasIn and PacketAliasOut. The other C program files are briefly described. The data structure framework which holds information needed to translate packets is encapsulated in alias_db.c. Data is accessed by function calls, so other segments of the program need not know about the underlying data structures. Alias_ftp.c contains special code for modifying the ftp PORT command used to establish data connections, while alias_irc.c does the same for IRC DCC. Alias_util.c contains a few utility routines. This software is placed into the public domain with no restrictions on its distribution. Version 1.0 August, 1996 (cjm) Version 1.1 August 20, 1996 (cjm) PPP host accepts incoming connections for ports 0 to 1023. (Gary Roberts pointed out the need to handle incoming connections.) Version 1.2 September 7, 1996 (cjm) Fragment handling error in alias_db.c corrected. (Tom Torrance helped fix this problem.) Version 1.4 September 16, 1996 (cjm) - A more generalized method for handling incoming connections, without the 0-1023 restriction, is implemented in alias_db.c - Improved ICMP support in alias.c. Traceroute packet streams can now be correctly aliased. - TCP connection closing logic simplified in alias.c and now allows for additional 1 minute "grace period" after FIN or RST is observed. Version 1.5 September 17, 1996 (cjm) Corrected error in handling incoming UDP packets with 0 checksum. (Tom Torrance helped fix this problem.) Version 1.6 September 18, 1996 (cjm) Simplified ICMP aliasing scheme. Should now support traceroute from Win95 as well as FreeBSD. Version 1.7 January 9, 1997 (cjm) - Out-of-order fragment handling. - IP checksum error fixed for ftp transfers from aliasing host. - Integer return codes added to all aliasing/de-aliasing functions. - Some obsolete comments cleaned up. - Differential checksum computations for IP header (TCP, UDP and ICMP were already differential). Version 2.1 May 1997 (cjm) - Added support for outgoing ICMP error messages. - Added two functions PacketAliasIn2() and PacketAliasOut2() for dynamic address control (e.g. round-robin allocation of incoming packets). Version 2.2 July 1997 (cjm) - Rationalized API function names to begin with "PacketAlias..." - Eliminated PacketAliasIn2() and PacketAliasOut2() as poorly conceived. Version 2.3 Dec 1998 (dillon) - Major bounds checking additions, see FreeBSD/CVS See HISTORY file for additional revisions. $FreeBSD$ */ #include #include #include #include #include #include #include #ifndef IPPROTO_GRE #define IPPROTO_GRE 47 #define IPPROTO_ESP 50 #define IPPROTO_AH 51 #endif #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 /* 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(), IcmpAliasIn3() IcmpAliasOut(), IcmpAliasOut1(), IcmpAliasOut2(), IcmpAliasOut3() + 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 ip *); static int IcmpAliasIn2(struct ip *); static int IcmpAliasIn3(struct ip *); static int IcmpAliasIn (struct ip *); static int IcmpAliasOut1(struct ip *); static int IcmpAliasOut2(struct ip *); static int IcmpAliasOut3(struct ip *); static int IcmpAliasOut (struct ip *); +static int ProtoAliasIn(struct ip *); +static int ProtoAliasOut(struct ip *); + static int UdpAliasOut(struct ip *); static int UdpAliasIn (struct ip *); static int TcpAliasOut(struct ip *, int); static int TcpAliasIn (struct ip *); static int IcmpAliasIn1(struct ip *pip) { /* De-alias incoming echo and timestamp replies */ struct alias_link *link; struct icmp *ic; ic = (struct icmp *) ((char *) pip + (pip->ip_hl << 2)); /* Get source address from ICMP data field and restore original data */ link = FindIcmpIn(pip->ip_src, pip->ip_dst, ic->icmp_id); if (link != NULL) { u_short original_id; int accumulate; original_id = GetOriginalPort(link); /* Adjust ICMP checksum */ accumulate = ic->icmp_id; accumulate -= original_id; ADJUST_CHECKSUM(accumulate, ic->icmp_cksum) /* Put original sequence number back in */ ic->icmp_id = original_id; /* Put original address back into IP header */ { struct in_addr original_address; original_address = GetOriginalAddress(link); DifferentialChecksum(&pip->ip_sum, (u_short *) &original_address, (u_short *) &pip->ip_dst, 2); pip->ip_dst = original_address; } return(PKT_ALIAS_OK); } return(PKT_ALIAS_IGNORED); } static int IcmpAliasIn2(struct ip *pip) { /* Alias incoming ICMP error messages containing IP header and first 64 bits of datagram. */ struct ip *ip; struct icmp *ic, *ic2; struct udphdr *ud; struct tcphdr *tc; struct alias_link *link; ic = (struct icmp *) ((char *) pip + (pip->ip_hl << 2)); ip = (struct ip *) ic->icmp_data; ud = (struct udphdr *) ((char *) ip + (ip->ip_hl <<2)); tc = (struct tcphdr *) ud; ic2 = (struct icmp *) ud; if (ip->ip_p == IPPROTO_UDP) link = FindUdpTcpIn(ip->ip_dst, ip->ip_src, ud->uh_dport, ud->uh_sport, IPPROTO_UDP); else if (ip->ip_p == IPPROTO_TCP) link = FindUdpTcpIn(ip->ip_dst, ip->ip_src, tc->th_dport, tc->th_sport, IPPROTO_TCP); else if (ip->ip_p == IPPROTO_ICMP) { if (ic2->icmp_type == ICMP_ECHO || ic2->icmp_type == ICMP_TSTAMP) link = FindIcmpIn(ip->ip_dst, ip->ip_src, ic2->icmp_id); else link = NULL; } else link = NULL; if (link != NULL) { if (ip->ip_p == IPPROTO_UDP || ip->ip_p == IPPROTO_TCP) { u_short *sptr; int accumulate; struct in_addr original_address; u_short original_port; original_address = GetOriginalAddress(link); original_port = GetOriginalPort(link); /* Adjust ICMP checksum */ sptr = (u_short *) &(ip->ip_src); accumulate = *sptr++; accumulate += *sptr; sptr = (u_short *) &original_address; accumulate -= *sptr++; accumulate -= *sptr; accumulate += ud->uh_sport; accumulate -= original_port; ADJUST_CHECKSUM(accumulate, ic->icmp_cksum) /* Un-alias address in IP header */ DifferentialChecksum(&pip->ip_sum, (u_short *) &original_address, (u_short *) &pip->ip_dst, 2); pip->ip_dst = original_address; /* Un-alias address and port number of original IP packet fragment contained in ICMP data section */ ip->ip_src = original_address; ud->uh_sport = original_port; } else if (pip->ip_p == IPPROTO_ICMP) { u_short *sptr; int accumulate; struct in_addr original_address; u_short original_id; original_address = GetOriginalAddress(link); original_id = GetOriginalPort(link); /* Adjust ICMP checksum */ sptr = (u_short *) &(ip->ip_src); accumulate = *sptr++; accumulate += *sptr; sptr = (u_short *) &original_address; accumulate -= *sptr++; accumulate -= *sptr; accumulate += ic2->icmp_id; accumulate -= original_id; ADJUST_CHECKSUM(accumulate, ic->icmp_cksum) /* Un-alias address in IP header */ DifferentialChecksum(&pip->ip_sum, (u_short *) &original_address, (u_short *) &pip->ip_dst, 2); pip->ip_dst = original_address; /* Un-alias address of original IP packet and 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 IcmpAliasIn3(struct ip *pip) { struct in_addr original_address; original_address = FindOriginalAddress(pip->ip_dst); DifferentialChecksum(&pip->ip_sum, (u_short *) &original_address, (u_short *) &pip->ip_dst, 2); pip->ip_dst = original_address; return PKT_ALIAS_OK; } static int IcmpAliasIn(struct ip *pip) { int iresult; struct icmp *ic; /* Return if proxy-only mode is enabled */ if (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(pip); } break; case ICMP_UNREACH: case ICMP_SOURCEQUENCH: case ICMP_TIMXCEED: case ICMP_PARAMPROB: iresult = IcmpAliasIn2(pip); break; case ICMP_ECHO: case ICMP_TSTAMP: iresult = IcmpAliasIn3(pip); break; } return(iresult); } static int IcmpAliasOut1(struct ip *pip) { /* Alias ICMP echo and timestamp packets */ struct alias_link *link; struct icmp *ic; ic = (struct icmp *) ((char *) pip + (pip->ip_hl << 2)); /* Save overwritten data for when echo packet returns */ link = FindIcmpOut(pip->ip_src, pip->ip_dst, ic->icmp_id); if (link != NULL) { u_short alias_id; int accumulate; alias_id = GetAliasPort(link); /* Since data field is being modified, adjust ICMP checksum */ accumulate = ic->icmp_id; accumulate -= alias_id; ADJUST_CHECKSUM(accumulate, ic->icmp_cksum) /* Alias sequence number */ ic->icmp_id = alias_id; /* Change source address */ { struct in_addr alias_address; alias_address = GetAliasAddress(link); DifferentialChecksum(&pip->ip_sum, (u_short *) &alias_address, (u_short *) &pip->ip_src, 2); pip->ip_src = alias_address; } return(PKT_ALIAS_OK); } return(PKT_ALIAS_IGNORED); } static int IcmpAliasOut2(struct ip *pip) { /* Alias outgoing ICMP error messages containing IP header and first 64 bits of datagram. */ struct ip *ip; struct icmp *ic, *ic2; struct udphdr *ud; struct tcphdr *tc; struct alias_link *link; ic = (struct icmp *) ((char *) pip + (pip->ip_hl << 2)); ip = (struct ip *) ic->icmp_data; ud = (struct udphdr *) ((char *) ip + (ip->ip_hl <<2)); tc = (struct tcphdr *) ud; ic2 = (struct icmp *) ud; if (ip->ip_p == IPPROTO_UDP) link = FindUdpTcpOut(ip->ip_dst, ip->ip_src, ud->uh_dport, ud->uh_sport, IPPROTO_UDP); else if (ip->ip_p == IPPROTO_TCP) link = FindUdpTcpOut(ip->ip_dst, ip->ip_src, tc->th_dport, tc->th_sport, IPPROTO_TCP); else if (ip->ip_p == IPPROTO_ICMP) { if (ic2->icmp_type == ICMP_ECHO || ic2->icmp_type == ICMP_TSTAMP) link = FindIcmpOut(ip->ip_dst, ip->ip_src, ic2->icmp_id); else link = NULL; } else link = NULL; if (link != NULL) { if (ip->ip_p == IPPROTO_UDP || ip->ip_p == IPPROTO_TCP) { u_short *sptr; int accumulate; struct in_addr alias_address; u_short alias_port; alias_address = GetAliasAddress(link); alias_port = GetAliasPort(link); /* Adjust ICMP checksum */ sptr = (u_short *) &(ip->ip_dst); accumulate = *sptr++; accumulate += *sptr; sptr = (u_short *) &alias_address; accumulate -= *sptr++; accumulate -= *sptr; accumulate += ud->uh_dport; accumulate -= alias_port; ADJUST_CHECKSUM(accumulate, ic->icmp_cksum) /* Alias address in IP header */ DifferentialChecksum(&pip->ip_sum, (u_short *) &alias_address, (u_short *) &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 (pip->ip_p == IPPROTO_ICMP) { u_short *sptr; int accumulate; struct in_addr alias_address; u_short alias_id; alias_address = GetAliasAddress(link); alias_id = GetAliasPort(link); /* Adjust ICMP checksum */ sptr = (u_short *) &(ip->ip_dst); accumulate = *sptr++; accumulate += *sptr; sptr = (u_short *) &alias_address; accumulate -= *sptr++; accumulate -= *sptr; accumulate += ic2->icmp_id; accumulate -= alias_id; ADJUST_CHECKSUM(accumulate, ic->icmp_cksum) /* Alias address in IP header */ DifferentialChecksum(&pip->ip_sum, (u_short *) &alias_address, (u_short *) &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 IcmpAliasOut3(struct ip *pip) { /* Handle outgoing echo and timestamp replies. The only thing which is done in this case is to alias the source IP address of the packet. */ struct in_addr alias_addr; alias_addr = FindAliasAddress(pip->ip_src); DifferentialChecksum(&pip->ip_sum, (u_short *) &alias_addr, (u_short *) &pip->ip_src, 2); pip->ip_src = alias_addr; return PKT_ALIAS_OK; } static int IcmpAliasOut(struct ip *pip) { int iresult; struct icmp *ic; /* Return if proxy-only mode is enabled */ if (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(pip); } break; case ICMP_UNREACH: case ICMP_SOURCEQUENCH: case ICMP_TIMXCEED: case ICMP_PARAMPROB: iresult = IcmpAliasOut2(pip); break; case ICMP_ECHOREPLY: case ICMP_TSTAMPREPLY: iresult = IcmpAliasOut3(pip); } return(iresult); } static int -PptpAliasIn(struct ip *pip) +ProtoAliasIn(struct ip *pip) { /* - Handle incoming PPTP packets. The + 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 (packetAliasMode & PKT_ALIAS_PROXY_ONLY) return PKT_ALIAS_OK; - if (packetAliasMode & PKT_ALIAS_DENY_PPTP) - return PKT_ALIAS_IGNORED; - - link = FindPptpIn(pip->ip_src, pip->ip_dst); + link = FindProtoIn(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, (u_short *) &original_address, (u_short *) &pip->ip_dst, 2); pip->ip_dst = original_address; return(PKT_ALIAS_OK); } return(PKT_ALIAS_IGNORED); } static int -PptpAliasOut(struct ip *pip) +ProtoAliasOut(struct ip *pip) { /* - Handle outgoing PPTP packets. The + 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 (packetAliasMode & PKT_ALIAS_PROXY_ONLY) return PKT_ALIAS_OK; - if (packetAliasMode & PKT_ALIAS_DENY_PPTP) - return PKT_ALIAS_IGNORED; - - link = FindPptpOut(pip->ip_src, pip->ip_dst); + link = FindProtoOut(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, (u_short *) &alias_address, (u_short *) &pip->ip_src, 2); pip->ip_src = alias_address; return(PKT_ALIAS_OK); } return(PKT_ALIAS_IGNORED); } static int UdpAliasIn(struct ip *pip) { struct udphdr *ud; struct alias_link *link; /* Return if proxy-only mode is enabled */ if (packetAliasMode & PKT_ALIAS_PROXY_ONLY) return PKT_ALIAS_OK; ud = (struct udphdr *) ((char *) pip + (pip->ip_hl << 2)); link = FindUdpTcpIn(pip->ip_src, pip->ip_dst, ud->uh_sport, ud->uh_dport, IPPROTO_UDP); if (link != NULL) { struct in_addr alias_address; struct in_addr original_address; u_short alias_port; int accumulate; u_short *sptr; int r = 0; alias_address = GetAliasAddress(link); original_address = GetOriginalAddress(link); alias_port = ud->uh_dport; ud->uh_dport = GetOriginalPort(link); /* If NETBIOS Datagram, It should be alias address in UDP Data, too */ if (ntohs(ud->uh_dport) == NETBIOS_DGM_PORT_NUMBER || ntohs(ud->uh_sport) == NETBIOS_DGM_PORT_NUMBER ) { r = AliasHandleUdpNbt(pip, link, &original_address, ud->uh_dport); } else if (ntohs(ud->uh_dport) == NETBIOS_NS_PORT_NUMBER || ntohs(ud->uh_sport) == NETBIOS_NS_PORT_NUMBER ) { r = AliasHandleUdpNbtNS(pip, link, &alias_address, &alias_port, &original_address, &ud->uh_dport ); } if (ntohs(ud->uh_dport) == CUSEEME_PORT_NUMBER) AliasHandleCUSeeMeIn(pip, original_address); /* If UDP checksum is not zero, then adjust since destination port */ /* is being unaliased and destination port is being altered. */ if (ud->uh_sum != 0) { accumulate = alias_port; accumulate -= ud->uh_dport; sptr = (u_short *) &alias_address; accumulate += *sptr++; accumulate += *sptr; sptr = (u_short *) &original_address; accumulate -= *sptr++; accumulate -= *sptr; ADJUST_CHECKSUM(accumulate, ud->uh_sum) } /* Restore original IP address */ DifferentialChecksum(&pip->ip_sum, (u_short *) &original_address, (u_short *) &pip->ip_dst, 2); pip->ip_dst = original_address; /* * 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 ip *pip) { struct udphdr *ud; struct alias_link *link; /* Return if proxy-only mode is enabled */ if (packetAliasMode & PKT_ALIAS_PROXY_ONLY) return PKT_ALIAS_OK; ud = (struct udphdr *) ((char *) pip + (pip->ip_hl << 2)); link = FindUdpTcpOut(pip->ip_src, pip->ip_dst, ud->uh_sport, ud->uh_dport, IPPROTO_UDP); if (link != NULL) { u_short alias_port; struct in_addr alias_address; alias_address = GetAliasAddress(link); alias_port = GetAliasPort(link); if (ntohs(ud->uh_dport) == CUSEEME_PORT_NUMBER) AliasHandleCUSeeMeOut(pip, link); /* If NETBIOS Datagram, It should be alias address in UDP Data, too */ if (ntohs(ud->uh_dport) == NETBIOS_DGM_PORT_NUMBER || ntohs(ud->uh_sport) == NETBIOS_DGM_PORT_NUMBER ) { AliasHandleUdpNbt(pip, link, &alias_address, alias_port); } else if (ntohs(ud->uh_dport) == NETBIOS_NS_PORT_NUMBER || ntohs(ud->uh_sport) == NETBIOS_NS_PORT_NUMBER ) { AliasHandleUdpNbtNS(pip, link, &pip->ip_src, &ud->uh_sport, &alias_address, &alias_port); } /* If UDP checksum is not zero, adjust since source port is */ /* being aliased and source address is being altered */ if (ud->uh_sum != 0) { int accumulate; u_short *sptr; accumulate = ud->uh_sport; accumulate -= alias_port; sptr = (u_short *) &(pip->ip_src); accumulate += *sptr++; accumulate += *sptr; sptr = (u_short *) &alias_address; accumulate -= *sptr++; accumulate -= *sptr; ADJUST_CHECKSUM(accumulate, ud->uh_sum) } /* Put alias port in UDP header */ ud->uh_sport = alias_port; /* Change source address */ DifferentialChecksum(&pip->ip_sum, (u_short *) &alias_address, (u_short *) &pip->ip_src, 2); pip->ip_src = alias_address; return(PKT_ALIAS_OK); } return(PKT_ALIAS_IGNORED); } static int TcpAliasIn(struct ip *pip) { struct tcphdr *tc; struct alias_link *link; tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2)); link = FindUdpTcpIn(pip->ip_src, pip->ip_dst, tc->th_sport, tc->th_dport, IPPROTO_TCP); if (link != NULL) { struct in_addr alias_address; struct in_addr original_address; struct in_addr proxy_address; u_short alias_port; u_short proxy_port; int accumulate; u_short *sptr; 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; sptr = (u_short *) &alias_address; accumulate += *sptr++; accumulate += *sptr; sptr = (u_short *) &original_address; accumulate -= *sptr++; accumulate -= *sptr; /* 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; sptr = (u_short *) &pip->ip_src; accumulate += *sptr++; accumulate += *sptr; sptr = (u_short *) &proxy_address; accumulate -= *sptr++; accumulate -= *sptr; } /* See if ACK number needs to be modified */ if (GetAckModified(link) == 1) { int delta; delta = GetDeltaAckIn(pip, link); if (delta != 0) { sptr = (u_short *) &tc->th_ack; accumulate += *sptr++; accumulate += *sptr; tc->th_ack = htonl(ntohl(tc->th_ack) - delta); sptr = (u_short *) &tc->th_ack; accumulate -= *sptr++; accumulate -= *sptr; } } ADJUST_CHECKSUM(accumulate, tc->th_sum); /* Restore original IP address */ sptr = (u_short *) &pip->ip_dst; accumulate = *sptr++; accumulate += *sptr; pip->ip_dst = original_address; sptr = (u_short *) &pip->ip_dst; accumulate -= *sptr++; accumulate -= *sptr; /* If this is a transparent proxy packet, then modify the source address */ if (proxy_address.s_addr != 0) { sptr = (u_short *) &pip->ip_src; accumulate += *sptr++; accumulate += *sptr; pip->ip_src = proxy_address; sptr = (u_short *) &pip->ip_src; accumulate -= *sptr++; accumulate -= *sptr; } 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 ip *pip, int maxpacketsize) { 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(pip, &proxy_server_address, &proxy_server_port); if (proxy_type == 0 && (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; u_short *sptr; accumulate = tc->th_dport; tc->th_dport = proxy_server_port; accumulate -= tc->th_dport; sptr = (u_short *) &(pip->ip_dst); accumulate += *sptr++; accumulate += *sptr; sptr = (u_short *) &proxy_server_address; accumulate -= *sptr++; accumulate -= *sptr; ADJUST_CHECKSUM(accumulate, tc->th_sum); sptr = (u_short *) &(pip->ip_dst); accumulate = *sptr++; accumulate += *sptr; pip->ip_dst = proxy_server_address; sptr = (u_short *) &(pip->ip_dst); accumulate -= *sptr++; accumulate -= *sptr; ADJUST_CHECKSUM(accumulate, pip->ip_sum); } link = FindUdpTcpOut(pip->ip_src, pip->ip_dst, tc->th_sport, tc->th_dport, IPPROTO_TCP); if (link !=NULL) { u_short alias_port; struct in_addr alias_address; int accumulate; u_short *sptr; /* Save original destination address, if this is a proxy packet. Also modify packet to include destination encoding. */ if (proxy_type != 0) { SetProxyPort(link, dest_port); SetProxyAddress(link, dest_address); ProxyModify(link, pip, maxpacketsize, proxy_type); } /* 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(pip, link, maxpacketsize); if (ntohs(tc->th_dport) == IRC_CONTROL_PORT_NUMBER_1 || ntohs(tc->th_dport) == IRC_CONTROL_PORT_NUMBER_2) AliasHandleIrcOut(pip, link, maxpacketsize); /* Adjust TCP checksum since source port is being aliased */ /* and source address is being altered */ accumulate = tc->th_sport; tc->th_sport = alias_port; accumulate -= tc->th_sport; sptr = (u_short *) &(pip->ip_src); accumulate += *sptr++; accumulate += *sptr; sptr = (u_short *) &alias_address; accumulate -= *sptr++; accumulate -= *sptr; /* Modify sequence number if necessary */ if (GetAckModified(link) == 1) { int delta; delta = GetDeltaSeqOut(pip, link); if (delta != 0) { sptr = (u_short *) &tc->th_seq; accumulate += *sptr++; accumulate += *sptr; tc->th_seq = htonl(ntohl(tc->th_seq) + delta); sptr = (u_short *) &tc->th_seq; accumulate -= *sptr++; accumulate -= *sptr; } } ADJUST_CHECKSUM(accumulate, tc->th_sum) /* Change source address */ sptr = (u_short *) &(pip->ip_src); accumulate = *sptr++; accumulate += *sptr; pip->ip_src = alias_address; sptr = (u_short *) &(pip->ip_src); accumulate -= *sptr++; accumulate -= *sptr; 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 ip *); static int FragmentOut(struct ip *); static int FragmentIn(struct ip *pip) { struct alias_link *link; link = FindFragmentIn2(pip->ip_src, pip->ip_dst, pip->ip_id); if (link != NULL) { struct in_addr original_address; GetFragmentAddr(link, &original_address); DifferentialChecksum(&pip->ip_sum, (u_short *) &original_address, (u_short *) &pip->ip_dst, 2); pip->ip_dst = original_address; return(PKT_ALIAS_OK); } return(PKT_ALIAS_UNRESOLVED_FRAGMENT); } static int FragmentOut(struct ip *pip) { struct in_addr alias_address; alias_address = FindAliasAddress(pip->ip_src); DifferentialChecksum(&pip->ip_sum, (u_short *) &alias_address, (u_short *) &pip->ip_src, 2); pip->ip_src = alias_address; return(PKT_ALIAS_OK); } /* Outside World Access PacketAliasSaveFragment() PacketAliasGetFragment() PacketAliasFragmentIn() PacketAliasIn() PacketAliasOut() (prototypes in alias.h) */ int PacketAliasSaveFragment(char *ptr) { int iresult; struct alias_link *link; struct ip *pip; pip = (struct ip *) ptr; link = AddFragmentPtrLink(pip->ip_src, pip->ip_id); iresult = PKT_ALIAS_ERROR; if (link != NULL) { SetFragmentPtr(link, ptr); iresult = PKT_ALIAS_OK; } return(iresult); } char * PacketAliasGetFragment(char *ptr) { struct alias_link *link; char *fptr; struct ip *pip; pip = (struct ip *) ptr; link = FindFragmentPtr(pip->ip_src, pip->ip_id); if (link != NULL) { GetFragmentPtr(link, &fptr); SetFragmentPtr(link, NULL); SetExpire(link, 0); /* Deletes link */ return(fptr); } else { return(NULL); } } void PacketAliasFragmentIn(char *ptr, /* Points to correctly de-aliased header fragment */ char *ptr_fragment /* Points to fragment which must be de-aliased */ ) { struct ip *pip; struct ip *fpip; pip = (struct ip *) ptr; fpip = (struct ip *) ptr_fragment; DifferentialChecksum(&fpip->ip_sum, (u_short *) &pip->ip_dst, (u_short *) &fpip->ip_dst, 2); fpip->ip_dst = pip->ip_dst; } int PacketAliasIn(char *ptr, int maxpacketsize) { struct in_addr alias_addr; struct ip *pip; int iresult; if (packetAliasMode & PKT_ALIAS_REVERSE) { packetAliasMode &= ~PKT_ALIAS_REVERSE; iresult = PacketAliasOut(ptr, maxpacketsize); packetAliasMode |= PKT_ALIAS_REVERSE; return iresult; } HouseKeeping(); ClearCheckNewLink(); pip = (struct ip *) ptr; alias_addr = pip->ip_dst; /* Defense against mangled packets */ if (ntohs(pip->ip_len) > maxpacketsize || (pip->ip_hl<<2) > maxpacketsize) return PKT_ALIAS_IGNORED; iresult = PKT_ALIAS_IGNORED; if ( (ntohs(pip->ip_off) & IP_OFFMASK) == 0 ) { switch (pip->ip_p) { case IPPROTO_ICMP: iresult = IcmpAliasIn(pip); break; case IPPROTO_UDP: iresult = UdpAliasIn(pip); break; case IPPROTO_TCP: iresult = TcpAliasIn(pip); break; - case IPPROTO_GRE: - case IPPROTO_ESP: - case IPPROTO_AH: - iresult = PptpAliasIn(pip); + default: + iresult = ProtoAliasIn(pip); break; } if (ntohs(pip->ip_off) & IP_MF) { struct alias_link *link; link = FindFragmentIn1(pip->ip_src, alias_addr, pip->ip_id); if (link != NULL) { iresult = PKT_ALIAS_FOUND_HEADER_FRAGMENT; SetFragmentAddr(link, pip->ip_dst); } else { iresult = PKT_ALIAS_ERROR; } } } else { iresult = FragmentIn(pip); } return(iresult); } /* Unregistered address ranges */ /* 10.0.0.0 -> 10.255.255.255 */ #define UNREG_ADDR_A_LOWER 0x0a000000 #define UNREG_ADDR_A_UPPER 0x0affffff /* 172.16.0.0 -> 172.31.255.255 */ #define UNREG_ADDR_B_LOWER 0xac100000 #define UNREG_ADDR_B_UPPER 0xac1fffff /* 192.168.0.0 -> 192.168.255.255 */ #define UNREG_ADDR_C_LOWER 0xc0a80000 #define UNREG_ADDR_C_UPPER 0xc0a8ffff int PacketAliasOut(char *ptr, /* valid IP packet */ int maxpacketsize /* How much the packet data may grow (FTP and IRC inline changes) */ ) { int iresult; struct in_addr addr_save; struct ip *pip; if (packetAliasMode & PKT_ALIAS_REVERSE) { packetAliasMode &= ~PKT_ALIAS_REVERSE; iresult = PacketAliasIn(ptr, maxpacketsize); packetAliasMode |= PKT_ALIAS_REVERSE; return iresult; } HouseKeeping(); ClearCheckNewLink(); pip = (struct ip *) ptr; /* Defense against mangled packets */ if (ntohs(pip->ip_len) > maxpacketsize || (pip->ip_hl<<2) > maxpacketsize) return PKT_ALIAS_IGNORED; addr_save = GetDefaultAliasAddress(); if (packetAliasMode & PKT_ALIAS_UNREGISTERED_ONLY) { 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(pip->ip_src); } } iresult = PKT_ALIAS_IGNORED; if ((ntohs(pip->ip_off) & IP_OFFMASK) == 0) { switch (pip->ip_p) { case IPPROTO_ICMP: iresult = IcmpAliasOut(pip); break; case IPPROTO_UDP: iresult = UdpAliasOut(pip); break; case IPPROTO_TCP: iresult = TcpAliasOut(pip, maxpacketsize); break; - case IPPROTO_GRE: - case IPPROTO_ESP: - case IPPROTO_AH: - iresult = PptpAliasOut(pip); + default: + iresult = ProtoAliasOut(pip); break; } } else { iresult = FragmentOut(pip); } SetDefaultAliasAddress(addr_save); return(iresult); } Index: head/sys/netinet/libalias/alias.h =================================================================== --- head/sys/netinet/libalias/alias.h (revision 59725) +++ head/sys/netinet/libalias/alias.h (revision 59726) @@ -1,172 +1,171 @@ /*lint -save -library Flexelint comment for external headers */ /* Alias.h defines the outside world interfaces for the packet aliasing software. This software is placed into the public domain with no restrictions on its distribution. $FreeBSD$ */ #ifndef _ALIAS_H_ #define _ALIAS_H_ /* Alias link representative (incomplete struct) */ struct alias_link; /* External interfaces (API) to packet aliasing engine */ /* Initialization and Control */ extern void PacketAliasInit(void); extern void PacketAliasUninit(void); extern void PacketAliasSetAddress(struct in_addr); extern unsigned int PacketAliasSetMode(unsigned int, unsigned int); #ifndef NO_FW_PUNCH extern void PacketAliasSetFWBase(unsigned int, unsigned int); #endif /* Packet Handling */ extern int PacketAliasIn(char *, int maxpacketsize); extern int PacketAliasOut(char *, int maxpacketsize); /* Port and Address Redirection */ extern struct alias_link * PacketAliasRedirectPort(struct in_addr, u_short, struct in_addr, u_short, struct in_addr, u_short, u_char); extern int PacketAliasAddServer(struct alias_link *link, struct in_addr addr, u_short port); extern int PacketAliasPptp(struct in_addr); extern struct alias_link * - PacketAliasRedirectPptp(struct in_addr, struct in_addr, struct in_addr); + PacketAliasRedirectProto(struct in_addr, + struct in_addr, + struct in_addr, + u_char); extern struct alias_link * PacketAliasRedirectAddr(struct in_addr, struct in_addr); extern void PacketAliasRedirectDelete(struct alias_link *); /* Fragment Handling */ extern int PacketAliasSaveFragment(char *); extern char * PacketAliasGetFragment(char *); extern void PacketAliasFragmentIn(char *, char *); /* Miscellaneous Functions */ extern void PacketAliasSetTarget(struct in_addr addr); extern int PacketAliasCheckNewLink(void); extern u_short PacketAliasInternetChecksum(u_short *, int); /* Transparent Proxying */ extern int PacketAliasProxyRule(const char *); /********************** Mode flags ********************/ /* Set these flags using PacketAliasSetMode() */ /* If PKT_ALIAS_LOG is set, a message will be printed to /var/log/alias.log every time a link is created or deleted. This is useful for debugging */ #define PKT_ALIAS_LOG 0x01 /* If PKT_ALIAS_DENY_INCOMING is set, then incoming connections (e.g. to ftp, telnet or web servers will be prevented by the aliasing mechanism. */ #define PKT_ALIAS_DENY_INCOMING 0x02 /* If PKT_ALIAS_SAME_PORTS is set, packets will be attempted sent from the same port as they originated on. This allows e.g. rsh to work *99% of the time*, but _not_ 100%. (It will be slightly flakey instead of not working at all.) This mode bit is set by PacketAliasInit(), so it is a default mode of operation. */ #define PKT_ALIAS_SAME_PORTS 0x04 /* If PKT_ALIAS_USE_SOCKETS is set, then when partially specified links (e.g. destination port and/or address is zero), the packet aliasing engine will attempt to allocate a socket for the aliasing port it chooses. This will avoid interference with the host machine. Fully specified links do not require this. This bit is set after a call to PacketAliasInit(), so it is a default mode of operation. */ #define PKT_ALIAS_USE_SOCKETS 0x08 /* If PKT_ALIAS_UNREGISTERED_ONLY is set, then only packets with unregistered source addresses will be aliased. Private addresses are those in the following ranges: 10.0.0.0 -> 10.255.255.255 172.16.0.0 -> 172.31.255.255 192.168.0.0 -> 192.168.255.255 */ #define PKT_ALIAS_UNREGISTERED_ONLY 0x10 /* If PKT_ALIAS_RESET_ON_ADDR_CHANGE is set, then the table of dynamic aliasing links will be reset whenever PacketAliasSetAddress() changes the default aliasing address. If the default aliasing address is left unchanged by this function call, then the table of dynamic aliasing links will be left intact. This bit is set after a call to PacketAliasInit(). */ #define PKT_ALIAS_RESET_ON_ADDR_CHANGE 0x20 #ifndef NO_FW_PUNCH /* If PKT_ALIAS_PUNCH_FW is set, active FTP and IRC DCC connections will create a 'hole' in the firewall to allow the transfers to work. Where (IPFW "line-numbers") the hole is created is controlled by PacketAliasSetFWBase(base, size). The hole will be attached to that particular alias_link, so when the link goes away so do the hole. */ #define PKT_ALIAS_PUNCH_FW 0x100 #endif /* If PKT_ALIAS_PROXY_ONLY is set, then NAT will be disabled and only transparent proxying performed */ #define PKT_ALIAS_PROXY_ONLY 0x40 /* If PKT_ALIAS_REVERSE is set, the actions of PacketAliasIn() and PacketAliasOut() are reversed */ #define PKT_ALIAS_REVERSE 0x80 - -/* If PKT_ALIAS_DENY_PPTP is set, then PPTP sessions will be - prevented by the aliasing engine. */ -#define PKT_ALIAS_DENY_PPTP 0x200 /* Return Codes */ #define PKT_ALIAS_ERROR -1 #define PKT_ALIAS_OK 1 #define PKT_ALIAS_IGNORED 2 #define PKT_ALIAS_UNRESOLVED_FRAGMENT 3 #define PKT_ALIAS_FOUND_HEADER_FRAGMENT 4 #endif /*lint -restore */ Index: head/sys/netinet/libalias/alias_db.c =================================================================== --- head/sys/netinet/libalias/alias_db.c (revision 59725) +++ head/sys/netinet/libalias/alias_db.c (revision 59726) @@ -1,2536 +1,2539 @@ /* -*- mode: c; tab-width: 8; c-basic-indent: 4; -*- Alias_db.c encapsulates all data structures used for storing packet aliasing data. Other parts of the aliasing software access data through functions provided in this file. Data storage is based on the notion of a "link", which is established for ICMP echo/reply packets, UDP datagrams and TCP stream connections. A link stores the original source and destination addresses. For UDP and TCP, it also stores source and destination port numbers, as well as an alias port number. Links are also used to store information about fragments. There is a facility for sweeping through and deleting old links as new packets are sent through. A simple timeout is used for ICMP and UDP links. TCP links are left alone unless there is an incomplete connection, in which case the link can be deleted after a certain amount of time. This software is placed into the public domain with no restrictions on its distribution. Initial version: August, 1996 (cjm) Version 1.4: September 16, 1996 (cjm) Facility for handling incoming links added. Version 1.6: September 18, 1996 (cjm) ICMP data handling simplified. Version 1.7: January 9, 1997 (cjm) Fragment handling simplified. Saves pointers for unresolved fragments. Permits links for unspecified remote ports or unspecified remote addresses. Fixed bug which did not properly zero port table entries after a link was deleted. Cleaned up some obsolete comments. Version 1.8: January 14, 1997 (cjm) Fixed data type error in StartPoint(). (This error did not exist prior to v1.7 and was discovered and fixed by Ari Suutari) Version 1.9: February 1, 1997 Optionally, connections initiated from packet aliasing host machine will will not have their port number aliased unless it conflicts with an aliasing port already being used. (cjm) All options earlier being #ifdef'ed are now available through a new interface, SetPacketAliasMode(). This allows run time control (which is now available in PPP+pktAlias through the 'alias' keyword). (ee) Added ability to create an alias port without either destination address or port specified. port type = ALIAS_PORT_UNKNOWN_DEST_ALL (ee) Removed K&R style function headers and general cleanup. (ee) Added packetAliasMode to replace compiler #defines's (ee) Allocates sockets for partially specified ports if ALIAS_USE_SOCKETS defined. (cjm) Version 2.0: March, 1997 SetAliasAddress() will now clean up alias links if the aliasing address is changed. (cjm) PacketAliasPermanentLink() function added to support permanent links. (J. Fortes suggested the need for this.) Examples: (192.168.0.1, port 23) <-> alias port 6002, unknown dest addr/port (192.168.0.2, port 21) <-> alias port 3604, known dest addr unknown dest port These permanent links allow for incoming connections to machines on the local network. They can be given with a user-chosen amount of specificity, with increasing specificity meaning more security. (cjm) Quite a bit of rework to the basic engine. The portTable[] array, which kept track of which ports were in use was replaced by a table/linked list structure. (cjm) SetExpire() function added. (cjm) DeleteLink() no longer frees memory association with a pointer to a fragment (this bug was first recognized by E. Eklund in v1.9). Version 2.1: May, 1997 (cjm) Packet aliasing engine reworked so that it can handle multiple external addresses rather than just a single host address. PacketAliasRedirectPort() and PacketAliasRedirectAddr() added to the API. The first function is a more generalized version of PacketAliasPermanentLink(). The second function implements static network address translation. See HISTORY file for additional revisions. $FreeBSD$ */ /* System include files */ #include #include #include #include #include #include #include /* BSD network include files */ #include #include #include #include #include #include "alias.h" #include "alias_local.h" /* Constants (note: constants are also defined near relevant functions or structs) */ /* Sizes of input and output link tables */ #define LINK_TABLE_OUT_SIZE 101 #define LINK_TABLE_IN_SIZE 4001 /* Parameters used for cleanup of expired links */ #define ALIAS_CLEANUP_INTERVAL_SECS 60 #define ALIAS_CLEANUP_MAX_SPOKES 30 /* Timeouts (in seconds) for different link types */ #define ICMP_EXPIRE_TIME 60 #define UDP_EXPIRE_TIME 60 -#define PPTP_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 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, PPTP, frag */ + int link_type; /* Type of link: TCP, UDP, ICMP, proto, frag */ /* values for link_type */ -#define LINK_ICMP 1 -#define LINK_UDP 2 -#define LINK_TCP 3 -#define LINK_FRAGMENT_ID 4 -#define LINK_FRAGMENT_PTR 5 -#define LINK_ADDR 6 -#define LINK_PPTP 7 +#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) int flags; /* indicates special characteristics */ /* flag bits */ #define LINK_UNKNOWN_DEST_PORT 0x01 #define LINK_UNKNOWN_DEST_ADDR 0x02 #define LINK_PERMANENT 0x04 #define LINK_PARTIALLY_SPECIFIED 0x03 /* logical-or of first two bits */ #define LINK_UNFIREWALLED 0x08 int timestamp; /* Time link was last accessed */ int expire_time; /* Expire time for link */ int sockfd; /* socket descriptor */ u_int start_point_out; /* Index number in output lookup table */ u_int start_point_in; struct alias_link *next_out; /* Linked list pointers for input and */ struct alias_link *last_out; /* output tables */ struct alias_link *next_in; /* . */ struct alias_link *last_in; /* . */ union /* Auxiliary data */ { char *frag_ptr; struct in_addr frag_addr; struct tcp_dat *tcp; } data; }; /* Global Variables The global variables listed here are only accessed from within alias_db.c and so are prefixed with the static designation. */ int packetAliasMode; /* Mode flags */ /* - documented in alias.h */ static struct in_addr aliasAddress; /* Address written onto source */ /* field of IP packet. */ static struct in_addr targetAddress; /* IP address incoming packets */ /* are sent to if no aliasing */ /* link already exists */ static struct in_addr nullAddress; /* Used as a dummy parameter for */ /* some function calls */ static struct alias_link * linkTableOut[LINK_TABLE_OUT_SIZE]; /* Lookup table of pointers to */ /* chains of link records. Each */ static struct alias_link * /* link record is doubly indexed */ linkTableIn[LINK_TABLE_IN_SIZE]; /* into input and output lookup */ /* tables. */ static int icmpLinkCount; /* Link statistics */ static int udpLinkCount; static int tcpLinkCount; -static int pptpLinkCount; +static int protoLinkCount; static int fragmentIdLinkCount; static int fragmentPtrLinkCount; static int sockCount; static int cleanupIndex; /* Index to chain of link table */ /* being inspected for old links */ static int timeStamp; /* System time in seconds for */ /* current packet */ static int lastCleanupTime; /* Last time IncrementalCleanup() */ /* was called */ static int houseKeepingResidual; /* used by HouseKeeping() */ static int deleteAllLinks; /* If equal to zero, DeleteLink() */ /* will not remove permanent links */ static FILE *monitorFile; /* File descriptor for link */ /* statistics monitoring file */ static int newDefaultLink; /* Indicates if a new aliasing */ /* link has been created after a */ /* call to PacketAliasIn/Out(). */ #ifndef NO_FW_PUNCH static int fireWallFD = -1; /* File descriptor to be able to */ /* control firewall. Opened by */ /* PacketAliasSetMode on first */ /* setting the PKT_ALIAS_PUNCH_FW */ /* flag. */ #endif /* Internal utility routines (used only in alias_db.c) Lookup table starting points: StartPointIn() -- link table initial search point for incoming packets StartPointOut() -- link table initial search point for outgoing packets Miscellaneous: SeqDiff() -- difference between two TCP sequences ShowAliasStats() -- send alias statistics to a monitor file */ /* Local prototypes */ static u_int StartPointIn(struct in_addr, u_short, int); static u_int StartPointOut(struct in_addr, struct in_addr, u_short, u_short, int); static int SeqDiff(u_long, u_long); static void ShowAliasStats(void); #ifndef NO_FW_PUNCH /* Firewall control */ static void InitPunchFW(void); static void UninitPunchFW(void); static void ClearFWHole(struct alias_link *link); #endif /* Log file control */ static void InitPacketAliasLog(void); static void UninitPacketAliasLog(void); static u_int StartPointIn(struct in_addr alias_addr, u_short alias_port, int link_type) { u_int n; n = alias_addr.s_addr; n += alias_port; n += link_type; return(n % LINK_TABLE_IN_SIZE); } static u_int StartPointOut(struct in_addr src_addr, struct in_addr dst_addr, u_short src_port, u_short dst_port, int link_type) { u_int n; n = src_addr.s_addr; n += dst_addr.s_addr; n += src_port; n += dst_port; n += link_type; return(n % LINK_TABLE_OUT_SIZE); } static int SeqDiff(u_long x, u_long y) { /* Return the difference between two TCP sequence numbers */ /* This function is encapsulated in case there are any unusual arithmetic conditions that need to be considered. */ return (ntohl(y) - ntohl(x)); } static void ShowAliasStats(void) { /* Used for debugging */ if (monitorFile) { - fprintf(monitorFile, "icmp=%d, udp=%d, tcp=%d, pptp=%d, frag_id=%d frag_ptr=%d", + fprintf(monitorFile, "icmp=%d, udp=%d, tcp=%d, proto=%d, frag_id=%d frag_ptr=%d", icmpLinkCount, udpLinkCount, tcpLinkCount, - pptpLinkCount, + protoLinkCount, fragmentIdLinkCount, fragmentPtrLinkCount); fprintf(monitorFile, " / tot=%d (sock=%d)\n", icmpLinkCount + udpLinkCount + tcpLinkCount - + pptpLinkCount + + protoLinkCount + fragmentIdLinkCount + fragmentPtrLinkCount, sockCount); fflush(monitorFile); } } /* Internal routines for finding, deleting and adding links Port Allocation: GetNewPort() -- find and reserve new alias port number GetSocket() -- try to allocate a socket for a given port Link creation and deletion: CleanupAliasData() - remove all link chains from lookup table IncrementalCleanup() - look for stale links in a single chain DeleteLink() - remove link AddLink() - add link ReLink() - change link Link search: FindLinkOut() - find link for outgoing packets FindLinkIn() - find link for incoming packets */ /* Local prototypes */ static int GetNewPort(struct alias_link *, int); static u_short GetSocket(u_short, int *, int); static void CleanupAliasData(void); static void IncrementalCleanup(void); static void DeleteLink(struct alias_link *); static struct alias_link * AddLink(struct in_addr, struct in_addr, struct in_addr, u_short, u_short, int, int); static struct alias_link * ReLink(struct alias_link *, struct in_addr, struct in_addr, struct in_addr, u_short, u_short, int, int); static struct alias_link * FindLinkOut(struct in_addr, struct in_addr, u_short, u_short, int, int); static struct alias_link * FindLinkIn(struct in_addr, struct in_addr, u_short, u_short, int, int); #define ALIAS_PORT_BASE 0x08000 #define ALIAS_PORT_MASK 0x07fff #define GET_NEW_PORT_MAX_ATTEMPTS 20 #define GET_ALIAS_PORT -1 #define GET_ALIAS_ID GET_ALIAS_PORT /* GetNewPort() allocates port numbers. Note that if a port number is already in use, that does not mean that it cannot be used by another link concurrently. This is because GetNewPort() looks for unused triplets: (dest addr, dest port, alias port). */ static int GetNewPort(struct alias_link *link, int alias_port_param) { int i; int max_trials; u_short port_sys; u_short port_net; /* Description of alias_port_param for GetNewPort(). When this parameter is zero or positive, it precisely specifies the port number. GetNewPort() will return this number without check that it is in use. When this parameter is -1, it indicates to get a randomly selected port number. */ if (alias_port_param == GET_ALIAS_PORT) { /* * The aliasing port is automatically selected * by one of two methods below: */ max_trials = GET_NEW_PORT_MAX_ATTEMPTS; if (packetAliasMode & PKT_ALIAS_SAME_PORTS) { /* * When the 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; idst_addr, link->alias_addr, link->dst_port, port_net, link->link_type, 0); if (search_result == NULL) go_ahead = 1; else if (!(link->flags & LINK_PARTIALLY_SPECIFIED) && (search_result->flags & LINK_PARTIALLY_SPECIFIED)) go_ahead = 1; else go_ahead = 0; if (go_ahead) { if ((packetAliasMode & PKT_ALIAS_USE_SOCKETS) && (link->flags & LINK_PARTIALLY_SPECIFIED)) { if (GetSocket(port_net, &link->sockfd, link->link_type)) { link->alias_port = port_net; return(0); } } else { link->alias_port = port_net; return(0); } } port_sys = random() & ALIAS_PORT_MASK; port_sys += ALIAS_PORT_BASE; port_net = htons(port_sys); } #ifdef DEBUG fprintf(stderr, "PacketAlias/GetnewPort(): "); fprintf(stderr, "could not find free port\n"); #endif return(-1); } static u_short GetSocket(u_short port_net, int *sockfd, int link_type) { int err; int sock; struct sockaddr_in sock_addr; if (link_type == LINK_TCP) sock = socket(AF_INET, SOCK_STREAM, 0); else if (link_type == LINK_UDP) sock = socket(AF_INET, SOCK_DGRAM, 0); else { #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) { sockCount++; *sockfd = sock; return(1); } else { close(sock); return(0); } } static void CleanupAliasData(void) { struct alias_link *link; int i, icount; icount = 0; for (i=0; inext_out; icount++; DeleteLink(link); link = link_next; } } cleanupIndex =0; } static void IncrementalCleanup(void) { int icount; struct alias_link *link; icount = 0; link = linkTableOut[cleanupIndex++]; while (link != NULL) { int idelta; struct alias_link *link_next; link_next = link->next_out; idelta = timeStamp - link->timestamp; switch (link->link_type) { - case LINK_ICMP: - case LINK_UDP: - case LINK_FRAGMENT_ID: - case LINK_FRAGMENT_PTR: - case LINK_PPTP: - if (idelta > link->expire_time) - { - DeleteLink(link); - icount++; - } - break; case LINK_TCP: if (idelta > link->expire_time) { struct tcp_dat *tcp_aux; tcp_aux = link->data.tcp; if (tcp_aux->state.in != ALIAS_TCP_STATE_CONNECTED || tcp_aux->state.out != ALIAS_TCP_STATE_CONNECTED) { DeleteLink(link); icount++; } } break; + default: + if (idelta > link->expire_time) + { + DeleteLink(link); + icount++; + } + break; } link = link_next; } if (cleanupIndex == LINK_TABLE_OUT_SIZE) cleanupIndex = 0; } void DeleteLink(struct alias_link *link) { struct alias_link *link_last; struct alias_link *link_next; /* Don't do anything if the link is marked permanent */ if (deleteAllLinks == 0 && link->flags & LINK_PERMANENT) return; #ifndef NO_FW_PUNCH /* Delete 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 */ link_last = link->last_out; link_next = link->next_out; if (link_last != NULL) link_last->next_out = link_next; else linkTableOut[link->start_point_out] = link_next; if (link_next != NULL) link_next->last_out = link_last; /* Adjust input table pointers */ link_last = link->last_in; link_next = link->next_in; if (link_last != NULL) link_last->next_in = link_next; else linkTableIn[link->start_point_in] = link_next; if (link_next != NULL) link_next->last_in = link_last; /* Close socket, if one has been allocated */ if (link->sockfd != -1) { sockCount--; close(link->sockfd); } /* Link-type dependent cleanup */ switch(link->link_type) { case LINK_ICMP: icmpLinkCount--; break; case LINK_UDP: udpLinkCount--; break; case LINK_TCP: tcpLinkCount--; if (link->data.tcp != NULL) free(link->data.tcp); break; - case LINK_PPTP: - pptpLinkCount--; - break; case LINK_FRAGMENT_ID: fragmentIdLinkCount--; break; case LINK_FRAGMENT_PTR: fragmentPtrLinkCount--; if (link->data.frag_ptr != NULL) free(link->data.frag_ptr); break; + case LINK_ADDR: + break; + default: + protoLinkCount--; + break; } /* Free memory */ free(link); /* Write statistics, if logging enabled */ if (packetAliasMode & PKT_ALIAS_LOG) { ShowAliasStats(); } } static struct alias_link * AddLink(struct in_addr src_addr, struct in_addr dst_addr, struct in_addr alias_addr, u_short src_port, u_short dst_port, int alias_port_param, /* if less than zero, alias */ int link_type) /* port will be automatically */ { /* chosen. If greater than */ u_int start_point; /* zero, equal to alias port */ struct alias_link *link; struct alias_link *first_link; link = malloc(sizeof(struct alias_link)); if (link != NULL) { /* Basic initialization */ 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->timestamp = timeStamp; /* Expiration time */ switch (link_type) { case LINK_ICMP: link->expire_time = ICMP_EXPIRE_TIME; break; case LINK_UDP: link->expire_time = UDP_EXPIRE_TIME; break; case LINK_TCP: link->expire_time = TCP_EXPIRE_INITIAL; break; - case LINK_PPTP: - link->expire_time = PPTP_EXPIRE_TIME; - break; case LINK_FRAGMENT_ID: link->expire_time = FRAGMENT_ID_EXPIRE_TIME; break; case LINK_FRAGMENT_PTR: link->expire_time = FRAGMENT_PTR_EXPIRE_TIME; break; + 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(link, alias_port_param) != 0) { free(link); return(NULL); } /* Set up pointers for output lookup table */ start_point = StartPointOut(src_addr, dst_addr, src_port, dst_port, link_type); first_link = linkTableOut[start_point]; link->last_out = NULL; link->next_out = first_link; link->start_point_out = start_point; if (first_link != NULL) first_link->last_out = link; linkTableOut[start_point] = link; /* Set up pointers for input lookup table */ start_point = StartPointIn(alias_addr, link->alias_port, link_type); first_link = linkTableIn[start_point]; link->last_in = NULL; link->next_in = first_link; link->start_point_in = start_point; if (first_link != NULL) first_link->last_in = link; linkTableIn[start_point] = link; /* Link-type dependent initialization */ switch(link_type) { struct tcp_dat *aux_tcp; case LINK_ICMP: icmpLinkCount++; break; case LINK_UDP: udpLinkCount++; break; case LINK_TCP: aux_tcp = malloc(sizeof(struct tcp_dat)); link->data.tcp = aux_tcp; if (aux_tcp != NULL) { int i; tcpLinkCount++; aux_tcp->state.in = ALIAS_TCP_STATE_NOT_CONNECTED; aux_tcp->state.out = ALIAS_TCP_STATE_NOT_CONNECTED; aux_tcp->state.index = 0; aux_tcp->state.ack_modified = 0; for (i=0; iack[i].active = 0; aux_tcp->fwhole = -1; } else { #ifdef DEBUG fprintf(stderr, "PacketAlias/AddLink: "); fprintf(stderr, " cannot allocate auxiliary TCP data\n"); #endif } break; - case LINK_PPTP: - pptpLinkCount++; - break; case LINK_FRAGMENT_ID: fragmentIdLinkCount++; break; case LINK_FRAGMENT_PTR: fragmentPtrLinkCount++; break; + case LINK_ADDR: + break; + default: + protoLinkCount++; + break; } } else { #ifdef DEBUG fprintf(stderr, "PacketAlias/AddLink(): "); fprintf(stderr, "malloc() call failed.\n"); #endif } if (packetAliasMode & PKT_ALIAS_LOG) { ShowAliasStats(); } return(link); } static struct alias_link * ReLink(struct alias_link *old_link, struct in_addr src_addr, struct in_addr dst_addr, struct in_addr alias_addr, u_short src_port, u_short dst_port, int alias_port_param, /* if less than zero, alias */ int link_type) /* port will be automatically */ { /* chosen. If greater than */ struct alias_link *new_link; /* zero, equal to alias port */ new_link = AddLink(src_addr, dst_addr, alias_addr, src_port, dst_port, alias_port_param, link_type); #ifndef NO_FW_PUNCH if (new_link != NULL && old_link->link_type == LINK_TCP && old_link->data.tcp && old_link->data.tcp->fwhole > 0) { PunchFWHole(new_link); } #endif DeleteLink(old_link); return new_link; } static struct alias_link * _FindLinkOut(struct in_addr src_addr, struct in_addr dst_addr, u_short src_port, u_short dst_port, int link_type, int replace_partial_links) { u_int i; struct alias_link *link; i = StartPointOut(src_addr, dst_addr, src_port, dst_port, link_type); link = linkTableOut[i]; while (link != NULL) { if (link->src_addr.s_addr == src_addr.s_addr && link->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 = timeStamp; break; } link = link->next_out; } /* Search for partially specified links. */ if (link == NULL && replace_partial_links) { if (dst_port != 0 && dst_addr.s_addr != INADDR_ANY) { link = _FindLinkOut(src_addr, dst_addr, src_port, 0, link_type, 0); if (link == NULL) link = _FindLinkOut(src_addr, nullAddress, src_port, dst_port, link_type, 0); } if (link == NULL && (dst_port != 0 || dst_addr.s_addr != INADDR_ANY)) { link = _FindLinkOut(src_addr, 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 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(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 (aliasAddress.s_addr != 0 && src_addr.s_addr == aliasAddress.s_addr) { link = _FindLinkOut(nullAddress, dst_addr, src_port, dst_port, link_type, replace_partial_links); } } return(link); } static struct alias_link * _FindLinkIn(struct in_addr dst_addr, struct in_addr alias_addr, u_short dst_port, u_short alias_port, int link_type, int replace_partial_links) { int flags_in; u_int start_point; struct alias_link *link; struct alias_link *link_fully_specified; struct alias_link *link_unknown_all; struct alias_link *link_unknown_dst_addr; struct alias_link *link_unknown_dst_port; /* Initialize pointers */ link_fully_specified = NULL; link_unknown_all = NULL; link_unknown_dst_addr = NULL; link_unknown_dst_port = NULL; /* If either the dest addr or port is unknown, the search loop will have to know about this. */ flags_in = 0; if (dst_addr.s_addr == 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); link = linkTableIn[start_point]; while (link != NULL) { int flags; flags = flags_in | link->flags; if (!(flags & LINK_PARTIALLY_SPECIFIED)) { if (link->alias_addr.s_addr == alias_addr.s_addr && link->alias_port == alias_port && link->dst_addr.s_addr == dst_addr.s_addr && link->dst_port == dst_port && link->link_type == link_type) { link_fully_specified = link; break; } } else if ((flags & LINK_UNKNOWN_DEST_ADDR) && (flags & LINK_UNKNOWN_DEST_PORT)) { if (link->alias_addr.s_addr == alias_addr.s_addr && link->alias_port == alias_port && link->link_type == link_type) { if (link_unknown_all == NULL) link_unknown_all = link; } } else if (flags & LINK_UNKNOWN_DEST_ADDR) { if (link->alias_addr.s_addr == alias_addr.s_addr && link->alias_port == alias_port && link->link_type == link_type && link->dst_port == dst_port) { if (link_unknown_dst_addr == NULL) link_unknown_dst_addr = link; } } else if (flags & LINK_UNKNOWN_DEST_PORT) { if (link->alias_addr.s_addr == alias_addr.s_addr && link->alias_port == alias_port && link->link_type == link_type && link->dst_addr.s_addr == dst_addr.s_addr) { if (link_unknown_dst_port == NULL) link_unknown_dst_port = link; } } link = link->next_in; } if (link_fully_specified != NULL) { link_fully_specified->timestamp = 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 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(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 (aliasAddress.s_addr != 0 && alias_addr.s_addr == aliasAddress.s_addr) { link = _FindLinkIn(dst_addr, 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() - FindPptpIn(), FindPptpOut() + FindProtoIn(), FindProtoOut() FindUdpTcpIn(), FindUdpTcpOut() FindOriginalAddress(), FindAliasAddress() (prototypes in alias_local.h) */ struct alias_link * FindIcmpIn(struct in_addr dst_addr, struct in_addr alias_addr, u_short id_alias) { return FindLinkIn(dst_addr, alias_addr, NO_DEST_PORT, id_alias, LINK_ICMP, 0); } struct alias_link * FindIcmpOut(struct in_addr src_addr, struct in_addr dst_addr, u_short id) { struct alias_link * link; link = FindLinkOut(src_addr, dst_addr, id, NO_DEST_PORT, LINK_ICMP, 0); if (link == NULL) { struct in_addr alias_addr; alias_addr = FindAliasAddress(src_addr); link = AddLink(src_addr, dst_addr, alias_addr, id, NO_DEST_PORT, GET_ALIAS_ID, LINK_ICMP); } return(link); } struct alias_link * FindFragmentIn1(struct in_addr dst_addr, struct in_addr alias_addr, u_short ip_id) { struct alias_link *link; link = FindLinkIn(dst_addr, alias_addr, NO_DEST_PORT, ip_id, LINK_FRAGMENT_ID, 0); if (link == NULL) { link = AddLink(nullAddress, dst_addr, alias_addr, NO_SRC_PORT, NO_DEST_PORT, ip_id, LINK_FRAGMENT_ID); } return(link); } struct alias_link * FindFragmentIn2(struct in_addr dst_addr, /* Doesn't add a link if one */ struct in_addr alias_addr, /* is not found. */ u_short ip_id) { return FindLinkIn(dst_addr, alias_addr, NO_DEST_PORT, ip_id, LINK_FRAGMENT_ID, 0); } struct alias_link * AddFragmentPtrLink(struct in_addr dst_addr, u_short ip_id) { return AddLink(nullAddress, dst_addr, nullAddress, NO_SRC_PORT, NO_DEST_PORT, ip_id, LINK_FRAGMENT_PTR); } struct alias_link * FindFragmentPtr(struct in_addr dst_addr, u_short ip_id) { return FindLinkIn(dst_addr, nullAddress, NO_DEST_PORT, ip_id, LINK_FRAGMENT_PTR, 0); } struct alias_link * -FindPptpIn(struct in_addr dst_addr, - struct in_addr alias_addr) +FindProtoIn(struct in_addr dst_addr, + struct in_addr alias_addr, + u_char proto) { struct alias_link *link; link = FindLinkIn(dst_addr, alias_addr, NO_DEST_PORT, 0, - LINK_PPTP, 1); + proto, 1); if (link == NULL && !(packetAliasMode & PKT_ALIAS_DENY_INCOMING)) { struct in_addr target_addr; target_addr = FindOriginalAddress(alias_addr); link = AddLink(target_addr, dst_addr, alias_addr, NO_SRC_PORT, NO_DEST_PORT, 0, - LINK_PPTP); + proto); } return (link); } struct alias_link * -FindPptpOut(struct in_addr src_addr, - struct in_addr dst_addr) +FindProtoOut(struct in_addr src_addr, + struct in_addr dst_addr, + u_char proto) { struct alias_link *link; link = FindLinkOut(src_addr, dst_addr, NO_SRC_PORT, NO_DEST_PORT, - LINK_PPTP, 1); + proto, 1); if (link == NULL) { struct in_addr alias_addr; alias_addr = FindAliasAddress(src_addr); link = AddLink(src_addr, dst_addr, alias_addr, NO_SRC_PORT, NO_DEST_PORT, 0, - LINK_PPTP); + proto); } return (link); } struct alias_link * FindUdpTcpIn(struct in_addr dst_addr, struct in_addr alias_addr, u_short dst_port, u_short alias_port, u_char proto) { int link_type; struct alias_link *link; switch (proto) { case IPPROTO_UDP: link_type = LINK_UDP; break; case IPPROTO_TCP: link_type = LINK_TCP; break; default: return NULL; break; } link = FindLinkIn(dst_addr, alias_addr, dst_port, alias_port, link_type, 1); if (!(packetAliasMode & PKT_ALIAS_DENY_INCOMING) && !(packetAliasMode & PKT_ALIAS_PROXY_ONLY) && link == NULL) { struct in_addr target_addr; target_addr = FindOriginalAddress(alias_addr); link = AddLink(target_addr, dst_addr, alias_addr, alias_port, dst_port, alias_port, link_type); } return(link); } struct alias_link * FindUdpTcpOut(struct in_addr src_addr, struct in_addr dst_addr, u_short src_port, u_short dst_port, u_char proto) { int link_type; struct alias_link *link; switch (proto) { case IPPROTO_UDP: link_type = LINK_UDP; break; case IPPROTO_TCP: link_type = LINK_TCP; break; default: return NULL; break; } link = FindLinkOut(src_addr, dst_addr, src_port, dst_port, link_type, 1); if (link == NULL) { struct in_addr alias_addr; alias_addr = FindAliasAddress(src_addr); link = AddLink(src_addr, dst_addr, alias_addr, src_port, dst_port, GET_ALIAS_PORT, link_type); } return(link); } struct in_addr FindOriginalAddress(struct in_addr alias_addr) { struct alias_link *link; link = FindLinkIn(nullAddress, alias_addr, 0, 0, LINK_ADDR, 0); if (link == NULL) { newDefaultLink = 1; if (targetAddress.s_addr == INADDR_ANY) return alias_addr; else if (targetAddress.s_addr == INADDR_NONE) return aliasAddress; else return 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 aliasAddress; else return link->src_addr; } } struct in_addr FindAliasAddress(struct in_addr original_addr) { struct alias_link *link; link = FindLinkOut(original_addr, nullAddress, 0, 0, LINK_ADDR, 0); if (link == NULL) { return aliasAddress; } else { if (link->alias_addr.s_addr == INADDR_ANY) return aliasAddress; else return link->alias_addr; } } /* External routines for getting or changing link data (external to alias_db.c, but internal to alias*.c) SetFragmentData(), GetFragmentData() SetFragmentPtr(), GetFragmentPtr() SetStateIn(), SetStateOut(), GetStateIn(), GetStateOut() GetOriginalAddress(), GetDestAddress(), GetAliasAddress() GetOriginalPort(), GetAliasPort() SetAckModified(), GetAckModified() GetDeltaAckIn(), GetDeltaSeqOut(), AddSeq() */ void SetFragmentAddr(struct alias_link *link, struct in_addr src_addr) { link->data.frag_addr = src_addr; } void GetFragmentAddr(struct alias_link *link, struct in_addr *src_addr) { *src_addr = link->data.frag_addr; } void SetFragmentPtr(struct alias_link *link, char *fptr) { link->data.frag_ptr = fptr; } void GetFragmentPtr(struct alias_link *link, char **fptr) { *fptr = link->data.frag_ptr; } void SetStateIn(struct alias_link *link, int state) { /* TCP input state */ switch (state) { case ALIAS_TCP_STATE_DISCONNECTED: if (link->data.tcp->state.out != ALIAS_TCP_STATE_CONNECTED) link->expire_time = TCP_EXPIRE_DEAD; else link->expire_time = TCP_EXPIRE_SINGLEDEAD; 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 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 aliasAddress; else return link->alias_addr; } struct in_addr GetDefaultAliasAddress() { return aliasAddress; } void SetDefaultAliasAddress(struct in_addr alias_addr) { aliasAddress = alias_addr; } u_short GetOriginalPort(struct alias_link *link) { return(link->src_port); } u_short GetAliasPort(struct alias_link *link) { return(link->alias_port); } #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; idata.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; idata.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(void) { newDefaultLink = 0; } /* Miscellaneous Functions HouseKeeping() InitPacketAliasLog() UninitPacketAliasLog() */ /* Whenever an outgoing or incoming packet is handled, HouseKeeping() is called to find and remove timed-out aliasing links. Logic exists to sweep through the entire table and linked list structure every 60 seconds. (prototype in alias_local.h) */ void HouseKeeping(void) { int i, n, n100; struct timeval tv; struct timezone tz; /* * Save system time (seconds) in global variable timeStamp for * use by other functions. This is done so as not to unnecessarily * waste timeline by making system calls. */ gettimeofday(&tv, &tz); timeStamp = tv.tv_sec; /* Compute number of spokes (output table link chains) to cover */ n100 = LINK_TABLE_OUT_SIZE * 100 + houseKeepingResidual; n100 *= timeStamp - lastCleanupTime; n100 /= ALIAS_CLEANUP_INTERVAL_SECS; n = n100/100; /* Handle different cases */ if (n > ALIAS_CLEANUP_MAX_SPOKES) { n = ALIAS_CLEANUP_MAX_SPOKES; lastCleanupTime = timeStamp; houseKeepingResidual = 0; for (i=0; i 0) { lastCleanupTime = timeStamp; houseKeepingResidual = n100 - 100*n; for (i=0; iflags |= LINK_PERMANENT; } #ifdef DEBUG else { fprintf(stderr, "PacketAliasRedirectPort(): " "call to AddLink() failed\n"); } #endif return link; } /* Add server to the pool of servers */ int PacketAliasAddServer(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); } /* Translate PPTP packets to a machine on the inside - * XXX This function is made obsolete by PacketAliasRedirectPptp(). + * XXX This function is made obsolete by PacketAliasRedirectProto(). */ int PacketAliasPptp(struct in_addr src_addr) { - if (src_addr.s_addr == INADDR_NONE) - packetAliasMode |= PKT_ALIAS_DENY_PPTP; - else - (void)PacketAliasRedirectPptp(src_addr, nullAddress, nullAddress); + if (src_addr.s_addr != INADDR_NONE) + (void)PacketAliasRedirectProto(src_addr, nullAddress, nullAddress, + IPPROTO_GRE); return 1; } -/* Redirect PPTP packets from a specific +/* Redirect packets of a given IP protocol from a specific public address to a private address */ struct alias_link * -PacketAliasRedirectPptp(struct in_addr src_addr, - struct in_addr dst_addr, - struct in_addr alias_addr) +PacketAliasRedirectProto(struct in_addr src_addr, + struct in_addr dst_addr, + struct in_addr alias_addr, + u_char proto) { struct alias_link *link; link = AddLink(src_addr, dst_addr, alias_addr, NO_SRC_PORT, NO_DEST_PORT, 0, - LINK_PPTP); + proto); if (link != NULL) { link->flags |= LINK_PERMANENT; } #ifdef DEBUG else { - fprintf(stderr, "PacketAliasRedirectPptp(): " + fprintf(stderr, "PacketAliasRedirectProto(): " "call to AddLink() failed\n"); } #endif return link; } /* Static address translation */ struct alias_link * PacketAliasRedirectAddr(struct in_addr src_addr, struct in_addr alias_addr) { struct alias_link *link; link = AddLink(src_addr, nullAddress, alias_addr, 0, 0, 0, LINK_ADDR); if (link != NULL) { link->flags |= LINK_PERMANENT; } #ifdef DEBUG else { fprintf(stderr, "PacketAliasRedirectAddr(): " "call to AddLink() failed\n"); } #endif return link; } void PacketAliasRedirectDelete(struct alias_link *link) { /* This is a dangerous function to put in the API, because an invalid pointer can crash the program. */ deleteAllLinks = 1; DeleteLink(link); deleteAllLinks = 0; } void PacketAliasSetAddress(struct in_addr addr) { if (packetAliasMode & PKT_ALIAS_RESET_ON_ADDR_CHANGE && aliasAddress.s_addr != addr.s_addr) CleanupAliasData(); aliasAddress = addr; } void PacketAliasSetTarget(struct in_addr target_addr) { targetAddress = target_addr; } void PacketAliasInit(void) { int i; struct timeval tv; struct timezone tz; static int firstCall = 1; if (firstCall == 1) { gettimeofday(&tv, &tz); timeStamp = tv.tv_sec; lastCleanupTime = tv.tv_sec; houseKeepingResidual = 0; for (i=0; i #include #include #include #include static void ClearAllFWHoles(void); static int fireWallBaseNum; /* The first firewall entry free for our use */ static int fireWallNumNums; /* How many entries can we use? */ static int fireWallActiveNum; /* Which entry did we last use? */ static char *fireWallField; /* bool array for entries */ #define fw_setfield(field, num) \ do { \ (field)[num] = 1; \ } /*lint -save -e717 */ while(0) /*lint -restore */ #define fw_clrfield(field, num) \ do { \ (field)[num] = 0; \ } /*lint -save -e717 */ while(0) /*lint -restore */ #define fw_tstfield(field, num) ((field)[num]) void PacketAliasSetFWBase(unsigned int base, unsigned int num) { fireWallBaseNum = base; fireWallNumNums = num; } static void InitPunchFW(void) { fireWallField = malloc(fireWallNumNums); if (fireWallField) { memset(fireWallField, 0, fireWallNumNums); if (fireWallFD < 0) { fireWallFD = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); } ClearAllFWHoles(); fireWallActiveNum = fireWallBaseNum; } } static void UninitPunchFW(void) { ClearAllFWHoles(); if (fireWallFD >= 0) close(fireWallFD); fireWallFD = -1; if (fireWallField) free(fireWallField); fireWallField = NULL; packetAliasMode &= ~PKT_ALIAS_PUNCH_FW; } /* Make a certain link go through the firewall */ void PunchFWHole(struct alias_link *link) { int r; /* Result code */ struct ip_fw rule; /* On-the-fly built rule */ int fwhole; /* Where to punch hole */ /* Don't do anything unless we are asked to */ if ( !(packetAliasMode & PKT_ALIAS_PUNCH_FW) || fireWallFD < 0 || link->link_type != LINK_TCP || !link->data.tcp) return; memset(&rule, 0, sizeof rule); /** Build rule **/ /* Find empty slot */ for (fwhole = fireWallActiveNum; fwhole < fireWallBaseNum + fireWallNumNums && fw_tstfield(fireWallField, fwhole); fwhole++) ; if (fwhole >= fireWallBaseNum + fireWallNumNums || fw_tstfield(fireWallField, fwhole)) { for (fwhole = fireWallBaseNum; fwhole < fireWallActiveNum && fw_tstfield(fireWallField, fwhole); fwhole++) ; if (fwhole == fireWallActiveNum) { /* No rule point empty - we can't punch more holes. */ fireWallActiveNum = fireWallBaseNum; #ifdef DEBUG fprintf(stderr, "libalias: Unable to create firewall hole!\n"); #endif return; } } /* Start next search at next position */ fireWallActiveNum = fwhole+1; /* Build generic part of the two rules */ rule.fw_number = fwhole; rule.fw_nports = 1; /* Number of source ports; dest ports follow */ rule.fw_flg = IP_FW_F_ACCEPT; rule.fw_prot = IPPROTO_TCP; rule.fw_smsk.s_addr = INADDR_BROADCAST; rule.fw_dmsk.s_addr = INADDR_BROADCAST; /* Build and apply specific part of the rules */ rule.fw_src = GetOriginalAddress(link); rule.fw_dst = GetDestAddress(link); rule.fw_uar.fw_pts[0] = ntohs(GetOriginalPort(link)); rule.fw_uar.fw_pts[1] = ntohs(GetDestPort(link)); /* Skip non-bound links - XXX should not be strictly necessary, but seems to leave hole if not done. Leak of non-bound links? (Code should be left even if the problem is fixed - it is a clear optimization) */ if (rule.fw_uar.fw_pts[0] != 0 && rule.fw_uar.fw_pts[1] != 0) { r = setsockopt(fireWallFD, IPPROTO_IP, IP_FW_ADD, &rule, sizeof rule); #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 } /* Indicate hole applied */ link->data.tcp->fwhole = fwhole; fw_setfield(fireWallField, fwhole); } /* Remove a hole in a firewall associated with a particular alias link. Calling this too often is harmless. */ static void ClearFWHole(struct alias_link *link) { if (link->link_type == LINK_TCP && link->data.tcp) { int fwhole = link->data.tcp->fwhole; /* Where is the firewall hole? */ struct ip_fw rule; if (fwhole < 0) return; memset(&rule, 0, sizeof rule); rule.fw_number = fwhole; while (!setsockopt(fireWallFD, IPPROTO_IP, IP_FW_DEL, &rule, sizeof rule)) ; fw_clrfield(fireWallField, fwhole); link->data.tcp->fwhole = -1; } } /* Clear out the entire range dedicated to firewall holes. */ static void ClearAllFWHoles(void) { struct ip_fw rule; /* On-the-fly built rule */ int i; if (fireWallFD < 0) return; memset(&rule, 0, sizeof rule); for (i = fireWallBaseNum; i < fireWallBaseNum + fireWallNumNums; i++) { rule.fw_number = i; while (!setsockopt(fireWallFD, IPPROTO_IP, IP_FW_DEL, &rule, sizeof rule)) ; } memset(fireWallField, 0, fireWallNumNums); } #endif Index: head/sys/netinet/libalias/alias_local.h =================================================================== --- head/sys/netinet/libalias/alias_local.h (revision 59725) +++ head/sys/netinet/libalias/alias_local.h (revision 59726) @@ -1,179 +1,179 @@ /* -*- mode: c; tab-width: 3; c-basic-offset: 3; -*- Alias_local.h contains the function prototypes for alias.c, alias_db.c, alias_util.c and alias_ftp.c, alias_irc.c (as well as any future add-ons). It also includes macros, globals and struct definitions shared by more than one alias*.c file. This include file is intended to be used only within the aliasing software. Outside world interfaces are defined in alias.h This software is placed into the public domain with no restrictions on its distribution. Initial version: August, 1996 (cjm) $FreeBSD$ */ #ifndef ALIAS_LOCAL_H #define ALIAS_LOCAL_H #ifndef NULL #define NULL 0 #endif /* Macros */ /* The following macro is used to update an internet checksum. "delta" is a 32-bit accumulation of all the changes to the checksum (adding in new 16-bit words and subtracting out old words), and "cksum" is the checksum value to be updated. */ #define ADJUST_CHECKSUM(acc, cksum) { \ acc += cksum; \ if (acc < 0) \ { \ acc = -acc; \ acc = (acc >> 16) + (acc & 0xffff); \ acc += acc >> 16; \ cksum = (u_short) ~acc; \ } \ else \ { \ acc = (acc >> 16) + (acc & 0xffff); \ acc += acc >> 16; \ cksum = (u_short) acc; \ } \ } /* Globals */ extern int packetAliasMode; /* Structs */ struct alias_link; /* Incomplete structure */ /* Prototypes */ /* General utilities */ u_short IpChecksum(struct ip *); u_short TcpChecksum(struct ip *); void DifferentialChecksum(u_short *, u_short *, u_short *, int); /* Internal data access */ struct alias_link * FindIcmpIn(struct in_addr, struct in_addr, u_short); struct alias_link * FindIcmpOut(struct in_addr, struct in_addr, u_short); struct alias_link * FindFragmentIn1(struct in_addr, struct in_addr, u_short); struct alias_link * FindFragmentIn2(struct in_addr, struct in_addr, u_short); struct alias_link * AddFragmentPtrLink(struct in_addr, u_short); struct alias_link * FindFragmentPtr(struct in_addr, u_short); struct alias_link * -FindPptpIn(struct in_addr, struct in_addr); +FindProtoIn(struct in_addr, struct in_addr, u_char); struct alias_link * -FindPptpOut(struct in_addr, struct in_addr); +FindProtoOut(struct in_addr, struct in_addr, u_char); struct alias_link * FindUdpTcpIn (struct in_addr, struct in_addr, u_short, u_short, u_char); struct alias_link * FindUdpTcpOut(struct in_addr, struct in_addr, u_short, u_short, u_char); struct in_addr FindOriginalAddress(struct in_addr); struct in_addr FindAliasAddress(struct in_addr); /* External data access/modification */ void GetFragmentAddr(struct alias_link *, struct in_addr *); void SetFragmentAddr(struct alias_link *, struct in_addr); void GetFragmentPtr(struct alias_link *, char **); void SetFragmentPtr(struct alias_link *, char *); void SetStateIn(struct alias_link *, int); void SetStateOut(struct alias_link *, int); int GetStateIn(struct alias_link *); int GetStateOut(struct alias_link *); struct in_addr GetOriginalAddress(struct alias_link *); struct in_addr GetDestAddress(struct alias_link *); struct in_addr GetAliasAddress(struct alias_link *); struct in_addr GetDefaultAliasAddress(void); void SetDefaultAliasAddress(struct in_addr); u_short GetOriginalPort(struct alias_link *); u_short GetAliasPort(struct alias_link *); struct in_addr GetProxyAddress(struct alias_link *); void SetProxyAddress(struct alias_link *, struct in_addr); u_short GetProxyPort(struct alias_link *); void SetProxyPort(struct alias_link *, u_short); void SetAckModified(struct alias_link *); int GetAckModified(struct alias_link *); int GetDeltaAckIn(struct ip *, struct alias_link *); int GetDeltaSeqOut(struct ip *, struct alias_link *); void AddSeq(struct ip *, struct alias_link *, int); void SetExpire(struct alias_link *, int); void ClearCheckNewLink(void); #ifndef NO_FW_PUNCH void PunchFWHole(struct alias_link *); #endif /* Housekeeping function */ void HouseKeeping(void); /* Tcp specfic routines */ /*lint -save -library Suppress flexelint warnings */ /* FTP routines */ void AliasHandleFtpOut(struct ip *, struct alias_link *, int); /* IRC routines */ void AliasHandleIrcOut(struct ip *, struct alias_link *, int); /* NetBIOS routines */ int AliasHandleUdpNbt(struct ip *, struct alias_link *, struct in_addr *, u_short); int AliasHandleUdpNbtNS(struct ip *, struct alias_link *, struct in_addr *, u_short *, struct in_addr *, u_short *); /* CUSeeMe routines */ void AliasHandleCUSeeMeOut(struct ip *, struct alias_link *); void AliasHandleCUSeeMeIn(struct ip *, struct in_addr); /* Transparent proxy routines */ int ProxyCheck(struct ip *, struct in_addr *, u_short *); void ProxyModify(struct alias_link *, struct ip *, int, int); enum alias_tcp_state { ALIAS_TCP_STATE_NOT_CONNECTED, ALIAS_TCP_STATE_CONNECTED, ALIAS_TCP_STATE_DISCONNECTED }; /*lint -restore */ #endif /* defined(ALIAS_LOCAL_H) */ Index: head/sys/netinet/libalias/libalias.3 =================================================================== --- head/sys/netinet/libalias/libalias.3 (revision 59725) +++ head/sys/netinet/libalias/libalias.3 (revision 59726) @@ -1,984 +1,965 @@ .\" $FreeBSD$ .\" .Dd April 13, 2000 .Dt LIBALIAS 3 .Os FreeBSD .Sh NAME .Nm libalias .Nd packet aliasing library for masquerading and network address translation .Sh SYNOPSIS .Fd #include .Fd #include .Fd #include .Pp Function prototypes are given in the main body of the text. .Sh DESCRIPTION The .Nm library is a collection of functions for aliasing and de-aliasing of IP packets, intended for masquerading and network address translation (NAT). .Sh INTRODUCTION This library is a moderately portable set of functions designed to assist in the process of IP masquerading and network address translation. Outgoing packets from a local network with unregistered IP addresses can be aliased to appear as if they came from an accessible IP address. Incoming packets are then de-aliased so that they are sent to the correct machine on the local network. .Pp A certain amount of flexibility is built into the packet aliasing engine. In the simplest mode of operation, a many-to-one address mapping takes place between local network and the packet aliasing host. This is known as IP masquerading. In addition, one-to-one mappings between local and public addresses can also be implemented, which is known as static NAT. In between these extremes, different groups of private addresses can be linked to different public addresses, comprising several distinct many-to-one mappings. Also, a given public address and port can be statically redirected to a private address/port. .Pp The packet aliasing engine was designed to operate in user space outside of the kernel, without any access to private kernel data structure, but the source code can also be ported to a kernel environment. .Sh INITIALIZATION AND CONTROL Two special functions, .Fn PacketAliasInit and .Fn PacketAliasSetAddress , must always be called before any packet handling may be performed. In addition, the operating mode of the packet aliasing engine can be customized by calling .Fn PacketAliasSetMode . .Pp .Ft void .Fn PacketAliasInit void .Bd -ragged -offset indent This function has no arguments or return value and is used to initialize internal data structures. The following mode bits are always set after calling .Fn PacketAliasInit . See the description of .Fn PacketAliasSetMode below for the meaning of these mode bits. .Pp .Bl -item -offset indent -compact .It .Dv PKT_ALIAS_SAME_PORTS .It .Dv PKT_ALIAS_USE_SOCKETS .It .Dv PKT_ALIAS_RESET_ON_ADDR_CHANGE .El .Pp This function will always return the packet aliasing engine to the same initial state. .Fn PacketAliasSetAddress must be called afterwards, and any desired changes from the default mode bits listed above require a call to .Fn PacketAliasSetMode . .Pp It is mandatory that this function be called at the beginning of a program prior to any packet handling. .Ed .Pp .Ft void .Fn PacketAliasUninit void .Bd -ragged -offset indent This function has no arguments or return value and is used to clear any resources attached to internal data structures. .Pp This functions should be called when a program stops using the aliasing engine; it does, amongst other things, clear out any firewall holes. To provide backwards compatibility and extra security, it is added to the .Xr atexit 3 chain by .Fn PacketAliasInit . Calling it multiple times is harmless. .Ed .Pp .Ft void .Fn PacketAliasSetAddress "struct in_addr addr" .Bd -ragged -offset indent This function sets the source address to which outgoing packets from the local area network are aliased. All outgoing packets are re-mapped to this address unless overridden by a static address mapping established by .Fn PacketAliasRedirectAddr . .Pp If the .Dv PKT_ALIAS_RESET_ON_ADDR_CHANGE mode bit is set (the default mode of operation), then the internal aliasing link tables will be reset any time the aliasing address changes. This is useful for interfaces such as .Xr ppp 8 , where the IP address may or may not change on successive dial-up attempts. .Pp If the .Dv PKT_ALIAS_RESET_ON_ADDR_CHANGE mode bit is set to zero, this function can also be used to dynamically change the aliasing address on a packet to packet basis (it is a low overhead call). .Pp It is mandatory that this function be called prior to any packet handling. .Ed .Pp .Ft unsigned int .Fn PacketAliasSetMode "unsigned int flags" "unsigned int mask" .Bd -ragged -offset indent This function sets or clears mode bits according to the value of .Fa flags . Only bits marked in .Fa mask are affected. The following mode bits are defined in .Aq Pa alias.h : .Bl -tag -width indent .It Dv PKT_ALIAS_LOG Enables logging into .Pa /var/log/alias.log . Each time an aliasing link is created or deleted, the log file is appended with the current number of ICMP, TCP and UDP links. Mainly useful for debugging when the log file is viewed continuously with .Xr tail 1 . .It Dv PKT_ALIAS_DENY_INCOMING If this mode bit is set, all incoming packets associated with new TCP connections or new UDP transactions will be marked for being ignored .Po .Fn PacketAliasIn returns .Dv PKT_ALIAS_IGNORED code .Pc by the calling program. Response packets to connections or transactions initiated from the packet aliasing host or local network will be unaffected. This mode bit is useful for implementing a one-way firewall. .It Dv PKT_ALIAS_SAME_PORTS If this mode bit is set, the packet aliasing engine will attempt to leave the alias port numbers unchanged from the actual local port numbers. This can be done as long as the quintuple (proto, alias addr, alias port, remote addr, remote port) is unique. If a conflict exists, a new aliasing port number is chosen even if this mode bit is set. .It Dv PKT_ALIAS_USE_SOCKETS This bit should be set when the packet aliasing host originates network traffic as well as forwards it. When the packet aliasing host is waiting for a connection from an unknown host address or unknown port number (e.g. an FTP data connection), this mode bit specifies that a socket be allocated as a place holder to prevent port conflicts. Once a connection is established, usually within a minute or so, the socket is closed. .It Dv PKT_ALIAS_UNREGISTERED_ONLY If this mode bit is set, traffic on the local network which does not originate from unregistered address spaces will be ignored. Standard Class A, B and C unregistered addresses are: .Bd -literal -offset indent 10.0.0.0 -> 10.255.255.255 (Class A subnet) 172.16.0.0 -> 172.31.255.255 (Class B subnets) 192.168.0.0 -> 192.168.255.255 (Class C subnets) .Ed .Pp This option is useful in the case that packet aliasing host has both registered and unregistered subnets on different interfaces. The registered subnet is fully accessible to the outside world, so traffic from it does not need to be passed through the packet aliasing engine. .It Dv PKT_ALIAS_RESET_ON_ADDR_CHANGE When this mode bit is set and .Fn PacketAliasSetAddress is called to change the aliasing address, the internal link table of the packet aliasing engine will be cleared. This operating mode is useful for .Xr ppp 8 links where the interface address can sometimes change or remain the same between dial-up attempts. If this mode bit is not set, the link table will never be reset in the event of an address change. .It Dv PKT_ALIAS_PUNCH_FW This option makes .Nm `punch holes' in an .Xr ipfirewall 4 based firewall for FTP/IRC DCC connections. The holes punched are bound by from/to IP address and port; it will not be possible to use a hole for another connection. A hole is removed when the connection that uses it dies. To cater to unexpected death of a program using .Nm (e.g. kill -9), changing the state of the flag will clear the entire firewall range allocated for holes. This will also happen on the initial call to .Fn PacketAliasSetFWBase . This call must happen prior to setting this flag. .It Dv PKT_ALIAS_REVERSE This option makes .Nm reverse the way it handles incoming and outgoing packets, allowing it to be fed with data that passes through the internal interface rather than the external one. .It Dv PKT_ALIAS_PROXY_ONLY This option tells .Nm to obey transparent proxy rules only. Normal packet aliasing is not performed. See .Fn PacketAliasProxyRule below for details. -.It Dv PKT_ALIAS_DENY_PPTP -If this mode bit is set, all PPTP packets will be marked for being ignored -(both -.Fn PacketAliasIn -and -.Fn PacketAliasOut -return -.Dv PKT_ALIAS_IGNORED -code). .El .Ed .Pp .Ft void .Fn PacketAliasSetFWBase "unsigned int base" "unsigned int num" .Bd -ragged -offset indent Set firewall range allocated for punching firewall holes (with the .Dv PKT_ALIAS_PUNCH_FW flag). The range will be cleared for all rules on initialization. .Ed .Sh PACKET HANDLING The packet handling functions are used to modify incoming (remote to local) and outgoing (local to remote) packets. The calling program is responsible for receiving and sending packets via network interfaces. .Pp Along with .Fn PacketAliasInit and .Fn PacketAliasSetAddress , the two packet handling functions, .Fn PacketAliasIn and .Fn PacketAliasOut , comprise minimal set of functions needed for a basic IP masquerading implementation. .Pp .Ft int .Fn PacketAliasIn "char *buffer" "int maxpacketsize" .Bd -ragged -offset indent An incoming packet coming from a remote machine to the local network is de-aliased by this function. The IP packet is pointed to by .Fa buffer , and .Fa maxpacketsize indicates the size of the data structure containing the packet and should be at least as large as the actual packet size. .Pp Return codes: .Bl -tag -width indent .It Dv PKT_ALIAS_OK The packet aliasing process was successful. .It Dv PKT_ALIAS_IGNORED The packet was ignored and not de-aliased. This can happen if the protocol is unrecognized, possibly an ICMP message type is not handled or if incoming packets for new connections are being ignored (if .Dv PKT_ALIAS_DENY_INCOMING mode bit was set by .Fn PacketAliasSetMode ) . .It Dv PKT_ALIAS_UNRESOLVED_FRAGMENT This is returned when a fragment cannot be resolved because the header fragment has not been sent yet. In this situation, fragments must be saved with .Fn PacketAliasSaveFragment until a header fragment is found. .It Dv PKT_ALIAS_FOUND_HEADER_FRAGMENT The packet aliasing process was successful, and a header fragment was found. This is a signal to retrieve any unresolved fragments with .Fn PacketAliasGetFragment and de-alias them with .Fn PacketAliasFragmentIn . .It Dv PKT_ALIAS_ERROR An internal error within the packet aliasing engine occurred. .El .Ed .Pp .Ft int .Fn PacketAliasOut "char *buffer" "int maxpacketsize" .Bd -ragged -offset indent An outgoing packet coming from the local network to a remote machine is aliased by this function. The IP packet is pointed to by .Fa buffer , and .Fa maxpacketsize indicates the maximum packet size permissible should the packet length be changed. IP encoding protocols place address and port information in the encapsulated data stream which has to be modified and can account for changes in packet length. Well known examples of such protocols are FTP and IRC DCC. .Pp Return codes: .Bl -tag -width indent .It Dv PKT_ALIAS_OK The packet aliasing process was successful. .It Dv PKT_ALIAS_IGNORED The packet was ignored and not aliased. This can happen if the protocol is unrecognized, or possibly an ICMP message type is not handled. .It Dv PKT_ALIAS_ERROR An internal error within the packet aliasing engine occurred. .El .Ed .Sh PORT AND ADDRESS REDIRECTION The functions described in this section allow machines on the local network to be accessible in some degree to new incoming connections from the external network. Individual ports can be re-mapped or static network address translations can be designated. .Pp .Ft struct alias_link * .Fo PacketAliasRedirectPort .Fa "struct in_addr local_addr" .Fa "u_short local_port" .Fa "struct in_addr remote_addr" .Fa "u_short remote_port" .Fa "struct in_addr alias_addr" .Fa "u_short alias_port" .Fa "u_char proto" .Fc .Bd -ragged -offset indent This function specifies that traffic from a given remote address/port to an alias address/port be redirected to a specified local address/port. The parameter .Fa proto can be either .Dv IPPROTO_TCP or .Dv IPPROTO_UDP , as defined in .Aq Pa netinet/in.h . .Pp If .Fa local_addr or .Fa alias_addr is zero, this indicates that the packet aliasing address as established by .Fn PacketAliasSetAddress is to be used. Even if .Fn PacketAliasSetAddress is called to change the address after .Fn PacketAliasRedirectPort is called, a zero reference will track this change. .Pp If the link is further set up to operate for a load sharing, then .Fa local_addr and .Fa local_port are ignored, and are selected dynamically from the server pool, as described in .Fn PacketAliasAddServer below. .Pp If .Fa remote_addr is zero, this indicates to redirect packets from any remote address. Likewise, if .Fa remote_port is zero, this indicates to redirect packets originating from any remote port number. Almost always, the remote port specification will be zero, but non-zero remote addresses can sometimes be useful for firewalling. If two calls to .Fn PacketAliasRedirectPort overlap in their address/port specifications, then the most recent call will have precedence. .Pp This function returns a pointer which can subsequently be used by .Fn PacketAliasRedirectDelete . If .Dv NULL is returned, then the function call did not complete successfully. .Pp All port numbers should be in network address byte order, so it is necessary to use .Xr htons 3 to convert these parameters from internally readable numbers to network byte order. Addresses are also in network byte order, which is implicit in the use of the .Fa struct in_addr data type. .Ed .Pp .Ft struct alias_link * .Fo PacketAliasRedirectAddr .Fa "struct in_addr local_addr" .Fa "struct in_addr alias_addr" .Fc .Bd -ragged -offset indent This function designates that all incoming traffic to .Fa alias_addr be redirected to .Fa local_addr . Similarly, all outgoing traffic from .Fa local_addr is aliased to .Fa alias_addr . .Pp If .Fa local_addr or .Fa alias_addr is zero, this indicates that the packet aliasing address as established by .Fn PacketAliasSetAddress is to be used. Even if .Fn PacketAliasSetAddress is called to change the address after .Fn PacketAliasRedirectAddr is called, a zero reference will track this change. .Pp If the link is further set up to operate for a load sharing, then .Fa local_addr is ignored, and is selected dynamically from the server pool, as described in .Fn PacketAliasAddServer below. .Pp If subsequent calls to .Fn PacketAliasRedirectAddr use the same aliasing address, all new incoming traffic to this aliasing address will be redirected to the local address made in the last function call. New traffic generated by any of the local machines, designated in the several function calls, will be aliased to the same address. Consider the following example: .Bd -literal -offset indent PacketAliasRedirectAddr(inet_aton("192.168.0.2"), inet_aton("141.221.254.101")); PacketAliasRedirectAddr(inet_aton("192.168.0.3"), inet_aton("141.221.254.101")); PacketAliasRedirectAddr(inet_aton("192.168.0.4"), inet_aton("141.221.254.101")); .Ed .Pp Any outgoing connections such as .Xr telnet 1 or .Xr ftp 1 from 192.168.0.2, 192.168.0.3 and 192.168.0.4 will appear to come from 141.221.254.101. Any incoming connections to 141.221.254.101 will be directed to 192.168.0.4. .Pp Any calls to .Fn PacketAliasRedirectPort will have precedence over address mappings designated by .Fn PacketAliasRedirectAddr . .Pp This function returns a pointer which can subsequently be used by .Fn PacketAliasRedirectDelete . If .Dv NULL is returned, then the function call did not complete successfully. .Ed .Pp .Ft int .Fo PacketAliasAddServer .Fa "struct alias_link *link" .Fa "struct in_addr addr" .Fa "u_short port" .Fc .Bd -ragged -offset indent This function sets the .Fa link up for Load Sharing using IP Network Address Translation (RFC 2391, LSNAT). LSNAT operates as follows. A client attempts to access a server by using the server virtual address. The LSNAT router transparently redirects the request to one of the hosts in server pool, selected using a real-time load sharing algorithm. Multiple sessions may be initiated from the same client, and each session could be directed to a different host based on load balance across server pool hosts at the time. If load share is desired for just a few specific services, the configuration on LSNAT could be defined to restrict load share for just the services desired. .Pp Currently, only the simplest selection algorithm is implemented, where a host is selected on a round-robin basis only, without regard to load on the host. .Pp First, the .Fa link is created by either .Fn PacketAliasRedirectPort or .Fn PacketAliasRedirectAddr . Then, .Fn PacketAliasAddServer is called multiple times to add entries to the .Fa link Ns 's server pool. .Pp For links created with .Fn PacketAliasRedirectAddr , the .Fa port argument is ignored and could have any value, e.g. htons(~0). .Pp This function returns 0 on success, -1 otherwise. .Ed .Pp .Ft void .Fn PacketAliasRedirectDelete "struct alias_link *link" .Bd -ragged -offset indent This function will delete a specific static redirect rule entered by .Fn PacketAliasRedirectPort or .Fn PacketAliasRedirectAddr . The parameter .Fa link is the pointer returned by either of the redirection functions. If an invalid pointer is passed to .Fn PacketAliasRedirectDelete , then a program crash or unpredictable operation could result, so it is necessary to be careful using this function. .Ed .Pp .Ft int .Fn PacketAliasProxyRule "const char *cmd" .Bd -ragged -offset indent The passed .Fa cmd string consists of one or more pairs of words. The first word in each pair is a token and the second is the value that should be applied for that token. Tokens and their argument types are as follows: .Bl -tag -width indent .It Cm type encode_ip_hdr | encode_tcp_stream | no_encode In order to support transparent proxying, it is necessary to somehow pass the original address and port information into the new destination server. If .Cm encode_ip_hdr is specified, the original address and port is passed as an extra IP option. If .Cm encode_tcp_stream is specified, the original address and port is passed as the first piece of data in the TCP stream in the format .Dq DEST Ar IP port . .It Cm port Ar portnum Only packets with the destination port .Ar portnum are proxied. .It Cm server Ar host Ns Xo .Op : Ns Ar portnum .Xc This specifies the .Ar host and .Ar portnum that the data is to be redirected to. .Ar host must be an IP address rather than a DNS host name. If .Ar portnum is not specified, the destination port number is not changed. .Pp The .Ar server specification is mandatory unless the .Cm delete command is being used. .It Cm rule Ar index Normally, each call to .Fn PacketAliasProxyRule inserts the next rule at the start of a linear list of rules. If an .Ar index is specified, the new rule will be checked after all rules with lower indices. Calls to .Fn PacketAliasProxyRule that do not specify a rule are assigned rule 0. .It Cm delete Ar index This token and its argument MUST NOT be used with any other tokens. When used, all existing rules with the given .Ar index are deleted. .It Cm proto tcp | udp If specified, only packets of the given protocol type are matched. .It Cm src Ar IP Ns Xo .Op / Ns Ar bits .Xc If specified, only packets with a source address matching the given .Ar IP are matched. If .Ar bits is also specified, then the first .Ar bits bits of .Ar IP are taken as a network specification, and all IP addresses from that network will be matched. .It Cm dst Ar IP Ns Xo .Op / Ns Ar bits .Xc If specified, only packets with a destination address matching the given .Ar IP are matched. If .Ar bits is also specified, then the first .Ar bits bits of .Ar IP are taken as a network specification, and all IP addresses from that network will be matched. .El .Pp This function is usually used to redirect outgoing connections for internal machines that are not permitted certain types of internet access, or to restrict access to certain external machines. .Ed .Pp .Ft struct alias_link * -.Fo PacketAliasRedirectPptp +.Fo PacketAliasRedirectProto .Fa "struct in_addr local_addr" .Fa "struct in_addr remote_addr" .Fa "struct in_addr alias_addr" +.Fa "u_char proto" .Fc .Bd -ragged -offset indent -This function specifies that any Point to Point Tunneling Protocol -(PPTP) traffic from a given remote address to an alias address be +This function specifies that any IP packet with protocol number of +.Fa proto +from a given remote address to an alias address be redirected to a specified local address. -Currently supported PPTP protocols include: .Pp -.Bl -tag -width "IPPROTO_GRE" -compact -.It IPPROTO_GRE -Generic Routing Encapsulation (RFC 1702) -.It IPPROTO_ESP -IP Encapsulating Security Payload (RFC 1827) -.It IPPROTO_AH -IP Authentication Header (RFC 1826) -.El -.Pp If .Fa local_addr or .Fa alias_addr is zero, this indicates that the packet aliasing address as established by .Fn PacketAliasSetAddress is to be used. Even if .Fn PacketAliasSetAddress is called to change the address after -.Fn PacketAliasRedirectPptp +.Fn PacketAliasRedirectProto is called, a zero reference will track this change. .Pp If .Fa remote_addr -is zero, this indicates to redirect PPTP packets from any remote address. +is zero, this indicates to redirect packets from any remote address. Non-zero remote addresses can sometimes be useful for firewalling. .Pp If two calls to -.Fn PacketAliasRedirectPptp +.Fn PacketAliasRedirectProto overlap in their address specifications, then the most recent call will have precedence. .Pp This function returns a pointer which can subsequently be used by .Fn PacketAliasRedirectDelete . If .Dv NULL is returned, then the function call did not complete successfully. .Ed .Pp .Ft int .Fn PacketAliasPptp "struct in_addr addr" .Bd -ragged -offset indent This function causes any PPTP packets to be aliased using .Fa addr rather than the address set via .Fn PacketAliasSetAddress . This allows the uses of the PPTP on a single machine on the internal network. .Pp If the passed address is .Dv INADDR_NONE , then PPTP aliasing is disabled. .Pp .Bf -symbolic This function is made obsolete by -.Fn PacketAliasRedirectPptp -and -.Dv PKT_ALIAS_DENY_PPTP -mode bit, and is provided only for backward compatibility. +.Fn PacketAliasRedirectProto , +and is provided only for backward compatibility. .Ef .Ed .Sh FRAGMENT HANDLING The functions in this section are used to deal with incoming fragments. .Pp Outgoing fragments are handled within .Fn PacketAliasOut by changing the address according to any applicable mapping set by .Fn PacketAliasRedirectAddr , or the default aliasing address set by .Fn PacketAliasSetAddress . .Pp Incoming fragments are handled in one of two ways. If the header of a fragmented IP packet has already been seen, then all subsequent fragments will be re-mapped in the same manner the header fragment was. Fragments which arrive before the header are saved and then retrieved once the header fragment has been resolved. .Pp .Ft int .Fn PacketAliasSaveFragment "char *ptr" .Bd -ragged -offset indent When .Fn PacketAliasIn returns .Dv PKT_ALIAS_UNRESOLVED_FRAGMENT , this function can be used to save the pointer to the unresolved fragment. .Pp It is implicitly assumed that .Fa ptr points to a block of memory allocated by .Xr malloc 3 . If the fragment is never resolved, the packet aliasing engine will automatically free the memory after a timeout period. [Eventually this function should be modified so that a callback function for freeing memory is passed as an argument.] .Pp This function returns .Dv PKT_ALIAS_OK if it was successful and .Dv PKT_ALIAS_ERROR if there was an error. .Ed .Pp .Ft char * .Fn PacketAliasGetFragment "char *buffer" .Bd -ragged -offset indent This function can be used to retrieve fragment pointers saved by .Fn PacketAliasSaveFragment . The IP header fragment pointed to by .Fa buffer is the header fragment indicated when .Fn PacketAliasIn returns .Dv PKT_ALIAS_FOUND_HEADER_FRAGMENT . Once a fragment pointer is retrieved, it becomes the calling program's responsibility to free the dynamically allocated memory for the fragment. .Pp .Fn PacketAliasGetFragment can be called sequentially until there are no more fragments available, at which time it returns .Dv NULL . .Ed .Pp .Ft void .Fn PacketAliasFragmentIn "char *header" "char *fragment" .Bd -ragged -offset indent When a fragment is retrieved with .Fn PacketAliasGetFragment , it can then be de-aliased with a call to .Fn PacketAliasFragmentIn . The .Fa header argument is the pointer to a header fragment used as a template, and .Fa fragment is the pointer to the packet to be de-aliased. .Ed .Sh MISCELLANEOUS FUNCTIONS .Ft void .Fn PacketAliasSetTarget "struct in_addr addr" .Bd -ragged -offset indent When an incoming packet not associated with any pre-existing aliasing link arrives at the host machine, it will be sent to the address indicated by a call to .Fn PacketAliasSetTarget . .Pp If this function is not called, or is called with an .Dv INADDR_NONE address argument, then all new incoming packets go to the address set by .Fn PacketAliasSetAddress . .Pp If this function is called with an .Dv INADDR_ANY address argument, then all new incoming packets go to the address specified in the packet. This allows external machines to talk directly to internal machines if they can route packets to the machine in question. .Ed .Pp .Ft int .Fn PacketAliasCheckNewLink void .Bd -ragged -offset indent This function returns a non-zero value when a new aliasing link is created. In circumstances where incoming traffic is being sequentially sent to different local servers, this function can be used to trigger when .Fn PacketAliasSetTarget is called to change the default target address. .Ed .Pp .Ft u_short .Fn PacketAliasInternetChecksum "u_short *buffer" "int nbytes" .Bd -ragged -offset indent This is a utility function that does not seem to be available elsewhere and is included as a convenience. It computes the internet checksum, which is used in both IP and protocol-specific headers (TCP, UDP, ICMP). .Pp The .Fa buffer argument points to the data block to be checksummed, and .Fa nbytes is the number of bytes. The 16-bit checksum field should be zeroed before computing the checksum. .Pp Checksums can also be verified by operating on a block of data including its checksum. If the checksum is valid, .Fn PacketAliasInternetChecksum will return zero. .Ed .Sh AUTHORS .An Charles Mott Aq cmott@scientech.com , versions 1.0 - 1.8, 2.0 - 2.4. .An Eivind Eklund Aq eivind@FreeBSD.org , versions 1.8b, 1.9 and 2.5. Added IRC DCC support as well as contributing a number of architectural improvements; added the firewall bypass for FTP/IRC DCC. .Sh ACKNOWLEDGMENTS Listed below, in approximate chronological order, are individuals who have provided valuable comments and/or debugging assistance. .Pp .Bl -item -offset indent -compact .It Gary Roberts .It Tom Torrance .It Reto Burkhalter .It Martin Renters .It Brian Somers .It Paul Traina .It Ari Suutari .It Dave Remien .It J. Fortes .It Andrzej Bialecki .It Gordon Burditt .El .Sh CONCEPTUAL BACKGROUND This section is intended for those who are planning to modify the source code or want to create somewhat esoteric applications using the packet aliasing functions. .Pp The conceptual framework under which the packet aliasing engine operates is described here. Central to the discussion is the idea of an .Em aliasing link which describes the relationship for a given packet transaction between the local machine, aliased identity and remote machine. It is discussed how such links come into existence and are destroyed. .Ss ALIASING LINKS There is a notion of an .Em aliasing link , which is a 7-tuple describing a specific translation: .Bd -literal -offset indent (local addr, local port, alias addr, alias port, remote addr, remote port, protocol) .Ed .Pp Outgoing packets have the local address and port number replaced with the alias address and port number. Incoming packets undergo the reverse process. The packet aliasing engine attempts to match packets against an internal table of aliasing links to determine how to modify a given IP packet. Both the IP header and protocol dependent headers are modified as necessary. Aliasing links are created and deleted as necessary according to network traffic. .Pp Protocols can be TCP, UDP or even ICMP in certain circumstances. (Some types of ICMP packets can be aliased according to sequence or ID number which acts as an equivalent port number for identifying how individual packets should be handled.) .Pp Each aliasing link must have a unique combination of the following five quantities: alias address/port, remote address/port and protocol. This ensures that several machines on a local network can share the same aliasing IP address. In cases where conflicts might arise, the aliasing port is chosen so that uniqueness is maintained. .Ss STATIC AND DYNAMIC LINKS Aliasing links can either be static or dynamic. Static links persist indefinitely and represent fixed rules for translating IP packets. Dynamic links come into existence for a specific TCP connection or UDP transaction or ICMP ECHO sequence. For the case of TCP, the connection can be monitored to see when the associated aliasing link should be deleted. Aliasing links for UDP transactions (and ICMP ECHO and TIMESTAMP requests) work on a simple timeout rule. When no activity is observed on a dynamic link for a certain amount of time it is automatically deleted. Timeout rules also apply to TCP connections which do not open or close properly. .Ss PARTIALLY SPECIFIED ALIASING LINKS Aliasing links can be partially specified, meaning that the remote address and/or remote port are unknown. In this case, when a packet matching the incomplete specification is found, a fully specified dynamic link is created. If the original partially specified link is dynamic, it will be deleted after the fully specified link is created, otherwise it will persist. .Pp For instance, a partially specified link might be .Bd -literal -offset indent (192.168.0.4, 23, 204.228.203.215, 8066, 0, 0, tcp) .Ed .Pp The zeros denote unspecified components for the remote address and port. If this link were static it would have the effect of redirecting all incoming traffic from port 8066 of 204.228.203.215 to port 23 (telnet) of machine 192.168.0.4 on the local network. Each individual telnet connection would initiate the creation of a distinct dynamic link. .Ss DYNAMIC LINK CREATION In addition to aliasing links, there are also address mappings that can be stored within the internal data table of the packet aliasing mechanism. .Bd -literal -offset indent (local addr, alias addr) .Ed .Pp Address mappings are searched when creating new dynamic links. .Pp All outgoing packets from the local network automatically create a dynamic link if they do not match an already existing fully specified link. If an address mapping exists for the outgoing packet, this determines the alias address to be used. If no mapping exists, then a default address, usually the address of the packet aliasing host, is used. If necessary, this default address can be changed as often as each individual packet arrives. .Pp The aliasing port number is determined such that the new dynamic link does not conflict with any existing links. In the default operating mode, the packet aliasing engine attempts to set the aliasing port equal to the local port number. If this results in a conflict, then port numbers are randomly chosen until a unique aliasing link can be established. In an alternate operating mode, the first choice of an aliasing port is also random and unrelated to the local port number.