diff --git a/sys/netinet/libalias/alias.c b/sys/netinet/libalias/alias.c index 900731fcbec6..0e2756affcb4 100644 --- a/sys/netinet/libalias/alias.c +++ b/sys/netinet/libalias/alias.c @@ -1,1799 +1,1794 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2001 Charles Mott * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); /* Alias.c provides supervisory control for the functions of the packet aliasing software. It consists of routines to monitor TCP connection state, protocol-specific aliasing routines, fragment handling and the following outside world functional interfaces: SaveFragmentPtr, GetFragmentPtr, FragmentAliasIn, PacketAliasIn and PacketAliasOut. The other C program files are briefly described. The data structure framework which holds information needed to translate packets is encapsulated in alias_db.c. Data is accessed by function calls, so other segments of the program need not know about the underlying data structures. Alias_ftp.c contains special code for modifying the ftp PORT command used to establish data connections, while alias_irc.c does the same for IRC DCC. Alias_util.c contains a few utility routines. Version 1.0 August, 1996 (cjm) Version 1.1 August 20, 1996 (cjm) PPP host accepts incoming connections for ports 0 to 1023. (Gary Roberts pointed out the need to handle incoming connections.) Version 1.2 September 7, 1996 (cjm) Fragment handling error in alias_db.c corrected. (Tom Torrance helped fix this problem.) Version 1.4 September 16, 1996 (cjm) - A more generalized method for handling incoming connections, without the 0-1023 restriction, is implemented in alias_db.c - Improved ICMP support in alias.c. Traceroute packet streams can now be correctly aliased. - TCP connection closing logic simplified in alias.c and now allows for additional 1 minute "grace period" after FIN or RST is observed. Version 1.5 September 17, 1996 (cjm) Corrected error in handling incoming UDP packets with 0 checksum. (Tom Torrance helped fix this problem.) Version 1.6 September 18, 1996 (cjm) Simplified ICMP aliasing scheme. Should now support traceroute from Win95 as well as FreeBSD. Version 1.7 January 9, 1997 (cjm) - Out-of-order fragment handling. - IP checksum error fixed for ftp transfers from aliasing host. - Integer return codes added to all aliasing/de-aliasing functions. - Some obsolete comments cleaned up. - Differential checksum computations for IP header (TCP, UDP and ICMP were already differential). Version 2.1 May 1997 (cjm) - Added support for outgoing ICMP error messages. - Added two functions PacketAliasIn2() and PacketAliasOut2() for dynamic address control (e.g. round-robin allocation of incoming packets). Version 2.2 July 1997 (cjm) - Rationalized API function names to begin with "PacketAlias..." - Eliminated PacketAliasIn2() and PacketAliasOut2() as poorly conceived. Version 2.3 Dec 1998 (dillon) - Major bounds checking additions, see FreeBSD/CVS Version 3.1 May, 2000 (salander) - Added hooks to handle PPTP. Version 3.2 July, 2000 (salander and satoh) - Added PacketUnaliasOut routine. - Added hooks to handle RTSP/RTP. See HISTORY file for additional revisions. */ #ifdef _KERNEL #include #include #include #include #else #include #include #include #include #include #include #include #endif #include #include #include #include #include #include #ifdef _KERNEL #include #include #include #else #include #include "alias.h" #include "alias_local.h" #include "alias_mod.h" #endif -/* +/* * Define libalias SYSCTL Node */ #ifdef SYSCTL_NODE SYSCTL_DECL(_net_inet); SYSCTL_DECL(_net_inet_ip); SYSCTL_NODE(_net_inet_ip, OID_AUTO, alias, CTLFLAG_RW | CTLFLAG_MPSAFE, NULL, "Libalias sysctl API"); #endif static __inline int twowords(void *p) { uint8_t *c = p; #if BYTE_ORDER == LITTLE_ENDIAN uint16_t s1 = ((uint16_t)c[1] << 8) + (uint16_t)c[0]; uint16_t s2 = ((uint16_t)c[3] << 8) + (uint16_t)c[2]; #else uint16_t s1 = ((uint16_t)c[0] << 8) + (uint16_t)c[1]; uint16_t s2 = ((uint16_t)c[2] << 8) + (uint16_t)c[3]; #endif return (s1 + s2); } /* TCP Handling Routines TcpMonitorIn() -- These routines monitor TCP connections, and TcpMonitorOut() delete a link when a connection is closed. These routines look for SYN, FIN and RST flags to determine when TCP connections open and close. When a TCP connection closes, the data structure containing packet aliasing information is deleted after a timeout period. */ /* Local prototypes */ static void TcpMonitorIn(u_char, struct alias_link *); static void TcpMonitorOut(u_char, struct alias_link *); static void TcpMonitorIn(u_char th_flags, struct alias_link *lnk) { - switch (GetStateIn(lnk)) { case ALIAS_TCP_STATE_NOT_CONNECTED: if (th_flags & TH_RST) SetStateIn(lnk, ALIAS_TCP_STATE_DISCONNECTED); else if (th_flags & TH_SYN) SetStateIn(lnk, ALIAS_TCP_STATE_CONNECTED); break; case ALIAS_TCP_STATE_CONNECTED: if (th_flags & (TH_FIN | TH_RST)) SetStateIn(lnk, ALIAS_TCP_STATE_DISCONNECTED); break; } } static void TcpMonitorOut(u_char th_flags, struct alias_link *lnk) { - switch (GetStateOut(lnk)) { case ALIAS_TCP_STATE_NOT_CONNECTED: if (th_flags & TH_RST) SetStateOut(lnk, ALIAS_TCP_STATE_DISCONNECTED); else if (th_flags & TH_SYN) SetStateOut(lnk, ALIAS_TCP_STATE_CONNECTED); break; case ALIAS_TCP_STATE_CONNECTED: if (th_flags & (TH_FIN | TH_RST)) SetStateOut(lnk, ALIAS_TCP_STATE_DISCONNECTED); break; } } /* Protocol Specific Packet Aliasing Routines IcmpAliasIn(), IcmpAliasIn1(), IcmpAliasIn2() IcmpAliasOut(), IcmpAliasOut1(), IcmpAliasOut2() ProtoAliasIn(), ProtoAliasOut() UdpAliasIn(), UdpAliasOut() TcpAliasIn(), TcpAliasOut() These routines handle protocol specific details of packet aliasing. One may observe a certain amount of repetitive arithmetic in these functions, the purpose of which is to compute a revised checksum without actually summing over the entire data packet, which could be unnecessarily time consuming. The purpose of the packet aliasing routines is to replace the source address of the outgoing packet and then correctly put it back for any incoming packets. For TCP and UDP, ports are also re-mapped. For ICMP echo/timestamp requests and replies, the following scheme is used: the ID number is replaced by an alias for the outgoing packet. ICMP error messages are handled by looking at the IP fragment in the data section of the message. For TCP and UDP protocols, a port number is chosen for an outgoing packet, and then incoming packets are identified by IP address and port numbers. For TCP packets, there is additional logic in the event that sequence and ACK numbers have been altered (as in the case for FTP data port commands). The port numbers used by the packet aliasing module are not true ports in the Unix sense. No sockets are actually bound to ports. They are more correctly thought of as placeholders. All packets go through the aliasing mechanism, whether they come from the gateway machine or other machines on a local area network. */ /* Local prototypes */ static int IcmpAliasIn1(struct libalias *, struct ip *); static int IcmpAliasIn2(struct libalias *, struct ip *); static int IcmpAliasIn(struct libalias *, struct ip *); static int IcmpAliasOut1(struct libalias *, struct ip *, int create); static int IcmpAliasOut2(struct libalias *, struct ip *); static int IcmpAliasOut(struct libalias *, struct ip *, int create); static int ProtoAliasIn(struct libalias *la, struct in_addr ip_src, struct ip *pip, u_char ip_p, u_short *ip_sum); static int ProtoAliasOut(struct libalias *la, struct ip *pip, struct in_addr ip_dst, u_char ip_p, u_short *ip_sum, int create); static int UdpAliasIn(struct libalias *, struct ip *); static int UdpAliasOut(struct libalias *, struct ip *, int, int create); static int TcpAliasIn(struct libalias *, struct ip *); static int TcpAliasOut(struct libalias *, struct ip *, int, int create); -static int -IcmpAliasIn1(struct libalias *la, struct ip *pip) -{ - - LIBALIAS_LOCK_ASSERT(la); /* De-alias incoming echo and timestamp replies. Alias incoming echo and timestamp requests. */ +static int +IcmpAliasIn1(struct libalias *la, struct ip *pip) +{ struct alias_link *lnk; struct icmp *ic; + LIBALIAS_LOCK_ASSERT(la); ic = (struct icmp *)ip_next(pip); -/* Get source address from ICMP data field and restore original data */ + /* Get source address from ICMP data field and restore original data */ lnk = FindIcmpIn(la, pip->ip_src, pip->ip_dst, ic->icmp_id, 1); if (lnk != NULL) { u_short original_id; int accumulate; original_id = GetOriginalPort(lnk); -/* Adjust ICMP checksum */ + /* Adjust ICMP checksum */ accumulate = ic->icmp_id; accumulate -= original_id; ADJUST_CHECKSUM(accumulate, ic->icmp_cksum); -/* Put original sequence number back in */ + /* Put original sequence number back in */ ic->icmp_id = original_id; -/* Put original address back into IP header */ + /* Put original address back into IP header */ { struct in_addr original_address; original_address = GetOriginalAddress(lnk); DifferentialChecksum(&pip->ip_sum, &original_address, &pip->ip_dst, 2); pip->ip_dst = original_address; } return (PKT_ALIAS_OK); } return (PKT_ALIAS_IGNORED); } -static int -IcmpAliasIn2(struct libalias *la, struct ip *pip) -{ - - LIBALIAS_LOCK_ASSERT(la); /* Alias incoming ICMP error messages containing IP header and first 64 bits of datagram. */ +static int +IcmpAliasIn2(struct libalias *la, struct ip *pip) +{ struct ip *ip; struct icmp *ic, *ic2; struct udphdr *ud; struct tcphdr *tc; struct alias_link *lnk; + LIBALIAS_LOCK_ASSERT(la); ic = (struct icmp *)ip_next(pip); ip = &ic->icmp_ip; ud = (struct udphdr *)ip_next(ip); tc = (struct tcphdr *)ip_next(ip); ic2 = (struct icmp *)ip_next(ip); if (ip->ip_p == IPPROTO_UDP) lnk = FindUdpTcpIn(la, ip->ip_dst, ip->ip_src, ud->uh_dport, ud->uh_sport, IPPROTO_UDP, 0); else if (ip->ip_p == IPPROTO_TCP) lnk = FindUdpTcpIn(la, ip->ip_dst, ip->ip_src, tc->th_dport, tc->th_sport, IPPROTO_TCP, 0); else if (ip->ip_p == IPPROTO_ICMP) { if (ic2->icmp_type == ICMP_ECHO || ic2->icmp_type == ICMP_TSTAMP) lnk = FindIcmpIn(la, ip->ip_dst, ip->ip_src, ic2->icmp_id, 0); else lnk = NULL; } else lnk = NULL; if (lnk != NULL) { if (ip->ip_p == IPPROTO_UDP || ip->ip_p == IPPROTO_TCP) { int accumulate, accumulate2; struct in_addr original_address; u_short original_port; original_address = GetOriginalAddress(lnk); original_port = GetOriginalPort(lnk); -/* Adjust ICMP checksum */ + /* Adjust ICMP checksum */ accumulate = twowords(&ip->ip_src); accumulate -= twowords(&original_address); accumulate += ud->uh_sport; accumulate -= original_port; accumulate2 = accumulate; accumulate2 += ip->ip_sum; ADJUST_CHECKSUM(accumulate, ip->ip_sum); accumulate2 -= ip->ip_sum; ADJUST_CHECKSUM(accumulate2, ic->icmp_cksum); -/* Un-alias address in IP header */ + /* Un-alias address in IP header */ DifferentialChecksum(&pip->ip_sum, &original_address, &pip->ip_dst, 2); pip->ip_dst = original_address; -/* Un-alias address and port number of original IP packet -fragment contained in ICMP data section */ + /* Un-alias address and port number of + * original IP packet fragment contained + * in ICMP data section */ ip->ip_src = original_address; ud->uh_sport = original_port; } else if (ip->ip_p == IPPROTO_ICMP) { int accumulate, accumulate2; struct in_addr original_address; u_short original_id; original_address = GetOriginalAddress(lnk); original_id = GetOriginalPort(lnk); -/* Adjust ICMP checksum */ + /* Adjust ICMP checksum */ accumulate = twowords(&ip->ip_src); accumulate -= twowords(&original_address); accumulate += ic2->icmp_id; accumulate -= original_id; accumulate2 = accumulate; accumulate2 += ip->ip_sum; ADJUST_CHECKSUM(accumulate, ip->ip_sum); accumulate2 -= ip->ip_sum; ADJUST_CHECKSUM(accumulate2, ic->icmp_cksum); -/* Un-alias address in IP header */ + /* Un-alias address in IP header */ DifferentialChecksum(&pip->ip_sum, &original_address, &pip->ip_dst, 2); pip->ip_dst = original_address; -/* Un-alias address of original IP packet and sequence number of - embedded ICMP datagram */ + /* Un-alias address of original IP packet and + * sequence number of embedded ICMP datagram */ ip->ip_src = original_address; ic2->icmp_id = original_id; } return (PKT_ALIAS_OK); } return (PKT_ALIAS_IGNORED); } static int IcmpAliasIn(struct libalias *la, struct ip *pip) { struct icmp *ic; int iresult; size_t dlen; LIBALIAS_LOCK_ASSERT(la); dlen = ntohs(pip->ip_len) - (pip->ip_hl << 2); if (dlen < ICMP_MINLEN) return (PKT_ALIAS_IGNORED); -/* Return if proxy-only mode is enabled */ + /* Return if proxy-only mode is enabled */ if (la->packetAliasMode & PKT_ALIAS_PROXY_ONLY) return (PKT_ALIAS_OK); ic = (struct icmp *)ip_next(pip); iresult = PKT_ALIAS_IGNORED; switch (ic->icmp_type) { case ICMP_ECHOREPLY: case ICMP_TSTAMPREPLY: if (ic->icmp_code == 0) { iresult = IcmpAliasIn1(la, pip); } break; case ICMP_UNREACH: case ICMP_SOURCEQUENCH: case ICMP_TIMXCEED: case ICMP_PARAMPROB: if (dlen < ICMP_ADVLENMIN || dlen < (size_t)ICMP_ADVLEN(ic)) return (PKT_ALIAS_IGNORED); iresult = IcmpAliasIn2(la, pip); break; case ICMP_ECHO: case ICMP_TSTAMP: iresult = IcmpAliasIn1(la, pip); break; } return (iresult); } -static int -IcmpAliasOut1(struct libalias *la, struct ip *pip, int create) -{ /* Alias outgoing echo and timestamp requests. De-alias outgoing echo and timestamp replies. */ +static int +IcmpAliasOut1(struct libalias *la, struct ip *pip, int create) +{ struct alias_link *lnk; struct icmp *ic; LIBALIAS_LOCK_ASSERT(la); ic = (struct icmp *)ip_next(pip); -/* Save overwritten data for when echo packet returns */ + /* Save overwritten data for when echo packet returns */ lnk = FindIcmpOut(la, pip->ip_src, pip->ip_dst, ic->icmp_id, create); if (lnk != NULL) { u_short alias_id; int accumulate; alias_id = GetAliasPort(lnk); -/* Since data field is being modified, adjust ICMP checksum */ + /* 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 */ + /* Alias sequence number */ ic->icmp_id = alias_id; -/* Change source address */ + /* Change source address */ { struct in_addr alias_address; alias_address = GetAliasAddress(lnk); DifferentialChecksum(&pip->ip_sum, &alias_address, &pip->ip_src, 2); pip->ip_src = alias_address; } return (PKT_ALIAS_OK); } return (PKT_ALIAS_IGNORED); } -static int -IcmpAliasOut2(struct libalias *la, struct ip *pip) -{ /* Alias outgoing ICMP error messages containing IP header and first 64 bits of datagram. */ +static int +IcmpAliasOut2(struct libalias *la, struct ip *pip) +{ struct ip *ip; struct icmp *ic, *ic2; struct udphdr *ud; struct tcphdr *tc; struct alias_link *lnk; LIBALIAS_LOCK_ASSERT(la); ic = (struct icmp *)ip_next(pip); ip = &ic->icmp_ip; ud = (struct udphdr *)ip_next(ip); tc = (struct tcphdr *)ip_next(ip); ic2 = (struct icmp *)ip_next(ip); if (ip->ip_p == IPPROTO_UDP) lnk = FindUdpTcpOut(la, ip->ip_dst, ip->ip_src, ud->uh_dport, ud->uh_sport, IPPROTO_UDP, 0); else if (ip->ip_p == IPPROTO_TCP) lnk = FindUdpTcpOut(la, ip->ip_dst, ip->ip_src, tc->th_dport, tc->th_sport, IPPROTO_TCP, 0); else if (ip->ip_p == IPPROTO_ICMP) { if (ic2->icmp_type == ICMP_ECHO || ic2->icmp_type == ICMP_TSTAMP) lnk = FindIcmpOut(la, ip->ip_dst, ip->ip_src, ic2->icmp_id, 0); else lnk = NULL; } else lnk = NULL; if (lnk != NULL) { if (ip->ip_p == IPPROTO_UDP || ip->ip_p == IPPROTO_TCP) { int accumulate; struct in_addr alias_address; u_short alias_port; alias_address = GetAliasAddress(lnk); alias_port = GetAliasPort(lnk); -/* Adjust ICMP checksum */ + /* Adjust ICMP checksum */ accumulate = twowords(&ip->ip_dst); accumulate -= twowords(&alias_address); accumulate += ud->uh_dport; accumulate -= alias_port; ADJUST_CHECKSUM(accumulate, ic->icmp_cksum); -/* - * Alias address in IP header if it comes from the host - * the original TCP/UDP packet was destined for. - */ + /* + * Alias address in IP header if it comes from the host + * the original TCP/UDP packet was destined for. + */ if (pip->ip_src.s_addr == ip->ip_dst.s_addr) { DifferentialChecksum(&pip->ip_sum, &alias_address, &pip->ip_src, 2); pip->ip_src = alias_address; } -/* Alias address and port number of original IP packet -fragment contained in ICMP data section */ + /* Alias address and port number of original IP packet + * fragment contained in ICMP data section */ ip->ip_dst = alias_address; ud->uh_dport = alias_port; } else if (ip->ip_p == IPPROTO_ICMP) { int accumulate; struct in_addr alias_address; u_short alias_id; alias_address = GetAliasAddress(lnk); alias_id = GetAliasPort(lnk); -/* Adjust ICMP checksum */ + /* Adjust ICMP checksum */ accumulate = twowords(&ip->ip_dst); accumulate -= twowords(&alias_address); accumulate += ic2->icmp_id; accumulate -= alias_id; ADJUST_CHECKSUM(accumulate, ic->icmp_cksum); -/* - * Alias address in IP header if it comes from the host - * the original ICMP message was destined for. - */ + /* + * Alias address in IP header if it comes from the host + * the original ICMP message was destined for. + */ if (pip->ip_src.s_addr == ip->ip_dst.s_addr) { DifferentialChecksum(&pip->ip_sum, &alias_address, &pip->ip_src, 2); pip->ip_src = alias_address; } -/* Alias address of original IP packet and sequence number of - embedded ICMP datagram */ + /* Alias address of original IP packet and + * sequence number of embedded ICMP datagram */ ip->ip_dst = alias_address; ic2->icmp_id = alias_id; } return (PKT_ALIAS_OK); } return (PKT_ALIAS_IGNORED); } static int IcmpAliasOut(struct libalias *la, struct ip *pip, int create) { int iresult; struct icmp *ic; LIBALIAS_LOCK_ASSERT(la); (void)create; -/* Return if proxy-only mode is enabled */ + /* Return if proxy-only mode is enabled */ if (la->packetAliasMode & PKT_ALIAS_PROXY_ONLY) return (PKT_ALIAS_OK); ic = (struct icmp *)ip_next(pip); iresult = PKT_ALIAS_IGNORED; switch (ic->icmp_type) { case ICMP_ECHO: case ICMP_TSTAMP: if (ic->icmp_code == 0) { iresult = IcmpAliasOut1(la, pip, create); } break; case ICMP_UNREACH: case ICMP_SOURCEQUENCH: case ICMP_TIMXCEED: case ICMP_PARAMPROB: iresult = IcmpAliasOut2(la, pip); break; case ICMP_ECHOREPLY: case ICMP_TSTAMPREPLY: iresult = IcmpAliasOut1(la, pip, create); } return (iresult); } -static int -ProtoAliasIn(struct libalias *la, struct in_addr ip_src, - struct ip *pip, u_char ip_p, u_short *ip_sum) -{ /* Handle incoming IP packets. The only thing which is done in this case is to alias the dest IP address of the packet to our inside machine. */ +static int +ProtoAliasIn(struct libalias *la, struct in_addr ip_src, + struct ip *pip, u_char ip_p, u_short *ip_sum) +{ struct alias_link *lnk; LIBALIAS_LOCK_ASSERT(la); -/* Return if proxy-only mode is enabled */ + /* Return if proxy-only mode is enabled */ if (la->packetAliasMode & PKT_ALIAS_PROXY_ONLY) return (PKT_ALIAS_OK); lnk = FindProtoIn(la, ip_src, pip->ip_dst, ip_p); if (lnk != NULL) { struct in_addr original_address; original_address = GetOriginalAddress(lnk); -/* Restore original IP address */ + /* Restore original IP address */ DifferentialChecksum(ip_sum, &original_address, &pip->ip_dst, 2); pip->ip_dst = original_address; return (PKT_ALIAS_OK); } return (PKT_ALIAS_IGNORED); } -static int -ProtoAliasOut(struct libalias *la, struct ip *pip, - struct in_addr ip_dst, u_char ip_p, u_short *ip_sum, int create) -{ /* Handle outgoing IP packets. The only thing which is done in this case is to alias the source IP address of the packet. */ +static int +ProtoAliasOut(struct libalias *la, struct ip *pip, + struct in_addr ip_dst, u_char ip_p, u_short *ip_sum, int create) +{ struct alias_link *lnk; LIBALIAS_LOCK_ASSERT(la); -/* Return if proxy-only mode is enabled */ + /* Return if proxy-only mode is enabled */ if (la->packetAliasMode & PKT_ALIAS_PROXY_ONLY) return (PKT_ALIAS_OK); if (!create) return (PKT_ALIAS_IGNORED); lnk = FindProtoOut(la, pip->ip_src, ip_dst, ip_p); if (lnk != NULL) { struct in_addr alias_address; alias_address = GetAliasAddress(lnk); -/* Change source address */ + /* Change source address */ DifferentialChecksum(ip_sum, &alias_address, &pip->ip_src, 2); pip->ip_src = alias_address; return (PKT_ALIAS_OK); } return (PKT_ALIAS_IGNORED); } static int UdpAliasIn(struct libalias *la, struct ip *pip) { struct udphdr *ud; struct alias_link *lnk; size_t dlen; LIBALIAS_LOCK_ASSERT(la); dlen = ntohs(pip->ip_len) - (pip->ip_hl << 2); if (dlen < sizeof(struct udphdr)) return (PKT_ALIAS_IGNORED); ud = (struct udphdr *)ip_next(pip); if (dlen < ntohs(ud->uh_ulen)) return (PKT_ALIAS_IGNORED); lnk = FindUdpTcpIn(la, pip->ip_src, pip->ip_dst, ud->uh_sport, ud->uh_dport, IPPROTO_UDP, !(la->packetAliasMode & PKT_ALIAS_PROXY_ONLY)); if (lnk != NULL) { struct in_addr alias_address; struct in_addr original_address; struct in_addr proxy_address; u_short alias_port; u_short proxy_port; int accumulate; int error; struct alias_data ad = { - .lnk = lnk, - .oaddr = &original_address, + .lnk = lnk, + .oaddr = &original_address, .aaddr = &alias_address, .aport = &alias_port, .sport = &ud->uh_sport, .dport = &ud->uh_dport, .maxpktsize = 0 }; alias_address = GetAliasAddress(lnk); original_address = GetOriginalAddress(lnk); proxy_address = GetProxyAddress(lnk); alias_port = ud->uh_dport; ud->uh_dport = GetOriginalPort(lnk); proxy_port = GetProxyPort(lnk); - /* Walk out chain. */ + /* Walk out chain. */ error = find_handler(IN, UDP, la, pip, &ad); /* If we cannot figure out the packet, ignore it. */ if (error < 0) return (PKT_ALIAS_IGNORED); -/* If UDP checksum is not zero, then adjust since destination port */ -/* is being unaliased and destination address is being altered. */ + /* If UDP checksum is not zero, then adjust since + * destination port is being unaliased and + * destination address is being altered. */ if (ud->uh_sum != 0) { accumulate = alias_port; accumulate -= ud->uh_dport; accumulate += twowords(&alias_address); accumulate -= twowords(&original_address); -/* If this is a proxy packet, modify checksum because of source change.*/ - if (proxy_port != 0) { - accumulate += ud->uh_sport; - accumulate -= proxy_port; - } + /* If this is a proxy packet, modify checksum + * because of source change.*/ + if (proxy_port != 0) { + accumulate += ud->uh_sport; + accumulate -= proxy_port; + } - if (proxy_address.s_addr != 0) { + if (proxy_address.s_addr != 0) { accumulate += twowords(&pip->ip_src); accumulate -= twowords(&proxy_address); - } + } ADJUST_CHECKSUM(accumulate, ud->uh_sum); } -/* XXX: Could the two if's below be concatenated to one ? */ -/* Restore source port and/or address in case of proxying*/ - if (proxy_port != 0) - ud->uh_sport = proxy_port; + /* XXX: Could the two if's below be concatenated to one ? */ + /* Restore source port and/or address in case of proxying*/ + if (proxy_port != 0) + ud->uh_sport = proxy_port; - if (proxy_address.s_addr != 0) { - DifferentialChecksum(&pip->ip_sum, - &proxy_address, &pip->ip_src, 2); - pip->ip_src = proxy_address; - } + if (proxy_address.s_addr != 0) { + DifferentialChecksum(&pip->ip_sum, + &proxy_address, &pip->ip_src, 2); + pip->ip_src = proxy_address; + } -/* Restore original IP address */ + /* Restore original IP address */ DifferentialChecksum(&pip->ip_sum, &original_address, &pip->ip_dst, 2); pip->ip_dst = original_address; return (PKT_ALIAS_OK); } return (PKT_ALIAS_IGNORED); } static int UdpAliasOut(struct libalias *la, struct ip *pip, int maxpacketsize, int create) { struct udphdr *ud; struct alias_link *lnk; struct in_addr dest_address; struct in_addr proxy_server_address; u_short dest_port; u_short proxy_server_port; int proxy_type; int error; size_t dlen; LIBALIAS_LOCK_ASSERT(la); -/* Return if proxy-only mode is enabled and not proxyrule found.*/ + /* Return if proxy-only mode is enabled and not proxyrule found.*/ dlen = ntohs(pip->ip_len) - (pip->ip_hl << 2); if (dlen < sizeof(struct udphdr)) return (PKT_ALIAS_IGNORED); ud = (struct udphdr *)ip_next(pip); if (dlen < ntohs(ud->uh_ulen)) return (PKT_ALIAS_IGNORED); - proxy_type = ProxyCheck(la, &proxy_server_address, - &proxy_server_port, pip->ip_src, pip->ip_dst, - ud->uh_dport, pip->ip_p); + proxy_type = ProxyCheck(la, &proxy_server_address, &proxy_server_port, + pip->ip_src, pip->ip_dst, ud->uh_dport, pip->ip_p); if (proxy_type == 0 && (la->packetAliasMode & PKT_ALIAS_PROXY_ONLY)) return (PKT_ALIAS_OK); -/* If this is a transparent proxy, save original destination, - * then alter the destination and adjust checksums */ + /* If this is a transparent proxy, save original destination, + * then alter the destination and adjust checksums */ dest_port = ud->uh_dport; dest_address = pip->ip_dst; if (proxy_type != 0) { - int accumulate; + int accumulate; accumulate = twowords(&pip->ip_dst); accumulate -= twowords(&proxy_server_address); - ADJUST_CHECKSUM(accumulate, pip->ip_sum); + ADJUST_CHECKSUM(accumulate, pip->ip_sum); if (ud->uh_sum != 0) { accumulate = twowords(&pip->ip_dst); accumulate -= twowords(&proxy_server_address); - accumulate += ud->uh_dport; - accumulate -= proxy_server_port; - ADJUST_CHECKSUM(accumulate, ud->uh_sum); + accumulate += ud->uh_dport; + accumulate -= proxy_server_port; + ADJUST_CHECKSUM(accumulate, ud->uh_sum); } - pip->ip_dst = proxy_server_address; - ud->uh_dport = proxy_server_port; + pip->ip_dst = proxy_server_address; + ud->uh_dport = proxy_server_port; } lnk = FindUdpTcpOut(la, pip->ip_src, pip->ip_dst, ud->uh_sport, ud->uh_dport, IPPROTO_UDP, create); if (lnk != NULL) { u_short alias_port; struct in_addr alias_address; struct alias_data ad = { - .lnk = lnk, + .lnk = lnk, .oaddr = NULL, .aaddr = &alias_address, .aport = &alias_port, .sport = &ud->uh_sport, .dport = &ud->uh_dport, .maxpktsize = 0 }; -/* Save original destination address, if this is a proxy packet. - * Also modify packet to include destination encoding. This may - * change the size of IP header. */ + /* Save original destination address, if this is a proxy packet. + * Also modify packet to include destination encoding. This may + * change the size of IP header. */ if (proxy_type != 0) { - SetProxyPort(lnk, dest_port); - SetProxyAddress(lnk, dest_address); - ProxyModify(la, lnk, pip, maxpacketsize, proxy_type); - ud = (struct udphdr *)ip_next(pip); - } + SetProxyPort(lnk, dest_port); + SetProxyAddress(lnk, dest_address); + ProxyModify(la, lnk, pip, maxpacketsize, proxy_type); + ud = (struct udphdr *)ip_next(pip); + } alias_address = GetAliasAddress(lnk); alias_port = GetAliasPort(lnk); - /* Walk out chain. */ + /* Walk out chain. */ error = find_handler(OUT, UDP, la, pip, &ad); -/* If UDP checksum is not zero, adjust since source port is */ -/* being aliased and source address is being altered */ + /* If UDP checksum is not zero, adjust since source port is */ + /* being aliased and source address is being altered */ if (ud->uh_sum != 0) { int accumulate; accumulate = ud->uh_sport; accumulate -= alias_port; accumulate += twowords(&pip->ip_src); accumulate -= twowords(&alias_address); ADJUST_CHECKSUM(accumulate, ud->uh_sum); } -/* Put alias port in UDP header */ + /* Put alias port in UDP header */ ud->uh_sport = alias_port; -/* Change source address */ + /* Change source address */ DifferentialChecksum(&pip->ip_sum, &alias_address, &pip->ip_src, 2); pip->ip_src = alias_address; return (PKT_ALIAS_OK); } return (PKT_ALIAS_IGNORED); } static int TcpAliasIn(struct libalias *la, struct ip *pip) { struct tcphdr *tc; struct alias_link *lnk; size_t dlen; LIBALIAS_LOCK_ASSERT(la); dlen = ntohs(pip->ip_len) - (pip->ip_hl << 2); if (dlen < sizeof(struct tcphdr)) return (PKT_ALIAS_IGNORED); tc = (struct tcphdr *)ip_next(pip); lnk = FindUdpTcpIn(la, pip->ip_src, pip->ip_dst, tc->th_sport, tc->th_dport, IPPROTO_TCP, !(la->packetAliasMode & PKT_ALIAS_PROXY_ONLY)); if (lnk != NULL) { struct in_addr alias_address; struct in_addr original_address; struct in_addr proxy_address; u_short alias_port; u_short proxy_port; int accumulate, error; - /* - * The init of MANY vars is a bit below, but aliashandlepptpin + /* + * The init of MANY vars is a bit below, but aliashandlepptpin * seems to need the destination port that came within the * packet and not the original one looks below [*]. */ struct alias_data ad = { - .lnk = lnk, + .lnk = lnk, .oaddr = NULL, .aaddr = NULL, .aport = NULL, .sport = &tc->th_sport, .dport = &tc->th_dport, .maxpktsize = 0 }; - /* Walk out chain. */ + /* Walk out chain. */ error = find_handler(IN, TCP, la, pip, &ad); alias_address = GetAliasAddress(lnk); original_address = GetOriginalAddress(lnk); proxy_address = GetProxyAddress(lnk); alias_port = tc->th_dport; tc->th_dport = GetOriginalPort(lnk); proxy_port = GetProxyPort(lnk); - /* - * Look above, if anyone is going to add find_handler AFTER + /* + * Look above, if anyone is going to add find_handler AFTER * this aliashandlepptpin/point, please redo alias_data too. * Uncommenting the piece here below should be enough. */ #if 0 struct alias_data ad = { .lnk = lnk, .oaddr = &original_address, .aaddr = &alias_address, .aport = &alias_port, .sport = &ud->uh_sport, .dport = &ud->uh_dport, .maxpktsize = 0 }; - + /* Walk out chain. */ error = find_handler(la, pip, &ad); if (error == EHDNOF) printf("Protocol handler not found\n"); #endif -/* Adjust TCP checksum since destination port is being unaliased */ -/* and destination port is being altered. */ + /* Adjust TCP checksum since destination port is being + * unaliased and destination port is being altered. */ accumulate = alias_port; accumulate -= tc->th_dport; accumulate += twowords(&alias_address); accumulate -= twowords(&original_address); -/* If this is a proxy, then modify the TCP source port and - checksum accumulation */ + /* If this is a proxy, then modify the TCP source port + * and checksum accumulation */ if (proxy_port != 0) { accumulate += tc->th_sport; tc->th_sport = proxy_port; accumulate -= tc->th_sport; accumulate += twowords(&pip->ip_src); accumulate -= twowords(&proxy_address); } -/* See if ACK number needs to be modified */ + /* See if ACK number needs to be modified */ if (GetAckModified(lnk) == 1) { int delta; tc = (struct tcphdr *)ip_next(pip); delta = GetDeltaAckIn(tc->th_ack, lnk); if (delta != 0) { accumulate += twowords(&tc->th_ack); tc->th_ack = htonl(ntohl(tc->th_ack) - delta); accumulate -= twowords(&tc->th_ack); } } ADJUST_CHECKSUM(accumulate, tc->th_sum); -/* Restore original IP address */ + /* Restore original IP address */ accumulate = twowords(&pip->ip_dst); pip->ip_dst = original_address; accumulate -= twowords(&pip->ip_dst); -/* If this is a transparent proxy packet, then modify the source - address */ + /* If this is a transparent proxy packet, + * then modify the source address */ if (proxy_address.s_addr != 0) { accumulate += twowords(&pip->ip_src); pip->ip_src = proxy_address; accumulate -= twowords(&pip->ip_src); } ADJUST_CHECKSUM(accumulate, pip->ip_sum); -/* Monitor TCP connection state */ + /* Monitor TCP connection state */ tc = (struct tcphdr *)ip_next(pip); TcpMonitorIn(tc->th_flags, lnk); return (PKT_ALIAS_OK); } return (PKT_ALIAS_IGNORED); } static int TcpAliasOut(struct libalias *la, struct ip *pip, int maxpacketsize, int create) { int proxy_type, error; u_short dest_port; u_short proxy_server_port; size_t dlen; struct in_addr dest_address; struct in_addr proxy_server_address; struct tcphdr *tc; struct alias_link *lnk; LIBALIAS_LOCK_ASSERT(la); dlen = ntohs(pip->ip_len) - (pip->ip_hl << 2); if (dlen < sizeof(struct tcphdr)) return (PKT_ALIAS_IGNORED); tc = (struct tcphdr *)ip_next(pip); if (create) - proxy_type = ProxyCheck(la, &proxy_server_address, - &proxy_server_port, pip->ip_src, pip->ip_dst, + proxy_type = ProxyCheck(la, &proxy_server_address, + &proxy_server_port, pip->ip_src, pip->ip_dst, tc->th_dport, pip->ip_p); else proxy_type = 0; if (proxy_type == 0 && (la->packetAliasMode & PKT_ALIAS_PROXY_ONLY)) return (PKT_ALIAS_OK); -/* If this is a transparent proxy, save original destination, - then alter the destination and adjust checksums */ + /* If this is a transparent proxy, save original destination, + * then alter the destination and adjust checksums */ dest_port = tc->th_dport; dest_address = pip->ip_dst; if (proxy_type != 0) { int accumulate; accumulate = tc->th_dport; tc->th_dport = proxy_server_port; accumulate -= tc->th_dport; accumulate += twowords(&pip->ip_dst); accumulate -= twowords(&proxy_server_address); ADJUST_CHECKSUM(accumulate, tc->th_sum); accumulate = twowords(&pip->ip_dst); pip->ip_dst = proxy_server_address; accumulate -= twowords(&pip->ip_dst); ADJUST_CHECKSUM(accumulate, pip->ip_sum); } lnk = FindUdpTcpOut(la, pip->ip_src, pip->ip_dst, tc->th_sport, tc->th_dport, IPPROTO_TCP, create); if (lnk == NULL) return (PKT_ALIAS_IGNORED); if (lnk != NULL) { u_short alias_port; struct in_addr alias_address; int accumulate; struct alias_data ad = { - .lnk = lnk, + .lnk = lnk, .oaddr = NULL, .aaddr = &alias_address, .aport = &alias_port, .sport = &tc->th_sport, .dport = &tc->th_dport, .maxpktsize = maxpacketsize }; -/* Save original destination address, if this is a proxy packet. - Also modify packet to include destination encoding. This may - change the size of IP header. */ + /* Save original destination address, if this is a proxy packet. + * Also modify packet to include destination + * encoding. This may change the size of IP header. */ if (proxy_type != 0) { SetProxyPort(lnk, dest_port); SetProxyAddress(lnk, dest_address); ProxyModify(la, lnk, pip, maxpacketsize, proxy_type); tc = (struct tcphdr *)ip_next(pip); } -/* Get alias address and port */ + /* Get alias address and port */ alias_port = GetAliasPort(lnk); alias_address = GetAliasAddress(lnk); -/* Monitor TCP connection state */ + /* Monitor TCP connection state */ tc = (struct tcphdr *)ip_next(pip); TcpMonitorOut(tc->th_flags, lnk); - - /* Walk out chain. */ + + /* Walk out chain. */ error = find_handler(OUT, TCP, la, pip, &ad); -/* Adjust TCP checksum since source port is being aliased */ -/* and source address is being altered */ + /* Adjust TCP checksum since source port is being aliased + * and source address is being altered */ accumulate = tc->th_sport; tc->th_sport = alias_port; accumulate -= tc->th_sport; accumulate += twowords(&pip->ip_src); accumulate -= twowords(&alias_address); -/* Modify sequence number if necessary */ + /* Modify sequence number if necessary */ if (GetAckModified(lnk) == 1) { int delta; - + tc = (struct tcphdr *)ip_next(pip); delta = GetDeltaSeqOut(tc->th_seq, lnk); if (delta != 0) { accumulate += twowords(&tc->th_seq); tc->th_seq = htonl(ntohl(tc->th_seq) + delta); accumulate -= twowords(&tc->th_seq); } } ADJUST_CHECKSUM(accumulate, tc->th_sum); -/* Change source address */ + /* Change source address */ accumulate = twowords(&pip->ip_src); pip->ip_src = alias_address; accumulate -= twowords(&pip->ip_src); ADJUST_CHECKSUM(accumulate, pip->ip_sum); return (PKT_ALIAS_OK); } return (PKT_ALIAS_IGNORED); } /* Fragment Handling FragmentIn() FragmentOut() The packet aliasing module has a limited ability for handling IP fragments. If the ICMP, TCP or UDP header is in the first fragment received, then the ID number of the IP packet is saved, and other fragments are identified according to their ID number and IP address they were sent from. Pointers to unresolved fragments can also be saved and recalled when a header fragment is seen. */ /* Local prototypes */ static int FragmentIn(struct libalias *la, struct in_addr ip_src, struct ip *pip, u_short ip_id, u_short *ip_sum); static int FragmentOut(struct libalias *, struct ip *pip, u_short *ip_sum); static int FragmentIn(struct libalias *la, struct in_addr ip_src, struct ip *pip, u_short ip_id, u_short *ip_sum) { struct alias_link *lnk; LIBALIAS_LOCK_ASSERT(la); lnk = FindFragmentIn2(la, ip_src, pip->ip_dst, ip_id); if (lnk != NULL) { struct in_addr original_address; GetFragmentAddr(lnk, &original_address); DifferentialChecksum(ip_sum, &original_address, &pip->ip_dst, 2); pip->ip_dst = original_address; return (PKT_ALIAS_OK); } return (PKT_ALIAS_UNRESOLVED_FRAGMENT); } static int FragmentOut(struct libalias *la, struct ip *pip, u_short *ip_sum) { struct in_addr alias_address; LIBALIAS_LOCK_ASSERT(la); alias_address = FindAliasAddress(la, pip->ip_src); DifferentialChecksum(ip_sum, &alias_address, &pip->ip_src, 2); pip->ip_src = alias_address; return (PKT_ALIAS_OK); } /* Outside World Access PacketAliasSaveFragment() PacketAliasGetFragment() PacketAliasFragmentIn() PacketAliasIn() PacketAliasOut() PacketUnaliasOut() (prototypes in alias.h) */ int LibAliasSaveFragment(struct libalias *la, void *ptr) { int iresult; struct alias_link *lnk; struct ip *pip; LIBALIAS_LOCK(la); pip = (struct ip *)ptr; lnk = AddFragmentPtrLink(la, pip->ip_src, pip->ip_id); iresult = PKT_ALIAS_ERROR; if (lnk != NULL) { SetFragmentPtr(lnk, ptr); iresult = PKT_ALIAS_OK; } LIBALIAS_UNLOCK(la); return (iresult); } -void * +void * LibAliasGetFragment(struct libalias *la, void *ptr) { struct alias_link *lnk; void *fptr; struct ip *pip; LIBALIAS_LOCK(la); pip = (struct ip *)ptr; lnk = FindFragmentPtr(la, pip->ip_src, pip->ip_id); if (lnk != NULL) { GetFragmentPtr(lnk, &fptr); SetFragmentPtr(lnk, NULL); SetExpire(lnk, 0); /* Deletes link */ - } else + } else fptr = NULL; LIBALIAS_UNLOCK(la); return (fptr); } void -LibAliasFragmentIn(struct libalias *la, void *ptr, /* Points to correctly - * de-aliased header - * fragment */ - void *ptr_fragment /* Points to fragment which must be - * de-aliased */ +LibAliasFragmentIn(struct libalias *la, + void *ptr, /* Points to correctly de-aliased header fragment */ + void *ptr_fragment /* fragment which must be de-aliased */ ) { struct ip *pip; struct ip *fpip; LIBALIAS_LOCK(la); (void)la; pip = (struct ip *)ptr; fpip = (struct ip *)ptr_fragment; DifferentialChecksum(&fpip->ip_sum, &pip->ip_dst, &fpip->ip_dst, 2); fpip->ip_dst = pip->ip_dst; LIBALIAS_UNLOCK(la); } /* Local prototypes */ static int LibAliasOutLocked(struct libalias *la, struct ip *pip, - int maxpacketsize, int create); + int maxpacketsize, int create); static int LibAliasInLocked(struct libalias *la, struct ip *pip, - int maxpacketsize); + int maxpacketsize); int LibAliasIn(struct libalias *la, void *ptr, int maxpacketsize) { int res; LIBALIAS_LOCK(la); res = LibAliasInLocked(la, (struct ip *)ptr, maxpacketsize); LIBALIAS_UNLOCK(la); return (res); } static int LibAliasInLocked(struct libalias *la, struct ip *pip, int maxpacketsize) { struct in_addr alias_addr; int iresult; if (la->packetAliasMode & PKT_ALIAS_REVERSE) { la->packetAliasMode &= ~PKT_ALIAS_REVERSE; iresult = LibAliasOutLocked(la, pip, maxpacketsize, 1); la->packetAliasMode |= PKT_ALIAS_REVERSE; goto getout; } HouseKeeping(la); ClearCheckNewLink(la); alias_addr = pip->ip_dst; /* Defense against mangled packets */ if (ntohs(pip->ip_len) > maxpacketsize || (pip->ip_hl << 2) > maxpacketsize) { - iresult = PKT_ALIAS_IGNORED; + iresult = PKT_ALIAS_IGNORED; goto getout; } iresult = PKT_ALIAS_IGNORED; if ((ntohs(pip->ip_off) & IP_OFFMASK) == 0) { switch (pip->ip_p) { case IPPROTO_ICMP: iresult = IcmpAliasIn(la, pip); break; case IPPROTO_UDP: iresult = UdpAliasIn(la, pip); break; case IPPROTO_TCP: iresult = TcpAliasIn(la, pip); break; #ifdef _KERNEL case IPPROTO_SCTP: - iresult = SctpAlias(la, pip, SN_TO_LOCAL); + iresult = SctpAlias(la, pip, SN_TO_LOCAL); break; #endif - case IPPROTO_GRE: { + case IPPROTO_GRE: { int error; struct alias_data ad = { - .lnk = NULL, - .oaddr = NULL, + .lnk = NULL, + .oaddr = NULL, .aaddr = NULL, .aport = NULL, .sport = NULL, .dport = NULL, - .maxpktsize = 0 + .maxpktsize = 0 }; - - /* Walk out chain. */ + + /* Walk out chain. */ error = find_handler(IN, IP, la, pip, &ad); - if (error == 0) + if (error == 0) iresult = PKT_ALIAS_OK; else iresult = ProtoAliasIn(la, pip->ip_src, pip, pip->ip_p, &pip->ip_sum); + break; } - break; default: iresult = ProtoAliasIn(la, pip->ip_src, pip, pip->ip_p, &pip->ip_sum); break; } if (ntohs(pip->ip_off) & IP_MF) { struct alias_link *lnk; lnk = FindFragmentIn1(la, pip->ip_src, alias_addr, pip->ip_id); if (lnk != NULL) { iresult = PKT_ALIAS_FOUND_HEADER_FRAGMENT; SetFragmentAddr(lnk, pip->ip_dst); } else { iresult = PKT_ALIAS_ERROR; } } } else { iresult = FragmentIn(la, pip->ip_src, pip, pip->ip_id, &pip->ip_sum); } getout: return (iresult); } /* Unregistered address ranges */ /* 10.0.0.0 -> 10.255.255.255 */ #define UNREG_ADDR_A_LOWER 0x0a000000 #define UNREG_ADDR_A_UPPER 0x0affffff /* 172.16.0.0 -> 172.31.255.255 */ #define UNREG_ADDR_B_LOWER 0xac100000 #define UNREG_ADDR_B_UPPER 0xac1fffff /* 192.168.0.0 -> 192.168.255.255 */ #define UNREG_ADDR_C_LOWER 0xc0a80000 #define UNREG_ADDR_C_UPPER 0xc0a8ffff /* 100.64.0.0 -> 100.127.255.255 (RFC 6598 - Carrier Grade NAT) */ #define UNREG_ADDR_CGN_LOWER 0x64400000 #define UNREG_ADDR_CGN_UPPER 0x647fffff int LibAliasOut(struct libalias *la, void *ptr, int maxpacketsize) { int res; LIBALIAS_LOCK(la); res = LibAliasOutLocked(la, (struct ip *)ptr, maxpacketsize, 1); LIBALIAS_UNLOCK(la); return (res); } int LibAliasOutTry(struct libalias *la, void *ptr, int maxpacketsize, int create) { int res; LIBALIAS_LOCK(la); res = LibAliasOutLocked(la, (struct ip *)ptr, maxpacketsize, create); LIBALIAS_UNLOCK(la); return (res); } static int -LibAliasOutLocked(struct libalias *la, struct ip *pip, /* valid IP packet */ - int maxpacketsize, /* How much the packet data may grow (FTP - * and IRC inline changes) */ - int create /* Create new entries ? */ +LibAliasOutLocked(struct libalias *la, + struct ip *pip, /* valid IP packet */ + int maxpacketsize, /* How much the packet data may grow (FTP and IRC inline changes) */ + int create /* Create new entries ? */ ) { int iresult; struct in_addr addr_save; if (la->packetAliasMode & PKT_ALIAS_REVERSE) { la->packetAliasMode &= ~PKT_ALIAS_REVERSE; iresult = LibAliasInLocked(la, pip, maxpacketsize); la->packetAliasMode |= PKT_ALIAS_REVERSE; goto getout; } HouseKeeping(la); ClearCheckNewLink(la); /* Defense against mangled packets */ if (ntohs(pip->ip_len) > maxpacketsize || (pip->ip_hl << 2) > maxpacketsize) { iresult = PKT_ALIAS_IGNORED; goto getout; } addr_save = GetDefaultAliasAddress(la); if (la->packetAliasMode & PKT_ALIAS_UNREGISTERED_ONLY || la->packetAliasMode & PKT_ALIAS_UNREGISTERED_CGN) { 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; else if (addr >= UNREG_ADDR_CGN_LOWER && addr <= UNREG_ADDR_CGN_UPPER && la->packetAliasMode & PKT_ALIAS_UNREGISTERED_CGN) iclass = 4; if (iclass == 0) { SetDefaultAliasAddress(la, pip->ip_src); } } else if (la->packetAliasMode & PKT_ALIAS_PROXY_ONLY) { SetDefaultAliasAddress(la, pip->ip_src); } iresult = PKT_ALIAS_IGNORED; if ((ntohs(pip->ip_off) & IP_OFFMASK) == 0) { switch (pip->ip_p) { case IPPROTO_ICMP: iresult = IcmpAliasOut(la, pip, create); break; case IPPROTO_UDP: iresult = UdpAliasOut(la, pip, maxpacketsize, create); break; case IPPROTO_TCP: iresult = TcpAliasOut(la, pip, maxpacketsize, create); break; #ifdef _KERNEL case IPPROTO_SCTP: - iresult = SctpAlias(la, pip, SN_TO_GLOBAL); + iresult = SctpAlias(la, pip, SN_TO_GLOBAL); break; #endif case IPPROTO_GRE: { int error; struct alias_data ad = { - .lnk = NULL, - .oaddr = NULL, + .lnk = NULL, + .oaddr = NULL, .aaddr = NULL, .aport = NULL, .sport = NULL, .dport = NULL, - .maxpktsize = 0 + .maxpktsize = 0 }; - /* Walk out chain. */ + /* Walk out chain. */ error = find_handler(OUT, IP, la, pip, &ad); if (error == 0) - iresult = PKT_ALIAS_OK; - else + iresult = PKT_ALIAS_OK; + else iresult = ProtoAliasOut(la, pip, pip->ip_dst, pip->ip_p, &pip->ip_sum, create); + break; } - break; default: iresult = ProtoAliasOut(la, pip, pip->ip_dst, pip->ip_p, &pip->ip_sum, create); break; } } else { iresult = FragmentOut(la, pip, &pip->ip_sum); } SetDefaultAliasAddress(la, addr_save); getout: return (iresult); } int -LibAliasUnaliasOut(struct libalias *la, void *ptr, /* valid IP packet */ - int maxpacketsize /* for error checking */ +LibAliasUnaliasOut(struct libalias *la, + void *ptr, /* valid IP packet */ + int maxpacketsize /* for error checking */ ) { struct ip *pip; struct icmp *ic; struct udphdr *ud; struct tcphdr *tc; struct alias_link *lnk; int iresult = PKT_ALIAS_IGNORED; LIBALIAS_LOCK(la); pip = (struct ip *)ptr; /* Defense against mangled packets */ if (ntohs(pip->ip_len) > maxpacketsize || (pip->ip_hl << 2) > maxpacketsize) goto getout; ud = (struct udphdr *)ip_next(pip); tc = (struct tcphdr *)ip_next(pip); ic = (struct icmp *)ip_next(pip); /* Find a link */ if (pip->ip_p == IPPROTO_UDP) lnk = FindUdpTcpIn(la, pip->ip_dst, pip->ip_src, ud->uh_dport, ud->uh_sport, IPPROTO_UDP, 0); else if (pip->ip_p == IPPROTO_TCP) lnk = FindUdpTcpIn(la, pip->ip_dst, pip->ip_src, tc->th_dport, tc->th_sport, IPPROTO_TCP, 0); else if (pip->ip_p == IPPROTO_ICMP) lnk = FindIcmpIn(la, pip->ip_dst, pip->ip_src, ic->icmp_id, 0); else lnk = NULL; /* Change it from an aliased packet to an unaliased packet */ if (lnk != NULL) { if (pip->ip_p == IPPROTO_UDP || pip->ip_p == IPPROTO_TCP) { int accumulate; struct in_addr original_address; u_short original_port; original_address = GetOriginalAddress(lnk); original_port = GetOriginalPort(lnk); /* Adjust TCP/UDP checksum */ accumulate = twowords(&pip->ip_src); accumulate -= twowords(&original_address); if (pip->ip_p == IPPROTO_UDP) { accumulate += ud->uh_sport; accumulate -= original_port; ADJUST_CHECKSUM(accumulate, ud->uh_sum); } else { accumulate += tc->th_sport; accumulate -= original_port; ADJUST_CHECKSUM(accumulate, tc->th_sum); } /* Adjust IP checksum */ DifferentialChecksum(&pip->ip_sum, &original_address, &pip->ip_src, 2); /* Un-alias source address and port number */ pip->ip_src = original_address; if (pip->ip_p == IPPROTO_UDP) ud->uh_sport = original_port; else tc->th_sport = original_port; iresult = PKT_ALIAS_OK; - } else if (pip->ip_p == IPPROTO_ICMP) { int accumulate; struct in_addr original_address; u_short original_id; original_address = GetOriginalAddress(lnk); original_id = GetOriginalPort(lnk); /* Adjust ICMP checksum */ accumulate = twowords(&pip->ip_src); accumulate -= twowords(&original_address); accumulate += ic->icmp_id; accumulate -= original_id; ADJUST_CHECKSUM(accumulate, ic->icmp_cksum); /* Adjust IP checksum */ DifferentialChecksum(&pip->ip_sum, &original_address, &pip->ip_src, 2); /* Un-alias source address and port number */ pip->ip_src = original_address; ic->icmp_id = original_id; iresult = PKT_ALIAS_OK; } } getout: LIBALIAS_UNLOCK(la); return (iresult); - } #ifndef _KERNEL int LibAliasRefreshModules(void) { char buf[256], conf[] = "/etc/libalias.conf"; FILE *fd; int i, len; fd = fopen(conf, "r"); if (fd == NULL) err(1, "fopen(%s)", conf); LibAliasUnLoadAllModule(); for (;;) { fgets(buf, 256, fd); - if (feof(fd)) - break; + if (feof(fd)) + break; len = strlen(buf); if (len > 1) { for (i = 0; i < len; i++) if (!isspace(buf[i])) break; if (buf[i] == '#') continue; buf[len - 1] = '\0'; LibAliasLoadModule(buf); } } fclose(fd); return (0); } int LibAliasLoadModule(char *path) { struct dll *t; void *handle; struct proto_handler *m; - const char *error; + const char *error; moduledata_t *p; - handle = dlopen (path, RTLD_LAZY); - if (!handle) { + handle = dlopen (path, RTLD_LAZY); + if (!handle) { fprintf(stderr, "%s\n", dlerror()); return (EINVAL); - } + } p = dlsym(handle, "alias_mod"); - if ((error = dlerror()) != NULL) { + if ((error = dlerror()) != NULL) { fprintf(stderr, "%s\n", dlerror()); return (EINVAL); - } + } t = malloc(sizeof(struct dll)); if (t == NULL) return (ENOMEM); strncpy(t->name, p->name, DLL_LEN); t->handle = handle; if (attach_dll(t) == EEXIST) { free(t); fprintf(stderr, "dll conflict\n"); return (EEXIST); } - m = dlsym(t->handle, "handlers"); - if ((error = dlerror()) != NULL) { + m = dlsym(t->handle, "handlers"); + if ((error = dlerror()) != NULL) { fprintf(stderr, "%s\n", error); return (EINVAL); } LibAliasAttachHandlers(m); return (0); } int LibAliasUnLoadAllModule(void) { struct dll *t; struct proto_handler *p; /* Unload all modules then reload everything. */ - while ((p = first_handler()) != NULL) { + while ((p = first_handler()) != NULL) { LibAliasDetachHandlers(p); } - while ((t = walk_dll_chain()) != NULL) { + while ((t = walk_dll_chain()) != NULL) { dlclose(t->handle); free(t); } return (1); } #endif #ifdef _KERNEL /* * m_megapullup() - this function is a big hack. * Thankfully, it's only used in ng_nat and ipfw+nat. * * It allocates an mbuf with cluster and copies the specified part of the chain * into cluster, so that it is all contiguous and can be accessed via a plain * (char *) pointer. This is required, because libalias doesn't know how to * handle mbuf chains. * * On success, m_megapullup returns an mbuf (possibly with cluster) containing * the input packet, on failure NULL. The input packet is always consumed. */ struct mbuf * m_megapullup(struct mbuf *m, int len) { struct mbuf *mcl; if (len > m->m_pkthdr.len) goto bad; if (m->m_next == NULL && M_WRITABLE(m)) return (m); if (len <= MJUMPAGESIZE) mcl = m_get2(len, M_NOWAIT, MT_DATA, M_PKTHDR); else if (len <= MJUM9BYTES) mcl = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, MJUM9BYTES); else if (len <= MJUM16BYTES) mcl = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, MJUM16BYTES); else goto bad; if (mcl == NULL) goto bad; m_align(mcl, len); m_move_pkthdr(mcl, m); m_copydata(m, 0, len, mtod(mcl, caddr_t)); mcl->m_len = mcl->m_pkthdr.len = len; m_freem(m); return (mcl); bad: m_freem(m); return (NULL); } #endif diff --git a/sys/netinet/libalias/alias.h b/sys/netinet/libalias/alias.h index 91351a9eb8b9..36f1ca168823 100644 --- a/sys/netinet/libalias/alias.h +++ b/sys/netinet/libalias/alias.h @@ -1,248 +1,242 @@ /* lint -save -library Flexelint comment for external headers */ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2001 Charles Mott * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /* * Alias.h defines the outside world interfaces for the packet aliasing * software. * * This software is placed into the public domain with no restrictions on its * distribution. */ #ifndef _ALIAS_H_ #define _ALIAS_H_ #include #include #include #define LIBALIAS_BUF_SIZE 128 -#ifdef _KERNEL +#ifdef _KERNEL /* * The kernel version of libalias does not support these features. */ #define NO_FW_PUNCH #define NO_USE_SOCKETS #endif /* * The external interface to libalias, the packet aliasing engine. * * There are two sets of functions: * * PacketAlias*() the old API which doesn't take an instance pointer * and therefore can only have one packet engine at a time. * * LibAlias*() the new API which takes as first argument a pointer to * the instance of the packet aliasing engine. * * The functions otherwise correspond to each other one for one, except * for the LibAliasUnaliasOut()/PacketUnaliasOut() function which were * were misnamed in the old API. */ /* * The instance structure */ struct libalias; /* * An anonymous structure, a pointer to which is returned from * PacketAliasRedirectAddr(), PacketAliasRedirectPort() or * PacketAliasRedirectProto(), passed to PacketAliasAddServer(), * and freed by PacketAliasRedirectDelete(). */ struct alias_link; /* Initialization and control functions. */ struct libalias *LibAliasInit(struct libalias *); void LibAliasSetAddress(struct libalias *, struct in_addr _addr); void LibAliasSetAliasPortRange(struct libalias *la, u_short port_low, u_short port_hi); void LibAliasSetFWBase(struct libalias *, unsigned int _base, unsigned int _num); void LibAliasSetSkinnyPort(struct libalias *, unsigned int _port); -unsigned int - LibAliasSetMode(struct libalias *, unsigned int _flags, unsigned int _mask); +unsigned int LibAliasSetMode(struct libalias *, unsigned int _flags, unsigned int _mask); void LibAliasUninit(struct libalias *); /* Packet Handling functions. */ int LibAliasIn (struct libalias *, void *_ptr, int _maxpacketsize); int LibAliasOut(struct libalias *, void *_ptr, int _maxpacketsize); int LibAliasOutTry(struct libalias *, void *_ptr, int _maxpacketsize, int _create); int LibAliasUnaliasOut(struct libalias *, void *_ptr, int _maxpacketsize); /* Port and address redirection functions. */ -int -LibAliasAddServer(struct libalias *, struct alias_link *_lnk, - struct in_addr _addr, unsigned short _port); -struct alias_link * -LibAliasRedirectAddr(struct libalias *, struct in_addr _src_addr, - struct in_addr _alias_addr); +int LibAliasAddServer(struct libalias *, struct alias_link *_lnk, + struct in_addr _addr, unsigned short _port); +struct alias_link * LibAliasRedirectAddr(struct libalias *, struct in_addr _src_addr, + struct in_addr _alias_addr); int LibAliasRedirectDynamic(struct libalias *, struct alias_link *_lnk); void LibAliasRedirectDelete(struct libalias *, struct alias_link *_lnk); -struct alias_link * -LibAliasRedirectPort(struct libalias *, struct in_addr _src_addr, - unsigned short _src_port, struct in_addr _dst_addr, - unsigned short _dst_port, struct in_addr _alias_addr, - unsigned short _alias_port, unsigned char _proto); -struct alias_link * -LibAliasRedirectProto(struct libalias *, struct in_addr _src_addr, - struct in_addr _dst_addr, struct in_addr _alias_addr, - unsigned char _proto); +struct alias_link * LibAliasRedirectPort(struct libalias *, struct in_addr _src_addr, + unsigned short _src_port, struct in_addr _dst_addr, + unsigned short _dst_port, struct in_addr _alias_addr, + unsigned short _alias_port, unsigned char _proto); +struct alias_link * LibAliasRedirectProto(struct libalias *, struct in_addr _src_addr, + struct in_addr _dst_addr, struct in_addr _alias_addr, + unsigned char _proto); /* Fragment Handling functions. */ void LibAliasFragmentIn(struct libalias *, void *_ptr, void *_ptr_fragment); -void *LibAliasGetFragment(struct libalias *, void *_ptr); +void *LibAliasGetFragment(struct libalias *, void *_ptr); int LibAliasSaveFragment(struct libalias *, void *_ptr); /* Miscellaneous functions. */ int LibAliasCheckNewLink(struct libalias *); -unsigned short - LibAliasInternetChecksum(struct libalias *, unsigned short *_ptr, int _nbytes); +unsigned short LibAliasInternetChecksum(struct libalias *, unsigned short *_ptr, int _nbytes); void LibAliasSetTarget(struct libalias *, struct in_addr _target_addr); /* Transparent proxying routines. */ int LibAliasProxyRule(struct libalias *, const char *_cmd); /* Module handling API */ -int LibAliasLoadModule(char *); -int LibAliasUnLoadAllModule(void); -int LibAliasRefreshModules(void); +int LibAliasLoadModule(char *); +int LibAliasUnLoadAllModule(void); +int LibAliasRefreshModules(void); /* Mbuf helper function. */ struct mbuf *m_megapullup(struct mbuf *, int); /* * Mode flags and other constants. */ /* Mode flags, set using PacketAliasSetMode() */ /* * If PKT_ALIAS_LOG is set, a message will be printed to /var/log/alias.log * every time a link is created or deleted. This is useful for debugging. */ #define PKT_ALIAS_LOG 0x01 /* * If PKT_ALIAS_DENY_INCOMING is set, then incoming connections (e.g. to ftp, * telnet or web servers will be prevented by the aliasing mechanism. */ #define PKT_ALIAS_DENY_INCOMING 0x02 /* * If PKT_ALIAS_SAME_PORTS is set, packets will be attempted sent from the * same port as they originated on. This allows e.g. rsh to work *99% of the * time*, but _not_ 100% (it will be slightly flakey instead of not working * at all). This mode bit is set by PacketAliasInit(), so it is a default * mode of operation. */ #define PKT_ALIAS_SAME_PORTS 0x04 /* * If PKT_ALIAS_USE_SOCKETS is set, then when partially specified links (e.g. * destination port and/or address is zero), the packet aliasing engine will * attempt to allocate a socket for the aliasing port it chooses. This will * avoid interference with the host machine. Fully specified links do not * require this. This bit is set after a call to PacketAliasInit(), so it is * a default mode of operation. */ -#ifndef NO_USE_SOCKETS +#ifndef NO_USE_SOCKETS #define PKT_ALIAS_USE_SOCKETS 0x08 #endif /*- * 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 /* * If PKT_ALIAS_PROXY_ONLY is set, then NAT will be disabled and only * transparent proxying is performed. */ #define PKT_ALIAS_PROXY_ONLY 0x40 /* * If PKT_ALIAS_REVERSE is set, the actions of PacketAliasIn() and * PacketAliasOut() are reversed. */ #define PKT_ALIAS_REVERSE 0x80 #ifndef NO_FW_PUNCH /* * If PKT_ALIAS_PUNCH_FW is set, active FTP and IRC DCC connections will * create a 'hole' in the firewall to allow the transfers to work. The * ipfw rule number that the hole is created with is controlled by * PacketAliasSetFWBase(). The hole will be attached to that * particular alias_link, so when the link goes away the hole is deleted. */ #define PKT_ALIAS_PUNCH_FW 0x100 #endif /* * If PKT_ALIAS_SKIP_GLOBAL is set, nat instance is not checked for matching * states in 'ipfw nat global' rule. */ #define PKT_ALIAS_SKIP_GLOBAL 0x200 /* * Like PKT_ALIAS_UNREGISTERED_ONLY, but includes the RFC 6598 * (Carrier Grade NAT) address range as follows: * * 100.64.0.0 -> 100.127.255.255 */ #define PKT_ALIAS_UNREGISTERED_CGN 0x400 /* Function return codes. */ #define PKT_ALIAS_ERROR -1 #define PKT_ALIAS_OK 1 #define PKT_ALIAS_IGNORED 2 #define PKT_ALIAS_UNRESOLVED_FRAGMENT 3 #define PKT_ALIAS_FOUND_HEADER_FRAGMENT 4 #endif /* !_ALIAS_H_ */ /* lint -restore */ diff --git a/sys/netinet/libalias/alias_db.c b/sys/netinet/libalias/alias_db.c index c87273c863ca..cd29c16ac945 100644 --- a/sys/netinet/libalias/alias_db.c +++ b/sys/netinet/libalias/alias_db.c @@ -1,2882 +1,2850 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2001 Charles Mott * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); /* Alias_db.c encapsulates all data structures used for storing packet aliasing data. Other parts of the aliasing software access data through functions provided in this file. Data storage is based on the notion of a "link", which is established for ICMP echo/reply packets, UDP datagrams and TCP stream connections. A link stores the original source and destination addresses. For UDP and TCP, it also stores source and destination port numbers, as well as an alias port number. Links are also used to store information about fragments. There is a facility for sweeping through and deleting old links as new packets are sent through. A simple timeout is used for ICMP and UDP links. TCP links are left alone unless there is an incomplete connection, in which case the link can be deleted after a certain amount of time. Initial version: August, 1996 (cjm) Version 1.4: September 16, 1996 (cjm) Facility for handling incoming links added. Version 1.6: September 18, 1996 (cjm) ICMP data handling simplified. Version 1.7: January 9, 1997 (cjm) Fragment handling simplified. Saves pointers for unresolved fragments. Permits links for unspecified remote ports or unspecified remote addresses. Fixed bug which did not properly zero port table entries after a link was deleted. Cleaned up some obsolete comments. Version 1.8: January 14, 1997 (cjm) Fixed data type error in StartPoint(). (This error did not exist prior to v1.7 and was discovered and fixed by Ari Suutari) Version 1.9: February 1, 1997 Optionally, connections initiated from packet aliasing host machine will will not have their port number aliased unless it conflicts with an aliasing port already being used. (cjm) All options earlier being #ifdef'ed are now available through a new interface, SetPacketAliasMode(). This allows run time control (which is now available in PPP+pktAlias through the 'alias' keyword). (ee) Added ability to create an alias port without either destination address or port specified. port type = ALIAS_PORT_UNKNOWN_DEST_ALL (ee) Removed K&R style function headers and general cleanup. (ee) Added packetAliasMode to replace compiler #defines's (ee) Allocates sockets for partially specified ports if ALIAS_USE_SOCKETS defined. (cjm) Version 2.0: March, 1997 SetAliasAddress() will now clean up alias links if the aliasing address is changed. (cjm) PacketAliasPermanentLink() function added to support permanent links. (J. Fortes suggested the need for this.) Examples: (192.168.0.1, port 23) <-> alias port 6002, unknown dest addr/port (192.168.0.2, port 21) <-> alias port 3604, known dest addr unknown dest port These permanent links allow for incoming connections to machines on the local network. They can be given with a user-chosen amount of specificity, with increasing specificity meaning more security. (cjm) Quite a bit of rework to the basic engine. The portTable[] array, which kept track of which ports were in use was replaced by a table/linked list structure. (cjm) SetExpire() function added. (cjm) DeleteLink() no longer frees memory association with a pointer to a fragment (this bug was first recognized by E. Eklund in v1.9). Version 2.1: May, 1997 (cjm) Packet aliasing engine reworked so that it can handle multiple external addresses rather than just a single host address. PacketAliasRedirectPort() and PacketAliasRedirectAddr() added to the API. The first function is a more generalized version of PacketAliasPermanentLink(). The second function implements static network address translation. Version 3.2: July, 2000 (salander and satoh) Added FindNewPortGroup to get contiguous range of port values. Added QueryUdpTcpIn and QueryUdpTcpOut to look for an aliasing link but not actually add one. Added FindRtspOut, which is closely derived from FindUdpTcpOut, except that the alias port (from FindNewPortGroup) is provided as input. See HISTORY file for additional revisions. */ #ifdef _KERNEL #include #include #include #include #include #include #include #include #else #include #include #include #include #include -#include +#include #endif #include #include -#ifdef _KERNEL +#ifdef _KERNEL #include #include #include #include #else #include "alias.h" #include "alias_local.h" #include "alias_mod.h" #endif -static LIST_HEAD(, libalias) instancehead = LIST_HEAD_INITIALIZER(instancehead); +static LIST_HEAD(, libalias) instancehead = LIST_HEAD_INITIALIZER(instancehead); /* Constants (note: constants are also defined near relevant functions or structs) */ /* Parameters used for cleanup of expired links */ /* NOTE: ALIAS_CLEANUP_INTERVAL_SECS must be less then LINK_TABLE_OUT_SIZE */ #define ALIAS_CLEANUP_INTERVAL_SECS 64 #define ALIAS_CLEANUP_MAX_SPOKES (LINK_TABLE_OUT_SIZE/5) /* Timeouts (in seconds) for different link types */ #define ICMP_EXPIRE_TIME 60 #define UDP_EXPIRE_TIME 60 #define PROTO_EXPIRE_TIME 60 #define FRAGMENT_ID_EXPIRE_TIME 10 #define FRAGMENT_PTR_EXPIRE_TIME 30 /* TCP link expire time for different cases */ /* When the link has been used and closed - minimal grace time to allow ACKs and potential re-connect in FTP (XXX - is this allowed?) */ #ifndef TCP_EXPIRE_DEAD #define TCP_EXPIRE_DEAD 10 #endif /* When the link has been used and closed on one side - the other side is allowed to still send data */ #ifndef TCP_EXPIRE_SINGLEDEAD #define TCP_EXPIRE_SINGLEDEAD 90 #endif /* When the link isn't yet up */ #ifndef TCP_EXPIRE_INITIAL #define TCP_EXPIRE_INITIAL 300 #endif /* When the link is up */ #ifndef TCP_EXPIRE_CONNECTED #define TCP_EXPIRE_CONNECTED 86400 #endif /* Dummy port number codes used for FindLinkIn/Out() and AddLink(). These constants can be anything except zero, which indicates an unknown port number. */ #define NO_DEST_PORT 1 #define NO_SRC_PORT 1 /* Data Structures The fundamental data structure used in this program is "struct alias_link". Whenever a TCP connection is made, a UDP datagram is sent out, or an ICMP echo request is made, a link record is made (if it has not already been created). The link record is identified by the source address/port and the destination address/port. In the case of an ICMP echo request, the source port is treated as being equivalent with the 16-bit ID number of the ICMP packet. The link record also can store some auxiliary data. For TCP connections that have had sequence and acknowledgment modifications, data space is available to track these changes. A state field is used to keep track in changes to the TCP connection state. ID numbers of fragments can also be stored in the auxiliary space. Pointers to unresolved fragments can also be stored. The link records support two independent chainings. Lookup tables for input and out tables hold the initial pointers the link chains. On input, the lookup table indexes on alias port and link type. On output, the lookup table indexes on source address, destination address, source port, destination port and link type. */ -struct ack_data_record { /* used to save changes to ACK/sequence - * numbers */ +/* used to save changes to ACK/sequence numbers */ +struct ack_data_record { 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 */ +/* Information about TCP connection */ +struct tcp_state { + int in; /* State for outside -> inside */ + int out; /* State for inside -> outside */ + int index; /* Index to ACK data array */ + /* Indicates whether ACK and sequence numbers been modified */ + int ack_modified; }; -#define N_LINK_TCP_DATA 3 /* Number of distinct ACK number changes - * saved for a modified TCP stream */ +/* Number of distinct ACK number changes + * saved for a modified TCP stream */ +#define N_LINK_TCP_DATA 3 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? */ + /* Which firewall record is used for this hole? */ + int fwhole; }; -struct server { /* LSNAT server pool (circular list) */ +/* LSNAT server pool (circular list) */ +struct server { struct in_addr addr; u_short port; struct server *next; }; -struct alias_link { /* Main data structure */ +/* Main data structure */ +struct alias_link { struct libalias *la; - struct in_addr src_addr; /* Address and port information */ + /* Address and port information */ + struct in_addr src_addr; struct in_addr dst_addr; struct in_addr alias_addr; struct in_addr proxy_addr; u_short src_port; u_short dst_port; u_short alias_port; u_short proxy_port; struct server *server; - - int link_type; /* Type of link: TCP, UDP, ICMP, - * proto, frag */ - + /* Type of link: TCP, UDP, ICMP, proto, frag */ + int link_type; /* values for link_type */ #define LINK_ICMP IPPROTO_ICMP #define LINK_UDP IPPROTO_UDP #define LINK_TCP IPPROTO_TCP #define LINK_FRAGMENT_ID (IPPROTO_MAX + 1) #define LINK_FRAGMENT_PTR (IPPROTO_MAX + 2) #define LINK_ADDR (IPPROTO_MAX + 3) #define LINK_PPTP (IPPROTO_MAX + 4) - int flags; /* indicates special characteristics */ + int flags; /* indicates special characteristics */ int pflags; /* protocol-specific flags */ - /* flag bits */ #define LINK_UNKNOWN_DEST_PORT 0x01 #define LINK_UNKNOWN_DEST_ADDR 0x02 #define LINK_PERMANENT 0x04 #define LINK_PARTIALLY_SPECIFIED 0x03 /* logical-or of first two bits */ #define LINK_UNFIREWALLED 0x08 - int timestamp; /* Time link was last accessed */ - int expire_time; /* Expire time for link */ -#ifndef NO_USE_SOCKETS - int sockfd; /* socket descriptor */ + int timestamp; /* Time link was last accessed */ + int expire_time; /* Expire time for link */ +#ifndef NO_USE_SOCKETS + int sockfd; /* socket descriptor */ #endif - LIST_ENTRY (alias_link) list_out; /* Linked list of - * pointers for */ - LIST_ENTRY (alias_link) list_in; /* input and output - * lookup tables */ - - union { /* Auxiliary data */ + /* Linked list of pointers for input and output lookup tables */ + LIST_ENTRY (alias_link) list_out; + LIST_ENTRY (alias_link) list_in; + /* Auxiliary data */ + union { char *frag_ptr; struct in_addr frag_addr; struct tcp_dat *tcp; - } data; + } data; }; /* Clean up procedure. */ static void finishoff(void); /* Kernel module definition. */ -#ifdef _KERNEL +#ifdef _KERNEL MALLOC_DEFINE(M_ALIAS, "libalias", "packet aliasing"); MODULE_VERSION(libalias, 1); static int alias_mod_handler(module_t mod, int type, void *data) { - switch (type) { case MOD_QUIESCE: case MOD_UNLOAD: - finishoff(); + finishoff(); case MOD_LOAD: return (0); default: return (EINVAL); } } static moduledata_t alias_mod = { "alias", alias_mod_handler, NULL }; DECLARE_MODULE(alias, alias_mod, SI_SUB_DRIVERS, SI_ORDER_SECOND); #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); #ifndef NO_FW_PUNCH /* Firewall control */ static void InitPunchFW(struct libalias *); static void UninitPunchFW(struct libalias *); static void ClearFWHole(struct alias_link *); #endif /* Log file control */ static void ShowAliasStats(struct libalias *); static int InitPacketAliasLog(struct libalias *); static void UninitPacketAliasLog(struct libalias *); -void SctpShowAliasStats(struct libalias *la); +void SctpShowAliasStats(struct libalias *la); -static u_int +static u_int StartPointIn(struct in_addr alias_addr, u_short alias_port, int link_type) { u_int n; n = alias_addr.s_addr; if (link_type != LINK_PPTP) n += alias_port; n += link_type; return (n % LINK_TABLE_IN_SIZE); } -static u_int +static u_int StartPointOut(struct in_addr src_addr, struct in_addr dst_addr, u_short src_port, u_short dst_port, int link_type) { u_int n; n = src_addr.s_addr; n += dst_addr.s_addr; if (link_type != LINK_PPTP) { n += src_port; n += dst_port; } n += link_type; return (n % LINK_TABLE_OUT_SIZE); } static int SeqDiff(u_long x, u_long y) { -/* Return the difference between two TCP sequence numbers */ - -/* - This function is encapsulated in case there are any unusual - arithmetic conditions that need to be considered. -*/ - +/* Return 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)); } #ifdef _KERNEL - static void AliasLog(char *str, const char *format, ...) -{ +{ va_list ap; va_start(ap, format); vsnprintf(str, LIBALIAS_BUF_SIZE, format, ap); va_end(ap); } #else static void AliasLog(FILE *stream, const char *format, ...) { va_list ap; va_start(ap, format); vfprintf(stream, format, ap); va_end(ap); fflush(stream); } #endif static void ShowAliasStats(struct libalias *la) { - LIBALIAS_LOCK_ASSERT(la); -/* Used for debugging */ + /* Used for debugging */ if (la->logDesc) { - int tot = la->icmpLinkCount + la->udpLinkCount + - (la->sctpLinkCount>>1) + /* sctp counts half associations */ - la->tcpLinkCount + la->pptpLinkCount + - la->protoLinkCount + la->fragmentIdLinkCount + - la->fragmentPtrLinkCount; - + int tot = la->icmpLinkCount + la->udpLinkCount + + (la->sctpLinkCount>>1) + /* sctp counts half associations */ + la->tcpLinkCount + la->pptpLinkCount + + la->protoLinkCount + la->fragmentIdLinkCount + + la->fragmentPtrLinkCount; + AliasLog(la->logDesc, - "icmp=%u, udp=%u, tcp=%u, sctp=%u, pptp=%u, proto=%u, frag_id=%u frag_ptr=%u / tot=%u", - la->icmpLinkCount, - la->udpLinkCount, - la->tcpLinkCount, - la->sctpLinkCount>>1, /* sctp counts half associations */ - la->pptpLinkCount, - la->protoLinkCount, - la->fragmentIdLinkCount, - la->fragmentPtrLinkCount, tot); + "icmp=%u, udp=%u, tcp=%u, sctp=%u, pptp=%u, proto=%u, frag_id=%u frag_ptr=%u / tot=%u", + la->icmpLinkCount, + la->udpLinkCount, + la->tcpLinkCount, + la->sctpLinkCount>>1, /* sctp counts half associations */ + la->pptpLinkCount, + la->protoLinkCount, + la->fragmentIdLinkCount, + la->fragmentPtrLinkCount, + tot); #ifndef _KERNEL - AliasLog(la->logDesc, " (sock=%u)\n", la->sockCount); + AliasLog(la->logDesc, " (sock=%u)\n", la->sockCount); #endif } } void SctpShowAliasStats(struct libalias *la) { - ShowAliasStats(la); } /* Internal routines for finding, deleting and adding links Port Allocation: GetNewPort() -- find and reserve new alias port number GetSocket() -- try to allocate a socket for a given port Link creation and deletion: CleanupAliasData() - remove all link chains from lookup table IncrementalCleanup() - look for stale links in a single chain DeleteLink() - remove link AddLink() - add link ReLink() - change link Link search: FindLinkOut() - find link for outgoing packets FindLinkIn() - find link for incoming packets Port search: FindNewPortGroup() - find an available group of ports */ /* Local prototypes */ static int GetNewPort(struct libalias *, struct alias_link *, int); -#ifndef NO_USE_SOCKETS +#ifndef NO_USE_SOCKETS static u_short GetSocket(struct libalias *, u_short, int *, int); #endif static void CleanupAliasData(struct libalias *); - static void IncrementalCleanup(struct libalias *); - static void DeleteLink(struct alias_link *); static struct alias_link * ReLink(struct alias_link *, struct in_addr, struct in_addr, struct in_addr, u_short, u_short, int, int); static struct alias_link * - FindLinkOut (struct libalias *, struct in_addr, struct in_addr, u_short, u_short, int, int); +FindLinkOut(struct libalias *, struct in_addr, struct in_addr, u_short, u_short, int, int); static struct alias_link * - FindLinkIn (struct libalias *, struct in_addr, struct in_addr, u_short, u_short, int, int); +FindLinkIn(struct libalias *, struct in_addr, struct in_addr, u_short, u_short, int, int); #define ALIAS_PORT_BASE 0x08000 #define ALIAS_PORT_MASK 0x07fff #define ALIAS_PORT_MASK_EVEN 0x07ffe #define GET_NEW_PORT_MAX_ATTEMPTS 20 #define FIND_EVEN_ALIAS_BASE 1 /* GetNewPort() allocates port numbers. Note that if a port number is already in use, that does not mean that it cannot be used by another link concurrently. This is because GetNewPort() looks for unused triplets: (dest addr, dest port, alias port). */ static int GetNewPort(struct libalias *la, struct alias_link *lnk, int alias_port_param) { int i; int max_trials; u_short port_sys; u_short port_net; LIBALIAS_LOCK_ASSERT(la); -/* - Description of alias_port_param for GetNewPort(). When - this parameter is zero or positive, it precisely specifies - the port number. GetNewPort() will return this number - without check that it is in use. - - When this parameter is GET_ALIAS_PORT, it indicates to get a randomly - selected port number. -*/ + /* + * Description of alias_port_param for GetNewPort(). When + * this parameter is zero or positive, it precisely specifies + * the port number. GetNewPort() will return this number + * without check that it is in use. + * When this parameter is GET_ALIAS_PORT, it indicates to get + * a randomly selected port number. + */ if (alias_port_param == GET_ALIAS_PORT) { /* * The aliasing port is automatically selected by one of * two methods below: */ max_trials = GET_NEW_PORT_MAX_ATTEMPTS; if (la->packetAliasMode & PKT_ALIAS_SAME_PORTS) { /* * When the PKT_ALIAS_SAME_PORTS option is chosen, * the first try will be the actual source port. If * this is already in use, the remainder of the * trials will be random. */ port_net = lnk->src_port; port_sys = ntohs(port_net); } else if (la->aliasPortLower) { /* First trial is a random port in the aliasing range. */ port_sys = la->aliasPortLower + (arc4random() % la->aliasPortLength); port_net = htons(port_sys); } else { /* First trial and all subsequent are random. */ port_sys = arc4random() & ALIAS_PORT_MASK; port_sys += ALIAS_PORT_BASE; port_net = htons(port_sys); } } else if (alias_port_param >= 0 && alias_port_param < 0x10000) { lnk->alias_port = (u_short) alias_port_param; return (0); } else { #ifdef LIBALIAS_DEBUG fprintf(stderr, "PacketAlias/GetNewPort(): "); fprintf(stderr, "input parameter error\n"); #endif return (-1); } -/* Port number search */ + /* Port number search */ for (i = 0; i < max_trials; i++) { int go_ahead; struct alias_link *search_result; search_result = FindLinkIn(la, lnk->dst_addr, lnk->alias_addr, lnk->dst_port, port_net, lnk->link_type, 0); if (search_result == NULL) go_ahead = 1; else if (!(lnk->flags & LINK_PARTIALLY_SPECIFIED) && (search_result->flags & LINK_PARTIALLY_SPECIFIED)) go_ahead = 1; else go_ahead = 0; if (go_ahead) { -#ifndef NO_USE_SOCKETS +#ifndef NO_USE_SOCKETS if ((la->packetAliasMode & PKT_ALIAS_USE_SOCKETS) && (lnk->flags & LINK_PARTIALLY_SPECIFIED) && ((lnk->link_type == LINK_TCP) || (lnk->link_type == LINK_UDP))) { if (GetSocket(la, port_net, &lnk->sockfd, lnk->link_type)) { lnk->alias_port = port_net; return (0); } } else { #endif lnk->alias_port = port_net; return (0); -#ifndef NO_USE_SOCKETS +#ifndef NO_USE_SOCKETS } #endif } if (la->aliasPortLower) { port_sys = la->aliasPortLower + (arc4random() % la->aliasPortLength); port_net = htons(port_sys); } else { port_sys = arc4random() & ALIAS_PORT_MASK; port_sys += ALIAS_PORT_BASE; port_net = htons(port_sys); } } #ifdef LIBALIAS_DEBUG fprintf(stderr, "PacketAlias/GetNewPort(): "); fprintf(stderr, "could not find free port\n"); #endif return (-1); } -#ifndef NO_USE_SOCKETS +#ifndef NO_USE_SOCKETS static u_short GetSocket(struct libalias *la, u_short port_net, int *sockfd, int link_type) { int err; int sock; struct sockaddr_in sock_addr; LIBALIAS_LOCK_ASSERT(la); 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 LIBALIAS_DEBUG fprintf(stderr, "PacketAlias/GetSocket(): "); fprintf(stderr, "incorrect link type\n"); #endif return (0); } if (sock < 0) { #ifdef LIBALIAS_DEBUG fprintf(stderr, "PacketAlias/GetSocket(): "); fprintf(stderr, "socket() error %d\n", *sockfd); #endif return (0); } sock_addr.sin_family = AF_INET; sock_addr.sin_addr.s_addr = htonl(INADDR_ANY); sock_addr.sin_port = port_net; err = bind(sock, (struct sockaddr *)&sock_addr, sizeof(sock_addr)); if (err == 0) { la->sockCount++; *sockfd = sock; return (1); } else { close(sock); return (0); } } #endif /* FindNewPortGroup() returns a base port number for an available range of contiguous port numbers. Note that if a port number is already in use, that does not mean that it cannot be used by another link concurrently. This is because FindNewPortGroup() looks for unused triplets: (dest addr, dest port, alias port). */ int FindNewPortGroup(struct libalias *la, struct in_addr dst_addr, struct in_addr alias_addr, u_short src_port, u_short dst_port, u_short port_count, u_char proto, u_char align) { int i, j; int max_trials; u_short port_sys; int link_type; LIBALIAS_LOCK_ASSERT(la); /* * Get link_type from protocol */ switch (proto) { case IPPROTO_UDP: link_type = LINK_UDP; break; case IPPROTO_TCP: link_type = LINK_TCP; break; default: return (0); break; } /* * The aliasing port is automatically selected by one of two * methods below: */ max_trials = GET_NEW_PORT_MAX_ATTEMPTS; if (la->packetAliasMode & PKT_ALIAS_SAME_PORTS) { /* * When the ALIAS_SAME_PORTS option is chosen, the first * try will be the actual source port. If this is already * in use, the remainder of the trials will be random. */ port_sys = ntohs(src_port); } else { /* First trial and all subsequent are random. */ if (align == FIND_EVEN_ALIAS_BASE) port_sys = arc4random() & ALIAS_PORT_MASK_EVEN; else port_sys = arc4random() & ALIAS_PORT_MASK; port_sys += ALIAS_PORT_BASE; } -/* Port number search */ + /* Port number search */ for (i = 0; i < max_trials; i++) { struct alias_link *search_result; for (j = 0; j < port_count; j++) if ((search_result = FindLinkIn(la, dst_addr, alias_addr, dst_port, htons(port_sys + j), link_type, 0)) != NULL) break; /* Found a good range, return base */ if (j == port_count) return (htons(port_sys)); /* Find a new base to try */ if (align == FIND_EVEN_ALIAS_BASE) port_sys = arc4random() & ALIAS_PORT_MASK_EVEN; else port_sys = arc4random() & ALIAS_PORT_MASK; port_sys += ALIAS_PORT_BASE; } #ifdef LIBALIAS_DEBUG fprintf(stderr, "PacketAlias/FindNewPortGroup(): "); fprintf(stderr, "could not find free port(s)\n"); #endif return (0); } static void CleanupAliasData(struct libalias *la) { struct alias_link *lnk; int i; LIBALIAS_LOCK_ASSERT(la); for (i = 0; i < LINK_TABLE_OUT_SIZE; i++) { lnk = LIST_FIRST(&la->linkTableOut[i]); while (lnk != NULL) { struct alias_link *link_next = LIST_NEXT(lnk, list_out); DeleteLink(lnk); lnk = link_next; } } la->cleanupIndex = 0; } static void IncrementalCleanup(struct libalias *la) { struct alias_link *lnk, *lnk_tmp; LIBALIAS_LOCK_ASSERT(la); LIST_FOREACH_SAFE(lnk, &la->linkTableOut[la->cleanupIndex++], list_out, lnk_tmp) { if (la->timeStamp - lnk->timestamp > lnk->expire_time) DeleteLink(lnk); } if (la->cleanupIndex == LINK_TABLE_OUT_SIZE) la->cleanupIndex = 0; } static void DeleteLink(struct alias_link *lnk) { struct libalias *la = lnk->la; LIBALIAS_LOCK_ASSERT(la); -/* Don't do anything if the link is marked permanent */ + /* Don't do anything if the link is marked permanent */ if (la->deleteAllLinks == 0 && lnk->flags & LINK_PERMANENT) return; #ifndef NO_FW_PUNCH -/* Delete associated firewall hole, if any */ + /* Delete associated firewall hole, if any */ ClearFWHole(lnk); #endif -/* Free memory allocated for LSNAT server pool */ + /* Free memory allocated for LSNAT server pool */ if (lnk->server != NULL) { struct server *head, *curr, *next; head = curr = lnk->server; do { next = curr->next; free(curr); } while ((curr = next) != head); } -/* Adjust output table pointers */ + /* Adjust output table pointers */ LIST_REMOVE(lnk, list_out); -/* Adjust input table pointers */ + /* Adjust input table pointers */ LIST_REMOVE(lnk, list_in); -#ifndef NO_USE_SOCKETS -/* Close socket, if one has been allocated */ +#ifndef NO_USE_SOCKETS + /* Close socket, if one has been allocated */ if (lnk->sockfd != -1) { la->sockCount--; close(lnk->sockfd); } #endif -/* Link-type dependent cleanup */ + /* Link-type dependent cleanup */ switch (lnk->link_type) { case LINK_ICMP: la->icmpLinkCount--; break; case LINK_UDP: la->udpLinkCount--; break; case LINK_TCP: la->tcpLinkCount--; free(lnk->data.tcp); break; case LINK_PPTP: la->pptpLinkCount--; break; case LINK_FRAGMENT_ID: la->fragmentIdLinkCount--; break; case LINK_FRAGMENT_PTR: la->fragmentPtrLinkCount--; if (lnk->data.frag_ptr != NULL) free(lnk->data.frag_ptr); break; case LINK_ADDR: break; default: la->protoLinkCount--; break; } -/* Free memory */ + /* Free memory */ free(lnk); -/* Write statistics, if logging enabled */ + /* Write statistics, if logging enabled */ if (la->packetAliasMode & PKT_ALIAS_LOG) { ShowAliasStats(la); } } struct alias_link * AddLink(struct libalias *la, struct in_addr src_addr, struct in_addr dst_addr, struct in_addr alias_addr, u_short src_port, u_short dst_port, int alias_port_param, int link_type) { u_int start_point; struct alias_link *lnk; LIBALIAS_LOCK_ASSERT(la); lnk = malloc(sizeof(struct alias_link)); if (lnk != NULL) { /* Basic initialization */ lnk->la = la; lnk->src_addr = src_addr; lnk->dst_addr = dst_addr; lnk->alias_addr = alias_addr; lnk->proxy_addr.s_addr = INADDR_ANY; lnk->src_port = src_port; lnk->dst_port = dst_port; lnk->proxy_port = 0; lnk->server = NULL; lnk->link_type = link_type; -#ifndef NO_USE_SOCKETS +#ifndef NO_USE_SOCKETS lnk->sockfd = -1; #endif lnk->flags = 0; lnk->pflags = 0; lnk->timestamp = la->timeStamp; /* Expiration time */ switch (link_type) { case LINK_ICMP: lnk->expire_time = ICMP_EXPIRE_TIME; break; case LINK_UDP: lnk->expire_time = UDP_EXPIRE_TIME; break; case LINK_TCP: lnk->expire_time = TCP_EXPIRE_INITIAL; break; case LINK_PPTP: lnk->flags |= LINK_PERMANENT; /* no timeout. */ break; case LINK_FRAGMENT_ID: lnk->expire_time = FRAGMENT_ID_EXPIRE_TIME; break; case LINK_FRAGMENT_PTR: lnk->expire_time = FRAGMENT_PTR_EXPIRE_TIME; break; case LINK_ADDR: break; default: lnk->expire_time = PROTO_EXPIRE_TIME; break; } /* Determine alias flags */ if (dst_addr.s_addr == INADDR_ANY) lnk->flags |= LINK_UNKNOWN_DEST_ADDR; if (dst_port == 0) lnk->flags |= LINK_UNKNOWN_DEST_PORT; /* Determine alias port */ if (GetNewPort(la, lnk, alias_port_param) != 0) { free(lnk); return (NULL); } /* Link-type dependent initialization */ switch (link_type) { struct tcp_dat *aux_tcp; case LINK_ICMP: la->icmpLinkCount++; break; case LINK_UDP: la->udpLinkCount++; break; case LINK_TCP: aux_tcp = malloc(sizeof(struct tcp_dat)); if (aux_tcp != NULL) { int i; la->tcpLinkCount++; aux_tcp->state.in = ALIAS_TCP_STATE_NOT_CONNECTED; aux_tcp->state.out = ALIAS_TCP_STATE_NOT_CONNECTED; aux_tcp->state.index = 0; aux_tcp->state.ack_modified = 0; for (i = 0; i < N_LINK_TCP_DATA; i++) aux_tcp->ack[i].active = 0; aux_tcp->fwhole = -1; lnk->data.tcp = aux_tcp; } else { #ifdef LIBALIAS_DEBUG fprintf(stderr, "PacketAlias/AddLink: "); fprintf(stderr, " cannot allocate auxiliary TCP data\n"); #endif free(lnk); return (NULL); } break; case LINK_PPTP: la->pptpLinkCount++; break; case LINK_FRAGMENT_ID: la->fragmentIdLinkCount++; break; case LINK_FRAGMENT_PTR: la->fragmentPtrLinkCount++; break; case LINK_ADDR: break; default: la->protoLinkCount++; break; } /* Set up pointers for output lookup table */ start_point = StartPointOut(src_addr, dst_addr, src_port, dst_port, link_type); LIST_INSERT_HEAD(&la->linkTableOut[start_point], lnk, list_out); /* Set up pointers for input lookup table */ start_point = StartPointIn(alias_addr, lnk->alias_port, link_type); LIST_INSERT_HEAD(&la->linkTableIn[start_point], lnk, list_in); } else { #ifdef LIBALIAS_DEBUG fprintf(stderr, "PacketAlias/AddLink(): "); fprintf(stderr, "malloc() call failed.\n"); #endif } if (la->packetAliasMode & PKT_ALIAS_LOG) { ShowAliasStats(la); } return (lnk); } +/* + * If alias_port_param is less than zero, alias port will be automatically + * chosen. If greater than zero, equal to alias port + */ static struct alias_link * ReLink(struct alias_link *old_lnk, struct in_addr src_addr, struct in_addr dst_addr, struct in_addr alias_addr, u_short src_port, u_short dst_port, - int alias_port_param, /* if less than zero, alias */ + int alias_port_param, int link_type) -{ /* port will be automatically *//* chosen. - * If greater than */ - struct alias_link *new_lnk; /* zero, equal to alias port */ +{ + struct alias_link *new_lnk; struct libalias *la = old_lnk->la; LIBALIAS_LOCK_ASSERT(la); new_lnk = AddLink(la, src_addr, dst_addr, alias_addr, src_port, dst_port, alias_port_param, link_type); #ifndef NO_FW_PUNCH if (new_lnk != NULL && old_lnk->link_type == LINK_TCP && old_lnk->data.tcp->fwhole > 0) { PunchFWHole(new_lnk); } #endif DeleteLink(old_lnk); return (new_lnk); } static struct alias_link * _FindLinkOut(struct libalias *la, struct in_addr src_addr, struct in_addr dst_addr, u_short src_port, u_short dst_port, int link_type, int replace_partial_links) { u_int i; struct alias_link *lnk; LIBALIAS_LOCK_ASSERT(la); i = StartPointOut(src_addr, dst_addr, src_port, dst_port, link_type); LIST_FOREACH(lnk, &la->linkTableOut[i], list_out) { if (lnk->dst_addr.s_addr == dst_addr.s_addr && lnk->src_addr.s_addr == src_addr.s_addr && lnk->src_port == src_port && lnk->dst_port == dst_port && lnk->link_type == link_type && lnk->server == NULL) { lnk->timestamp = la->timeStamp; break; } } -/* Search for partially specified links. */ + /* Search for partially specified links. */ if (lnk == NULL && replace_partial_links) { if (dst_port != 0 && dst_addr.s_addr != INADDR_ANY) { lnk = _FindLinkOut(la, src_addr, dst_addr, src_port, 0, link_type, 0); if (lnk == NULL) lnk = _FindLinkOut(la, src_addr, la->nullAddress, src_port, dst_port, link_type, 0); } if (lnk == NULL && (dst_port != 0 || dst_addr.s_addr != INADDR_ANY)) { lnk = _FindLinkOut(la, src_addr, la->nullAddress, src_port, 0, link_type, 0); } if (lnk != NULL) { lnk = ReLink(lnk, src_addr, dst_addr, lnk->alias_addr, src_port, dst_port, lnk->alias_port, link_type); } } return (lnk); } static struct alias_link * FindLinkOut(struct libalias *la, struct in_addr src_addr, struct in_addr dst_addr, u_short src_port, u_short dst_port, int link_type, int replace_partial_links) { struct alias_link *lnk; LIBALIAS_LOCK_ASSERT(la); lnk = _FindLinkOut(la, src_addr, dst_addr, src_port, dst_port, link_type, replace_partial_links); if (lnk == NULL) { /* * The following allows permanent links to be specified as * using the default source address (i.e. device interface * address) without knowing in advance what that address * is. */ if (la->aliasAddress.s_addr != INADDR_ANY && src_addr.s_addr == la->aliasAddress.s_addr) { lnk = _FindLinkOut(la, la->nullAddress, dst_addr, src_port, dst_port, link_type, replace_partial_links); } } return (lnk); } static struct alias_link * _FindLinkIn(struct libalias *la, struct in_addr dst_addr, struct in_addr alias_addr, u_short dst_port, u_short alias_port, int link_type, int replace_partial_links) { int flags_in; u_int start_point; struct alias_link *lnk; struct alias_link *lnk_fully_specified; struct alias_link *lnk_unknown_all; struct alias_link *lnk_unknown_dst_addr; struct alias_link *lnk_unknown_dst_port; LIBALIAS_LOCK_ASSERT(la); -/* Initialize pointers */ + /* Initialize pointers */ lnk_fully_specified = NULL; lnk_unknown_all = NULL; lnk_unknown_dst_addr = NULL; lnk_unknown_dst_port = NULL; -/* If either the dest addr or port is unknown, the search - loop will have to know about this. */ - + /* 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 */ + /* Search loop */ start_point = StartPointIn(alias_addr, alias_port, link_type); LIST_FOREACH(lnk, &la->linkTableIn[start_point], list_in) { int flags; flags = flags_in | lnk->flags; if (!(flags & LINK_PARTIALLY_SPECIFIED)) { if (lnk->alias_addr.s_addr == alias_addr.s_addr && lnk->alias_port == alias_port && lnk->dst_addr.s_addr == dst_addr.s_addr && lnk->dst_port == dst_port && lnk->link_type == link_type) { lnk_fully_specified = lnk; break; } } else if ((flags & LINK_UNKNOWN_DEST_ADDR) && (flags & LINK_UNKNOWN_DEST_PORT)) { if (lnk->alias_addr.s_addr == alias_addr.s_addr && lnk->alias_port == alias_port && lnk->link_type == link_type) { if (lnk_unknown_all == NULL) lnk_unknown_all = lnk; } } else if (flags & LINK_UNKNOWN_DEST_ADDR) { if (lnk->alias_addr.s_addr == alias_addr.s_addr && lnk->alias_port == alias_port && lnk->link_type == link_type && lnk->dst_port == dst_port) { if (lnk_unknown_dst_addr == NULL) lnk_unknown_dst_addr = lnk; } } else if (flags & LINK_UNKNOWN_DEST_PORT) { if (lnk->alias_addr.s_addr == alias_addr.s_addr && lnk->alias_port == alias_port && lnk->link_type == link_type && lnk->dst_addr.s_addr == dst_addr.s_addr) { if (lnk_unknown_dst_port == NULL) lnk_unknown_dst_port = lnk; } } } if (lnk_fully_specified != NULL) { lnk_fully_specified->timestamp = la->timeStamp; lnk = lnk_fully_specified; } else if (lnk_unknown_dst_port != NULL) lnk = lnk_unknown_dst_port; else if (lnk_unknown_dst_addr != NULL) lnk = lnk_unknown_dst_addr; else if (lnk_unknown_all != NULL) lnk = lnk_unknown_all; else return (NULL); if (replace_partial_links && (lnk->flags & LINK_PARTIALLY_SPECIFIED || lnk->server != NULL)) { struct in_addr src_addr; u_short src_port; if (lnk->server != NULL) { /* LSNAT link */ src_addr = lnk->server->addr; src_port = lnk->server->port; lnk->server = lnk->server->next; } else { src_addr = lnk->src_addr; src_port = lnk->src_port; } if (link_type == LINK_SCTP) { - lnk->src_addr = src_addr; - lnk->src_port = src_port; - return(lnk); + lnk->src_addr = src_addr; + lnk->src_port = src_port; + return (lnk); } lnk = ReLink(lnk, src_addr, dst_addr, alias_addr, src_port, dst_port, alias_port, link_type); } return (lnk); } static struct alias_link * FindLinkIn(struct libalias *la, struct in_addr dst_addr, struct in_addr alias_addr, u_short dst_port, u_short alias_port, int link_type, int replace_partial_links) { struct alias_link *lnk; LIBALIAS_LOCK_ASSERT(la); lnk = _FindLinkIn(la, dst_addr, alias_addr, dst_port, alias_port, link_type, replace_partial_links); if (lnk == NULL) { /* * The following allows permanent links to be specified as * using the default aliasing address (i.e. device * interface address) without knowing in advance what that * address is. */ if (la->aliasAddress.s_addr != INADDR_ANY && alias_addr.s_addr == la->aliasAddress.s_addr) { lnk = _FindLinkIn(la, dst_addr, la->nullAddress, dst_port, alias_port, link_type, replace_partial_links); } } return (lnk); } /* External routines for finding/adding links -- "external" means outside alias_db.c, but within alias*.c -- FindIcmpIn(), FindIcmpOut() FindFragmentIn1(), FindFragmentIn2() AddFragmentPtrLink(), FindFragmentPtr() FindProtoIn(), FindProtoOut() FindUdpTcpIn(), FindUdpTcpOut() AddPptp(), FindPptpOutByCallId(), FindPptpInByCallId(), FindPptpOutByPeerCallId(), FindPptpInByPeerCallId() FindOriginalAddress(), FindAliasAddress() (prototypes in alias_local.h) */ struct alias_link * FindIcmpIn(struct libalias *la, struct in_addr dst_addr, struct in_addr alias_addr, u_short id_alias, int create) { struct alias_link *lnk; LIBALIAS_LOCK_ASSERT(la); lnk = FindLinkIn(la, dst_addr, alias_addr, NO_DEST_PORT, id_alias, LINK_ICMP, 0); if (lnk == NULL && create && !(la->packetAliasMode & PKT_ALIAS_DENY_INCOMING)) { struct in_addr target_addr; target_addr = FindOriginalAddress(la, alias_addr); lnk = AddLink(la, target_addr, dst_addr, alias_addr, id_alias, NO_DEST_PORT, id_alias, LINK_ICMP); } return (lnk); } struct alias_link * FindIcmpOut(struct libalias *la, struct in_addr src_addr, struct in_addr dst_addr, u_short id, int create) { struct alias_link *lnk; LIBALIAS_LOCK_ASSERT(la); lnk = FindLinkOut(la, src_addr, dst_addr, id, NO_DEST_PORT, LINK_ICMP, 0); if (lnk == NULL && create) { struct in_addr alias_addr; alias_addr = FindAliasAddress(la, src_addr); lnk = AddLink(la, src_addr, dst_addr, alias_addr, id, NO_DEST_PORT, GET_ALIAS_ID, LINK_ICMP); } return (lnk); } struct alias_link * FindFragmentIn1(struct libalias *la, struct in_addr dst_addr, struct in_addr alias_addr, u_short ip_id) { struct alias_link *lnk; LIBALIAS_LOCK_ASSERT(la); lnk = FindLinkIn(la, dst_addr, alias_addr, NO_DEST_PORT, ip_id, LINK_FRAGMENT_ID, 0); if (lnk == NULL) { lnk = AddLink(la, la->nullAddress, dst_addr, alias_addr, NO_SRC_PORT, NO_DEST_PORT, ip_id, LINK_FRAGMENT_ID); } return (lnk); } +/* Doesn't add a link if one is not found. */ struct alias_link * -FindFragmentIn2(struct libalias *la, struct in_addr dst_addr, /* Doesn't add a link if - * one */ - struct in_addr alias_addr, /* is not found. */ - u_short ip_id) +FindFragmentIn2(struct libalias *la, struct in_addr dst_addr, + struct in_addr alias_addr, u_short ip_id) { - LIBALIAS_LOCK_ASSERT(la); return FindLinkIn(la, dst_addr, alias_addr, NO_DEST_PORT, ip_id, LINK_FRAGMENT_ID, 0); } struct alias_link * AddFragmentPtrLink(struct libalias *la, struct in_addr dst_addr, u_short ip_id) { - LIBALIAS_LOCK_ASSERT(la); return AddLink(la, la->nullAddress, dst_addr, la->nullAddress, NO_SRC_PORT, NO_DEST_PORT, ip_id, LINK_FRAGMENT_PTR); } struct alias_link * FindFragmentPtr(struct libalias *la, struct in_addr dst_addr, u_short ip_id) { - LIBALIAS_LOCK_ASSERT(la); return FindLinkIn(la, dst_addr, la->nullAddress, NO_DEST_PORT, ip_id, LINK_FRAGMENT_PTR, 0); } struct alias_link * FindProtoIn(struct libalias *la, struct in_addr dst_addr, struct in_addr alias_addr, u_char proto) { struct alias_link *lnk; LIBALIAS_LOCK_ASSERT(la); lnk = FindLinkIn(la, dst_addr, alias_addr, NO_DEST_PORT, 0, proto, 1); if (lnk == NULL && !(la->packetAliasMode & PKT_ALIAS_DENY_INCOMING)) { struct in_addr target_addr; target_addr = FindOriginalAddress(la, alias_addr); lnk = AddLink(la, target_addr, dst_addr, alias_addr, NO_SRC_PORT, NO_DEST_PORT, 0, proto); } return (lnk); } struct alias_link * FindProtoOut(struct libalias *la, struct in_addr src_addr, struct in_addr dst_addr, u_char proto) { struct alias_link *lnk; LIBALIAS_LOCK_ASSERT(la); lnk = FindLinkOut(la, src_addr, dst_addr, NO_SRC_PORT, NO_DEST_PORT, proto, 1); if (lnk == NULL) { struct in_addr alias_addr; alias_addr = FindAliasAddress(la, src_addr); lnk = AddLink(la, src_addr, dst_addr, alias_addr, NO_SRC_PORT, NO_DEST_PORT, 0, proto); } return (lnk); } struct alias_link * FindUdpTcpIn(struct libalias *la, struct in_addr dst_addr, struct in_addr alias_addr, u_short dst_port, u_short alias_port, u_char proto, int create) { int link_type; struct alias_link *lnk; LIBALIAS_LOCK_ASSERT(la); switch (proto) { case IPPROTO_UDP: link_type = LINK_UDP; break; case IPPROTO_TCP: link_type = LINK_TCP; break; default: return (NULL); break; } lnk = FindLinkIn(la, dst_addr, alias_addr, dst_port, alias_port, link_type, create); if (lnk == NULL && create && !(la->packetAliasMode & PKT_ALIAS_DENY_INCOMING)) { struct in_addr target_addr; target_addr = FindOriginalAddress(la, alias_addr); lnk = AddLink(la, target_addr, dst_addr, alias_addr, alias_port, dst_port, alias_port, link_type); } return (lnk); } struct alias_link * FindUdpTcpOut(struct libalias *la, struct in_addr src_addr, struct in_addr dst_addr, u_short src_port, u_short dst_port, u_char proto, int create) { int link_type; struct alias_link *lnk; LIBALIAS_LOCK_ASSERT(la); switch (proto) { case IPPROTO_UDP: link_type = LINK_UDP; break; case IPPROTO_TCP: link_type = LINK_TCP; break; default: return (NULL); break; } lnk = FindLinkOut(la, src_addr, dst_addr, src_port, dst_port, link_type, create); if (lnk == NULL && create) { struct in_addr alias_addr; alias_addr = FindAliasAddress(la, src_addr); lnk = AddLink(la, src_addr, dst_addr, alias_addr, src_port, dst_port, GET_ALIAS_PORT, link_type); } return (lnk); } struct alias_link * AddPptp(struct libalias *la, struct in_addr src_addr, struct in_addr dst_addr, struct in_addr alias_addr, u_int16_t src_call_id) { struct alias_link *lnk; LIBALIAS_LOCK_ASSERT(la); lnk = AddLink(la, src_addr, dst_addr, alias_addr, src_call_id, 0, GET_ALIAS_PORT, LINK_PPTP); return (lnk); } struct alias_link * FindPptpOutByCallId(struct libalias *la, struct in_addr src_addr, struct in_addr dst_addr, u_int16_t src_call_id) { u_int i; struct alias_link *lnk; LIBALIAS_LOCK_ASSERT(la); i = StartPointOut(src_addr, dst_addr, 0, 0, LINK_PPTP); LIST_FOREACH(lnk, &la->linkTableOut[i], list_out) - if (lnk->link_type == LINK_PPTP && - lnk->src_addr.s_addr == src_addr.s_addr && - lnk->dst_addr.s_addr == dst_addr.s_addr && - lnk->src_port == src_call_id) - break; + if (lnk->link_type == LINK_PPTP && + lnk->src_addr.s_addr == src_addr.s_addr && + lnk->dst_addr.s_addr == dst_addr.s_addr && + lnk->src_port == src_call_id) + break; return (lnk); } struct alias_link * FindPptpOutByPeerCallId(struct libalias *la, struct in_addr src_addr, struct in_addr dst_addr, u_int16_t dst_call_id) { u_int i; struct alias_link *lnk; LIBALIAS_LOCK_ASSERT(la); i = StartPointOut(src_addr, dst_addr, 0, 0, LINK_PPTP); LIST_FOREACH(lnk, &la->linkTableOut[i], list_out) - if (lnk->link_type == LINK_PPTP && - lnk->src_addr.s_addr == src_addr.s_addr && - lnk->dst_addr.s_addr == dst_addr.s_addr && - lnk->dst_port == dst_call_id) - break; + if (lnk->link_type == LINK_PPTP && + lnk->src_addr.s_addr == src_addr.s_addr && + lnk->dst_addr.s_addr == dst_addr.s_addr && + lnk->dst_port == dst_call_id) + break; return (lnk); } struct alias_link * FindPptpInByCallId(struct libalias *la, struct in_addr dst_addr, struct in_addr alias_addr, u_int16_t dst_call_id) { u_int i; struct alias_link *lnk; LIBALIAS_LOCK_ASSERT(la); i = StartPointIn(alias_addr, 0, LINK_PPTP); LIST_FOREACH(lnk, &la->linkTableIn[i], list_in) - if (lnk->link_type == LINK_PPTP && - lnk->dst_addr.s_addr == dst_addr.s_addr && - lnk->alias_addr.s_addr == alias_addr.s_addr && - lnk->dst_port == dst_call_id) - break; + if (lnk->link_type == LINK_PPTP && + lnk->dst_addr.s_addr == dst_addr.s_addr && + lnk->alias_addr.s_addr == alias_addr.s_addr && + lnk->dst_port == dst_call_id) + break; return (lnk); } struct alias_link * FindPptpInByPeerCallId(struct libalias *la, struct in_addr dst_addr, struct in_addr alias_addr, u_int16_t alias_call_id) { struct alias_link *lnk; LIBALIAS_LOCK_ASSERT(la); lnk = FindLinkIn(la, dst_addr, alias_addr, 0 /* any */ , alias_call_id, LINK_PPTP, 0); return (lnk); } struct alias_link * FindRtspOut(struct libalias *la, struct in_addr src_addr, struct in_addr dst_addr, u_short src_port, u_short alias_port, u_char proto) { int link_type; struct alias_link *lnk; LIBALIAS_LOCK_ASSERT(la); switch (proto) { case IPPROTO_UDP: link_type = LINK_UDP; break; case IPPROTO_TCP: link_type = LINK_TCP; break; default: return (NULL); break; } lnk = FindLinkOut(la, src_addr, dst_addr, src_port, 0, link_type, 1); if (lnk == NULL) { struct in_addr alias_addr; alias_addr = FindAliasAddress(la, src_addr); lnk = AddLink(la, src_addr, dst_addr, alias_addr, src_port, 0, alias_port, link_type); } return (lnk); } struct in_addr FindOriginalAddress(struct libalias *la, struct in_addr alias_addr) { struct alias_link *lnk; LIBALIAS_LOCK_ASSERT(la); lnk = FindLinkIn(la, la->nullAddress, alias_addr, 0, 0, LINK_ADDR, 0); if (lnk == NULL) { la->newDefaultLink = 1; if (la->targetAddress.s_addr == INADDR_ANY) return (alias_addr); else if (la->targetAddress.s_addr == INADDR_NONE) return (la->aliasAddress.s_addr != INADDR_ANY) ? la->aliasAddress : alias_addr; else return (la->targetAddress); } else { if (lnk->server != NULL) { /* LSNAT link */ struct in_addr src_addr; src_addr = lnk->server->addr; lnk->server = lnk->server->next; return (src_addr); } else if (lnk->src_addr.s_addr == INADDR_ANY) return (la->aliasAddress.s_addr != INADDR_ANY) ? la->aliasAddress : alias_addr; else return (lnk->src_addr); } } struct in_addr FindAliasAddress(struct libalias *la, struct in_addr original_addr) { struct alias_link *lnk; LIBALIAS_LOCK_ASSERT(la); lnk = FindLinkOut(la, original_addr, la->nullAddress, 0, 0, LINK_ADDR, 0); if (lnk == NULL) { return (la->aliasAddress.s_addr != INADDR_ANY) ? la->aliasAddress : original_addr; } else { if (lnk->alias_addr.s_addr == INADDR_ANY) return (la->aliasAddress.s_addr != INADDR_ANY) ? la->aliasAddress : original_addr; else return (lnk->alias_addr); } } /* External routines for getting or changing link data (external to alias_db.c, but internal to alias*.c) SetFragmentData(), GetFragmentData() SetFragmentPtr(), GetFragmentPtr() SetStateIn(), SetStateOut(), GetStateIn(), GetStateOut() GetOriginalAddress(), GetDestAddress(), GetAliasAddress() GetOriginalPort(), GetAliasPort() SetAckModified(), GetAckModified() GetDeltaAckIn(), GetDeltaSeqOut(), AddSeq() SetProtocolFlags(), GetProtocolFlags() SetDestCallId() */ void SetFragmentAddr(struct alias_link *lnk, struct in_addr src_addr) { lnk->data.frag_addr = src_addr; } void GetFragmentAddr(struct alias_link *lnk, struct in_addr *src_addr) { *src_addr = lnk->data.frag_addr; } void SetFragmentPtr(struct alias_link *lnk, void *fptr) { lnk->data.frag_ptr = fptr; } void GetFragmentPtr(struct alias_link *lnk, void **fptr) { *fptr = lnk->data.frag_ptr; } void SetStateIn(struct alias_link *lnk, int state) { /* TCP input state */ switch (state) { case ALIAS_TCP_STATE_DISCONNECTED: if (lnk->data.tcp->state.out != ALIAS_TCP_STATE_CONNECTED) lnk->expire_time = TCP_EXPIRE_DEAD; else lnk->expire_time = TCP_EXPIRE_SINGLEDEAD; break; case ALIAS_TCP_STATE_CONNECTED: if (lnk->data.tcp->state.out == ALIAS_TCP_STATE_CONNECTED) lnk->expire_time = TCP_EXPIRE_CONNECTED; break; default: -#ifdef _KERNEL +#ifdef _KERNEL panic("libalias:SetStateIn() unknown state"); #else abort(); #endif } lnk->data.tcp->state.in = state; } void SetStateOut(struct alias_link *lnk, int state) { /* TCP output state */ switch (state) { case ALIAS_TCP_STATE_DISCONNECTED: if (lnk->data.tcp->state.in != ALIAS_TCP_STATE_CONNECTED) lnk->expire_time = TCP_EXPIRE_DEAD; else lnk->expire_time = TCP_EXPIRE_SINGLEDEAD; break; case ALIAS_TCP_STATE_CONNECTED: if (lnk->data.tcp->state.in == ALIAS_TCP_STATE_CONNECTED) lnk->expire_time = TCP_EXPIRE_CONNECTED; break; default: -#ifdef _KERNEL +#ifdef _KERNEL panic("libalias:SetStateOut() unknown state"); #else abort(); #endif } lnk->data.tcp->state.out = state; } int GetStateIn(struct alias_link *lnk) { /* TCP input state */ return (lnk->data.tcp->state.in); } int GetStateOut(struct alias_link *lnk) { /* TCP output state */ return (lnk->data.tcp->state.out); } struct in_addr GetOriginalAddress(struct alias_link *lnk) { if (lnk->src_addr.s_addr == INADDR_ANY) return (lnk->la->aliasAddress); else return (lnk->src_addr); } struct in_addr GetDestAddress(struct alias_link *lnk) { return (lnk->dst_addr); } struct in_addr GetAliasAddress(struct alias_link *lnk) { if (lnk->alias_addr.s_addr == INADDR_ANY) return (lnk->la->aliasAddress); else return (lnk->alias_addr); } struct in_addr GetDefaultAliasAddress(struct libalias *la) { - LIBALIAS_LOCK_ASSERT(la); return (la->aliasAddress); } void SetDefaultAliasAddress(struct libalias *la, struct in_addr alias_addr) { - LIBALIAS_LOCK_ASSERT(la); la->aliasAddress = alias_addr; } u_short GetOriginalPort(struct alias_link *lnk) { return (lnk->src_port); } u_short GetAliasPort(struct alias_link *lnk) { return (lnk->alias_port); } #ifndef NO_FW_PUNCH -static u_short +static u_short GetDestPort(struct alias_link *lnk) { return (lnk->dst_port); } #endif +/* Indicate that ACK numbers have been modified in a TCP connection */ void SetAckModified(struct alias_link *lnk) { -/* Indicate that ACK numbers have been modified in a TCP connection */ lnk->data.tcp->state.ack_modified = 1; } struct in_addr GetProxyAddress(struct alias_link *lnk) { return (lnk->proxy_addr); } void SetProxyAddress(struct alias_link *lnk, struct in_addr addr) { lnk->proxy_addr = addr; } u_short GetProxyPort(struct alias_link *lnk) { return (lnk->proxy_port); } void SetProxyPort(struct alias_link *lnk, u_short port) { lnk->proxy_port = port; } +/* See if ACK numbers have been modified */ int GetAckModified(struct alias_link *lnk) { -/* See if ACK numbers have been modified */ return (lnk->data.tcp->state.ack_modified); } +/* + * 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. + */ // XXX ip free int GetDeltaAckIn(u_long ack, struct alias_link *lnk) { -/* -Find out how much the ACK number has been altered for an incoming -TCP packet. To do this, a circular list of ACK numbers where the TCP -packet size was altered is searched. -*/ - int i, j; int delta, ack_diff_min; delta = 0; ack_diff_min = -1; i = lnk->data.tcp->state.index; for (j = 0; j < N_LINK_TCP_DATA; j++) { struct ack_data_record x; if (i == 0) i = N_LINK_TCP_DATA; i--; x = lnk->data.tcp->ack[i]; if (x.active == 1) { int ack_diff; ack_diff = SeqDiff(x.ack_new, ack); if (ack_diff >= 0) { if (ack_diff_min >= 0) { if (ack_diff < ack_diff_min) { delta = x.delta; ack_diff_min = ack_diff; } } else { delta = x.delta; ack_diff_min = ack_diff; } } } } return (delta); } +/* + * 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. + */ // XXX ip free int GetDeltaSeqOut(u_long seq, struct alias_link *lnk) { -/* -Find out how much the sequence number has been altered for an outgoing -TCP packet. To do this, a circular list of ACK numbers where the TCP -packet size was altered is searched. -*/ - int i, j; int delta, seq_diff_min; delta = 0; seq_diff_min = -1; i = lnk->data.tcp->state.index; for (j = 0; j < N_LINK_TCP_DATA; j++) { struct ack_data_record x; if (i == 0) i = N_LINK_TCP_DATA; i--; x = lnk->data.tcp->ack[i]; if (x.active == 1) { int seq_diff; seq_diff = SeqDiff(x.ack_old, seq); if (seq_diff >= 0) { if (seq_diff_min >= 0) { if (seq_diff < seq_diff_min) { delta = x.delta; seq_diff_min = seq_diff; } } else { delta = x.delta; seq_diff_min = seq_diff; } } } } return (delta); } +/* + * 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. + */ // XXX ip free void -AddSeq(struct alias_link *lnk, int delta, u_int ip_hl, u_short ip_len, +AddSeq(struct alias_link *lnk, int delta, u_int ip_hl, u_short ip_len, u_long th_seq, u_int th_off) { -/* -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 ack_data_record x; int hlen, tlen, dlen; int i; hlen = (ip_hl + th_off) << 2; tlen = ntohs(ip_len); dlen = tlen - hlen; x.ack_old = htonl(ntohl(th_seq) + dlen); x.ack_new = htonl(ntohl(th_seq) + dlen + delta); x.delta = delta; x.active = 1; i = lnk->data.tcp->state.index; lnk->data.tcp->ack[i] = x; i++; if (i == N_LINK_TCP_DATA) lnk->data.tcp->state.index = 0; else lnk->data.tcp->state.index = i; } void SetExpire(struct alias_link *lnk, int expire) { if (expire == 0) { lnk->flags &= ~LINK_PERMANENT; DeleteLink(lnk); } else if (expire == -1) { lnk->flags |= LINK_PERMANENT; } else if (expire > 0) { lnk->expire_time = expire; } else { #ifdef LIBALIAS_DEBUG fprintf(stderr, "PacketAlias/SetExpire(): "); fprintf(stderr, "error in expire parameter\n"); #endif } } void ClearCheckNewLink(struct libalias *la) { - LIBALIAS_LOCK_ASSERT(la); la->newDefaultLink = 0; } void SetProtocolFlags(struct alias_link *lnk, int pflags) { - lnk->pflags = pflags; } int GetProtocolFlags(struct alias_link *lnk) { - return (lnk->pflags); } void SetDestCallId(struct alias_link *lnk, u_int16_t cid) { struct libalias *la = lnk->la; LIBALIAS_LOCK_ASSERT(la); la->deleteAllLinks = 1; ReLink(lnk, lnk->src_addr, lnk->dst_addr, lnk->alias_addr, lnk->src_port, cid, lnk->alias_port, lnk->link_type); la->deleteAllLinks = 0; } /* Miscellaneous Functions HouseKeeping() InitPacketAliasLog() UninitPacketAliasLog() */ /* Whenever an outgoing or incoming packet is handled, HouseKeeping() is called to find and remove timed-out aliasing links. Logic exists to sweep through the entire table and linked list structure every 60 seconds. (prototype in alias_local.h) */ void HouseKeeping(struct libalias *la) { int i, n; -#ifndef _KERNEL +#ifndef _KERNEL struct timeval tv; #endif LIBALIAS_LOCK_ASSERT(la); /* * 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. */ -#ifdef _KERNEL +#ifdef _KERNEL la->timeStamp = time_uptime; #else gettimeofday(&tv, NULL); la->timeStamp = tv.tv_sec; #endif /* Compute number of spokes (output table link chains) to cover */ n = LINK_TABLE_OUT_SIZE * (la->timeStamp - la->lastCleanupTime); n /= ALIAS_CLEANUP_INTERVAL_SECS; /* Handle different cases */ if (n > 0) { if (n > ALIAS_CLEANUP_MAX_SPOKES) n = ALIAS_CLEANUP_MAX_SPOKES; la->lastCleanupTime = la->timeStamp; for (i = 0; i < n; i++) IncrementalCleanup(la); } else if (n < 0) { #ifdef LIBALIAS_DEBUG fprintf(stderr, "PacketAlias/HouseKeeping(): "); fprintf(stderr, "something unexpected in time values\n"); #endif la->lastCleanupTime = la->timeStamp; } } /* Init the log file and enable logging */ static int InitPacketAliasLog(struct libalias *la) { - LIBALIAS_LOCK_ASSERT(la); if (~la->packetAliasMode & PKT_ALIAS_LOG) { #ifdef _KERNEL if ((la->logDesc = malloc(LIBALIAS_BUF_SIZE))) ; -#else +#else if ((la->logDesc = fopen("/var/log/alias.log", "w"))) - fprintf(la->logDesc, "PacketAlias/InitPacketAliasLog: Packet alias logging enabled.\n"); + fprintf(la->logDesc, "PacketAlias/InitPacketAliasLog: Packet alias logging enabled.\n"); #endif - else + else return (ENOMEM); /* log initialization failed */ la->packetAliasMode |= PKT_ALIAS_LOG; } return (1); } /* Close the log-file and disable logging. */ static void UninitPacketAliasLog(struct libalias *la) { - LIBALIAS_LOCK_ASSERT(la); if (la->logDesc) { #ifdef _KERNEL free(la->logDesc); #else fclose(la->logDesc); #endif la->logDesc = NULL; } la->packetAliasMode &= ~PKT_ALIAS_LOG; } /* Outside world interfaces -- "outside world" means other than alias*.c routines -- PacketAliasRedirectPort() PacketAliasAddServer() PacketAliasRedirectProto() PacketAliasRedirectAddr() PacketAliasRedirectDynamic() PacketAliasRedirectDelete() PacketAliasSetAddress() PacketAliasInit() PacketAliasUninit() PacketAliasSetMode() (prototypes in alias.h) */ /* Redirection from a specific public addr:port to a private addr:port */ struct alias_link * LibAliasRedirectPort(struct libalias *la, struct in_addr src_addr, u_short src_port, struct in_addr dst_addr, u_short dst_port, struct in_addr alias_addr, u_short alias_port, u_char proto) { int link_type; struct alias_link *lnk; LIBALIAS_LOCK(la); switch (proto) { case IPPROTO_UDP: link_type = LINK_UDP; break; case IPPROTO_TCP: link_type = LINK_TCP; break; case IPPROTO_SCTP: link_type = LINK_SCTP; break; default: #ifdef LIBALIAS_DEBUG fprintf(stderr, "PacketAliasRedirectPort(): "); fprintf(stderr, "only SCTP, TCP and UDP protocols allowed\n"); #endif lnk = NULL; goto getout; } lnk = AddLink(la, src_addr, dst_addr, alias_addr, src_port, dst_port, alias_port, link_type); if (lnk != NULL) { lnk->flags |= LINK_PERMANENT; } #ifdef LIBALIAS_DEBUG else { fprintf(stderr, "PacketAliasRedirectPort(): " "call to AddLink() failed\n"); } #endif getout: LIBALIAS_UNLOCK(la); return (lnk); } /* Add server to the pool of servers */ int LibAliasAddServer(struct libalias *la, struct alias_link *lnk, struct in_addr addr, u_short port) { struct server *server; int res; LIBALIAS_LOCK(la); (void)la; server = malloc(sizeof(struct server)); if (server != NULL) { struct server *head; server->addr = addr; server->port = port; head = lnk->server; if (head == NULL) server->next = server; else { struct server *s; - for (s = head; s->next != head; s = s->next); + for (s = head; s->next != head; s = s->next) + ; s->next = server; server->next = head; } lnk->server = server; res = 0; } else res = -1; LIBALIAS_UNLOCK(la); return (res); } /* Redirect packets of a given IP protocol from a specific public address to a private address */ struct alias_link * LibAliasRedirectProto(struct libalias *la, struct in_addr src_addr, struct in_addr dst_addr, struct in_addr alias_addr, u_char proto) { struct alias_link *lnk; LIBALIAS_LOCK(la); lnk = AddLink(la, src_addr, dst_addr, alias_addr, NO_SRC_PORT, NO_DEST_PORT, 0, proto); if (lnk != NULL) { lnk->flags |= LINK_PERMANENT; } #ifdef LIBALIAS_DEBUG else { fprintf(stderr, "PacketAliasRedirectProto(): " "call to AddLink() failed\n"); } #endif LIBALIAS_UNLOCK(la); return (lnk); } /* Static address translation */ struct alias_link * LibAliasRedirectAddr(struct libalias *la, struct in_addr src_addr, struct in_addr alias_addr) { struct alias_link *lnk; LIBALIAS_LOCK(la); lnk = AddLink(la, src_addr, la->nullAddress, alias_addr, 0, 0, 0, LINK_ADDR); if (lnk != NULL) { lnk->flags |= LINK_PERMANENT; } #ifdef LIBALIAS_DEBUG else { fprintf(stderr, "PacketAliasRedirectAddr(): " "call to AddLink() failed\n"); } #endif LIBALIAS_UNLOCK(la); return (lnk); } /* Mark the aliasing link dynamic */ int LibAliasRedirectDynamic(struct libalias *la, struct alias_link *lnk) { int res; LIBALIAS_LOCK(la); (void)la; if (lnk->flags & LINK_PARTIALLY_SPECIFIED) res = -1; else { lnk->flags &= ~LINK_PERMANENT; res = 0; } LIBALIAS_UNLOCK(la); return (res); } +/* This is a dangerous function to put in the API, + because an invalid pointer can crash the program. */ void LibAliasRedirectDelete(struct libalias *la, struct alias_link *lnk) { -/* This is a dangerous function to put in the API, - because an invalid pointer can crash the program. */ - LIBALIAS_LOCK(la); la->deleteAllLinks = 1; DeleteLink(lnk); la->deleteAllLinks = 0; LIBALIAS_UNLOCK(la); } void LibAliasSetAddress(struct libalias *la, struct in_addr addr) { - LIBALIAS_LOCK(la); if (la->packetAliasMode & PKT_ALIAS_RESET_ON_ADDR_CHANGE && la->aliasAddress.s_addr != addr.s_addr) CleanupAliasData(la); la->aliasAddress = addr; LIBALIAS_UNLOCK(la); } - void LibAliasSetAliasPortRange(struct libalias *la, u_short port_low, u_short port_high) { - LIBALIAS_LOCK(la); la->aliasPortLower = port_low; /* Add 1 to the aliasPortLength as modulo has range of 1 to n-1 */ la->aliasPortLength = port_high - port_low + 1; LIBALIAS_UNLOCK(la); } void LibAliasSetTarget(struct libalias *la, struct in_addr target_addr) { - LIBALIAS_LOCK(la); la->targetAddress = target_addr; LIBALIAS_UNLOCK(la); } static void finishoff(void) { - while (!LIST_EMPTY(&instancehead)) LibAliasUninit(LIST_FIRST(&instancehead)); } struct libalias * LibAliasInit(struct libalias *la) { int i; -#ifndef _KERNEL +#ifndef _KERNEL struct timeval tv; #endif if (la == NULL) { #ifdef _KERNEL #undef malloc /* XXX: ugly */ la = malloc(sizeof *la, M_ALIAS, M_WAITOK | M_ZERO); #else la = calloc(sizeof *la, 1); if (la == NULL) return (la); #endif -#ifndef _KERNEL /* kernel cleans up on module unload */ +#ifndef _KERNEL + /* kernel cleans up on module unload */ if (LIST_EMPTY(&instancehead)) atexit(finishoff); #endif LIST_INSERT_HEAD(&instancehead, la, instancelist); -#ifdef _KERNEL +#ifdef _KERNEL la->timeStamp = time_uptime; la->lastCleanupTime = time_uptime; #else gettimeofday(&tv, NULL); la->timeStamp = tv.tv_sec; la->lastCleanupTime = tv.tv_sec; #endif for (i = 0; i < LINK_TABLE_OUT_SIZE; i++) LIST_INIT(&la->linkTableOut[i]); for (i = 0; i < LINK_TABLE_IN_SIZE; i++) LIST_INIT(&la->linkTableIn[i]); #ifdef _KERNEL AliasSctpInit(la); #endif LIBALIAS_LOCK_INIT(la); LIBALIAS_LOCK(la); } else { LIBALIAS_LOCK(la); la->deleteAllLinks = 1; CleanupAliasData(la); la->deleteAllLinks = 0; #ifdef _KERNEL AliasSctpTerm(la); AliasSctpInit(la); #endif } la->aliasAddress.s_addr = INADDR_ANY; la->targetAddress.s_addr = INADDR_ANY; la->icmpLinkCount = 0; la->udpLinkCount = 0; la->tcpLinkCount = 0; la->sctpLinkCount = 0; la->pptpLinkCount = 0; la->protoLinkCount = 0; la->fragmentIdLinkCount = 0; la->fragmentPtrLinkCount = 0; la->sockCount = 0; la->cleanupIndex = 0; la->packetAliasMode = PKT_ALIAS_SAME_PORTS -#ifndef NO_USE_SOCKETS +#ifndef NO_USE_SOCKETS | PKT_ALIAS_USE_SOCKETS #endif | PKT_ALIAS_RESET_ON_ADDR_CHANGE; #ifndef NO_FW_PUNCH la->fireWallFD = -1; #endif #ifndef _KERNEL LibAliasRefreshModules(); #endif LIBALIAS_UNLOCK(la); return (la); } void LibAliasUninit(struct libalias *la) { - LIBALIAS_LOCK(la); #ifdef _KERNEL AliasSctpTerm(la); #endif la->deleteAllLinks = 1; CleanupAliasData(la); la->deleteAllLinks = 0; UninitPacketAliasLog(la); #ifndef NO_FW_PUNCH UninitPunchFW(la); #endif LIST_REMOVE(la, instancelist); LIBALIAS_UNLOCK(la); LIBALIAS_LOCK_DESTROY(la); free(la); } /* Change mode for some operations */ unsigned int LibAliasSetMode( struct libalias *la, unsigned int flags, /* Which state to bring flags to */ unsigned int mask /* Mask of which flags to affect (use 0 to * do a probe for flag values) */ ) { int res = -1; LIBALIAS_LOCK(la); -/* Enable logging? */ if (flags & mask & PKT_ALIAS_LOG) { - /* Do the enable */ + /* Enable logging */ if (InitPacketAliasLog(la) == ENOMEM) goto getout; - } else -/* _Disable_ logging? */ - if (~flags & mask & PKT_ALIAS_LOG) { + } else if (~flags & mask & PKT_ALIAS_LOG) + /* _Disable_ logging */ UninitPacketAliasLog(la); - } + #ifndef NO_FW_PUNCH -/* Start punching holes in the firewall? */ - if (flags & mask & PKT_ALIAS_PUNCH_FW) { + if (flags & mask & PKT_ALIAS_PUNCH_FW) + /* Start punching holes in the firewall? */ InitPunchFW(la); - } else -/* Stop punching holes in the firewall? */ - if (~flags & mask & PKT_ALIAS_PUNCH_FW) { + else if (~flags & mask & PKT_ALIAS_PUNCH_FW) + /* Stop punching holes in the firewall? */ UninitPunchFW(la); - } #endif -/* Other flags can be set/cleared without special action */ + /* Other flags can be set/cleared without special action */ la->packetAliasMode = (flags & mask) | (la->packetAliasMode & ~mask); res = la->packetAliasMode; getout: LIBALIAS_UNLOCK(la); return (res); } int LibAliasCheckNewLink(struct libalias *la) { int res; LIBALIAS_LOCK(la); res = la->newDefaultLink; LIBALIAS_UNLOCK(la); return (res); } #ifndef NO_FW_PUNCH /***************** Code to support firewall punching. This shouldn't really be in this file, but making variables global is evil too. ****************/ /* Firewall include files */ #include #include #include #include /* * helper function, updates the pointer to cmd with the length * of the current command, and also cleans up the first word of * the new command in case it has been clobbered before. */ static ipfw_insn * next_cmd(ipfw_insn * cmd) { cmd += F_LEN(cmd); bzero(cmd, sizeof(*cmd)); return (cmd); } /* * A function to fill simple commands of size 1. * Existing flags are preserved. */ static ipfw_insn * fill_cmd(ipfw_insn * cmd, enum ipfw_opcodes opcode, int size, int flags, u_int16_t arg) { cmd->opcode = opcode; cmd->len = ((cmd->len | flags) & (F_NOT | F_OR)) | (size & F_LEN_MASK); cmd->arg1 = arg; return next_cmd(cmd); } static ipfw_insn * fill_ip(ipfw_insn * cmd1, enum ipfw_opcodes opcode, u_int32_t addr) { - ipfw_insn_ip *cmd = (ipfw_insn_ip *) cmd1; + ipfw_insn_ip *cmd = (ipfw_insn_ip *)cmd1; cmd->addr.s_addr = addr; return fill_cmd(cmd1, opcode, F_INSN_SIZE(ipfw_insn_u32), 0, 0); } static ipfw_insn * fill_one_port(ipfw_insn * cmd1, enum ipfw_opcodes opcode, u_int16_t port) { - ipfw_insn_u16 *cmd = (ipfw_insn_u16 *) cmd1; + ipfw_insn_u16 *cmd = (ipfw_insn_u16 *)cmd1; cmd->ports[0] = cmd->ports[1] = port; return fill_cmd(cmd1, opcode, F_INSN_SIZE(ipfw_insn_u16), 0, 0); } static int fill_rule(void *buf, int bufsize, int rulenum, enum ipfw_opcodes action, int proto, struct in_addr sa, u_int16_t sp, struct in_addr da, u_int16_t dp) { struct ip_fw *rule = (struct ip_fw *)buf; - ipfw_insn *cmd = (ipfw_insn *) rule->cmd; + ipfw_insn *cmd = (ipfw_insn *)rule->cmd; bzero(buf, bufsize); rule->rulenum = rulenum; cmd = fill_cmd(cmd, O_PROTO, F_INSN_SIZE(ipfw_insn), 0, proto); cmd = fill_ip(cmd, O_IP_SRC, sa.s_addr); cmd = fill_one_port(cmd, O_IP_SRCPORT, sp); cmd = fill_ip(cmd, O_IP_DST, da.s_addr); cmd = fill_one_port(cmd, O_IP_DSTPORT, dp); - rule->act_ofs = (u_int32_t *) cmd - (u_int32_t *) rule->cmd; + rule->act_ofs = (u_int32_t *)cmd - (u_int32_t *)rule->cmd; cmd = fill_cmd(cmd, action, F_INSN_SIZE(ipfw_insn), 0, 0); - rule->cmd_len = (u_int32_t *) cmd - (u_int32_t *) rule->cmd; + rule->cmd_len = (u_int32_t *)cmd - (u_int32_t *)rule->cmd; return ((char *)cmd - (char *)buf); } -static void ClearAllFWHoles(struct libalias *la); +static void ClearAllFWHoles(struct libalias *la); -#define fw_setfield(la, field, num) \ -do { \ - (field)[(num) - la->fireWallBaseNum] = 1; \ +#define fw_setfield(la, field, num) \ +do { \ + (field)[(num) - la->fireWallBaseNum] = 1; \ } /*lint -save -e717 */ while(0)/* lint -restore */ -#define fw_clrfield(la, field, num) \ -do { \ - (field)[(num) - la->fireWallBaseNum] = 0; \ +#define fw_clrfield(la, field, num) \ +do { \ + (field)[(num) - la->fireWallBaseNum] = 0; \ } /*lint -save -e717 */ while(0)/* lint -restore */ #define fw_tstfield(la, field, num) ((field)[(num) - la->fireWallBaseNum]) static void InitPunchFW(struct libalias *la) { - la->fireWallField = malloc(la->fireWallNumNums); if (la->fireWallField) { memset(la->fireWallField, 0, la->fireWallNumNums); if (la->fireWallFD < 0) { la->fireWallFD = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); } ClearAllFWHoles(la); la->fireWallActiveNum = la->fireWallBaseNum; } } static void UninitPunchFW(struct libalias *la) { - ClearAllFWHoles(la); if (la->fireWallFD >= 0) close(la->fireWallFD); la->fireWallFD = -1; if (la->fireWallField) free(la->fireWallField); la->fireWallField = NULL; la->packetAliasMode &= ~PKT_ALIAS_PUNCH_FW; } /* Make a certain link go through the firewall */ void PunchFWHole(struct alias_link *lnk) { struct libalias *la; int r; /* Result code */ struct ip_fw rule; /* On-the-fly built rule */ int fwhole; /* Where to punch hole */ la = lnk->la; -/* Don't do anything unless we are asked to */ + /* Don't do anything unless we are asked to */ if (!(la->packetAliasMode & PKT_ALIAS_PUNCH_FW) || la->fireWallFD < 0 || lnk->link_type != LINK_TCP) return; memset(&rule, 0, sizeof rule); -/** Build rule **/ + /** Build rule **/ /* Find empty slot */ for (fwhole = la->fireWallActiveNum; fwhole < la->fireWallBaseNum + la->fireWallNumNums && fw_tstfield(la, la->fireWallField, fwhole); fwhole++); if (fwhole == la->fireWallBaseNum + la->fireWallNumNums) { for (fwhole = la->fireWallBaseNum; fwhole < la->fireWallActiveNum && fw_tstfield(la, la->fireWallField, fwhole); fwhole++); if (fwhole == la->fireWallActiveNum) { /* No rule point empty - we can't punch more holes. */ la->fireWallActiveNum = la->fireWallBaseNum; #ifdef LIBALIAS_DEBUG fprintf(stderr, "libalias: Unable to create firewall hole!\n"); #endif return; } } /* Start next search at next position */ la->fireWallActiveNum = fwhole + 1; /* * generate two rules of the form * * add fwhole accept tcp from OAddr OPort to DAddr DPort add fwhole * accept tcp from DAddr DPort to OAddr OPort */ if (GetOriginalPort(lnk) != 0 && GetDestPort(lnk) != 0) { u_int32_t rulebuf[255]; int i; i = fill_rule(rulebuf, sizeof(rulebuf), fwhole, O_ACCEPT, IPPROTO_TCP, GetOriginalAddress(lnk), ntohs(GetOriginalPort(lnk)), GetDestAddress(lnk), ntohs(GetDestPort(lnk))); r = setsockopt(la->fireWallFD, IPPROTO_IP, IP_FW_ADD, rulebuf, i); if (r) err(1, "alias punch inbound(1) setsockopt(IP_FW_ADD)"); i = fill_rule(rulebuf, sizeof(rulebuf), fwhole, O_ACCEPT, IPPROTO_TCP, GetDestAddress(lnk), ntohs(GetDestPort(lnk)), GetOriginalAddress(lnk), ntohs(GetOriginalPort(lnk))); r = setsockopt(la->fireWallFD, IPPROTO_IP, IP_FW_ADD, rulebuf, i); if (r) err(1, "alias punch inbound(2) setsockopt(IP_FW_ADD)"); } -/* Indicate hole applied */ + /* Indicate hole applied */ lnk->data.tcp->fwhole = fwhole; fw_setfield(la, la->fireWallField, fwhole); } /* Remove a hole in a firewall associated with a particular alias lnk. Calling this too often is harmless. */ static void ClearFWHole(struct alias_link *lnk) { struct libalias *la; la = lnk->la; if (lnk->link_type == LINK_TCP) { - int fwhole = lnk->data.tcp->fwhole; /* Where is the firewall - * hole? */ + int fwhole = lnk->data.tcp->fwhole; /* Where is the firewall hole? */ struct ip_fw rule; if (fwhole < 0) return; memset(&rule, 0, sizeof rule); /* useless for ipfw2 */ while (!setsockopt(la->fireWallFD, IPPROTO_IP, IP_FW_DEL, &fwhole, sizeof fwhole)); fw_clrfield(la, la->fireWallField, fwhole); lnk->data.tcp->fwhole = -1; } } /* Clear out the entire range dedicated to firewall holes. */ static void ClearAllFWHoles(struct libalias *la) { struct ip_fw rule; /* On-the-fly built rule */ int i; if (la->fireWallFD < 0) return; memset(&rule, 0, sizeof rule); for (i = la->fireWallBaseNum; i < la->fireWallBaseNum + la->fireWallNumNums; i++) { int r = i; while (!setsockopt(la->fireWallFD, IPPROTO_IP, IP_FW_DEL, &r, sizeof r)); } /* XXX: third arg correct here ? /phk */ memset(la->fireWallField, 0, la->fireWallNumNums); } #endif /* !NO_FW_PUNCH */ void LibAliasSetFWBase(struct libalias *la, unsigned int base, unsigned int num) { - LIBALIAS_LOCK(la); #ifndef NO_FW_PUNCH la->fireWallBaseNum = base; la->fireWallNumNums = num; #endif LIBALIAS_UNLOCK(la); } void LibAliasSetSkinnyPort(struct libalias *la, unsigned int port) { - LIBALIAS_LOCK(la); la->skinnyPort = port; LIBALIAS_UNLOCK(la); } /* * Find the address to redirect incoming packets */ struct in_addr FindSctpRedirectAddress(struct libalias *la, struct sctp_nat_msg *sm) { struct alias_link *lnk; struct in_addr redir; LIBALIAS_LOCK_ASSERT(la); lnk = FindLinkIn(la, sm->ip_hdr->ip_src, sm->ip_hdr->ip_dst, sm->sctp_hdr->dest_port,sm->sctp_hdr->dest_port, LINK_SCTP, 1); if (lnk != NULL) { - return(lnk->src_addr); /* port redirect */ + /* port redirect */ + return (lnk->src_addr); } else { redir = FindOriginalAddress(la,sm->ip_hdr->ip_dst); if (redir.s_addr == la->aliasAddress.s_addr || - redir.s_addr == la->targetAddress.s_addr) { /* No address found */ + redir.s_addr == la->targetAddress.s_addr) { + /* No address found */ lnk = FindLinkIn(la, sm->ip_hdr->ip_src, sm->ip_hdr->ip_dst, NO_DEST_PORT, 0, LINK_SCTP, 1); if (lnk != NULL) - return(lnk->src_addr); /* redirect proto */ + /* redirect proto */ + return (lnk->src_addr); } - return(redir); /* address redirect */ + return (redir); /* address redirect */ } } diff --git a/sys/netinet/libalias/alias_dummy.c b/sys/netinet/libalias/alias_dummy.c index 375dbc9f5b4c..a88f50aada57 100644 --- a/sys/netinet/libalias/alias_dummy.c +++ b/sys/netinet/libalias/alias_dummy.c @@ -1,154 +1,151 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2005 Paolo Pisati * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); /* * Alias_dummy is just an empty skeleton used to demostrate how to write * a module for libalias, that will run unalterated in userland or in * kernel land. */ #ifdef _KERNEL #include #include #include #else #include #include #include #endif #include #include #include #include #ifdef _KERNEL #include #include #else #include "alias_local.h" #include "alias_mod.h" #endif static void AliasHandleDummy(struct libalias *la, struct ip *ip, struct alias_data *ah); static int fingerprint(struct libalias *la, struct alias_data *ah) { - /* * Check here all the data that will be used later, if any field * is empy/NULL, return a -1 value. */ if (ah->dport == NULL || ah->sport == NULL || ah->lnk == NULL || - ah->maxpktsize == 0) + ah->maxpktsize == 0) return (-1); /* * Fingerprint the incoming packet, if it matches any conditions * return an OK value. */ - if (ntohs(*ah->dport) == 123 - || ntohs(*ah->sport) == 456) - return (0); /* I know how to handle it. */ - return (-1); /* I don't recognize this packet. */ + if (ntohs(*ah->dport) == 123 || ntohs(*ah->sport) == 456) + return (0); /* I know how to handle it. */ + return (-1); /* I don't recognize this packet. */ } /* * Wrap in this general purpose function, the real function used to alias the * packets. */ static int protohandler(struct libalias *la, struct ip *pip, struct alias_data *ah) { - AliasHandleDummy(la, pip, ah); return (0); } /* * NOTA BENE: the next variable MUST NOT be renamed in any case if you want * your module to work in userland, cause it's used to find and use all * the protocol handlers present in every module. * So WATCH OUT, your module needs this variables and it needs it with * ITS EXACT NAME: handlers. */ struct proto_handler handlers [] = { { .pri = 666, .dir = IN|OUT, .proto = UDP|TCP, .fingerprint = &fingerprint, .protohandler = &protohandler }, { EOH } }; static int mod_handler(module_t mod, int type, void *data) { int error; - switch (type) { + switch (type) { case MOD_LOAD: error = 0; LibAliasAttachHandlers(handlers); break; case MOD_UNLOAD: error = 0; LibAliasDetachHandlers(handlers); break; default: error = EINVAL; } return (error); } #ifdef _KERNEL static #endif moduledata_t alias_mod = { "alias_dummy", mod_handler, NULL }; -#ifdef _KERNEL +#ifdef _KERNEL DECLARE_MODULE(alias_dummy, alias_mod, SI_SUB_DRIVERS, SI_ORDER_SECOND); MODULE_VERSION(alias_dummy, 1); MODULE_DEPEND(alias_dummy, libalias, 1, 1, 1); #endif static void AliasHandleDummy(struct libalias *la, struct ip *ip, struct alias_data *ah) { ; /* Dummy. */ } diff --git a/sys/netinet/libalias/alias_ftp.c b/sys/netinet/libalias/alias_ftp.c index e74093f6e5dd..962194ec0a68 100644 --- a/sys/netinet/libalias/alias_ftp.c +++ b/sys/netinet/libalias/alias_ftp.c @@ -1,777 +1,767 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2001 Charles Mott * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); /* Alias_ftp.c performs special processing for FTP sessions under TCP. Specifically, when a PORT/EPRT command from the client side or 227/229 reply from the server is sent, it is intercepted and modified. The address is changed to the gateway machine and an aliasing port is used. For this routine to work, the message must fit entirely into a single TCP packet. This is typically the case, but exceptions can easily be envisioned under the actual specifications. Probably the most troubling aspect of the approach taken here is that the new message will typically be a different length, and this causes a certain amount of bookkeeping to keep track of the changes of sequence and acknowledgment numbers, since the client machine is totally unaware of the modification to the TCP stream. References: RFC 959, RFC 2428. Initial version: August, 1996 (cjm) Version 1.6 Brian Somers and Martin Renters identified an IP checksum error for modified IP packets. Version 1.7: January 9, 1996 (cjm) Differential checksum computation for change in IP packet length. Version 2.1: May, 1997 (cjm) Very minor changes to conform with local/global/function naming conventions within the packet aliasing module. Version 3.1: May, 2000 (eds) Add support for passive mode, alias the 227 replies. See HISTORY file for record of revisions. */ /* Includes */ #ifdef _KERNEL #include #include #include #include #include #else #include #include #include #include #include #endif #include #include #include #include #ifdef _KERNEL #include #include #include #else #include "alias_local.h" #include "alias_mod.h" #endif #define FTP_CONTROL_PORT_NUMBER 21 static void AliasHandleFtpOut(struct libalias *, struct ip *, struct alias_link *, int maxpacketsize); static void AliasHandleFtpIn(struct libalias *, struct ip *, struct alias_link *); static int fingerprint_out(struct libalias *la, struct alias_data *ah) { - if (ah->dport == NULL || ah->sport == NULL || ah->lnk == NULL || ah->maxpktsize == 0) return (-1); if (ntohs(*ah->dport) == FTP_CONTROL_PORT_NUMBER || ntohs(*ah->sport) == FTP_CONTROL_PORT_NUMBER) return (0); return (-1); } static int fingerprint_in(struct libalias *la, struct alias_data *ah) { - if (ah->dport == NULL || ah->sport == NULL || ah->lnk == NULL) return (-1); if (ntohs(*ah->dport) == FTP_CONTROL_PORT_NUMBER || ntohs(*ah->sport) == FTP_CONTROL_PORT_NUMBER) return (0); return (-1); } static int protohandler_out(struct libalias *la, struct ip *pip, struct alias_data *ah) { - AliasHandleFtpOut(la, pip, ah->lnk, ah->maxpktsize); return (0); } static int protohandler_in(struct libalias *la, struct ip *pip, struct alias_data *ah) { - AliasHandleFtpIn(la, pip, ah->lnk); return (0); } struct proto_handler handlers[] = { { .pri = 80, .dir = OUT, .proto = TCP, .fingerprint = &fingerprint_out, .protohandler = &protohandler_out }, { .pri = 80, .dir = IN, .proto = TCP, .fingerprint = &fingerprint_in, .protohandler = &protohandler_in }, { EOH } }; static int mod_handler(module_t mod, int type, void *data) { int error; - switch (type) { + switch (type) { case MOD_LOAD: error = 0; LibAliasAttachHandlers(handlers); break; case MOD_UNLOAD: error = 0; LibAliasDetachHandlers(handlers); break; default: error = EINVAL; } return (error); } #ifdef _KERNEL static #endif moduledata_t alias_mod = { "alias_ftp", mod_handler, NULL }; -#ifdef _KERNEL +#ifdef _KERNEL DECLARE_MODULE(alias_ftp, alias_mod, SI_SUB_DRIVERS, SI_ORDER_SECOND); MODULE_VERSION(alias_ftp, 1); MODULE_DEPEND(alias_ftp, libalias, 1, 1, 1); #endif #define FTP_CONTROL_PORT_NUMBER 21 #define MAX_MESSAGE_SIZE 128 /* FTP protocol flags. */ #define WAIT_CRLF 0x01 enum ftp_message_type { FTP_PORT_COMMAND, FTP_EPRT_COMMAND, FTP_227_REPLY, FTP_229_REPLY, FTP_UNKNOWN_MESSAGE }; static int ParseFtpPortCommand(struct libalias *la, char *, int); static int ParseFtpEprtCommand(struct libalias *la, char *, int); static int ParseFtp227Reply(struct libalias *la, char *, int); static int ParseFtp229Reply(struct libalias *la, char *, int); static void NewFtpMessage(struct libalias *la, struct ip *, struct alias_link *, int, int); static void AliasHandleFtpOut( struct libalias *la, struct ip *pip, /* IP packet to examine/patch */ struct alias_link *lnk, /* The link to go through (aliased port) */ int maxpacketsize /* The maximum size this packet can grow to - (including headers) */ ) + (including headers) */ ) { int hlen, tlen, dlen, pflags; char *sptr; struct tcphdr *tc; int ftp_message_type; -/* Calculate data length of TCP packet */ + /* Calculate data length of TCP packet */ tc = (struct tcphdr *)ip_next(pip); hlen = (pip->ip_hl + tc->th_off) << 2; tlen = ntohs(pip->ip_len); dlen = tlen - hlen; -/* Place string pointer and beginning of data */ + /* Place string pointer and beginning of data */ sptr = (char *)pip; sptr += hlen; -/* - * Check that data length is not too long and previous message was - * properly terminated with CRLF. - */ + /* + * Check that data length is not too long and previous message was + * properly terminated with CRLF. + */ pflags = GetProtocolFlags(lnk); if (dlen <= MAX_MESSAGE_SIZE && !(pflags & WAIT_CRLF)) { ftp_message_type = FTP_UNKNOWN_MESSAGE; if (ntohs(tc->th_dport) == FTP_CONTROL_PORT_NUMBER) { -/* - * When aliasing a client, check for the PORT/EPRT command. - */ + /* When aliasing a client, check for the PORT/EPRT command. */ if (ParseFtpPortCommand(la, sptr, dlen)) ftp_message_type = FTP_PORT_COMMAND; else if (ParseFtpEprtCommand(la, sptr, dlen)) ftp_message_type = FTP_EPRT_COMMAND; } else { -/* - * When aliasing a server, check for the 227/229 reply. - */ + /* When aliasing a server, check for the 227/229 reply. */ if (ParseFtp227Reply(la, sptr, dlen)) ftp_message_type = FTP_227_REPLY; else if (ParseFtp229Reply(la, sptr, dlen)) { ftp_message_type = FTP_229_REPLY; la->true_addr.s_addr = pip->ip_src.s_addr; } } if (ftp_message_type != FTP_UNKNOWN_MESSAGE) NewFtpMessage(la, pip, lnk, maxpacketsize, ftp_message_type); } -/* Track the msgs which are CRLF term'd for PORT/PASV FW breach */ - if (dlen) { /* only if there's data */ + /* Track the msgs which are CRLF term'd for PORT/PASV FW breach */ + if (dlen) { /* only if there's data */ sptr = (char *)pip; /* start over at beginning */ - tlen = ntohs(pip->ip_len); /* recalc tlen, pkt may - * have grown */ + tlen = ntohs(pip->ip_len); /* recalc tlen, pkt may have grown */ if (sptr[tlen - 2] == '\r' && sptr[tlen - 1] == '\n') pflags &= ~WAIT_CRLF; else pflags |= WAIT_CRLF; SetProtocolFlags(lnk, pflags); } } static void AliasHandleFtpIn(struct libalias *la, struct ip *pip, /* IP packet to examine/patch */ struct alias_link *lnk) /* The link to go through (aliased port) */ { int hlen, tlen, dlen, pflags; char *sptr; struct tcphdr *tc; /* Calculate data length of TCP packet */ tc = (struct tcphdr *)ip_next(pip); hlen = (pip->ip_hl + tc->th_off) << 2; tlen = ntohs(pip->ip_len); dlen = tlen - hlen; /* Place string pointer and beginning of data */ sptr = (char *)pip; sptr += hlen; /* * Check that data length is not too long and previous message was * properly terminated with CRLF. */ pflags = GetProtocolFlags(lnk); if (dlen <= MAX_MESSAGE_SIZE && (pflags & WAIT_CRLF) == 0 && ntohs(tc->th_dport) == FTP_CONTROL_PORT_NUMBER && (ParseFtpPortCommand(la, sptr, dlen) != 0 || - ParseFtpEprtCommand(la, sptr, dlen) != 0)) { + ParseFtpEprtCommand(la, sptr, dlen) != 0)) { /* * Alias active mode client requesting data from server * behind NAT. We need to alias server->client connection * to external address client is connecting to. */ AddLink(la, GetOriginalAddress(lnk), la->true_addr, GetAliasAddress(lnk), htons(FTP_CONTROL_PORT_NUMBER - 1), htons(la->true_port), GET_ALIAS_PORT, IPPROTO_TCP); } /* Track the msgs which are CRLF term'd for PORT/PASV FW breach */ if (dlen) { sptr = (char *)pip; /* start over at beginning */ tlen = ntohs(pip->ip_len); /* recalc tlen, pkt may - * have grown. - */ + * have grown. */ if (sptr[tlen - 2] == '\r' && sptr[tlen - 1] == '\n') pflags &= ~WAIT_CRLF; else pflags |= WAIT_CRLF; SetProtocolFlags(lnk, pflags); } } static int ParseFtpPortCommand(struct libalias *la, char *sptr, int dlen) { char ch; int i, state; u_int32_t addr; u_short port; u_int8_t octet; /* Format: "PORT A,D,D,R,PO,RT". */ /* Return if data length is too short. */ if (dlen < 18) return (0); if (strncasecmp("PORT ", sptr, 5)) return (0); addr = port = octet = 0; state = 0; for (i = 5; i < dlen; i++) { ch = sptr[i]; switch (state) { case 0: if (isspace(ch)) break; else state++; case 1: case 3: case 5: case 7: case 9: case 11: if (isdigit(ch)) { octet = ch - '0'; state++; } else return (0); break; case 2: case 4: case 6: case 8: if (isdigit(ch)) octet = 10 * octet + ch - '0'; else if (ch == ',') { addr = (addr << 8) + octet; state++; } else return (0); break; case 10: case 12: if (isdigit(ch)) octet = 10 * octet + ch - '0'; else if (ch == ',' || state == 12) { port = (port << 8) + octet; state++; } else return (0); break; } } if (state == 13) { la->true_addr.s_addr = htonl(addr); la->true_port = port; return (1); } else return (0); } static int ParseFtpEprtCommand(struct libalias *la, char *sptr, int dlen) { char ch, delim; int i, state; u_int32_t addr; u_short port; u_int8_t octet; /* Format: "EPRT |1|A.D.D.R|PORT|". */ /* Return if data length is too short. */ if (dlen < 18) return (0); if (strncasecmp("EPRT ", sptr, 5)) return (0); addr = port = octet = 0; delim = '|'; /* XXX gcc -Wuninitialized */ state = 0; for (i = 5; i < dlen; i++) { ch = sptr[i]; switch (state) { case 0: if (!isspace(ch)) { delim = ch; state++; } break; case 1: if (ch == '1') /* IPv4 address */ state++; else return (0); break; case 2: if (ch == delim) state++; else return (0); break; case 3: case 5: case 7: case 9: if (isdigit(ch)) { octet = ch - '0'; state++; } else return (0); break; case 4: case 6: case 8: case 10: if (isdigit(ch)) octet = 10 * octet + ch - '0'; else if (ch == '.' || state == 10) { addr = (addr << 8) + octet; state++; } else return (0); break; case 11: if (isdigit(ch)) { port = ch - '0'; state++; } else return (0); break; case 12: if (isdigit(ch)) port = 10 * port + ch - '0'; else if (ch == delim) state++; else return (0); break; } } if (state == 13) { la->true_addr.s_addr = htonl(addr); la->true_port = port; return (1); } else return (0); } static int ParseFtp227Reply(struct libalias *la, char *sptr, int dlen) { char ch; int i, state; u_int32_t addr; u_short port; u_int8_t octet; /* Format: "227 Entering Passive Mode (A,D,D,R,PO,RT)" */ /* Return if data length is too short. */ if (dlen < 17) return (0); if (strncmp("227 ", sptr, 4)) return (0); addr = port = octet = 0; state = 0; for (i = 4; i < dlen; i++) { ch = sptr[i]; switch (state) { case 0: if (ch == '(') state++; break; case 1: case 3: case 5: case 7: case 9: case 11: if (isdigit(ch)) { octet = ch - '0'; state++; } else return (0); break; case 2: case 4: case 6: case 8: if (isdigit(ch)) octet = 10 * octet + ch - '0'; else if (ch == ',') { addr = (addr << 8) + octet; state++; } else return (0); break; case 10: case 12: if (isdigit(ch)) octet = 10 * octet + ch - '0'; else if (ch == ',' || (state == 12 && ch == ')')) { port = (port << 8) + octet; state++; } else return (0); break; } } if (state == 13) { la->true_port = port; la->true_addr.s_addr = htonl(addr); return (1); } else return (0); } static int ParseFtp229Reply(struct libalias *la, char *sptr, int dlen) { char ch, delim; int i, state; u_short port; /* Format: "229 Entering Extended Passive Mode (|||PORT|)" */ /* Return if data length is too short. */ if (dlen < 11) return (0); if (strncmp("229 ", sptr, 4)) return (0); port = 0; delim = '|'; /* XXX gcc -Wuninitialized */ state = 0; for (i = 4; i < dlen; i++) { ch = sptr[i]; switch (state) { case 0: if (ch == '(') state++; break; case 1: delim = ch; state++; break; case 2: case 3: if (ch == delim) state++; else return (0); break; case 4: if (isdigit(ch)) { port = ch - '0'; state++; } else return (0); break; case 5: if (isdigit(ch)) port = 10 * port + ch - '0'; else if (ch == delim) state++; else return (0); break; case 6: if (ch == ')') state++; else return (0); break; } } if (state == 7) { la->true_port = port; return (1); } else return (0); } static void NewFtpMessage(struct libalias *la, struct ip *pip, struct alias_link *lnk, int maxpacketsize, int ftp_message_type) { struct alias_link *ftp_lnk; -/* Security checks. */ + /* Security checks. */ if (pip->ip_src.s_addr != la->true_addr.s_addr) return; if (la->true_port < IPPORT_RESERVED) return; /* Establish link to address and port found in FTP control message. */ ftp_lnk = AddLink(la, la->true_addr, GetDestAddress(lnk), GetAliasAddress(lnk), htons(la->true_port), 0, GET_ALIAS_PORT, IPPROTO_TCP); if (ftp_lnk != NULL) { int slen, hlen, tlen, dlen; struct tcphdr *tc; #ifndef NO_FW_PUNCH /* Punch hole in firewall */ PunchFWHole(ftp_lnk); #endif -/* Calculate data length of TCP packet */ + /* Calculate data length of TCP packet */ tc = (struct tcphdr *)ip_next(pip); hlen = (pip->ip_hl + tc->th_off) << 2; tlen = ntohs(pip->ip_len); dlen = tlen - hlen; -/* Create new FTP message. */ + /* Create new FTP message. */ { char stemp[MAX_MESSAGE_SIZE + 1]; char *sptr; u_short alias_port; u_char *ptr; int a1, a2, a3, a4, p1, p2; struct in_addr alias_address; -/* Decompose alias address into quad format */ + /* Decompose alias address into quad format */ alias_address = GetAliasAddress(lnk); - ptr = (u_char *) & alias_address.s_addr; + ptr = (u_char *)&alias_address.s_addr; a1 = *ptr++; a2 = *ptr++; a3 = *ptr++; a4 = *ptr; alias_port = GetAliasPort(ftp_lnk); -/* Prepare new command */ + /* Prepare new command */ switch (ftp_message_type) { case FTP_PORT_COMMAND: case FTP_227_REPLY: /* Decompose alias port into pair format. */ ptr = (char *)&alias_port; p1 = *ptr++; p2 = *ptr; if (ftp_message_type == FTP_PORT_COMMAND) { /* Generate PORT command string. */ sprintf(stemp, "PORT %d,%d,%d,%d,%d,%d\r\n", a1, a2, a3, a4, p1, p2); } else { /* Generate 227 reply string. */ sprintf(stemp, "227 Entering Passive Mode (%d,%d,%d,%d,%d,%d)\r\n", a1, a2, a3, a4, p1, p2); } break; case FTP_EPRT_COMMAND: /* Generate EPRT command string. */ sprintf(stemp, "EPRT |1|%d.%d.%d.%d|%d|\r\n", a1, a2, a3, a4, ntohs(alias_port)); break; case FTP_229_REPLY: /* Generate 229 reply string. */ sprintf(stemp, "229 Entering Extended Passive Mode (|||%d|)\r\n", ntohs(alias_port)); break; } -/* Save string length for IP header modification */ + /* Save string length for IP header modification */ slen = strlen(stemp); -/* Copy modified buffer into IP packet. */ + /* Copy modified buffer into IP packet. */ sptr = (char *)pip; sptr += hlen; strncpy(sptr, stemp, maxpacketsize - hlen); } -/* Save information regarding modified seq and ack numbers */ + /* Save information regarding modified seq and ack numbers */ { int delta; SetAckModified(lnk); - tc = (struct tcphdr *)ip_next(pip); + tc = (struct tcphdr *)ip_next(pip); delta = GetDeltaSeqOut(tc->th_seq, lnk); - AddSeq(lnk, delta + slen - dlen, pip->ip_hl, + AddSeq(lnk, delta + slen - dlen, pip->ip_hl, pip->ip_len, tc->th_seq, tc->th_off); } -/* Revise IP header */ + /* Revise IP header */ { u_short new_len; new_len = htons(hlen + MIN(slen, maxpacketsize - hlen)); DifferentialChecksum(&pip->ip_sum, &new_len, &pip->ip_len, 1); pip->ip_len = new_len; } -/* Compute TCP checksum for revised packet */ + /* Compute TCP checksum for revised packet */ tc->th_sum = 0; #ifdef _KERNEL tc->th_x2 = 1; #else tc->th_sum = TcpChecksum(pip); #endif } else { #ifdef LIBALIAS_DEBUG fprintf(stderr, "PacketAlias/HandleFtpOut: Cannot allocate FTP data port\n"); #endif } } diff --git a/sys/netinet/libalias/alias_irc.c b/sys/netinet/libalias/alias_irc.c index ed8e9a95926d..32e831742048 100644 --- a/sys/netinet/libalias/alias_irc.c +++ b/sys/netinet/libalias/alias_irc.c @@ -1,488 +1,486 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2001 Charles Mott * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); /* Alias_irc.c intercepts packages contain IRC CTCP commands, and changes DCC commands to export a port on the aliasing host instead of an aliased host. For this routine to work, the DCC command must fit entirely into a single TCP packet. This will usually happen, but is not guaranteed. The interception is likely to change the length of the packet. The handling of this is copied more-or-less verbatim from ftp_alias.c Initial version: Eivind Eklund (ee) 97-01-29 Version 2.1: May, 1997 (cjm) Very minor changes to conform with local/global/function naming conventions within the packet alising module. */ /* Includes */ #ifdef _KERNEL #include #include #include #include #include #include #else #include #include #include #include #include #include #include #endif #include #include #include #include #ifdef _KERNEL #include #include #include #else #include "alias_local.h" #include "alias_mod.h" #endif #define IRC_CONTROL_PORT_NUMBER_1 6667 #define IRC_CONTROL_PORT_NUMBER_2 6668 #define PKTSIZE (IP_MAXPACKET + 1) char *newpacket; /* Local defines */ #define DBprintf(a) static void -AliasHandleIrcOut(struct libalias *, struct ip *, struct alias_link *, - int maxpacketsize); +AliasHandleIrcOut(struct libalias *, struct ip *, struct alias_link *, + int maxpacketsize); static int fingerprint(struct libalias *la, struct alias_data *ah) { - if (ah->dport == NULL || ah->lnk == NULL || ah->maxpktsize == 0) return (-1); if (ntohs(*ah->dport) == IRC_CONTROL_PORT_NUMBER_1 || ntohs(*ah->dport) == IRC_CONTROL_PORT_NUMBER_2) return (0); return (-1); } static int protohandler(struct libalias *la, struct ip *pip, struct alias_data *ah) { - newpacket = malloc(PKTSIZE); if (newpacket) { AliasHandleIrcOut(la, pip, ah->lnk, ah->maxpktsize); free(newpacket); } return (0); } struct proto_handler handlers[] = { { .pri = 90, .dir = OUT, .proto = TCP, .fingerprint = &fingerprint, .protohandler = &protohandler }, { EOH } }; static int mod_handler(module_t mod, int type, void *data) { int error; switch (type) { case MOD_LOAD: error = 0; LibAliasAttachHandlers(handlers); break; case MOD_UNLOAD: error = 0; LibAliasDetachHandlers(handlers); break; default: error = EINVAL; } return (error); } #ifdef _KERNEL static #endif moduledata_t alias_mod = { "alias_irc", mod_handler, NULL }; /* Kernel module definition. */ -#ifdef _KERNEL +#ifdef _KERNEL DECLARE_MODULE(alias_irc, alias_mod, SI_SUB_DRIVERS, SI_ORDER_SECOND); MODULE_VERSION(alias_irc, 1); MODULE_DEPEND(alias_irc, libalias, 1, 1, 1); #endif static void AliasHandleIrcOut(struct libalias *la, struct ip *pip, /* IP packet to examine */ struct alias_link *lnk, /* Which link are we on? */ int maxsize /* Maximum size of IP packet including * headers */ ) { int hlen, tlen, dlen; struct in_addr true_addr; u_short true_port; char *sptr; struct tcphdr *tc; int i; /* Iterator through the source */ -/* Calculate data length of TCP packet */ + /* Calculate data length of TCP packet */ tc = (struct tcphdr *)ip_next(pip); hlen = (pip->ip_hl + tc->th_off) << 2; tlen = ntohs(pip->ip_len); dlen = tlen - hlen; /* * Return if data length is too short - assume an entire PRIVMSG in * each packet. */ if (dlen < (int)sizeof(":A!a@n.n PRIVMSG A :aDCC 1 1a") - 1) return; -/* Place string pointer at beginning of data */ + /* Place string pointer at beginning of data */ sptr = (char *)pip; sptr += hlen; maxsize -= hlen; /* We're interested in maximum size of * data, not packet */ /* Search for a CTCP command [Note 1] */ for (i = 0; i < dlen; i++) { if (sptr[i] == '\001') goto lFOUND_CTCP; } return; /* No CTCP commands in */ /* Handle CTCP commands - the buffer may have to be copied */ -lFOUND_CTCP: + lFOUND_CTCP: { unsigned int copyat = i; unsigned int iCopy = 0; /* How much data have we written to * copy-back string? */ unsigned long org_addr; /* Original IP address */ unsigned short org_port; /* Original source port * address */ -lCTCP_START: + lCTCP_START: if (i >= dlen || iCopy >= PKTSIZE) goto lPACKET_DONE; newpacket[iCopy++] = sptr[i++]; /* Copy the CTCP start * character */ /* Start of a CTCP */ if (i + 4 >= dlen) /* Too short for DCC */ goto lBAD_CTCP; if (sptr[i + 0] != 'D') goto lBAD_CTCP; if (sptr[i + 1] != 'C') goto lBAD_CTCP; if (sptr[i + 2] != 'C') goto lBAD_CTCP; if (sptr[i + 3] != ' ') goto lBAD_CTCP; /* We have a DCC command - handle it! */ i += 4; /* Skip "DCC " */ if (iCopy + 4 > PKTSIZE) goto lPACKET_DONE; newpacket[iCopy++] = 'D'; newpacket[iCopy++] = 'C'; newpacket[iCopy++] = 'C'; newpacket[iCopy++] = ' '; DBprintf(("Found DCC\n")); /* * Skip any extra spaces (should not occur according to * protocol, but DCC breaks CTCP protocol anyway */ while (sptr[i] == ' ') { if (++i >= dlen) { DBprintf(("DCC packet terminated in just spaces\n")); goto lPACKET_DONE; } } DBprintf(("Transferring command...\n")); while (sptr[i] != ' ') { newpacket[iCopy++] = sptr[i]; if (++i >= dlen || iCopy >= PKTSIZE) { DBprintf(("DCC packet terminated during command\n")); goto lPACKET_DONE; } } /* Copy _one_ space */ if (i + 1 < dlen && iCopy < PKTSIZE) newpacket[iCopy++] = sptr[i++]; DBprintf(("Done command - removing spaces\n")); /* * Skip any extra spaces (should not occur according to * protocol, but DCC breaks CTCP protocol anyway */ while (sptr[i] == ' ') { if (++i >= dlen) { DBprintf(("DCC packet terminated in just spaces (post-command)\n")); goto lPACKET_DONE; } } DBprintf(("Transferring filename...\n")); while (sptr[i] != ' ') { newpacket[iCopy++] = sptr[i]; if (++i >= dlen || iCopy >= PKTSIZE) { DBprintf(("DCC packet terminated during filename\n")); goto lPACKET_DONE; } } /* Copy _one_ space */ if (i + 1 < dlen && iCopy < PKTSIZE) newpacket[iCopy++] = sptr[i++]; DBprintf(("Done filename - removing spaces\n")); /* * Skip any extra spaces (should not occur according to * protocol, but DCC breaks CTCP protocol anyway */ while (sptr[i] == ' ') { if (++i >= dlen) { DBprintf(("DCC packet terminated in just spaces (post-filename)\n")); goto lPACKET_DONE; } } DBprintf(("Fetching IP address\n")); /* Fetch IP address */ org_addr = 0; while (i < dlen && isdigit(sptr[i])) { if (org_addr > ULONG_MAX / 10UL) { /* Terminate on overflow */ DBprintf(("DCC Address overflow (org_addr == 0x%08lx, next char %c\n", org_addr, sptr[i])); goto lBAD_CTCP; } org_addr *= 10; org_addr += sptr[i++] - '0'; } DBprintf(("Skipping space\n")); if (i + 1 >= dlen || sptr[i] != ' ') { DBprintf(("Overflow (%d >= %d) or bad character (%02x) terminating IP address\n", i + 1, dlen, sptr[i])); goto lBAD_CTCP; } /* * Skip any extra spaces (should not occur according to * protocol, but DCC breaks CTCP protocol anyway, so we * might as well play it safe */ while (sptr[i] == ' ') { if (++i >= dlen) { DBprintf(("Packet failure - space overflow.\n")); goto lPACKET_DONE; } } DBprintf(("Fetching port number\n")); /* Fetch source port */ org_port = 0; while (i < dlen && isdigit(sptr[i])) { if (org_port > 6554) { /* Terminate on overflow * (65536/10 rounded up */ DBprintf(("DCC: port number overflow\n")); goto lBAD_CTCP; } org_port *= 10; org_port += sptr[i++] - '0'; } /* Skip illegal addresses (or early termination) */ if (i >= dlen || (sptr[i] != '\001' && sptr[i] != ' ')) { DBprintf(("Bad port termination\n")); goto lBAD_CTCP; } DBprintf(("Got IP %lu and port %u\n", org_addr, (unsigned)org_port)); /* We've got the address and port - now alias it */ { struct alias_link *dcc_lnk; struct in_addr destaddr; true_port = htons(org_port); true_addr.s_addr = htonl(org_addr); destaddr.s_addr = 0; /* Sanity/Security checking */ if (!org_addr || !org_port || pip->ip_src.s_addr != true_addr.s_addr || org_port < IPPORT_RESERVED) goto lBAD_CTCP; /* * Steal the FTP_DATA_PORT - it doesn't really * matter, and this would probably allow it through * at least _some_ firewalls. */ dcc_lnk = FindUdpTcpOut(la, true_addr, destaddr, true_port, 0, IPPROTO_TCP, 1); DBprintf(("Got a DCC link\n")); if (dcc_lnk) { struct in_addr alias_address; /* Address from aliasing */ u_short alias_port; /* Port given by * aliasing */ int n; #ifndef NO_FW_PUNCH /* Generate firewall hole as appropriate */ PunchFWHole(dcc_lnk); #endif alias_address = GetAliasAddress(lnk); n = snprintf(&newpacket[iCopy], PKTSIZE - iCopy, "%lu ", (u_long) htonl(alias_address.s_addr)); if (n < 0) { DBprintf(("DCC packet construct failure.\n")); goto lBAD_CTCP; } if ((iCopy += n) >= PKTSIZE) { /* Truncated/fit exactly * - bad news */ DBprintf(("DCC constructed packet overflow.\n")); goto lBAD_CTCP; } alias_port = GetAliasPort(dcc_lnk); n = snprintf(&newpacket[iCopy], PKTSIZE - iCopy, "%u", htons(alias_port)); if (n < 0) { DBprintf(("DCC packet construct failure.\n")); goto lBAD_CTCP; } iCopy += n; /* * Done - truncated cases will be taken * care of by lBAD_CTCP */ DBprintf(("Aliased IP %lu and port %u\n", alias_address.s_addr, (unsigned)alias_port)); } } /* * An uninteresting CTCP - state entered right after '\001' * has been pushed. Also used to copy the rest of a DCC, * after IP address and port has been handled */ -lBAD_CTCP: + lBAD_CTCP: for (; i < dlen && iCopy < PKTSIZE; i++, iCopy++) { newpacket[iCopy] = sptr[i]; /* Copy CTCP unchanged */ if (sptr[i] == '\001') { goto lNORMAL_TEXT; } } goto lPACKET_DONE; /* Normal text */ -lNORMAL_TEXT: + lNORMAL_TEXT: for (; i < dlen && iCopy < PKTSIZE; i++, iCopy++) { newpacket[iCopy] = sptr[i]; /* Copy CTCP unchanged */ if (sptr[i] == '\001') { goto lCTCP_START; } } /* Handle the end of a packet */ -lPACKET_DONE: + lPACKET_DONE: iCopy = iCopy > maxsize - copyat ? maxsize - copyat : iCopy; memcpy(sptr + copyat, newpacket, iCopy); -/* Save information regarding modified seq and ack numbers */ + /* Save information regarding modified seq and ack numbers */ { int delta; SetAckModified(lnk); - tc = (struct tcphdr *)ip_next(pip); + tc = (struct tcphdr *)ip_next(pip); delta = GetDeltaSeqOut(tc->th_seq, lnk); AddSeq(lnk, delta + copyat + iCopy - dlen, pip->ip_hl, pip->ip_len, tc->th_seq, tc->th_off); } /* Revise IP header */ { u_short new_len; new_len = htons(hlen + iCopy + copyat); DifferentialChecksum(&pip->ip_sum, &new_len, &pip->ip_len, 1); pip->ip_len = new_len; } /* Compute TCP checksum for revised packet */ tc->th_sum = 0; #ifdef _KERNEL tc->th_x2 = 1; #else tc->th_sum = TcpChecksum(pip); #endif return; } } /* Notes: - [Note 1] - The initial search will most often fail; it could be replaced with a 32-bit specific search. - Such a search would be done for 32-bit unsigned value V: - V ^= 0x01010101; (Search is for null bytes) - if( ((V-0x01010101)^V) & 0x80808080 ) { + [Note 1] + The initial search will most often fail; it could be replaced with a 32-bit specific search. + Such a search would be done for 32-bit unsigned value V: + V ^= 0x01010101; (Search is for null bytes) + if( ((V-0x01010101)^V) & 0x80808080 ) { (found a null bytes which was a 01 byte) - } - To assert that the processor is 32-bits, do + } + To assert that the processor is 32-bits, do extern int ircdccar[32]; (32 bits) extern int ircdccar[CHAR_BIT*sizeof(unsigned int)]; - which will generate a type-error on all but 32-bit machines. + which will generate a type-error on all but 32-bit machines. - [Note 2] This routine really ought to be replaced with one that - creates a transparent proxy on the aliasing host, to allow arbitrary - changes in the TCP stream. This should not be too difficult given - this base; I (ee) will try to do this some time later. - */ + [Note 2] This routine really ought to be replaced with one that + creates a transparent proxy on the aliasing host, to allow arbitrary + changes in the TCP stream. This should not be too difficult given + this base; I (ee) will try to do this some time later. +*/ diff --git a/sys/netinet/libalias/alias_local.h b/sys/netinet/libalias/alias_local.h index ba128638c1fe..e6374a72ef75 100644 --- a/sys/netinet/libalias/alias_local.h +++ b/sys/netinet/libalias/alias_local.h @@ -1,415 +1,385 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2001 Charles Mott * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /* * Alias_local.h contains the function prototypes for alias.c, * alias_db.c, alias_util.c and alias_ftp.c, alias_irc.c (as well * as any future add-ons). It also includes macros, globals and * struct definitions shared by more than one alias*.c file. * * This include file is intended to be used only within the aliasing * software. Outside world interfaces are defined in alias.h * * This software is placed into the public domain with no restrictions * on its distribution. * * Initial version: August, 1996 (cjm) * * */ #ifndef _ALIAS_LOCAL_H_ #define _ALIAS_LOCAL_H_ #include #include #ifdef _KERNEL #include #include #include #include /* XXX: LibAliasSetTarget() uses this constant. */ #define INADDR_NONE 0xffffffff #include #else #include "alias_sctp.h" #endif /* Sizes of input and output link tables */ -#define LINK_TABLE_OUT_SIZE 4001 -#define LINK_TABLE_IN_SIZE 4001 +#define LINK_TABLE_OUT_SIZE 4001 +#define LINK_TABLE_IN_SIZE 4001 #define GET_ALIAS_PORT -1 #define GET_ALIAS_ID GET_ALIAS_PORT #ifdef _KERNEL #define INET_NTOA_BUF(buf) (buf) #else #define INET_NTOA_BUF(buf) (buf), sizeof(buf) #endif struct proxy_entry; struct libalias { LIST_ENTRY(libalias) instancelist; - - int packetAliasMode; /* Mode flags */ - /* - documented in alias.h */ - - struct in_addr aliasAddress; /* Address written onto source */ - /* field of IP packet. */ - - struct in_addr targetAddress; /* IP address incoming packets */ - /* are sent to if no aliasing */ - /* link already exists */ - - struct in_addr nullAddress; /* Used as a dummy parameter for */ - /* some function calls */ - - LIST_HEAD (, alias_link) linkTableOut[LINK_TABLE_OUT_SIZE]; - /* Lookup table of pointers to */ - /* chains of link records. Each */ - - LIST_HEAD (, alias_link) linkTableIn[LINK_TABLE_IN_SIZE]; - /* link record is doubly indexed */ - /* into input and output lookup */ - /* tables. */ - - /* Link statistics */ + /* Mode flags documented in alias.h */ + int packetAliasMode; + /* Address written onto source field of IP packet. */ + struct in_addr aliasAddress; + /* IP address incoming packets are sent to + * if no aliasing link already exists */ + struct in_addr targetAddress; + /* Used as a dummy parameter for some function calls */ + struct in_addr nullAddress; + /* Lookup table of pointers to chains of link records. + * Each link record is doubly indexed into input and + * output lookup tables. */ + LIST_HEAD (, alias_link) linkTableOut[LINK_TABLE_OUT_SIZE]; + LIST_HEAD (, alias_link) linkTableIn[LINK_TABLE_IN_SIZE]; + /* Link statistics */ int icmpLinkCount; int udpLinkCount; int tcpLinkCount; int pptpLinkCount; int protoLinkCount; int fragmentIdLinkCount; int fragmentPtrLinkCount; int sockCount; - - int cleanupIndex; /* Index to chain of link table */ - /* being inspected for old links */ - - int timeStamp; /* System time in seconds for */ - /* current packet */ - - int lastCleanupTime; /* Last time - * IncrementalCleanup() */ - /* was called */ - - int deleteAllLinks; /* If equal to zero, DeleteLink() */ - /* will not remove permanent links */ - - /* log descriptor */ -#ifdef _KERNEL - char *logDesc; -#else - FILE *logDesc; + /* Index to chain of link table being inspected for old links */ + int cleanupIndex; + /* System time in seconds for current packet */ + int timeStamp; + /* Last time IncrementalCleanup() was called */ + int lastCleanupTime; + /* If equal to zero, DeleteLink() + * will not remove permanent links */ + int deleteAllLinks; + /* log descriptor */ +#ifdef _KERNEL + char *logDesc; +#else + FILE *logDesc; #endif - /* statistics monitoring */ - - int newDefaultLink; /* Indicates if a new aliasing */ - /* link has been created after a */ - /* call to PacketAliasIn/Out(). */ + /* Indicates if a new aliasing link has been created + * after a call to PacketAliasIn/Out(). */ + int newDefaultLink; #ifndef NO_FW_PUNCH - int fireWallFD; /* File descriptor to be able to */ - /* control firewall. Opened by */ - /* PacketAliasSetMode on first */ - /* setting the PKT_ALIAS_PUNCH_FW */ - /* flag. */ - int fireWallBaseNum; /* The first firewall entry - * free for our use */ - int fireWallNumNums; /* How many entries can we - * use? */ - int fireWallActiveNum; /* Which entry did we last - * use? */ - char *fireWallField; /* bool array for entries */ + /* File descriptor to be able to control firewall. + * Opened by PacketAliasSetMode on first setting + * the PKT_ALIAS_PUNCH_FW flag. */ + int fireWallFD; + /* The first firewall entry free for our use */ + int fireWallBaseNum; + /* How many entries can we use? */ + int fireWallNumNums; + /* Which entry did we last use? */ + int fireWallActiveNum; + /* bool array for entries */ + char *fireWallField; #endif - - unsigned int skinnyPort; /* TCP port used by the Skinny */ - /* protocol. */ + /* TCP port used by the Skinny protocol. */ + unsigned int skinnyPort; struct proxy_entry *proxyList; struct in_addr true_addr; /* in network byte order. */ u_short true_port; /* in host byte order. */ /* Port ranges for aliasing. */ u_short aliasPortLower; u_short aliasPortLength; /* * sctp code support */ /* counts associations that have progressed to UP and not yet removed */ int sctpLinkCount; -#ifdef _KERNEL +#ifdef _KERNEL /* timing queue for keeping track of association timeouts */ struct sctp_nat_timer sctpNatTimer; - /* size of hash table used in this instance */ u_int sctpNatTableSize; - -/* - * local look up table sorted by l_vtag/l_port - */ + /* local look up table sorted by l_vtag/l_port */ LIST_HEAD(sctpNatTableL, sctp_nat_assoc) *sctpTableLocal; -/* - * global look up table sorted by g_vtag/g_port - */ + /* global look up table sorted by g_vtag/g_port */ LIST_HEAD(sctpNatTableG, sctp_nat_assoc) *sctpTableGlobal; - /* - * avoid races in libalias: every public function has to use it. - */ + /* avoid races in libalias: every public function has to use it. */ struct mtx mutex; #endif }; /* Macros */ #ifdef _KERNEL #define LIBALIAS_LOCK_INIT(l) \ - mtx_init(&l->mutex, "per-instance libalias mutex", NULL, MTX_DEF) + mtx_init(&l->mutex, "per-instance libalias mutex", NULL, MTX_DEF) #define LIBALIAS_LOCK_ASSERT(l) mtx_assert(&l->mutex, MA_OWNED) #define LIBALIAS_LOCK(l) mtx_lock(&l->mutex) #define LIBALIAS_UNLOCK(l) mtx_unlock(&l->mutex) #define LIBALIAS_LOCK_DESTROY(l) mtx_destroy(&l->mutex) #else #define LIBALIAS_LOCK_INIT(l) #define LIBALIAS_LOCK_ASSERT(l) #define LIBALIAS_LOCK(l) #define LIBALIAS_UNLOCK(l) #define LIBALIAS_LOCK_DESTROY(l) #endif /* * The following macro is used to update an * internet checksum. "delta" is a 32-bit * accumulation of all the changes to the * checksum (adding in new 16-bit words and * subtracting out old words), and "cksum" * is the checksum value to be updated. */ #define ADJUST_CHECKSUM(acc, cksum) \ do { \ acc += cksum; \ if (acc < 0) { \ acc = -acc; \ acc = (acc >> 16) + (acc & 0xffff); \ acc += acc >> 16; \ cksum = (u_short) ~acc; \ } else { \ acc = (acc >> 16) + (acc & 0xffff); \ acc += acc >> 16; \ cksum = (u_short) acc; \ } \ } while (0) /* Prototypes */ /* * SctpFunction prototypes - * + * */ void AliasSctpInit(struct libalias *la); void AliasSctpTerm(struct libalias *la); int SctpAlias(struct libalias *la, struct ip *ip, int direction); /* * We do not calculate TCP checksums when libalias is a kernel * module, since it has no idea about checksum offloading. * If TCP data has changed, then we just set checksum to zero, * and caller must recalculate it himself. * In case if libalias will edit UDP data, the same approach * should be used. */ #ifndef _KERNEL u_short IpChecksum(struct ip *_pip); u_short TcpChecksum(struct ip *_pip); #endif void DifferentialChecksum(u_short * _cksum, void * _new, void * _old, int _n); /* Internal data access */ struct alias_link * AddLink(struct libalias *la, struct in_addr src_addr, struct in_addr dst_addr, struct in_addr alias_addr, u_short src_port, u_short dst_port, int alias_param, int link_type); struct alias_link * FindIcmpIn(struct libalias *la, struct in_addr _dst_addr, struct in_addr _alias_addr, u_short _id_alias, int _create); struct alias_link * FindIcmpOut(struct libalias *la, struct in_addr _src_addr, struct in_addr _dst_addr, u_short _id, int _create); struct alias_link * FindFragmentIn1(struct libalias *la, struct in_addr _dst_addr, struct in_addr _alias_addr, u_short _ip_id); struct alias_link * FindFragmentIn2(struct libalias *la, struct in_addr _dst_addr, struct in_addr _alias_addr, u_short _ip_id); struct alias_link * - AddFragmentPtrLink(struct libalias *la, struct in_addr _dst_addr, u_short _ip_id); +AddFragmentPtrLink(struct libalias *la, struct in_addr _dst_addr, u_short _ip_id); struct alias_link * - FindFragmentPtr(struct libalias *la, struct in_addr _dst_addr, u_short _ip_id); +FindFragmentPtr(struct libalias *la, struct in_addr _dst_addr, u_short _ip_id); struct alias_link * FindProtoIn(struct libalias *la, struct in_addr _dst_addr, struct in_addr _alias_addr, u_char _proto); struct alias_link * FindProtoOut(struct libalias *la, struct in_addr _src_addr, struct in_addr _dst_addr, u_char _proto); struct alias_link * FindUdpTcpIn(struct libalias *la, struct in_addr _dst_addr, struct in_addr _alias_addr, u_short _dst_port, u_short _alias_port, u_char _proto, int _create); struct alias_link * FindUdpTcpOut(struct libalias *la, struct in_addr _src_addr, struct in_addr _dst_addr, u_short _src_port, u_short _dst_port, u_char _proto, int _create); struct alias_link * AddPptp(struct libalias *la, struct in_addr _src_addr, struct in_addr _dst_addr, struct in_addr _alias_addr, u_int16_t _src_call_id); struct alias_link * FindPptpOutByCallId(struct libalias *la, struct in_addr _src_addr, struct in_addr _dst_addr, u_int16_t _src_call_id); struct alias_link * FindPptpInByCallId(struct libalias *la, struct in_addr _dst_addr, struct in_addr _alias_addr, u_int16_t _dst_call_id); struct alias_link * FindPptpOutByPeerCallId(struct libalias *la, struct in_addr _src_addr, struct in_addr _dst_addr, u_int16_t _dst_call_id); struct alias_link * FindPptpInByPeerCallId(struct libalias *la, struct in_addr _dst_addr, struct in_addr _alias_addr, u_int16_t _alias_call_id); struct alias_link * FindRtspOut(struct libalias *la, struct in_addr _src_addr, struct in_addr _dst_addr, u_short _src_port, u_short _alias_port, u_char _proto); struct in_addr - FindOriginalAddress(struct libalias *la, struct in_addr _alias_addr); +FindOriginalAddress(struct libalias *la, struct in_addr _alias_addr); +struct in_addr +FindAliasAddress(struct libalias *la, struct in_addr _original_addr); struct in_addr - FindAliasAddress(struct libalias *la, struct in_addr _original_addr); -struct in_addr FindSctpRedirectAddress(struct libalias *la, struct sctp_nat_msg *sm); /* External data access/modification */ -int -FindNewPortGroup(struct libalias *la, struct in_addr _dst_addr, struct in_addr _alias_addr, - u_short _src_port, u_short _dst_port, u_short _port_count, - u_char _proto, u_char _align); +int FindNewPortGroup(struct libalias *la, struct in_addr _dst_addr, + struct in_addr _alias_addr, u_short _src_port, + u_short _dst_port, u_short _port_count, u_char _proto, + u_char _align); void GetFragmentAddr(struct alias_link *_lnk, struct in_addr *_src_addr); void SetFragmentAddr(struct alias_link *_lnk, struct in_addr _src_addr); void GetFragmentPtr(struct alias_link *_lnk, void **_fptr); void SetFragmentPtr(struct alias_link *_lnk, void *fptr); void SetStateIn(struct alias_link *_lnk, int _state); void SetStateOut(struct alias_link *_lnk, int _state); int GetStateIn (struct alias_link *_lnk); int GetStateOut(struct alias_link *_lnk); -struct in_addr - GetOriginalAddress(struct alias_link *_lnk); -struct in_addr - GetDestAddress(struct alias_link *_lnk); -struct in_addr - GetAliasAddress(struct alias_link *_lnk); -struct in_addr - GetDefaultAliasAddress(struct libalias *la); +struct in_addr GetOriginalAddress(struct alias_link *_lnk); +struct in_addr GetDestAddress(struct alias_link *_lnk); +struct in_addr GetAliasAddress(struct alias_link *_lnk); +struct in_addr GetDefaultAliasAddress(struct libalias *la); void SetDefaultAliasAddress(struct libalias *la, struct in_addr _alias_addr); u_short GetOriginalPort(struct alias_link *_lnk); u_short GetAliasPort(struct alias_link *_lnk); -struct in_addr - GetProxyAddress(struct alias_link *_lnk); +struct in_addr GetProxyAddress(struct alias_link *_lnk); void SetProxyAddress(struct alias_link *_lnk, struct in_addr _addr); u_short GetProxyPort(struct alias_link *_lnk); void SetProxyPort(struct alias_link *_lnk, u_short _port); void SetAckModified(struct alias_link *_lnk); int GetAckModified(struct alias_link *_lnk); int GetDeltaAckIn(u_long, struct alias_link *_lnk); -int GetDeltaSeqOut(u_long, struct alias_link *lnk); -void AddSeq(struct alias_link *lnk, int delta, u_int ip_hl, +int GetDeltaSeqOut(u_long, struct alias_link *lnk); +void AddSeq(struct alias_link *lnk, int delta, u_int ip_hl, u_short ip_len, u_long th_seq, u_int th_off); void SetExpire (struct alias_link *_lnk, int _expire); void ClearCheckNewLink(struct libalias *la); void SetProtocolFlags(struct alias_link *_lnk, int _pflags); int GetProtocolFlags(struct alias_link *_lnk); void SetDestCallId(struct alias_link *_lnk, u_int16_t _cid); #ifndef NO_FW_PUNCH void PunchFWHole(struct alias_link *_lnk); #endif /* Housekeeping function */ void HouseKeeping(struct libalias *); -/* Tcp specific routines */ -/* lint -save -library Suppress flexelint warnings */ - /* Transparent proxy routines */ int ProxyCheck(struct libalias *la, struct in_addr *proxy_server_addr, - u_short * proxy_server_port, struct in_addr src_addr, + u_short * proxy_server_port, struct in_addr src_addr, struct in_addr dst_addr, u_short dst_port, u_char ip_p); void ProxyModify(struct libalias *la, struct alias_link *_lnk, struct ip *_pip, int _maxpacketsize, int _proxy_type); +/* Tcp specific routines */ +/* lint -save -library Suppress flexelint warnings */ + enum alias_tcp_state { ALIAS_TCP_STATE_NOT_CONNECTED, ALIAS_TCP_STATE_CONNECTED, ALIAS_TCP_STATE_DISCONNECTED }; #if defined(_NETINET_IP_H_) static __inline void * ip_next(struct ip *iphdr) { char *p = (char *)iphdr; return (&p[iphdr->ip_hl * 4]); } #endif #if defined(_NETINET_TCP_H_) static __inline void * tcp_next(struct tcphdr *tcphdr) { char *p = (char *)tcphdr; return (&p[tcphdr->th_off * 4]); } #endif #if defined(_NETINET_UDP_H_) static __inline void * udp_next(struct udphdr *udphdr) { return ((void *)(udphdr + 1)); } #endif #endif /* !_ALIAS_LOCAL_H_ */ diff --git a/sys/netinet/libalias/alias_mod.c b/sys/netinet/libalias/alias_mod.c index 835d76cf5152..dba9a0553af5 100644 --- a/sys/netinet/libalias/alias_mod.c +++ b/sys/netinet/libalias/alias_mod.c @@ -1,174 +1,172 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2005 Paolo Pisati * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #ifdef _KERNEL #include #include #include #include #else #include #include #include #include #endif #include #include #include #ifdef _KERNEL #include #include #else #include "alias_local.h" #include "alias_mod.h" #endif /* Protocol and userland module handlers chains. */ static TAILQ_HEAD(handler_chain, proto_handler) handler_chain = TAILQ_HEAD_INITIALIZER(handler_chain); static int attach_handler(struct proto_handler *p) { struct proto_handler *b; TAILQ_FOREACH(b, &handler_chain, link) { if ((b->pri == p->pri) && (b->dir == p->dir) && (b->proto == p->proto)) return (EEXIST); if (b->pri > p->pri) { TAILQ_INSERT_BEFORE(b, p, link); return (0); } } TAILQ_INSERT_TAIL(&handler_chain, p, link); return (0); } int LibAliasAttachHandlers(struct proto_handler *p) { int error; while (p->dir != NODIR) { error = attach_handler(p); if (error) return (error); p++; } return (0); } /* XXXGL: should be void, but no good reason to break ABI */ int LibAliasDetachHandlers(struct proto_handler *p) { - while (p->dir != NODIR) { TAILQ_REMOVE(&handler_chain, p, link); p++; } return (0); } int find_handler(int8_t dir, int8_t proto, struct libalias *la, struct ip *ip, struct alias_data *ad) { struct proto_handler *p; TAILQ_FOREACH(p, &handler_chain, link) if ((p->dir & dir) && (p->proto & proto) && p->fingerprint(la, ad) == 0) return (p->protohandler(la, ip, ad)); return (ENOENT); } struct proto_handler * first_handler(void) { - return (TAILQ_FIRST(&handler_chain)); } #ifndef _KERNEL /* Dll manipulation code - this code is not thread safe... */ static SLIST_HEAD(dll_chain, dll) dll_chain = SLIST_HEAD_INITIALIZER(dll_chain); int attach_dll(struct dll *p) { struct dll *b; SLIST_FOREACH(b, &dll_chain, next) { if (!strncmp(b->name, p->name, DLL_LEN)) return (EEXIST); /* Dll name conflict. */ } SLIST_INSERT_HEAD(&dll_chain, p, next); return (0); } void * detach_dll(char *p) { struct dll *b, *b_tmp; void *error; b = NULL; error = NULL; SLIST_FOREACH_SAFE(b, &dll_chain, next, b_tmp) if (!strncmp(b->name, p, DLL_LEN)) { SLIST_REMOVE(&dll_chain, b, dll, next); error = b; break; } return (error); } struct dll * walk_dll_chain(void) { struct dll *t; t = SLIST_FIRST(&dll_chain); if (t == NULL) return (NULL); SLIST_REMOVE_HEAD(&dll_chain, next); return (t); } #endif /* !_KERNEL */ diff --git a/sys/netinet/libalias/alias_nbt.c b/sys/netinet/libalias/alias_nbt.c index e29dc4789248..ab4bd818e1a5 100644 --- a/sys/netinet/libalias/alias_nbt.c +++ b/sys/netinet/libalias/alias_nbt.c @@ -1,868 +1,843 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Written by Atsushi Murai * Copyright (c) 1998, System Planning and Engineering Co. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * TODO: * oClean up. * oConsidering for word alignment for other platform. */ #include __FBSDID("$FreeBSD$"); /* alias_nbt.c performs special processing for NetBios over TCP/IP sessions by UDP. Initial version: May, 1998 (Atsushi Murai ) See HISTORY file for record of revisions. */ /* Includes */ #ifdef _KERNEL #include #include #include #include #else #include #include #include #include #endif #include #include #include #include #ifdef _KERNEL #include #include #else #include "alias_local.h" #include "alias_mod.h" #endif #define NETBIOS_NS_PORT_NUMBER 137 #define NETBIOS_DGM_PORT_NUMBER 138 static int AliasHandleUdpNbt(struct libalias *, struct ip *, struct alias_link *, - struct in_addr *, u_short); - + struct in_addr *, u_short); static int AliasHandleUdpNbtNS(struct libalias *, struct ip *, struct alias_link *, - struct in_addr *, u_short *, struct in_addr *, u_short *); + struct in_addr *, u_short *, struct in_addr *, u_short *); + static int fingerprint1(struct libalias *la, struct alias_data *ah) { - if (ah->dport == NULL || ah->sport == NULL || ah->lnk == NULL || ah->aaddr == NULL || ah->aport == NULL) return (-1); if (ntohs(*ah->dport) == NETBIOS_DGM_PORT_NUMBER - || ntohs(*ah->sport) == NETBIOS_DGM_PORT_NUMBER) + || ntohs(*ah->sport) == NETBIOS_DGM_PORT_NUMBER) return (0); return (-1); } static int protohandler1(struct libalias *la, struct ip *pip, struct alias_data *ah) { - return (AliasHandleUdpNbt(la, pip, ah->lnk, ah->aaddr, *ah->aport)); } static int fingerprint2(struct libalias *la, struct alias_data *ah) { - if (ah->dport == NULL || ah->sport == NULL || ah->lnk == NULL || ah->aaddr == NULL || ah->aport == NULL) return (-1); if (ntohs(*ah->dport) == NETBIOS_NS_PORT_NUMBER || ntohs(*ah->sport) == NETBIOS_NS_PORT_NUMBER) return (0); return (-1); } static int protohandler2in(struct libalias *la, struct ip *pip, struct alias_data *ah) { - AliasHandleUdpNbtNS(la, pip, ah->lnk, ah->aaddr, ah->aport, - ah->oaddr, ah->dport); + ah->oaddr, ah->dport); return (0); } static int protohandler2out(struct libalias *la, struct ip *pip, struct alias_data *ah) { - return (AliasHandleUdpNbtNS(la, pip, ah->lnk, &pip->ip_src, ah->sport, - ah->aaddr, ah->aport)); + ah->aaddr, ah->aport)); } /* Kernel module definition. */ struct proto_handler handlers[] = { { .pri = 130, .dir = IN|OUT, .proto = UDP, .fingerprint = &fingerprint1, .protohandler = &protohandler1 }, { .pri = 140, .dir = IN, .proto = UDP, .fingerprint = &fingerprint2, .protohandler = &protohandler2in }, { .pri = 140, .dir = OUT, .proto = UDP, .fingerprint = &fingerprint2, .protohandler = &protohandler2out }, { EOH } }; static int mod_handler(module_t mod, int type, void *data) { int error; switch (type) { case MOD_LOAD: error = 0; LibAliasAttachHandlers(handlers); break; case MOD_UNLOAD: error = 0; LibAliasDetachHandlers(handlers); break; default: error = EINVAL; } return (error); } -#ifdef _KERNEL +#ifdef _KERNEL static #endif moduledata_t alias_mod = { "alias_nbt", mod_handler, NULL }; -#ifdef _KERNEL +#ifdef _KERNEL DECLARE_MODULE(alias_nbt, alias_mod, SI_SUB_DRIVERS, SI_ORDER_SECOND); MODULE_VERSION(alias_nbt, 1); MODULE_DEPEND(alias_nbt, libalias, 1, 1, 1); #endif typedef struct { struct in_addr oldaddr; u_short oldport; struct in_addr newaddr; u_short newport; - u_short *uh_sum; -} NBTArguments; + u_short *uh_sum; +} NBTArguments; typedef struct { unsigned char type; unsigned char flags; u_short id; struct in_addr source_ip; u_short source_port; u_short len; u_short offset; -} NbtDataHeader; +} NbtDataHeader; #define OpQuery 0 #define OpUnknown 4 #define OpRegist 5 #define OpRelease 6 #define OpWACK 7 #define OpRefresh 8 typedef struct { u_short nametrid; - u_short dir: 1, opcode:4, nmflags:7, rcode:4; + u_short dir:1, opcode:4, nmflags:7, rcode:4; u_short qdcount; u_short ancount; u_short nscount; u_short arcount; -} NbtNSHeader; +} NbtNSHeader; #define FMT_ERR 0x1 #define SRV_ERR 0x2 #define IMP_ERR 0x4 #define RFS_ERR 0x5 #define ACT_ERR 0x6 #define CFT_ERR 0x7 #ifdef LIBALIAS_DEBUG static void PrintRcode(u_char rcode) { - switch (rcode) { - case FMT_ERR: + case FMT_ERR: printf("\nFormat Error."); case SRV_ERR: printf("\nSever failure."); case IMP_ERR: printf("\nUnsupported request error.\n"); case RFS_ERR: printf("\nRefused error.\n"); case ACT_ERR: printf("\nActive error.\n"); case CFT_ERR: printf("\nName in conflict error.\n"); default: printf("\n?%c?=%0x\n", '?', rcode); } } #endif /* Handling Name field */ -static u_char * -AliasHandleName(u_char * p, char *pmax) +static u_char * +AliasHandleName(u_char *p, char *pmax) { - u_char *s; u_char c; int compress; /* Following length field */ if (p == NULL || (char *)p >= pmax) return (NULL); if (*p & 0xc0) { p = p + 2; if ((char *)p > pmax) return (NULL); - return ((u_char *) p); + return ((u_char *)p); } while ((*p & 0x3f) != 0x00) { s = p + 1; if (*p == 0x20) compress = 1; else compress = 0; /* Get next length field */ - p = (u_char *) (p + (*p & 0x3f) + 1); + p = (u_char *)(p + (*p & 0x3f) + 1); if ((char *)p > pmax) { p = NULL; break; } #ifdef LIBALIAS_DEBUG printf(":"); #endif while (s < p) { if (compress == 1) { c = (u_char) (((((*s & 0x0f) << 4) | (*(s + 1) & 0x0f)) - 0x11)); #ifdef LIBALIAS_DEBUG if (isprint(c)) printf("%c", c); else printf("<0x%02x>", c); #endif s += 2; } else { #ifdef LIBALIAS_DEBUG printf("%c", *s); #endif s++; } } #ifdef LIBALIAS_DEBUG printf(":"); fflush(stdout); #endif } /* Set up to out of Name field */ if (p == NULL || (char *)p >= pmax) p = NULL; else p++; - return ((u_char *) p); + return ((u_char *)p); } /* * NetBios Datagram Handler (IP/UDP) */ #define DGM_DIRECT_UNIQ 0x10 #define DGM_DIRECT_GROUP 0x11 #define DGM_BROADCAST 0x12 -#define DGM_ERROR 0x13 -#define DGM_QUERY 0x14 +#define DGM_ERROR 0x13 +#define DGM_QUERY 0x14 #define DGM_POSITIVE_RES 0x15 #define DGM_NEGATIVE_RES 0x16 static int AliasHandleUdpNbt( - struct libalias *la, - struct ip *pip, /* IP packet to examine/patch */ + struct libalias *la, + struct ip *pip, /* IP packet to examine/patch */ struct alias_link *lnk, - struct in_addr *alias_address, - u_short alias_port -) + struct in_addr *alias_address, + u_short alias_port) { struct udphdr *uh; NbtDataHeader *ndh; u_char *p = NULL; char *pmax; #ifdef LIBALIAS_DEBUG char addrbuf[INET_ADDRSTRLEN]; #endif (void)la; (void)lnk; /* Calculate data length of UDP packet */ uh = (struct udphdr *)ip_next(pip); pmax = (char *)uh + ntohs(uh->uh_ulen); ndh = (NbtDataHeader *)udp_next(uh); if ((char *)(ndh + 1) > pmax) return (-1); #ifdef LIBALIAS_DEBUG printf("\nType=%02x,", ndh->type); #endif switch (ndh->type) { case DGM_DIRECT_UNIQ: case DGM_DIRECT_GROUP: case DGM_BROADCAST: - p = (u_char *) ndh + 14; + p = (u_char *)ndh + 14; p = AliasHandleName(p, pmax); /* Source Name */ p = AliasHandleName(p, pmax); /* Destination Name */ break; case DGM_ERROR: - p = (u_char *) ndh + 11; + p = (u_char *)ndh + 11; break; case DGM_QUERY: case DGM_POSITIVE_RES: case DGM_NEGATIVE_RES: - p = (u_char *) ndh + 10; + p = (u_char *)ndh + 10; p = AliasHandleName(p, pmax); /* Destination Name */ break; } if (p == NULL || (char *)p > pmax) p = NULL; #ifdef LIBALIAS_DEBUG printf("%s:%d-->", inet_ntoa_r(ndh->source_ip, INET_NTOA_BUF(addrbuf)), ntohs(ndh->source_port)); #endif /* Doing an IP address and Port number Translation */ if (uh->uh_sum != 0) { int acc; u_short *sptr; acc = ndh->source_port; acc -= alias_port; - sptr = (u_short *) & (ndh->source_ip); + sptr = (u_short *)&(ndh->source_ip); acc += *sptr++; acc += *sptr; - sptr = (u_short *) alias_address; + sptr = (u_short *)alias_address; acc -= *sptr++; acc -= *sptr; ADJUST_CHECKSUM(acc, uh->uh_sum); } ndh->source_ip = *alias_address; ndh->source_port = alias_port; #ifdef LIBALIAS_DEBUG printf("%s:%d\n", inet_ntoa_r(ndh->source_ip, INET_NTOA_BUF(addrbuf)), ntohs(ndh->source_port)); fflush(stdout); #endif return ((p == NULL) ? -1 : 0); } /* Question Section */ -#define QS_TYPE_NB 0x0020 +#define QS_TYPE_NB 0x0020 #define QS_TYPE_NBSTAT 0x0021 -#define QS_CLAS_IN 0x0001 +#define QS_CLAS_IN 0x0001 typedef struct { u_short type; /* The type of Request */ u_short class; /* The class of Request */ -} NBTNsQuestion; +} NBTNsQuestion; -static u_char * +static u_char * AliasHandleQuestion( u_short count, NBTNsQuestion * q, char *pmax, NBTArguments * nbtarg) { - (void)nbtarg; while (count != 0) { /* Name Filed */ - q = (NBTNsQuestion *) AliasHandleName((u_char *) q, pmax); + q = (NBTNsQuestion *)AliasHandleName((u_char *)q, pmax); if (q == NULL || (char *)(q + 1) > pmax) { q = NULL; break; } /* Type and Class filed */ switch (ntohs(q->type)) { case QS_TYPE_NB: case QS_TYPE_NBSTAT: q = q + 1; break; default: #ifdef LIBALIAS_DEBUG printf("\nUnknown Type on Question %0x\n", ntohs(q->type)); #endif break; } count--; } /* Set up to out of Question Section */ - return ((u_char *) q); + return ((u_char *)q); } /* Resource Record */ -#define RR_TYPE_A 0x0001 -#define RR_TYPE_NS 0x0002 +#define RR_TYPE_A 0x0001 +#define RR_TYPE_NS 0x0002 #define RR_TYPE_NULL 0x000a -#define RR_TYPE_NB 0x0020 +#define RR_TYPE_NB 0x0020 #define RR_TYPE_NBSTAT 0x0021 -#define RR_CLAS_IN 0x0001 +#define RR_CLAS_IN 0x0001 #define SizeOfNsResource 8 typedef struct { u_short type; u_short class; unsigned int ttl; u_short rdlen; -} NBTNsResource; +} NBTNsResource; -#define SizeOfNsRNB 6 +#define SizeOfNsRNB 6 typedef struct { - u_short g: 1 , ont:2, resv:13; + u_short g:1, ont:2, resv:13; struct in_addr addr; -} NBTNsRNB; +} NBTNsRNB; -static u_char * +static u_char * AliasHandleResourceNB( - NBTNsResource * q, - char *pmax, - NBTArguments * nbtarg) + NBTNsResource *q, + char *pmax, + NBTArguments *nbtarg) { NBTNsRNB *nb; u_short bcount; #ifdef LIBALIAS_DEBUG char oldbuf[INET_ADDRSTRLEN]; char newbuf[INET_ADDRSTRLEN]; #endif if (q == NULL || (char *)(q + 1) > pmax) return (NULL); /* Check out a length */ bcount = ntohs(q->rdlen); /* Forward to Resource NB position */ - nb = (NBTNsRNB *) ((u_char *) q + SizeOfNsResource); + nb = (NBTNsRNB *)((u_char *)q + SizeOfNsResource); /* Processing all in_addr array */ #ifdef LIBALIAS_DEBUG printf("NB rec[%s->%s, %dbytes] ", inet_ntoa_r(nbtarg->oldaddr, INET_NTOA_BUF(oldbuf)), inet_ntoa_r(nbtarg->newaddr, INET_NTOA_BUF(newbuf)), bcount); #endif while (nb != NULL && bcount != 0) { if ((char *)(nb + 1) > pmax) { nb = NULL; break; } #ifdef LIBALIAS_DEBUG printf("<%s>", inet_ntoa_r(nb->addr, INET_NTOA_BUF(newbuf))); #endif if (!bcmp(&nbtarg->oldaddr, &nb->addr, sizeof(struct in_addr))) { if (*nbtarg->uh_sum != 0) { int acc; u_short *sptr; - sptr = (u_short *) & (nb->addr); + sptr = (u_short *)&(nb->addr); acc = *sptr++; acc += *sptr; - sptr = (u_short *) & (nbtarg->newaddr); + sptr = (u_short *)&(nbtarg->newaddr); acc -= *sptr++; acc -= *sptr; ADJUST_CHECKSUM(acc, *nbtarg->uh_sum); } nb->addr = nbtarg->newaddr; #ifdef LIBALIAS_DEBUG printf("O"); #endif } #ifdef LIBALIAS_DEBUG else { printf("."); } #endif - nb = (NBTNsRNB *) ((u_char *) nb + SizeOfNsRNB); + nb = (NBTNsRNB *)((u_char *)nb + SizeOfNsRNB); bcount -= SizeOfNsRNB; } if (nb == NULL || (char *)(nb + 1) > pmax) { nb = NULL; } - return ((u_char *) nb); + return ((u_char *)nb); } #define SizeOfResourceA 6 typedef struct { struct in_addr addr; -} NBTNsResourceA; +} NBTNsResourceA; -static u_char * +static u_char * AliasHandleResourceA( - NBTNsResource * q, - char *pmax, - NBTArguments * nbtarg) + NBTNsResource *q, + char *pmax, + NBTArguments *nbtarg) { NBTNsResourceA *a; u_short bcount; #ifdef LIBALIAS_DEBUG char oldbuf[INET_ADDRSTRLEN]; char newbuf[INET_ADDRSTRLEN]; #endif if (q == NULL || (char *)(q + 1) > pmax) return (NULL); /* Forward to Resource A position */ - a = (NBTNsResourceA *) ((u_char *) q + sizeof(NBTNsResource)); + a = (NBTNsResourceA *)((u_char *)q + sizeof(NBTNsResource)); /* Check out of length */ bcount = ntohs(q->rdlen); /* Processing all in_addr array */ #ifdef LIBALIAS_DEBUG printf("Arec [%s->%s]", inet_ntoa_r(nbtarg->oldaddr, INET_NTOA_BUF(oldbuf)), inet_ntoa_r(nbtarg->newaddr, INET_NTOA_BUF(newbuf))); #endif while (bcount != 0) { if (a == NULL || (char *)(a + 1) > pmax) return (NULL); #ifdef LIBALIAS_DEBUG printf("..%s", inet_ntoa_r(a->addr, INET_NTOA_BUF(newbuf))); #endif if (!bcmp(&nbtarg->oldaddr, &a->addr, sizeof(struct in_addr))) { if (*nbtarg->uh_sum != 0) { int acc; u_short *sptr; - sptr = (u_short *) & (a->addr); /* Old */ + sptr = (u_short *)&(a->addr); /* Old */ acc = *sptr++; acc += *sptr; - sptr = (u_short *) & nbtarg->newaddr; /* New */ + sptr = (u_short *)&nbtarg->newaddr; /* New */ acc -= *sptr++; acc -= *sptr; ADJUST_CHECKSUM(acc, *nbtarg->uh_sum); } a->addr = nbtarg->newaddr; } a++; /* XXXX */ bcount -= SizeOfResourceA; } if (a == NULL || (char *)(a + 1) > pmax) a = NULL; - return ((u_char *) a); + return ((u_char *)a); } typedef struct { u_short opcode:4, flags:8, resv:4; -} NBTNsResourceNULL; +} NBTNsResourceNULL; -static u_char * +static u_char * AliasHandleResourceNULL( - NBTNsResource * q, - char *pmax, - NBTArguments * nbtarg) + NBTNsResource *q, + char *pmax, + NBTArguments *nbtarg) { NBTNsResourceNULL *n; u_short bcount; (void)nbtarg; if (q == NULL || (char *)(q + 1) > pmax) return (NULL); /* Forward to Resource NULL position */ - n = (NBTNsResourceNULL *) ((u_char *) q + sizeof(NBTNsResource)); + n = (NBTNsResourceNULL *)((u_char *)q + sizeof(NBTNsResource)); /* Check out of length */ bcount = ntohs(q->rdlen); /* Processing all in_addr array */ while (bcount != 0) { if ((char *)(n + 1) > pmax) { n = NULL; break; } n++; bcount -= sizeof(NBTNsResourceNULL); } if ((char *)(n + 1) > pmax) n = NULL; - return ((u_char *) n); + return ((u_char *)n); } -static u_char * +static u_char * AliasHandleResourceNS( - NBTNsResource * q, - char *pmax, - NBTArguments * nbtarg) + NBTNsResource *q, + char *pmax, + NBTArguments *nbtarg) { NBTNsResourceNULL *n; u_short bcount; (void)nbtarg; if (q == NULL || (char *)(q + 1) > pmax) return (NULL); /* Forward to Resource NULL position */ - n = (NBTNsResourceNULL *) ((u_char *) q + sizeof(NBTNsResource)); + n = (NBTNsResourceNULL *)((u_char *)q + sizeof(NBTNsResource)); /* Check out of length */ bcount = ntohs(q->rdlen); /* Resource Record Name Filed */ - q = (NBTNsResource *) AliasHandleName((u_char *) n, pmax); /* XXX */ + q = (NBTNsResource *)AliasHandleName((u_char *)n, pmax); /* XXX */ - if (q == NULL || (char *)((u_char *) n + bcount) > pmax) + if (q == NULL || (char *)((u_char *)n + bcount) > pmax) return (NULL); else - return ((u_char *) n + bcount); + return ((u_char *)n + bcount); } typedef struct { u_short numnames; -} NBTNsResourceNBSTAT; +} NBTNsResourceNBSTAT; -static u_char * +static u_char * AliasHandleResourceNBSTAT( - NBTNsResource * q, - char *pmax, - NBTArguments * nbtarg) + NBTNsResource *q, + char *pmax, + NBTArguments *nbtarg) { NBTNsResourceNBSTAT *n; u_short bcount; (void)nbtarg; if (q == NULL || (char *)(q + 1) > pmax) return (NULL); /* Forward to Resource NBSTAT position */ - n = (NBTNsResourceNBSTAT *) ((u_char *) q + sizeof(NBTNsResource)); + n = (NBTNsResourceNBSTAT *)((u_char *)q + sizeof(NBTNsResource)); /* Check out of length */ bcount = ntohs(q->rdlen); - if (q == NULL || (char *)((u_char *) n + bcount) > pmax) + if (q == NULL || (char *)((u_char *)n + bcount) > pmax) return (NULL); else - return ((u_char *) n + bcount); + return ((u_char *)n + bcount); } -static u_char * +static u_char * AliasHandleResource( - u_short count, - NBTNsResource * q, - char *pmax, - NBTArguments - * nbtarg) + u_short count, + NBTNsResource *q, + char *pmax, + NBTArguments *nbtarg) { while (count != 0) { /* Resource Record Name Filed */ - q = (NBTNsResource *) AliasHandleName((u_char *) q, pmax); + q = (NBTNsResource *)AliasHandleName((u_char *)q, pmax); if (q == NULL || (char *)(q + 1) > pmax) break; #ifdef LIBALIAS_DEBUG printf("type=%02x, count=%d\n", ntohs(q->type), count); #endif /* Type and Class filed */ switch (ntohs(q->type)) { case RR_TYPE_NB: - q = (NBTNsResource *) AliasHandleResourceNB( - q, - pmax, - nbtarg - ); + q = (NBTNsResource *)AliasHandleResourceNB( + q, pmax, nbtarg); break; case RR_TYPE_A: - q = (NBTNsResource *) AliasHandleResourceA( - q, - pmax, - nbtarg - ); + q = (NBTNsResource *)AliasHandleResourceA( + q, pmax, nbtarg); break; case RR_TYPE_NS: - q = (NBTNsResource *) AliasHandleResourceNS( - q, - pmax, - nbtarg - ); + q = (NBTNsResource *)AliasHandleResourceNS( + q, pmax, nbtarg); break; case RR_TYPE_NULL: - q = (NBTNsResource *) AliasHandleResourceNULL( - q, - pmax, - nbtarg - ); + q = (NBTNsResource *)AliasHandleResourceNULL( + q, pmax, nbtarg); break; case RR_TYPE_NBSTAT: - q = (NBTNsResource *) AliasHandleResourceNBSTAT( - q, - pmax, - nbtarg - ); + q = (NBTNsResource *)AliasHandleResourceNBSTAT( + q, pmax, nbtarg); break; default: #ifdef LIBALIAS_DEBUG printf( "\nUnknown Type of Resource %0x\n", ntohs(q->type) ); fflush(stdout); #endif break; } count--; } - return ((u_char *) q); + return ((u_char *)q); } static int AliasHandleUdpNbtNS( - struct libalias *la, - struct ip *pip, /* IP packet to examine/patch */ + struct libalias *la, + struct ip *pip, /* IP packet to examine/patch */ struct alias_link *lnk, - struct in_addr *alias_address, - u_short * alias_port, - struct in_addr *original_address, - u_short * original_port) + struct in_addr *alias_address, + u_short *alias_port, + struct in_addr *original_address, + u_short *original_port) { struct udphdr *uh; NbtNSHeader *nsh; u_char *p; char *pmax; NBTArguments nbtarg; (void)la; (void)lnk; /* Set up Common Parameter */ nbtarg.oldaddr = *alias_address; nbtarg.oldport = *alias_port; nbtarg.newaddr = *original_address; nbtarg.newport = *original_port; /* Calculate data length of UDP packet */ uh = (struct udphdr *)ip_next(pip); nbtarg.uh_sum = &(uh->uh_sum); nsh = (NbtNSHeader *)udp_next(uh); - p = (u_char *) (nsh + 1); + p = (u_char *)(nsh + 1); pmax = (char *)uh + ntohs(uh->uh_ulen); if ((char *)(nsh + 1) > pmax) return (-1); #ifdef LIBALIAS_DEBUG printf(" [%s] ID=%02x, op=%01x, flag=%02x, rcode=%01x, qd=%04x" ", an=%04x, ns=%04x, ar=%04x, [%d]-->", nsh->dir ? "Response" : "Request", nsh->nametrid, nsh->opcode, nsh->nmflags, nsh->rcode, ntohs(nsh->qdcount), ntohs(nsh->ancount), ntohs(nsh->nscount), ntohs(nsh->arcount), - (u_char *) p - (u_char *) nsh + (u_char *)p - (u_char *)nsh ); #endif /* Question Entries */ if (ntohs(nsh->qdcount) != 0) { p = AliasHandleQuestion( ntohs(nsh->qdcount), - (NBTNsQuestion *) p, + (NBTNsQuestion *)p, pmax, &nbtarg ); } /* Answer Resource Records */ if (ntohs(nsh->ancount) != 0) { p = AliasHandleResource( ntohs(nsh->ancount), - (NBTNsResource *) p, + (NBTNsResource *)p, pmax, &nbtarg ); } /* Authority Resource Recodrs */ if (ntohs(nsh->nscount) != 0) { p = AliasHandleResource( ntohs(nsh->nscount), - (NBTNsResource *) p, + (NBTNsResource *)p, pmax, &nbtarg ); } /* Additional Resource Recodrs */ if (ntohs(nsh->arcount) != 0) { p = AliasHandleResource( ntohs(nsh->arcount), - (NBTNsResource *) p, + (NBTNsResource *)p, pmax, &nbtarg ); } #ifdef LIBALIAS_DEBUG PrintRcode(nsh->rcode); #endif return ((p == NULL) ? -1 : 0); } diff --git a/sys/netinet/libalias/alias_pptp.c b/sys/netinet/libalias/alias_pptp.c index b28462ce5f75..a43508b502a4 100644 --- a/sys/netinet/libalias/alias_pptp.c +++ b/sys/netinet/libalias/alias_pptp.c @@ -1,522 +1,516 @@ /* * alias_pptp.c * * Copyright (c) 2000 Whistle Communications, Inc. * All rights reserved. * * Subject to the following obligations and disclaimer of warranty, use and * redistribution of this software, in source or object code forms, with or * without modifications are expressly permitted by Whistle Communications; * provided, however, that: * 1. Any and all reproductions of the source or object code must include the * copyright notice above and the following disclaimer of warranties; and * 2. No rights are granted, in any manner or form, to use Whistle * Communications, Inc. trademarks, including the mark "WHISTLE * COMMUNICATIONS" on advertising, endorsements, or otherwise except as * such appears in the above copyright notice or in the software. * * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY * OF SUCH DAMAGE. * * Author: Erik Salander */ #include __FBSDID("$FreeBSD$"); /* Includes */ #ifdef _KERNEL #include #include #include #include #else #include #include #include #include #endif #include #ifdef _KERNEL #include #include #include #else #include "alias.h" #include "alias_local.h" #include "alias_mod.h" #endif #define PPTP_CONTROL_PORT_NUMBER 1723 static void AliasHandlePptpOut(struct libalias *, struct ip *, struct alias_link *); static void AliasHandlePptpIn(struct libalias *, struct ip *, struct alias_link *); static int AliasHandlePptpGreOut(struct libalias *, struct ip *); static int AliasHandlePptpGreIn(struct libalias *, struct ip *); static int fingerprint(struct libalias *la, struct alias_data *ah) { - if (ah->dport == NULL || ah->sport == NULL || ah->lnk == NULL) return (-1); if (ntohs(*ah->dport) == PPTP_CONTROL_PORT_NUMBER || ntohs(*ah->sport) == PPTP_CONTROL_PORT_NUMBER) return (0); return (-1); } static int fingerprintgre(struct libalias *la, struct alias_data *ah) { - return (0); } static int protohandlerin(struct libalias *la, struct ip *pip, struct alias_data *ah) { - AliasHandlePptpIn(la, pip, ah->lnk); return (0); } static int protohandlerout(struct libalias *la, struct ip *pip, struct alias_data *ah) { - AliasHandlePptpOut(la, pip, ah->lnk); return (0); } static int protohandlergrein(struct libalias *la, struct ip *pip, struct alias_data *ah) { - if (la->packetAliasMode & PKT_ALIAS_PROXY_ONLY || AliasHandlePptpGreIn(la, pip) == 0) return (0); return (-1); } static int protohandlergreout(struct libalias *la, struct ip *pip, struct alias_data *ah) { - if (AliasHandlePptpGreOut(la, pip) == 0) return (0); return (-1); } /* Kernel module definition. */ struct proto_handler handlers[] = { { .pri = 200, .dir = IN, .proto = TCP, .fingerprint = &fingerprint, .protohandler = &protohandlerin }, { .pri = 210, .dir = OUT, .proto = TCP, .fingerprint = &fingerprint, .protohandler = &protohandlerout }, /* * WATCH OUT!!! these 2 handlers NEED a priority of INT_MAX (highest possible) * cause they will ALWAYS process packets, so they must be the last one * in chain: look fingerprintgre() above. */ { .pri = INT_MAX, .dir = IN, .proto = IP, .fingerprint = &fingerprintgre, .protohandler = &protohandlergrein }, { .pri = INT_MAX, .dir = OUT, .proto = IP, .fingerprint = &fingerprintgre, .protohandler = &protohandlergreout }, { EOH } }; static int mod_handler(module_t mod, int type, void *data) { int error; switch (type) { case MOD_LOAD: error = 0; LibAliasAttachHandlers(handlers); break; case MOD_UNLOAD: error = 0; LibAliasDetachHandlers(handlers); break; default: error = EINVAL; } return (error); } #ifdef _KERNEL static #endif moduledata_t alias_mod = { "alias_pptp", mod_handler, NULL }; -#ifdef _KERNEL +#ifdef _KERNEL DECLARE_MODULE(alias_pptp, alias_mod, SI_SUB_DRIVERS, SI_ORDER_SECOND); MODULE_VERSION(alias_pptp, 1); MODULE_DEPEND(alias_pptp, libalias, 1, 1, 1); #endif /* Alias_pptp.c performs special processing for PPTP sessions under TCP. Specifically, watch PPTP control messages and alias the Call ID or the Peer's Call ID in the appropriate messages. Note, PPTP requires "de-aliasing" of incoming packets, this is different than any other TCP applications that are currently (ie. FTP, IRC and RTSP) aliased. For Call IDs encountered for the first time, a PPTP alias link is created. The PPTP alias link uses the Call ID in place of the original port number. An alias Call ID is created. For this routine to work, the PPTP control messages must fit entirely into a single TCP packet. This is typically the case, but is not required by the spec. Unlike some of the other TCP applications that are aliased (ie. FTP, IRC and RTSP), the PPTP control messages that need to be aliased are guaranteed to remain the same length. The aliased Call ID is a fixed length field. Reference: RFC 2637 Initial version: May, 2000 (eds) - */ /* * PPTP definitions */ -struct grehdr { /* Enhanced GRE header. */ +struct grehdr { /* Enhanced GRE header. */ u_int16_t gh_flags; /* Flags. */ u_int16_t gh_protocol; /* Protocol type. */ u_int16_t gh_length; /* Payload length. */ u_int16_t gh_call_id; /* Call ID. */ u_int32_t gh_seq_no; /* Sequence number (optional). */ u_int32_t gh_ack_no; /* Acknowledgment number * (optional). */ }; typedef struct grehdr GreHdr; /* The PPTP protocol ID used in the GRE 'proto' field. */ #define PPTP_GRE_PROTO 0x880b /* Bits that must be set a certain way in all PPTP/GRE packets. */ #define PPTP_INIT_VALUE ((0x2001 << 16) | PPTP_GRE_PROTO) #define PPTP_INIT_MASK 0xef7fffff #define PPTP_MAGIC 0x1a2b3c4d #define PPTP_CTRL_MSG_TYPE 1 enum { PPTP_StartCtrlConnRequest = 1, PPTP_StartCtrlConnReply = 2, PPTP_StopCtrlConnRequest = 3, PPTP_StopCtrlConnReply = 4, PPTP_EchoRequest = 5, PPTP_EchoReply = 6, PPTP_OutCallRequest = 7, PPTP_OutCallReply = 8, PPTP_InCallRequest = 9, PPTP_InCallReply = 10, PPTP_InCallConn = 11, PPTP_CallClearRequest = 12, PPTP_CallDiscNotify = 13, PPTP_WanErrorNotify = 14, PPTP_SetLinkInfo = 15 }; - /* Message structures */ +/* Message structures */ struct pptpMsgHead { u_int16_t length; /* total length */ u_int16_t msgType;/* PPTP message type */ u_int32_t magic; /* magic cookie */ u_int16_t type; /* control message type */ u_int16_t resv0; /* reserved */ }; typedef struct pptpMsgHead *PptpMsgHead; struct pptpCodes { u_int8_t resCode;/* Result Code */ u_int8_t errCode;/* Error Code */ }; typedef struct pptpCodes *PptpCode; struct pptpCallIds { u_int16_t cid1; /* Call ID field #1 */ u_int16_t cid2; /* Call ID field #2 */ }; typedef struct pptpCallIds *PptpCallId; static PptpCallId AliasVerifyPptp(struct ip *, u_int16_t *); static void AliasHandlePptpOut(struct libalias *la, struct ip *pip, /* IP packet to examine/patch */ - struct alias_link *lnk) -{ /* The PPTP control link */ + struct alias_link *lnk) /* The PPTP control link */ +{ struct alias_link *pptp_lnk; PptpCallId cptr; PptpCode codes; u_int16_t ctl_type; /* control message type */ struct tcphdr *tc; /* Verify valid PPTP control message */ if ((cptr = AliasVerifyPptp(pip, &ctl_type)) == NULL) return; /* Modify certain PPTP messages */ switch (ctl_type) { case PPTP_OutCallRequest: case PPTP_OutCallReply: case PPTP_InCallRequest: case PPTP_InCallReply: /* * Establish PPTP link for address and Call ID found in * control message. */ pptp_lnk = AddPptp(la, GetOriginalAddress(lnk), GetDestAddress(lnk), GetAliasAddress(lnk), cptr->cid1); break; case PPTP_CallClearRequest: case PPTP_CallDiscNotify: /* * Find PPTP link for address and Call ID found in control * message. */ pptp_lnk = FindPptpOutByCallId(la, GetOriginalAddress(lnk), - GetDestAddress(lnk), - cptr->cid1); + GetDestAddress(lnk), cptr->cid1); break; default: return; } if (pptp_lnk != NULL) { int accumulate = cptr->cid1; /* alias the Call Id */ cptr->cid1 = GetAliasPort(pptp_lnk); /* Compute TCP checksum for revised packet */ tc = (struct tcphdr *)ip_next(pip); accumulate -= cptr->cid1; ADJUST_CHECKSUM(accumulate, tc->th_sum); switch (ctl_type) { case PPTP_OutCallReply: case PPTP_InCallReply: - codes = (PptpCode) (cptr + 1); - if (codes->resCode == 1) /* Connection - * established, */ - SetDestCallId(pptp_lnk, /* note the Peer's Call - * ID. */ - cptr->cid2); + codes = (PptpCode)(cptr + 1); + if (codes->resCode == 1) + /* Connection established, + * note the Peer's Call ID. */ + SetDestCallId(pptp_lnk, cptr->cid2); else - SetExpire(pptp_lnk, 0); /* Connection refused. */ + /* Connection refused. */ + SetExpire(pptp_lnk, 0); break; - case PPTP_CallDiscNotify: /* Connection closed. */ + case PPTP_CallDiscNotify: + /* Connection closed. */ SetExpire(pptp_lnk, 0); break; } } } static void AliasHandlePptpIn(struct libalias *la, struct ip *pip, /* IP packet to examine/patch */ - struct alias_link *lnk) -{ /* The PPTP control link */ + struct alias_link *lnk) /* The PPTP control link */ +{ struct alias_link *pptp_lnk; PptpCallId cptr; u_int16_t *pcall_id; u_int16_t ctl_type; /* control message type */ struct tcphdr *tc; /* Verify valid PPTP control message */ if ((cptr = AliasVerifyPptp(pip, &ctl_type)) == NULL) return; /* Modify certain PPTP messages */ switch (ctl_type) { case PPTP_InCallConn: case PPTP_WanErrorNotify: case PPTP_SetLinkInfo: pcall_id = &cptr->cid1; break; case PPTP_OutCallReply: case PPTP_InCallReply: pcall_id = &cptr->cid2; break; - case PPTP_CallDiscNotify: /* Connection closed. */ + case PPTP_CallDiscNotify: + /* Connection closed. */ pptp_lnk = FindPptpInByCallId(la, GetDestAddress(lnk), - GetAliasAddress(lnk), - cptr->cid1); + GetAliasAddress(lnk), cptr->cid1); if (pptp_lnk != NULL) SetExpire(pptp_lnk, 0); return; default: return; } /* Find PPTP link for address and Call ID found in PPTP Control Msg */ pptp_lnk = FindPptpInByPeerCallId(la, GetDestAddress(lnk), - GetAliasAddress(lnk), - *pcall_id); + GetAliasAddress(lnk), *pcall_id); if (pptp_lnk != NULL) { int accumulate = *pcall_id; /* De-alias the Peer's Call Id. */ *pcall_id = GetOriginalPort(pptp_lnk); /* Compute TCP checksum for modified packet */ tc = (struct tcphdr *)ip_next(pip); accumulate -= *pcall_id; ADJUST_CHECKSUM(accumulate, tc->th_sum); - if (ctl_type == PPTP_OutCallReply || ctl_type == PPTP_InCallReply) { - PptpCode codes = (PptpCode) (cptr + 1); + if (ctl_type == PPTP_OutCallReply || + ctl_type == PPTP_InCallReply) { + PptpCode codes = (PptpCode)(cptr + 1); - if (codes->resCode == 1) /* Connection - * established, */ - SetDestCallId(pptp_lnk, /* note the Call ID. */ - cptr->cid1); + if (codes->resCode == 1) + /* Connection established, + * note the Call ID. */ + SetDestCallId(pptp_lnk, cptr->cid1); else - SetExpire(pptp_lnk, 0); /* Connection refused. */ + /* Connection refused. */ + SetExpire(pptp_lnk, 0); } } } -static PptpCallId -AliasVerifyPptp(struct ip *pip, u_int16_t * ptype) -{ /* IP packet to examine/patch */ +static PptpCallId +AliasVerifyPptp(struct ip *pip, u_int16_t * ptype) /* IP packet to examine/patch */ +{ int hlen, tlen, dlen; PptpMsgHead hptr; struct tcphdr *tc; /* Calculate some lengths */ tc = (struct tcphdr *)ip_next(pip); hlen = (pip->ip_hl + tc->th_off) << 2; tlen = ntohs(pip->ip_len); dlen = tlen - hlen; /* Verify data length */ if (dlen < (int)(sizeof(struct pptpMsgHead) + sizeof(struct pptpCallIds))) return (NULL); /* Move up to PPTP message header */ - hptr = (PptpMsgHead) tcp_next(tc); + hptr = (PptpMsgHead)tcp_next(tc); /* Return the control message type */ *ptype = ntohs(hptr->type); /* Verify PPTP Control Message */ if ((ntohs(hptr->msgType) != PPTP_CTRL_MSG_TYPE) || (ntohl(hptr->magic) != PPTP_MAGIC)) return (NULL); /* Verify data length. */ if ((*ptype == PPTP_OutCallReply || *ptype == PPTP_InCallReply) && (dlen < (int)(sizeof(struct pptpMsgHead) + sizeof(struct pptpCallIds) + sizeof(struct pptpCodes)))) return (NULL); else - return (PptpCallId) (hptr + 1); + return ((PptpCallId)(hptr + 1)); } static int AliasHandlePptpGreOut(struct libalias *la, struct ip *pip) { GreHdr *gr; struct alias_link *lnk; - gr = (GreHdr *) ip_next(pip); + gr = (GreHdr *)ip_next(pip); /* Check GRE header bits. */ - if ((ntohl(*((u_int32_t *) gr)) & PPTP_INIT_MASK) != PPTP_INIT_VALUE) + if ((ntohl(*((u_int32_t *)gr)) & PPTP_INIT_MASK) != PPTP_INIT_VALUE) return (-1); lnk = FindPptpOutByPeerCallId(la, pip->ip_src, pip->ip_dst, gr->gh_call_id); if (lnk != NULL) { struct in_addr alias_addr = GetAliasAddress(lnk); /* Change source IP address. */ DifferentialChecksum(&pip->ip_sum, &alias_addr, &pip->ip_src, 2); pip->ip_src = alias_addr; } return (0); } static int AliasHandlePptpGreIn(struct libalias *la, struct ip *pip) { GreHdr *gr; struct alias_link *lnk; - gr = (GreHdr *) ip_next(pip); + gr = (GreHdr *)ip_next(pip); /* Check GRE header bits. */ - if ((ntohl(*((u_int32_t *) gr)) & PPTP_INIT_MASK) != PPTP_INIT_VALUE) + if ((ntohl(*((u_int32_t *)gr)) & PPTP_INIT_MASK) != PPTP_INIT_VALUE) return (-1); lnk = FindPptpInByPeerCallId(la, pip->ip_src, pip->ip_dst, gr->gh_call_id); if (lnk != NULL) { struct in_addr src_addr = GetOriginalAddress(lnk); /* De-alias the Peer's Call Id. */ gr->gh_call_id = GetOriginalPort(lnk); /* Restore original IP address. */ DifferentialChecksum(&pip->ip_sum, &src_addr, &pip->ip_dst, 2); pip->ip_dst = src_addr; } return (0); } diff --git a/sys/netinet/libalias/alias_proxy.c b/sys/netinet/libalias/alias_proxy.c index 79d9db82914c..e2b0f1009617 100644 --- a/sys/netinet/libalias/alias_proxy.c +++ b/sys/netinet/libalias/alias_proxy.c @@ -1,865 +1,862 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2001 Charles Mott * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); /* file: alias_proxy.c This file encapsulates special operations related to transparent proxy redirection. This is where packets with a particular destination, usually tcp port 80, are redirected to a proxy server. When packets are proxied, the destination address and port are modified. In certain cases, it is necessary to somehow encode the original address/port info into the packet. Two methods are presently supported: addition of a [DEST addr port] string at the beginning of a tcp stream, or inclusion of an optional field in the IP header. There is one public API function: PacketAliasProxyRule() -- Adds and deletes proxy rules. Rules are stored in a linear linked list, so lookup efficiency won't be too good for large lists. Initial development: April, 1998 (cjm) */ /* System includes */ #ifdef _KERNEL #include #include #include #include #else #include #include #include #include #include #include #endif #include #ifdef _KERNEL #include #include #include #else #include + #include "alias.h" /* Public API functions for libalias */ #include "alias_local.h" /* Functions used by alias*.c */ #endif /* Data structures */ /* * A linked list of arbitrary length, based on struct proxy_entry is * used to store proxy rules. */ struct proxy_entry { struct libalias *la; #define PROXY_TYPE_ENCODE_NONE 1 #define PROXY_TYPE_ENCODE_TCPSTREAM 2 #define PROXY_TYPE_ENCODE_IPHDR 3 int rule_index; int proxy_type; u_char proto; u_short proxy_port; u_short server_port; struct in_addr server_addr; struct in_addr src_addr; struct in_addr src_mask; struct in_addr dst_addr; struct in_addr dst_mask; struct proxy_entry *next; struct proxy_entry *last; }; /* File scope variables */ /* Local (static) functions: IpMask() -- Utility function for creating IP masks from integer (1-32) specification. IpAddr() -- Utility function for converting string to IP address IpPort() -- Utility function for converting string to port number RuleAdd() -- Adds an element to the rule list. RuleDelete() -- Removes an element from the rule list. RuleNumberDelete() -- Removes all elements from the rule list having a certain rule number. ProxyEncodeTcpStream() -- Adds [DEST x.x.x.x xxxx] to the beginning of a TCP stream. ProxyEncodeIpHeader() -- Adds an IP option indicating the true destination of a proxied IP packet */ static int IpMask(int, struct in_addr *); static int IpAddr(char *, struct in_addr *); static int IpPort(char *, int, int *); static void RuleAdd(struct libalias *la, struct proxy_entry *); static void RuleDelete(struct proxy_entry *); static int RuleNumberDelete(struct libalias *la, int); static void ProxyEncodeTcpStream(struct alias_link *, struct ip *, int); static void ProxyEncodeIpHeader(struct ip *, int); static int IpMask(int nbits, struct in_addr *mask) { int i; u_int imask; if (nbits < 0 || nbits > 32) return (-1); imask = 0; for (i = 0; i < nbits; i++) imask = (imask >> 1) + 0x80000000; mask->s_addr = htonl(imask); return (0); } static int IpAddr(char *s, struct in_addr *addr) { if (inet_aton(s, addr) == 0) return (-1); else return (0); } static int IpPort(char *s, int proto, int *port) { int n; n = sscanf(s, "%d", port); if (n != 1) #ifndef _KERNEL /* XXX: we accept only numeric ports in kernel */ { struct servent *se; if (proto == IPPROTO_TCP) se = getservbyname(s, "tcp"); else if (proto == IPPROTO_UDP) se = getservbyname(s, "udp"); else return (-1); if (se == NULL) return (-1); - *port = (u_int) ntohs(se->s_port); + *port = (u_int)ntohs(se->s_port); } #else return (-1); #endif return (0); } void RuleAdd(struct libalias *la, struct proxy_entry *entry) { int rule_index; struct proxy_entry *ptr; struct proxy_entry *ptr_last; LIBALIAS_LOCK_ASSERT(la); entry->la = la; if (la->proxyList == NULL) { la->proxyList = entry; entry->last = NULL; entry->next = NULL; return; } rule_index = entry->rule_index; ptr = la->proxyList; ptr_last = NULL; while (ptr != NULL) { if (ptr->rule_index >= rule_index) { if (ptr_last == NULL) { entry->next = la->proxyList; entry->last = NULL; la->proxyList->last = entry; la->proxyList = entry; return; } ptr_last->next = entry; ptr->last = entry; entry->last = ptr->last; entry->next = ptr; return; } ptr_last = ptr; ptr = ptr->next; } ptr_last->next = entry; entry->last = ptr_last; entry->next = NULL; } static void RuleDelete(struct proxy_entry *entry) { struct libalias *la; la = entry->la; LIBALIAS_LOCK_ASSERT(la); if (entry->last != NULL) entry->last->next = entry->next; else la->proxyList = entry->next; if (entry->next != NULL) entry->next->last = entry->last; free(entry); } static int RuleNumberDelete(struct libalias *la, int rule_index) { int err; struct proxy_entry *ptr; LIBALIAS_LOCK_ASSERT(la); err = -1; ptr = la->proxyList; while (ptr != NULL) { struct proxy_entry *ptr_next; ptr_next = ptr->next; if (ptr->rule_index == rule_index) { err = 0; RuleDelete(ptr); } ptr = ptr_next; } return (err); } static void ProxyEncodeTcpStream(struct alias_link *lnk, struct ip *pip, int maxpacketsize) { int slen; char buffer[40]; struct tcphdr *tc; char addrbuf[INET_ADDRSTRLEN]; -/* Compute pointer to tcp header */ + /* Compute pointer to tcp header */ tc = (struct tcphdr *)ip_next(pip); -/* Don't modify if once already modified */ - + /* Don't modify if once already modified */ if (GetAckModified(lnk)) return; -/* Translate destination address and port to string form */ + /* Translate destination address and port to string form */ snprintf(buffer, sizeof(buffer) - 2, "[DEST %s %d]", inet_ntoa_r(GetProxyAddress(lnk), INET_NTOA_BUF(addrbuf)), - (u_int) ntohs(GetProxyPort(lnk))); + (u_int)ntohs(GetProxyPort(lnk))); -/* Pad string out to a multiple of two in length */ + /* Pad string out to a multiple of two in length */ slen = strlen(buffer); switch (slen % 2) { case 0: strcat(buffer, " \n"); slen += 2; break; case 1: strcat(buffer, "\n"); slen += 1; } -/* Check for packet overflow */ + /* Check for packet overflow */ if ((int)(ntohs(pip->ip_len) + strlen(buffer)) > maxpacketsize) return; -/* Shift existing TCP data and insert destination string */ + /* Shift existing TCP data and insert destination string */ { int dlen; int hlen; char *p; hlen = (pip->ip_hl + tc->th_off) << 2; dlen = ntohs(pip->ip_len) - hlen; -/* Modify first packet that has data in it */ - + /* Modify first packet that has data in it */ if (dlen == 0) return; p = (char *)pip; p += hlen; bcopy(p, p + slen, dlen); memcpy(p, buffer, slen); } -/* Save information about modfied sequence number */ + /* Save information about modfied sequence number */ { int delta; SetAckModified(lnk); - tc = (struct tcphdr *)ip_next(pip); + tc = (struct tcphdr *)ip_next(pip); delta = GetDeltaSeqOut(tc->th_seq, lnk); AddSeq(lnk, delta + slen, pip->ip_hl, pip->ip_len, tc->th_seq, tc->th_off); } -/* Update IP header packet length and checksum */ + /* Update IP header packet length and checksum */ { int accumulate; accumulate = pip->ip_len; pip->ip_len = htons(ntohs(pip->ip_len) + slen); accumulate -= pip->ip_len; ADJUST_CHECKSUM(accumulate, pip->ip_sum); } -/* Update TCP checksum, Use TcpChecksum since so many things have - already changed. */ + /* Update TCP checksum, Use TcpChecksum since so many things have + already changed. */ tc->th_sum = 0; #ifdef _KERNEL tc->th_x2 = 1; #else tc->th_sum = TcpChecksum(pip); #endif } static void -ProxyEncodeIpHeader(struct ip *pip, - int maxpacketsize) +ProxyEncodeIpHeader(struct ip *pip, int maxpacketsize) { #define OPTION_LEN_BYTES 8 #define OPTION_LEN_INT16 4 #define OPTION_LEN_INT32 2 _Alignas(_Alignof(u_short)) u_char option[OPTION_LEN_BYTES]; #ifdef LIBALIAS_DEBUG - fprintf(stdout, " ip cksum 1 = %x\n", (u_int) IpChecksum(pip)); - fprintf(stdout, "tcp cksum 1 = %x\n", (u_int) TcpChecksum(pip)); + fprintf(stdout, " ip cksum 1 = %x\n", (u_int)IpChecksum(pip)); + fprintf(stdout, "tcp cksum 1 = %x\n", (u_int)TcpChecksum(pip)); #endif (void)maxpacketsize; -/* Check to see that there is room to add an IP option */ + /* Check to see that there is room to add an IP option */ if (pip->ip_hl > (0x0f - OPTION_LEN_INT32)) return; -/* Build option and copy into packet */ + /* Build option and copy into packet */ { u_char *ptr; struct tcphdr *tc; ptr = (u_char *) pip; ptr += 20; memcpy(ptr + OPTION_LEN_BYTES, ptr, ntohs(pip->ip_len) - 20); option[0] = 0x64; /* class: 3 (reserved), option 4 */ option[1] = OPTION_LEN_BYTES; - memcpy(&option[2], (u_char *) & pip->ip_dst, 4); + memcpy(&option[2], (u_char *)&pip->ip_dst, 4); tc = (struct tcphdr *)ip_next(pip); - memcpy(&option[6], (u_char *) & tc->th_sport, 2); + memcpy(&option[6], (u_char *)&tc->th_sport, 2); memcpy(ptr, option, 8); } -/* Update checksum, header length and packet length */ + /* Update checksum, header length and packet length */ { int i; int accumulate; u_short *sptr; sptr = (u_short *) option; accumulate = 0; for (i = 0; i < OPTION_LEN_INT16; i++) accumulate -= *(sptr++); sptr = (u_short *) pip; accumulate += *sptr; pip->ip_hl += OPTION_LEN_INT32; accumulate -= *sptr; accumulate += pip->ip_len; pip->ip_len = htons(ntohs(pip->ip_len) + OPTION_LEN_BYTES); accumulate -= pip->ip_len; ADJUST_CHECKSUM(accumulate, pip->ip_sum); } #undef OPTION_LEN_BYTES #undef OPTION_LEN_INT16 #undef OPTION_LEN_INT32 #ifdef LIBALIAS_DEBUG - fprintf(stdout, " ip cksum 2 = %x\n", (u_int) IpChecksum(pip)); - fprintf(stdout, "tcp cksum 2 = %x\n", (u_int) TcpChecksum(pip)); + fprintf(stdout, " ip cksum 2 = %x\n", (u_int)IpChecksum(pip)); + fprintf(stdout, "tcp cksum 2 = %x\n", (u_int)TcpChecksum(pip)); #endif } /* Functions by other packet alias source files ProxyCheck() -- Checks whether an outgoing packet should be proxied. ProxyModify() -- Encodes the original destination address/port for a packet which is to be redirected to a proxy server. */ int ProxyCheck(struct libalias *la, struct in_addr *proxy_server_addr, - u_short * proxy_server_port, struct in_addr src_addr, + u_short * proxy_server_port, struct in_addr src_addr, struct in_addr dst_addr, u_short dst_port, u_char ip_p) { struct proxy_entry *ptr; LIBALIAS_LOCK_ASSERT(la); ptr = la->proxyList; while (ptr != NULL) { u_short proxy_port; proxy_port = ptr->proxy_port; if ((dst_port == proxy_port || proxy_port == 0) && ip_p == ptr->proto && src_addr.s_addr != ptr->server_addr.s_addr) { struct in_addr src_addr_masked; struct in_addr dst_addr_masked; src_addr_masked.s_addr = src_addr.s_addr & ptr->src_mask.s_addr; dst_addr_masked.s_addr = dst_addr.s_addr & ptr->dst_mask.s_addr; if ((src_addr_masked.s_addr == ptr->src_addr.s_addr) && (dst_addr_masked.s_addr == ptr->dst_addr.s_addr)) { if ((*proxy_server_port = ptr->server_port) == 0) *proxy_server_port = dst_port; *proxy_server_addr = ptr->server_addr; return (ptr->proxy_type); } } ptr = ptr->next; } return (0); } void ProxyModify(struct libalias *la, struct alias_link *lnk, struct ip *pip, int maxpacketsize, int proxy_type) { - LIBALIAS_LOCK_ASSERT(la); (void)la; switch (proxy_type) { - case PROXY_TYPE_ENCODE_IPHDR: + case PROXY_TYPE_ENCODE_IPHDR: ProxyEncodeIpHeader(pip, maxpacketsize); break; case PROXY_TYPE_ENCODE_TCPSTREAM: ProxyEncodeTcpStream(lnk, pip, maxpacketsize); break; } } /* Public API functions */ -int -LibAliasProxyRule(struct libalias *la, const char *cmd) -{ /* * This function takes command strings of the form: * * server [:] * [port ] * [rule n] * [proto tcp|udp] * [src [/n]] * [dst [/n]] * [type encode_tcp_stream|encode_ip_hdr|no_encode] * * delete * * Subfields can be in arbitrary order. Port numbers and addresses * must be in either numeric or symbolic form. An optional rule number * is used to control the order in which rules are searched. If two * rules have the same number, then search order cannot be guaranteed, * and the rules should be disjoint. If no rule number is specified, * then 0 is used, and group 0 rules are always checked before any * others. */ +int +LibAliasProxyRule(struct libalias *la, const char *cmd) +{ int i, n, len, ret; int cmd_len; int token_count; int state; char *token; char buffer[256]; char str_port[sizeof(buffer)]; char str_server_port[sizeof(buffer)]; char *res = buffer; int rule_index; int proto; int proxy_type; int proxy_port; int server_port; struct in_addr server_addr; struct in_addr src_addr, src_mask; struct in_addr dst_addr, dst_mask; struct proxy_entry *proxy_entry; LIBALIAS_LOCK(la); ret = 0; -/* Copy command line into a buffer */ + + /* Copy command line into a buffer */ cmd += strspn(cmd, " \t"); cmd_len = strlen(cmd); if (cmd_len > (int)(sizeof(buffer) - 1)) { ret = -1; goto getout; } strcpy(buffer, cmd); -/* Convert to lower case */ + /* Convert to lower case */ len = strlen(buffer); for (i = 0; i < len; i++) buffer[i] = tolower((unsigned char)buffer[i]); -/* Set default proxy type */ + /* Set default proxy type */ -/* Set up default values */ + /* Set up default values */ rule_index = 0; proxy_type = PROXY_TYPE_ENCODE_NONE; proto = IPPROTO_TCP; proxy_port = 0; server_addr.s_addr = 0; server_port = 0; src_addr.s_addr = 0; IpMask(0, &src_mask); dst_addr.s_addr = 0; IpMask(0, &dst_mask); str_port[0] = 0; str_server_port[0] = 0; -/* Parse command string with state machine */ + /* Parse command string with state machine */ #define STATE_READ_KEYWORD 0 #define STATE_READ_TYPE 1 #define STATE_READ_PORT 2 #define STATE_READ_SERVER 3 #define STATE_READ_RULE 4 #define STATE_READ_DELETE 5 #define STATE_READ_PROTO 6 #define STATE_READ_SRC 7 #define STATE_READ_DST 8 state = STATE_READ_KEYWORD; token = strsep(&res, " \t"); token_count = 0; while (token != NULL) { token_count++; switch (state) { case STATE_READ_KEYWORD: if (strcmp(token, "type") == 0) state = STATE_READ_TYPE; else if (strcmp(token, "port") == 0) state = STATE_READ_PORT; else if (strcmp(token, "server") == 0) state = STATE_READ_SERVER; else if (strcmp(token, "rule") == 0) state = STATE_READ_RULE; else if (strcmp(token, "delete") == 0) state = STATE_READ_DELETE; else if (strcmp(token, "proto") == 0) state = STATE_READ_PROTO; else if (strcmp(token, "src") == 0) state = STATE_READ_SRC; else if (strcmp(token, "dst") == 0) state = STATE_READ_DST; else { ret = -1; goto getout; } break; case STATE_READ_TYPE: if (strcmp(token, "encode_ip_hdr") == 0) proxy_type = PROXY_TYPE_ENCODE_IPHDR; else if (strcmp(token, "encode_tcp_stream") == 0) proxy_type = PROXY_TYPE_ENCODE_TCPSTREAM; else if (strcmp(token, "no_encode") == 0) proxy_type = PROXY_TYPE_ENCODE_NONE; else { ret = -1; goto getout; } state = STATE_READ_KEYWORD; break; case STATE_READ_PORT: strcpy(str_port, token); state = STATE_READ_KEYWORD; break; - case STATE_READ_SERVER: - { - int err; - char *p; - char s[sizeof(buffer)]; + case STATE_READ_SERVER: { + int err; + char *p; + char s[sizeof(buffer)]; + + p = token; + while (*p != ':' && *p != 0) + p++; - p = token; - while (*p != ':' && *p != 0) - p++; - - if (*p != ':') { - err = IpAddr(token, &server_addr); - if (err) { - ret = -1; - goto getout; - } - } else { - *p = ' '; - - n = sscanf(token, "%s %s", s, str_server_port); - if (n != 2) { - ret = -1; - goto getout; - } - - err = IpAddr(s, &server_addr); - if (err) { - ret = -1; - goto getout; - } + if (*p != ':') { + err = IpAddr(token, &server_addr); + if (err) { + ret = -1; + goto getout; + } + } else { + *p = ' '; + + n = sscanf(token, "%s %s", s, str_server_port); + if (n != 2) { + ret = -1; + goto getout; + } + + err = IpAddr(s, &server_addr); + if (err) { + ret = -1; + goto getout; } } + state = STATE_READ_KEYWORD; break; - + } case STATE_READ_RULE: n = sscanf(token, "%d", &rule_index); if (n != 1 || rule_index < 0) { ret = -1; goto getout; } state = STATE_READ_KEYWORD; break; - case STATE_READ_DELETE: - { - int err; - int rule_to_delete; + case STATE_READ_DELETE: { + int err; + int rule_to_delete; - if (token_count != 2) { - ret = -1; - goto getout; - } + if (token_count != 2) { + ret = -1; + goto getout; + } - n = sscanf(token, "%d", &rule_to_delete); - if (n != 1) { - ret = -1; - goto getout; - } - err = RuleNumberDelete(la, rule_to_delete); - if (err) - ret = -1; - else - ret = 0; + n = sscanf(token, "%d", &rule_to_delete); + if (n != 1) { + ret = -1; goto getout; } + err = RuleNumberDelete(la, rule_to_delete); + if (err) + ret = -1; + else + ret = 0; + goto getout; + } case STATE_READ_PROTO: if (strcmp(token, "tcp") == 0) proto = IPPROTO_TCP; else if (strcmp(token, "udp") == 0) proto = IPPROTO_UDP; else { ret = -1; goto getout; } state = STATE_READ_KEYWORD; break; case STATE_READ_SRC: - case STATE_READ_DST: - { - int err; - char *p; - struct in_addr mask; - struct in_addr addr; - - p = token; - while (*p != '/' && *p != 0) - p++; - - if (*p != '/') { - IpMask(32, &mask); - err = IpAddr(token, &addr); - if (err) { - ret = -1; - goto getout; - } - } else { - int nbits; - char s[sizeof(buffer)]; - - *p = ' '; - n = sscanf(token, "%s %d", s, &nbits); - if (n != 2) { - ret = -1; - goto getout; - } - - err = IpAddr(s, &addr); - if (err) { - ret = -1; - goto getout; - } - - err = IpMask(nbits, &mask); - if (err) { - ret = -1; - goto getout; - } + case STATE_READ_DST: { + int err; + char *p; + struct in_addr mask; + struct in_addr addr; + + p = token; + while (*p != '/' && *p != 0) + p++; + + if (*p != '/') { + IpMask(32, &mask); + err = IpAddr(token, &addr); + if (err) { + ret = -1; + goto getout; } + } else { + int nbits; + char s[sizeof(buffer)]; - if (state == STATE_READ_SRC) { - src_addr = addr; - src_mask = mask; - } else { - dst_addr = addr; - dst_mask = mask; + *p = ' '; + n = sscanf(token, "%s %d", s, &nbits); + if (n != 2) { + ret = -1; + goto getout; + } + + err = IpAddr(s, &addr); + if (err) { + ret = -1; + goto getout; + } + + err = IpMask(nbits, &mask); + if (err) { + ret = -1; + goto getout; } } + + if (state == STATE_READ_SRC) { + src_addr = addr; + src_mask = mask; + } else { + dst_addr = addr; + dst_mask = mask; + } + state = STATE_READ_KEYWORD; break; + } default: ret = -1; goto getout; break; } do { token = strsep(&res, " \t"); } while (token != NULL && !*token); } #undef STATE_READ_KEYWORD #undef STATE_READ_TYPE #undef STATE_READ_PORT #undef STATE_READ_SERVER #undef STATE_READ_RULE #undef STATE_READ_DELETE #undef STATE_READ_PROTO #undef STATE_READ_SRC #undef STATE_READ_DST -/* Convert port strings to numbers. This needs to be done after - the string is parsed, because the prototype might not be designated - before the ports (which might be symbolic entries in /etc/services) */ + /* Convert port strings to numbers. + This needs to be done after the string is parsed, because + the prototype might not be designated before the ports + (which might be symbolic entries in /etc/services) */ if (strlen(str_port) != 0) { int err; err = IpPort(str_port, proto, &proxy_port); if (err) { ret = -1; goto getout; } } else { proxy_port = 0; } if (strlen(str_server_port) != 0) { int err; err = IpPort(str_server_port, proto, &server_port); if (err) { ret = -1; goto getout; } } else { server_port = 0; } -/* Check that at least the server address has been defined */ + /* Check that at least the server address has been defined */ if (server_addr.s_addr == 0) { ret = -1; goto getout; } -/* Add to linked list */ + /* Add to linked list */ proxy_entry = malloc(sizeof(struct proxy_entry)); if (proxy_entry == NULL) { ret = -1; goto getout; } proxy_entry->proxy_type = proxy_type; proxy_entry->rule_index = rule_index; proxy_entry->proto = proto; proxy_entry->proxy_port = htons(proxy_port); proxy_entry->server_port = htons(server_port); proxy_entry->server_addr = server_addr; proxy_entry->src_addr.s_addr = src_addr.s_addr & src_mask.s_addr; proxy_entry->dst_addr.s_addr = dst_addr.s_addr & dst_mask.s_addr; proxy_entry->src_mask = src_mask; proxy_entry->dst_mask = dst_mask; RuleAdd(la, proxy_entry); getout: LIBALIAS_UNLOCK(la); return (ret); } diff --git a/sys/netinet/libalias/alias_sctp.c b/sys/netinet/libalias/alias_sctp.c index 83290148bfa6..719af1512a05 100644 --- a/sys/netinet/libalias/alias_sctp.c +++ b/sys/netinet/libalias/alias_sctp.c @@ -1,2722 +1,2740 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2008 - * Swinburne University of Technology, Melbourne, Australia. + * Swinburne University of Technology, Melbourne, Australia. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Alias_sctp forms part of the libalias kernel module to handle * Network Address Translation (NAT) for the SCTP protocol. * * This software was developed by David A. Hayes and Jason But * * The design is outlined in CAIA technical report number 080618A * (D. Hayes and J. But, "Alias_sctp Version 0.1: SCTP NAT implementation in IPFW") * * Development is part of the CAIA SONATA project, * proposed by Jason But and Grenville Armitage: * http://caia.swin.edu.au/urp/sonata/ * * * This project has been made possible in part by a grant from * the Cisco University Research Program Fund at Community * Foundation Silicon Valley. * */ /** @mainpage * Alias_sctp is part of the SONATA (http://caia.swin.edu.au/urp/sonata) project * to develop and release a BSD licensed implementation of a Network Address * Translation (NAT) module that supports the Stream Control Transmission * Protocol (SCTP). * * Traditional address and port number look ups are inadequate for SCTP's * operation due to both processing requirements and issues with multi-homing. * Alias_sctp integrates with FreeBSD's ipfw/libalias NAT system. * * Version 0.2 features include: * - Support for global multi-homing * - Support for ASCONF modification from Internet Draft * (draft-stewart-behave-sctpnat-04, R. Stewart and M. Tuexen, "Stream control * transmission protocol (SCTP) network address translation," Jul. 2008) to * provide support for multi-homed privately addressed hosts * - Support for forwarding of T-flagged packets * - Generation and delivery of AbortM/ErrorM packets upon detection of NAT * collisions * - Per-port forwarding rules * - Dynamically controllable logging and statistics * - Dynamic management of timers * - Dynamic control of hash-table size */ /* $FreeBSD$ */ #ifdef _KERNEL #include #include #include #include #include #include #include #include #include #include #include #include #else #include "alias_sctp.h" #include #include "alias.h" #include "alias_local.h" #include #include #endif //#ifdef _KERNEL /* ---------------------------------------------------------------------- * FUNCTION PROTOTYPES * ---------------------------------------------------------------------- */ /* Packet Parsing Functions */ static int sctp_PktParser(struct libalias *la, int direction, struct ip *pip, struct sctp_nat_msg *sm, struct sctp_nat_assoc **passoc); static int GetAsconfVtags(struct libalias *la, struct sctp_nat_msg *sm, uint32_t *l_vtag, uint32_t *g_vtag, int direction); static int IsASCONFack(struct libalias *la, struct sctp_nat_msg *sm, int direction); static void AddGlobalIPAddresses(struct sctp_nat_msg *sm, struct sctp_nat_assoc *assoc, int direction); static int Add_Global_Address_to_List(struct sctp_nat_assoc *assoc, struct sctp_GlobalAddress *G_addr); static void RmGlobalIPAddresses(struct sctp_nat_msg *sm, struct sctp_nat_assoc *assoc, int direction); static int IsADDorDEL(struct libalias *la, struct sctp_nat_msg *sm, int direction); /* State Machine Functions */ static int ProcessSctpMsg(struct libalias *la, int direction, \ struct sctp_nat_msg *sm, struct sctp_nat_assoc *assoc); static int ID_process(struct libalias *la, int direction,\ struct sctp_nat_assoc *assoc, struct sctp_nat_msg *sm); static int INi_process(struct libalias *la, int direction,\ struct sctp_nat_assoc *assoc, struct sctp_nat_msg *sm); static int INa_process(struct libalias *la, int direction,\ struct sctp_nat_assoc *assoc, struct sctp_nat_msg *sm); static int UP_process(struct libalias *la, int direction,\ struct sctp_nat_assoc *assoc, struct sctp_nat_msg *sm); static int CL_process(struct libalias *la, int direction,\ struct sctp_nat_assoc *assoc, struct sctp_nat_msg *sm); static void TxAbortErrorM(struct libalias *la, struct sctp_nat_msg *sm,\ struct sctp_nat_assoc *assoc, int sndrply, int direction); /* Hash Table Functions */ -static struct sctp_nat_assoc* +static struct sctp_nat_assoc * FindSctpLocal(struct libalias *la, struct in_addr l_addr, struct in_addr g_addr, uint32_t l_vtag, uint16_t l_port, uint16_t g_port); -static struct sctp_nat_assoc* +static struct sctp_nat_assoc * FindSctpGlobal(struct libalias *la, struct in_addr g_addr, uint32_t g_vtag, uint16_t g_port, uint16_t l_port, int *partial_match); -static struct sctp_nat_assoc* +static struct sctp_nat_assoc * FindSctpGlobalClash(struct libalias *la, struct sctp_nat_assoc *Cassoc); -static struct sctp_nat_assoc* +static struct sctp_nat_assoc * FindSctpLocalT(struct libalias *la, struct in_addr g_addr, uint32_t l_vtag, uint16_t g_port, uint16_t l_port); -static struct sctp_nat_assoc* +static struct sctp_nat_assoc * FindSctpGlobalT(struct libalias *la, struct in_addr g_addr, uint32_t g_vtag, uint16_t l_port, uint16_t g_port); static int AddSctpAssocLocal(struct libalias *la, struct sctp_nat_assoc *assoc, struct in_addr g_addr); static int AddSctpAssocGlobal(struct libalias *la, struct sctp_nat_assoc *assoc); static void RmSctpAssoc(struct libalias *la, struct sctp_nat_assoc *assoc); static void freeGlobalAddressList(struct sctp_nat_assoc *assoc); /* Timer Queue Functions */ static void sctp_AddTimeOut(struct libalias *la, struct sctp_nat_assoc *assoc); static void sctp_RmTimeOut(struct libalias *la, struct sctp_nat_assoc *assoc); static void sctp_ResetTimeOut(struct libalias *la, struct sctp_nat_assoc *assoc, int newexp); void sctp_CheckTimers(struct libalias *la); /* Logging Functions */ -static void logsctperror(char* errormsg, uint32_t vtag, int error, int direction); +static void logsctperror(char *errormsg, uint32_t vtag, int error, int direction); static void logsctpparse(int direction, struct sctp_nat_msg *sm); static void logsctpassoc(struct sctp_nat_assoc *assoc, char *s); static void logTimerQ(struct libalias *la); static void logSctpGlobal(struct libalias *la); static void logSctpLocal(struct libalias *la); #ifdef _KERNEL static void SctpAliasLog(const char *format, ...); #endif /** @defgroup external External code changes and modifications * * Some changes have been made to files external to alias_sctp.(c|h). These * changes are primarily due to code needing to call static functions within * those files or to perform extra functionality that can only be performed * within these files. */ /** @ingroup external * @brief Log current statistics for the libalias instance * * This function is defined in alias_db.c, since it calls static functions in * this file * * Calls the higher level ShowAliasStats() in alias_db.c which logs all current * statistics about the libalias instance - including SCTP statistics * * @param la Pointer to the libalias instance */ void SctpShowAliasStats(struct libalias *la); -#ifdef _KERNEL +#ifdef _KERNEL static MALLOC_DEFINE(M_SCTPNAT, "sctpnat", "sctp nat dbs"); /* Use kernel allocator. */ #ifdef _SYS_MALLOC_H_ #define sn_malloc(x) malloc(x, M_SCTPNAT, M_NOWAIT|M_ZERO) #define sn_calloc(n,x) mallocarray((n), (x), M_SCTPNAT, M_NOWAIT|M_ZERO) #define sn_free(x) free(x, M_SCTPNAT) #endif// #ifdef _SYS_MALLOC_H_ #else //#ifdef _KERNEL #define sn_malloc(x) malloc(x) #define sn_calloc(n, x) calloc(n, x) #define sn_free(x) free(x) #endif //#ifdef _KERNEL /** @defgroup packet_parser SCTP Packet Parsing * * Macros to: * - Return pointers to the first and next SCTP chunks within an SCTP Packet * - Define possible return values of the packet parsing process * - SCTP message types for storing in the sctp_nat_msg structure @{ */ #define SN_SCTP_FIRSTCHUNK(sctphead) (struct sctp_chunkhdr *)(((char *)sctphead) + sizeof(struct sctphdr)) /**< Returns a pointer to the first chunk in an SCTP packet given a pointer to the SCTP header */ #define SN_SCTP_NEXTCHUNK(chunkhead) (struct sctp_chunkhdr *)(((char *)chunkhead) + SCTP_SIZE32(ntohs(chunkhead->chunk_length))) /**< Returns a pointer to the next chunk in an SCTP packet given a pointer to the current chunk */ #define SN_SCTP_NEXTPARAM(param) (struct sctp_paramhdr *)(((char *)param) + SCTP_SIZE32(ntohs(param->param_length))) /**< Returns a pointer to the next parameter in an SCTP packet given a pointer to the current parameter */ #define SN_MIN_CHUNK_SIZE 4 /**< Smallest possible SCTP chunk size in bytes */ #define SN_MIN_PARAM_SIZE 4 /**< Smallest possible SCTP param size in bytes */ #define SN_VTAG_PARAM_SIZE 12 /**< Size of SCTP ASCONF vtag param in bytes */ #define SN_ASCONFACK_PARAM_SIZE 8 /**< Size of SCTP ASCONF ACK param in bytes */ /* Packet parsing return codes */ #define SN_PARSE_OK 0 /**< Packet parsed for SCTP messages */ #define SN_PARSE_ERROR_IPSHL 1 /**< Packet parsing error - IP and SCTP common header len */ #define SN_PARSE_ERROR_AS_MALLOC 2 /**< Packet parsing error - assoc malloc */ #define SN_PARSE_ERROR_CHHL 3 /**< Packet parsing error - Chunk header len */ #define SN_PARSE_ERROR_DIR 4 /**< Packet parsing error - Direction */ #define SN_PARSE_ERROR_VTAG 5 /**< Packet parsing error - Vtag */ #define SN_PARSE_ERROR_CHUNK 6 /**< Packet parsing error - Chunk */ #define SN_PARSE_ERROR_PORT 7 /**< Packet parsing error - Port=0 */ #define SN_PARSE_ERROR_LOOKUP 8 /**< Packet parsing error - Lookup */ #define SN_PARSE_ERROR_PARTIALLOOKUP 9 /**< Packet parsing error - partial lookup only found */ #define SN_PARSE_ERROR_LOOKUP_ABORT 10 /**< Packet parsing error - Lookup - but abort packet */ /* Alias_sctp performs its processing based on a number of key messages */ #define SN_SCTP_ABORT 0x0000 /**< a packet containing an ABORT chunk */ #define SN_SCTP_INIT 0x0001 /**< a packet containing an INIT chunk */ #define SN_SCTP_INITACK 0x0002 /**< a packet containing an INIT-ACK chunk */ #define SN_SCTP_SHUTCOMP 0x0010 /**< a packet containing a SHUTDOWN-COMPLETE chunk */ #define SN_SCTP_SHUTACK 0x0020 /**< a packet containing a SHUTDOWN-ACK chunk */ #define SN_SCTP_ASCONF 0x0100 /**< a packet containing an ASCONF chunk */ #define SN_SCTP_ASCONFACK 0x0200 /**< a packet containing an ASCONF-ACK chunk */ #define SN_SCTP_OTHER 0xFFFF /**< a packet containing a chunk that is not of interest */ /** @} * @defgroup state_machine SCTP NAT State Machine * * Defines the various states an association can be within the NAT @{ */ #define SN_ID 0x0000 /**< Idle state */ #define SN_INi 0x0010 /**< Initialising, waiting for InitAck state */ #define SN_INa 0x0020 /**< Initialising, waiting for AddIpAck state */ #define SN_UP 0x0100 /**< Association in UP state */ #define SN_CL 0x1000 /**< Closing state */ #define SN_RM 0x2000 /**< Removing state */ /** @} * @defgroup Logging Logging Functionality * * Define various log levels and a macro to call specified log functions only if * the current log level (sysctl_log_level) matches the specified level @{ */ #define SN_LOG_LOW 0 #define SN_LOG_EVENT 1 #define SN_LOG_INFO 2 #define SN_LOG_DETAIL 3 #define SN_LOG_DEBUG 4 #define SN_LOG_DEBUG_MAX 5 #define SN_LOG(level, action) if (sysctl_log_level >= level) { action; } /**< Perform log action ONLY if the current log level meets the specified log level */ /** @} * @defgroup Hash Hash Table Macros and Functions * * Defines minimum/maximum/default values for the hash table size @{ */ #define SN_MIN_HASH_SIZE 101 /**< Minimum hash table size (set to stop users choosing stupid values) */ #define SN_MAX_HASH_SIZE 1000001 /**< Maximum hash table size (NB must be less than max int) */ #define SN_DEFAULT_HASH_SIZE 2003 /**< A reasonable default size for the hash tables */ #define SN_LOCAL_TBL 0x01 /**< assoc in local table */ #define SN_GLOBAL_TBL 0x02 /**< assoc in global table */ #define SN_BOTH_TBL 0x03 /**< assoc in both tables */ #define SN_WAIT_TOLOCAL 0x10 /**< assoc waiting for TOLOCAL asconf ACK*/ #define SN_WAIT_TOGLOBAL 0x20 /**< assoc waiting for TOLOCAL asconf ACK*/ #define SN_NULL_TBL 0x00 /**< assoc in No table */ #define SN_MAX_GLOBAL_ADDRESSES 100 /**< absolute maximum global address count*/ #define SN_ADD_OK 0 /**< Association added to the table */ #define SN_ADD_CLASH 1 /**< Clash when trying to add the assoc. info to the table */ #define SN_TABLE_HASH(vtag, port, size) (((u_int) vtag + (u_int) port) % (u_int) size) /**< Calculate the hash table lookup position */ /** @} * @defgroup Timer Timer Queue Macros and Functions * * Timer macros set minimum/maximum timeout values and calculate timer expiry * times for the provided libalias instance @{ */ #define SN_MIN_TIMER 1 #define SN_MAX_TIMER 600 #define SN_TIMER_QUEUE_SIZE SN_MAX_TIMER+2 #define SN_I_T(la) (la->timeStamp + sysctl_init_timer) /**< INIT State expiration time in seconds */ #define SN_U_T(la) (la->timeStamp + sysctl_up_timer) /**< UP State expiration time in seconds */ #define SN_C_T(la) (la->timeStamp + sysctl_shutdown_timer) /**< CL State expiration time in seconds */ #define SN_X_T(la) (la->timeStamp + sysctl_holddown_timer) /**< Wait after a shutdown complete in seconds */ /** @} * @defgroup sysctl SysCtl Variable and callback function declarations * * Sysctl variables to modify NAT functionality in real-time along with associated functions * to manage modifications to the sysctl variables @{ */ /* Callbacks */ int sysctl_chg_loglevel(SYSCTL_HANDLER_ARGS); int sysctl_chg_timer(SYSCTL_HANDLER_ARGS); int sysctl_chg_hashtable_size(SYSCTL_HANDLER_ARGS); int sysctl_chg_error_on_ootb(SYSCTL_HANDLER_ARGS); int sysctl_chg_accept_global_ootb_addip(SYSCTL_HANDLER_ARGS); int sysctl_chg_initialising_chunk_proc_limit(SYSCTL_HANDLER_ARGS); int sysctl_chg_chunk_proc_limit(SYSCTL_HANDLER_ARGS); int sysctl_chg_param_proc_limit(SYSCTL_HANDLER_ARGS); int sysctl_chg_track_global_addresses(SYSCTL_HANDLER_ARGS); /* Sysctl variables */ /** @brief net.inet.ip.alias.sctp.log_level */ static u_int sysctl_log_level = 0; /**< Stores the current level of logging */ /** @brief net.inet.ip.alias.sctp.init_timer */ static u_int sysctl_init_timer = 15; /**< Seconds to hold an association in the table waiting for an INIT-ACK or AddIP-ACK */ /** @brief net.inet.ip.alias.sctp.up_timer */ static u_int sysctl_up_timer = 300; /**< Seconds to hold an association in the table while no packets are transmitted */ /** @brief net.inet.ip.alias.sctp.shutdown_timer */ static u_int sysctl_shutdown_timer = 15; /**< Seconds to hold an association in the table waiting for a SHUTDOWN-COMPLETE */ /** @brief net.inet.ip.alias.sctp.holddown_timer */ static u_int sysctl_holddown_timer = 0; /**< Seconds to hold an association in the table after it has been shutdown (to allow for lost SHUTDOWN-COMPLETEs) */ /** @brief net.inet.ip.alias.sctp.hashtable_size */ static u_int sysctl_hashtable_size = SN_DEFAULT_HASH_SIZE; /**< Sets the hash table size for any NEW NAT instances (existing instances retain their existing Hash Table */ /** @brief net.inet.ip.alias.sctp.error_on_ootb */ static u_int sysctl_error_on_ootb = 1; /**< NAT response to receipt of OOTB packet (0 - No response, 1 - NAT will send ErrorM only to local side, 2 - NAT will send local ErrorM and global ErrorM if there was a partial association match 3 - NAT will send ErrorM to both local and global) */ /** @brief net.inet.ip.alias.sctp.accept_global_ootb_addip */ static u_int sysctl_accept_global_ootb_addip = 0; /** 0 - enables tracking but limits the number of global IP addresses to this value) If set to >=1 the NAT will track that many global IP addresses. This may reduce look up table conflicts, but increases processing */ #define SN_NO_ERROR_ON_OOTB 0 /**< Send no errorM on out of the blue packets */ #define SN_LOCAL_ERROR_ON_OOTB 1 /**< Send only local errorM on out of the blue packets */ #define SN_LOCALandPARTIAL_ERROR_ON_OOTB 2 /**< Send local errorM and global errorM for out of the blue packets only if partial match found */ #define SN_ERROR_ON_OOTB 3 /**< Send errorM on out of the blue packets */ #ifdef SYSCTL_NODE SYSCTL_DECL(_net_inet); SYSCTL_DECL(_net_inet_ip); SYSCTL_DECL(_net_inet_ip_alias); static SYSCTL_NODE(_net_inet_ip_alias, OID_AUTO, sctp, CTLFLAG_RW | CTLFLAG_MPSAFE, NULL, "SCTP NAT"); SYSCTL_PROC(_net_inet_ip_alias_sctp, OID_AUTO, log_level, CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, &sysctl_log_level, 0, sysctl_chg_loglevel, "IU", "Level of detail (0 - default, 1 - event, 2 - info, 3 - detail, 4 - debug, 5 - max debug)"); SYSCTL_PROC(_net_inet_ip_alias_sctp, OID_AUTO, init_timer, CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, &sysctl_init_timer, 0, sysctl_chg_timer, "IU", "Timeout value (s) while waiting for (INIT-ACK|AddIP-ACK)"); SYSCTL_PROC(_net_inet_ip_alias_sctp, OID_AUTO, up_timer, CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, &sysctl_up_timer, 0, sysctl_chg_timer, "IU", "Timeout value (s) to keep an association up with no traffic"); SYSCTL_PROC(_net_inet_ip_alias_sctp, OID_AUTO, shutdown_timer, CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, &sysctl_shutdown_timer, 0, sysctl_chg_timer, "IU", "Timeout value (s) while waiting for SHUTDOWN-COMPLETE"); SYSCTL_PROC(_net_inet_ip_alias_sctp, OID_AUTO, holddown_timer, CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, &sysctl_holddown_timer, 0, sysctl_chg_timer, "IU", "Hold association in table for this many seconds after receiving a SHUTDOWN-COMPLETE"); SYSCTL_PROC(_net_inet_ip_alias_sctp, OID_AUTO, hashtable_size, CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, &sysctl_hashtable_size, 0, sysctl_chg_hashtable_size, "IU", "Size of hash tables used for NAT lookups (100 < prime_number > 1000001)"); SYSCTL_PROC(_net_inet_ip_alias_sctp, OID_AUTO, error_on_ootb, CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, &sysctl_error_on_ootb, 0, sysctl_chg_error_on_ootb, "IU", "ErrorM sent on receipt of ootb packet:\n\t0 - none,\n" "\t1 - to local only,\n" "\t2 - to local and global if a partial association match,\n" "\t3 - to local and global (DoS risk)"); SYSCTL_PROC(_net_inet_ip_alias_sctp, OID_AUTO, accept_global_ootb_addip, CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, &sysctl_accept_global_ootb_addip, 0, sysctl_chg_accept_global_ootb_addip, "IU", "NAT response to receipt of global OOTB AddIP:\n" "\t0 - No response,\n" "\t1 - NAT will accept OOTB global AddIP messages for processing (Security risk)"); SYSCTL_PROC(_net_inet_ip_alias_sctp, OID_AUTO, initialising_chunk_proc_limit, CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, &sysctl_initialising_chunk_proc_limit, 0, sysctl_chg_initialising_chunk_proc_limit, "IU", "Number of chunks that should be processed if there is no current " "association found:\n\t > 0 (A high value is a DoS risk)"); SYSCTL_PROC(_net_inet_ip_alias_sctp, OID_AUTO, chunk_proc_limit, CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, &sysctl_chunk_proc_limit, 0, sysctl_chg_chunk_proc_limit, "IU", "Number of chunks that should be processed to find key chunk:\n" "\t>= initialising_chunk_proc_limit (A high value is a DoS risk)"); SYSCTL_PROC(_net_inet_ip_alias_sctp, OID_AUTO, param_proc_limit, CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, &sysctl_param_proc_limit, 0, sysctl_chg_param_proc_limit, "IU", "Number of parameters (in a chunk) that should be processed to find key " "parameters:\n\t> 1 (A high value is a DoS risk)"); SYSCTL_PROC(_net_inet_ip_alias_sctp, OID_AUTO, track_global_addresses, CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, &sysctl_track_global_addresses, 0, sysctl_chg_track_global_addresses, "IU", "Configures the global address tracking option within the NAT:\n" "\t0 - Global tracking is disabled,\n" "\t> 0 - enables tracking but limits the number of global IP addresses to this value"); #endif /* SYSCTL_NODE */ /** @} * @ingroup sysctl * @brief sysctl callback for changing net.inet.ip.fw.sctp.log_level * * Updates the variable sysctl_log_level to the provided value and ensures * it is in the valid range (SN_LOG_LOW -> SN_LOG_DEBUG) */ int sysctl_chg_loglevel(SYSCTL_HANDLER_ARGS) { u_int level = *(u_int *)arg1; int error; error = sysctl_handle_int(oidp, &level, 0, req); - if (error) return (error); + if (error) + return (error); level = (level > SN_LOG_DEBUG_MAX) ? (SN_LOG_DEBUG_MAX) : (level); level = (level < SN_LOG_LOW) ? (SN_LOG_LOW) : (level); sysctl_log_level = level; return (0); } /** @ingroup sysctl * @brief sysctl callback for changing net.inet.ip.fw.sctp.(init_timer|up_timer|shutdown_timer) * * Updates the timer-based sysctl variables. The new values are sanity-checked * to make sure that they are within the range SN_MIN_TIMER-SN_MAX_TIMER. The * holddown timer is allowed to be 0 */ int sysctl_chg_timer(SYSCTL_HANDLER_ARGS) { u_int timer = *(u_int *)arg1; int error; error = sysctl_handle_int(oidp, &timer, 0, req); - if (error) return (error); + if (error) + return (error); timer = (timer > SN_MAX_TIMER) ? (SN_MAX_TIMER) : (timer); if (((u_int *)arg1) != &sysctl_holddown_timer) { timer = (timer < SN_MIN_TIMER) ? (SN_MIN_TIMER) : (timer); } *(u_int *)arg1 = timer; return (0); } /** @ingroup sysctl * @brief sysctl callback for changing net.inet.ip.alias.sctp.hashtable_size * * Updates the hashtable_size sysctl variable. The new value should be a prime * number. We sanity check to ensure that the size is within the range * SN_MIN_HASH_SIZE-SN_MAX_HASH_SIZE. We then check the provided number to see * if it is prime. We approximate by checking that (2,3,5,7,11) are not factors, * incrementing the user provided value until we find a suitable number. */ int sysctl_chg_hashtable_size(SYSCTL_HANDLER_ARGS) { u_int size = *(u_int *)arg1; int error; error = sysctl_handle_int(oidp, &size, 0, req); - if (error) return (error); + if (error) + return (error); size = (size < SN_MIN_HASH_SIZE) ? (SN_MIN_HASH_SIZE) : ((size > SN_MAX_HASH_SIZE) ? (SN_MAX_HASH_SIZE) : (size)); size |= 0x00000001; /* make odd */ for (;(((size % 3) == 0) || ((size % 5) == 0) || ((size % 7) == 0) || ((size % 11) == 0)); size+=2); sysctl_hashtable_size = size; return (0); } /** @ingroup sysctl * @brief sysctl callback for changing net.inet.ip.alias.sctp.error_on_ootb * * Updates the error_on_clash sysctl variable. * If set to 0, no ErrorM will be sent if there is a look up table clash * If set to 1, an ErrorM is sent only to the local side * If set to 2, an ErrorM is sent to the local side and global side if there is * a partial association match * If set to 3, an ErrorM is sent to both local and global sides (DoS) risk. */ int sysctl_chg_error_on_ootb(SYSCTL_HANDLER_ARGS) { u_int flag = *(u_int *)arg1; int error; error = sysctl_handle_int(oidp, &flag, 0, req); - if (error) return (error); + if (error) + return (error); sysctl_error_on_ootb = (flag > SN_ERROR_ON_OOTB) ? SN_ERROR_ON_OOTB: flag; return (0); } /** @ingroup sysctl * @brief sysctl callback for changing net.inet.ip.alias.sctp.accept_global_ootb_addip * * If set to 1 the NAT will accept ootb global addip messages for processing (Security risk) * Default is 0, only responding to local ootb AddIP messages */ int sysctl_chg_accept_global_ootb_addip(SYSCTL_HANDLER_ARGS) { u_int flag = *(u_int *)arg1; int error; error = sysctl_handle_int(oidp, &flag, 0, req); - if (error) return (error); + if (error) + return (error); sysctl_accept_global_ootb_addip = (flag == 1) ? 1: 0; return (0); } /** @ingroup sysctl * @brief sysctl callback for changing net.inet.ip.alias.sctp.initialising_chunk_proc_limit * * Updates the initialising_chunk_proc_limit sysctl variable. Number of chunks * that should be processed if there is no current association found: > 0 (A * high value is a DoS risk) */ int sysctl_chg_initialising_chunk_proc_limit(SYSCTL_HANDLER_ARGS) { u_int proclimit = *(u_int *)arg1; int error; error = sysctl_handle_int(oidp, &proclimit, 0, req); - if (error) return (error); + if (error) + return (error); sysctl_initialising_chunk_proc_limit = (proclimit < 1) ? 1: proclimit; sysctl_chunk_proc_limit = (sysctl_chunk_proc_limit < sysctl_initialising_chunk_proc_limit) ? sysctl_initialising_chunk_proc_limit : sysctl_chunk_proc_limit; return (0); } /** @ingroup sysctl * @brief sysctl callback for changing net.inet.ip.alias.sctp.chunk_proc_limit * * Updates the chunk_proc_limit sysctl variable. * Number of chunks that should be processed to find key chunk: * >= initialising_chunk_proc_limit (A high value is a DoS risk) */ int sysctl_chg_chunk_proc_limit(SYSCTL_HANDLER_ARGS) { u_int proclimit = *(u_int *)arg1; int error; error = sysctl_handle_int(oidp, &proclimit, 0, req); - if (error) return (error); + if (error) + return (error); sysctl_chunk_proc_limit = (proclimit < sysctl_initialising_chunk_proc_limit) ? sysctl_initialising_chunk_proc_limit : proclimit; return (0); } /** @ingroup sysctl * @brief sysctl callback for changing net.inet.ip.alias.sctp.param_proc_limit * * Updates the param_proc_limit sysctl variable. * Number of parameters that should be processed to find key parameters: * > 1 (A high value is a DoS risk) */ int sysctl_chg_param_proc_limit(SYSCTL_HANDLER_ARGS) { u_int proclimit = *(u_int *)arg1; int error; error = sysctl_handle_int(oidp, &proclimit, 0, req); - if (error) return (error); + if (error) + return (error); sysctl_param_proc_limit = (proclimit < 2) ? 2 : proclimit; return (0); } /** @ingroup sysctl * @brief sysctl callback for changing net.inet.ip.alias.sctp.track_global_addresses * *Configures the global address tracking option within the NAT (0 - Global *tracking is disabled, > 0 - enables tracking but limits the number of global *IP addresses to this value) */ int sysctl_chg_track_global_addresses(SYSCTL_HANDLER_ARGS) { u_int num_to_track = *(u_int *)arg1; int error; error = sysctl_handle_int(oidp, &num_to_track, 0, req); - if (error) return (error); + if (error) + return (error); sysctl_track_global_addresses = (num_to_track > SN_MAX_GLOBAL_ADDRESSES) ? SN_MAX_GLOBAL_ADDRESSES : num_to_track; return (0); } /* ---------------------------------------------------------------------- * CODE BEGINS HERE * ---------------------------------------------------------------------- */ /** * @brief Initialises the SCTP NAT Implementation * * Creates the look-up tables and the timer queue and initialises all state * variables * * @param la Pointer to the relevant libalias instance */ -void AliasSctpInit(struct libalias *la) +void +AliasSctpInit(struct libalias *la) { /* Initialise association tables*/ int i; la->sctpNatTableSize = sysctl_hashtable_size; SN_LOG(SN_LOG_EVENT, SctpAliasLog("Initialising SCTP NAT Instance (hash_table_size:%d)\n", la->sctpNatTableSize)); la->sctpTableLocal = sn_calloc(la->sctpNatTableSize, sizeof(struct sctpNatTableL)); la->sctpTableGlobal = sn_calloc(la->sctpNatTableSize, sizeof(struct sctpNatTableG)); la->sctpNatTimer.TimerQ = sn_calloc(SN_TIMER_QUEUE_SIZE, sizeof(struct sctpTimerQ)); /* Initialise hash table */ for (i = 0; i < la->sctpNatTableSize; i++) { LIST_INIT(&la->sctpTableLocal[i]); LIST_INIT(&la->sctpTableGlobal[i]); } /* Initialise circular timer Q*/ for (i = 0; i < SN_TIMER_QUEUE_SIZE; i++) LIST_INIT(&la->sctpNatTimer.TimerQ[i]); #ifdef _KERNEL la->sctpNatTimer.loc_time=time_uptime; /* la->timeStamp is not set yet */ #else la->sctpNatTimer.loc_time=la->timeStamp; #endif la->sctpNatTimer.cur_loc = 0; la->sctpLinkCount = 0; } /** * @brief Cleans-up the SCTP NAT Implementation prior to unloading * * Removes all entries from the timer queue, freeing associations as it goes. * We then free memory allocated to the look-up tables and the time queue * * NOTE: We do not need to traverse the look-up tables as each association * will always have an entry in the timer queue, freeing this memory * once will free all memory allocated to entries in the look-up tables * * @param la Pointer to the relevant libalias instance */ -void AliasSctpTerm(struct libalias *la) +void +AliasSctpTerm(struct libalias *la) { struct sctp_nat_assoc *assoc1, *assoc2; int i; LIBALIAS_LOCK_ASSERT(la); SN_LOG(SN_LOG_EVENT, SctpAliasLog("Removing SCTP NAT Instance\n")); for (i = 0; i < SN_TIMER_QUEUE_SIZE; i++) { assoc1 = LIST_FIRST(&la->sctpNatTimer.TimerQ[i]); while (assoc1 != NULL) { freeGlobalAddressList(assoc1); assoc2 = LIST_NEXT(assoc1, timer_Q); sn_free(assoc1); assoc1 = assoc2; } } sn_free(la->sctpTableLocal); sn_free(la->sctpTableGlobal); sn_free(la->sctpNatTimer.TimerQ); } /** * @brief Handles SCTP packets passed from libalias * * This function needs to actually NAT/drop packets and possibly create and * send AbortM or ErrorM packets in response. The process involves: * - Validating the direction parameter passed by the caller * - Checking and handling any expired timers for the NAT * - Calling sctp_PktParser() to parse the packet * - Call ProcessSctpMsg() to decide the appropriate outcome and to update * the NAT tables * - Based on the return code either: * - NAT the packet * - Construct and send an ErrorM|AbortM packet * - Mark the association for removal from the tables * - Potentially remove the association from all lookup tables * - Return the appropriate result to libalias * * @param la Pointer to the relevant libalias instance * @param pip Pointer to IP packet to process * @param direction SN_TO_LOCAL | SN_TO_GLOBAL * * @return PKT_ALIAS_OK | PKT_ALIAS_IGNORE | PKT_ALIAS_ERROR */ int SctpAlias(struct libalias *la, struct ip *pip, int direction) { int rtnval; struct sctp_nat_msg msg; struct sctp_nat_assoc *assoc = NULL; if ((direction != SN_TO_LOCAL) && (direction != SN_TO_GLOBAL)) { SctpAliasLog("ERROR: Invalid direction\n"); return (PKT_ALIAS_ERROR); } sctp_CheckTimers(la); /* Check timers */ /* Parse the packet */ rtnval = sctp_PktParser(la, direction, pip, &msg, &assoc); //using *char (change to mbuf when get code from paolo) switch (rtnval) { case SN_PARSE_OK: break; case SN_PARSE_ERROR_CHHL: /* Not an error if there is a chunk length parsing error and this is a fragmented packet */ if (ntohs(pip->ip_off) & IP_MF) { rtnval = SN_PARSE_OK; break; } SN_LOG(SN_LOG_EVENT, logsctperror("SN_PARSE_ERROR", msg.sctp_hdr->v_tag, rtnval, direction)); return (PKT_ALIAS_ERROR); case SN_PARSE_ERROR_PARTIALLOOKUP: if (sysctl_error_on_ootb > SN_LOCALandPARTIAL_ERROR_ON_OOTB) { SN_LOG(SN_LOG_EVENT, logsctperror("SN_PARSE_ERROR", msg.sctp_hdr->v_tag, rtnval, direction)); return (PKT_ALIAS_ERROR); } case SN_PARSE_ERROR_LOOKUP: if (sysctl_error_on_ootb == SN_ERROR_ON_OOTB || (sysctl_error_on_ootb == SN_LOCALandPARTIAL_ERROR_ON_OOTB && direction == SN_TO_LOCAL) || (sysctl_error_on_ootb == SN_LOCAL_ERROR_ON_OOTB && direction == SN_TO_GLOBAL)) { TxAbortErrorM(la, &msg, assoc, SN_REFLECT_ERROR, direction); /*NB assoc=NULL */ return (PKT_ALIAS_RESPOND); } default: SN_LOG(SN_LOG_EVENT, logsctperror("SN_PARSE_ERROR", msg.sctp_hdr->v_tag, rtnval, direction)); return (PKT_ALIAS_ERROR); } SN_LOG(SN_LOG_DETAIL, logsctpassoc(assoc, "*"); logsctpparse(direction, &msg); - ); + ); /* Process the SCTP message */ rtnval = ProcessSctpMsg(la, direction, &msg, assoc); SN_LOG(SN_LOG_DEBUG_MAX, logsctpassoc(assoc, "-"); logSctpLocal(la); logSctpGlobal(la); - ); + ); SN_LOG(SN_LOG_DEBUG, logTimerQ(la)); switch (rtnval) { case SN_NAT_PKT: switch (direction) { case SN_TO_LOCAL: DifferentialChecksum(&(msg.ip_hdr->ip_sum), &(assoc->l_addr), &(msg.ip_hdr->ip_dst), 2); msg.ip_hdr->ip_dst = assoc->l_addr; /* change dst address to local address*/ break; case SN_TO_GLOBAL: DifferentialChecksum(&(msg.ip_hdr->ip_sum), &(assoc->a_addr), &(msg.ip_hdr->ip_src), 2); msg.ip_hdr->ip_src = assoc->a_addr; /* change src to alias addr*/ break; default: rtnval = SN_DROP_PKT; /* shouldn't get here, but if it does drop packet */ SN_LOG(SN_LOG_LOW, logsctperror("ERROR: Invalid direction", msg.sctp_hdr->v_tag, rtnval, direction)); break; } break; case SN_DROP_PKT: SN_LOG(SN_LOG_DETAIL, logsctperror("SN_DROP_PKT", msg.sctp_hdr->v_tag, rtnval, direction)); break; case SN_REPLY_ABORT: case SN_REPLY_ERROR: case SN_SEND_ABORT: TxAbortErrorM(la, &msg, assoc, rtnval, direction); break; default: // big error, remove association and go to idle and write log messages SN_LOG(SN_LOG_LOW, logsctperror("SN_PROCESSING_ERROR", msg.sctp_hdr->v_tag, rtnval, direction)); - assoc->state=SN_RM;/* Mark for removal*/ + assoc->state = SN_RM;/* Mark for removal*/ break; } /* Remove association if tagged for removal */ if (assoc->state == SN_RM) { if (assoc->TableRegister) { sctp_RmTimeOut(la, assoc); RmSctpAssoc(la, assoc); } LIBALIAS_LOCK_ASSERT(la); freeGlobalAddressList(assoc); sn_free(assoc); } switch (rtnval) { case SN_NAT_PKT: return (PKT_ALIAS_OK); case SN_SEND_ABORT: return (PKT_ALIAS_OK); case SN_REPLY_ABORT: case SN_REPLY_ERROR: case SN_REFLECT_ERROR: return (PKT_ALIAS_RESPOND); case SN_DROP_PKT: default: return (PKT_ALIAS_ERROR); } } /** * @brief Send an AbortM or ErrorM * * We construct the new SCTP packet to send in place of the existing packet we * have been asked to NAT. This function can only be called if the original * packet was successfully parsed as a valid SCTP packet. * * An AbortM (without cause) packet is the smallest SCTP packet available and as * such there is always space in the existing packet buffer to fit the AbortM * packet. An ErrorM packet is 4 bytes longer than the (the error cause is not * optional). An ErrorM is sent in response to an AddIP when the Vtag/address * combination, if added, will produce a conflict in the association look up * tables. It may also be used for an unexpected packet - a packet with no * matching association in the NAT table and we are requesting an AddIP so we * can add it. The smallest valid SCTP packet while the association is in an * up-state is a Heartbeat packet, which is big enough to be transformed to an * ErrorM. * * We create a temporary character array to store the packet as we are constructing * it. We then populate the array with appropriate values based on: * - Packet type (AbortM | ErrorM) * - Initial packet direction (SN_TO_LOCAL | SN_TO_GLOBAL) * - NAT response (Send packet | Reply packet) * * Once complete, we copy the contents of the temporary packet over the original * SCTP packet we were asked to NAT * * @param la Pointer to the relevant libalias instance * @param sm Pointer to sctp message information * @param assoc Pointer to current association details * @param sndrply SN_SEND_ABORT | SN_REPLY_ABORT | SN_REPLY_ERROR * @param direction SN_TO_LOCAL | SN_TO_GLOBAL */ static uint32_t local_sctp_finalize_crc32(uint32_t crc32c) { /* This routine is duplicated from SCTP * we need to do that since it MAY be that SCTP * is NOT compiled into the kernel. The CRC32C routines * however are always available in libkern. */ uint32_t result; #if BYTE_ORDER == BIG_ENDIAN uint8_t byte0, byte1, byte2, byte3; #endif /* Complement the result */ result = ~crc32c; #if BYTE_ORDER == BIG_ENDIAN /* * For BIG-ENDIAN.. aka Motorola byte order the result is in * little-endian form. So we must manually swap the bytes. Then we * can call htonl() which does nothing... */ byte0 = result & 0x000000ff; byte1 = (result >> 8) & 0x000000ff; byte2 = (result >> 16) & 0x000000ff; byte3 = (result >> 24) & 0x000000ff; crc32c = ((byte0 << 24) | (byte1 << 16) | (byte2 << 8) | byte3); #else /* * For INTEL platforms the result comes out in network order. No * htonl is required or the swap above. So we optimize out both the * htonl and the manual swap above. */ crc32c = result; #endif return (crc32c); } static void TxAbortErrorM(struct libalias *la, struct sctp_nat_msg *sm, struct sctp_nat_assoc *assoc, int sndrply, int direction) { int sctp_size = sizeof(struct sctphdr) + sizeof(struct sctp_chunkhdr) + sizeof(struct sctp_error_cause); int ip_size = sizeof(struct ip) + sctp_size; int include_error_cause = 1; char tmp_ip[ip_size]; char addrbuf[INET_ADDRSTRLEN]; if (ntohs(sm->ip_hdr->ip_len) < ip_size) { /* short packet, cannot send error cause */ include_error_cause = 0; ip_size = ip_size - sizeof(struct sctp_error_cause); sctp_size = sctp_size - sizeof(struct sctp_error_cause); } /* Assign header pointers packet */ struct ip* ip = (struct ip *) tmp_ip; struct sctphdr* sctp_hdr = (struct sctphdr *) ((char *) ip + sizeof(*ip)); struct sctp_chunkhdr* chunk_hdr = (struct sctp_chunkhdr *) ((char *) sctp_hdr + sizeof(*sctp_hdr)); struct sctp_error_cause* error_cause = (struct sctp_error_cause *) ((char *) chunk_hdr + sizeof(*chunk_hdr)); /* construct ip header */ ip->ip_v = sm->ip_hdr->ip_v; ip->ip_hl = 5; /* 5*32 bit words */ ip->ip_tos = 0; ip->ip_len = htons(ip_size); ip->ip_id = sm->ip_hdr->ip_id; ip->ip_off = 0; ip->ip_ttl = 255; ip->ip_p = IPPROTO_SCTP; /* The definitions below should be removed when they make it into the SCTP stack */ #define SCTP_MIDDLEBOX_FLAG 0x02 #define SCTP_NAT_TABLE_COLLISION 0x00b0 #define SCTP_MISSING_NAT 0x00b1 chunk_hdr->chunk_type = (sndrply & SN_TX_ABORT) ? SCTP_ABORT_ASSOCIATION : SCTP_OPERATION_ERROR; chunk_hdr->chunk_flags = SCTP_MIDDLEBOX_FLAG; if (include_error_cause) { error_cause->code = htons((sndrply & SN_REFLECT_ERROR) ? SCTP_MISSING_NAT : SCTP_NAT_TABLE_COLLISION); error_cause->length = htons(sizeof(struct sctp_error_cause)); chunk_hdr->chunk_length = htons(sizeof(*chunk_hdr) + sizeof(struct sctp_error_cause)); } else { chunk_hdr->chunk_length = htons(sizeof(*chunk_hdr)); } /* set specific values */ switch (sndrply) { case SN_REFLECT_ERROR: chunk_hdr->chunk_flags |= SCTP_HAD_NO_TCB; /* set Tbit */ sctp_hdr->v_tag = sm->sctp_hdr->v_tag; break; case SN_REPLY_ERROR: sctp_hdr->v_tag = (direction == SN_TO_LOCAL) ? assoc->g_vtag : assoc->l_vtag ; break; case SN_SEND_ABORT: sctp_hdr->v_tag = sm->sctp_hdr->v_tag; break; case SN_REPLY_ABORT: sctp_hdr->v_tag = sm->sctpchnk.Init->initiate_tag; break; } /* Set send/reply values */ if (sndrply == SN_SEND_ABORT) { /*pass through NAT */ ip->ip_src = (direction == SN_TO_LOCAL) ? sm->ip_hdr->ip_src : assoc->a_addr; ip->ip_dst = (direction == SN_TO_LOCAL) ? assoc->l_addr : sm->ip_hdr->ip_dst; sctp_hdr->src_port = sm->sctp_hdr->src_port; sctp_hdr->dest_port = sm->sctp_hdr->dest_port; } else { /* reply and reflect */ ip->ip_src = sm->ip_hdr->ip_dst; ip->ip_dst = sm->ip_hdr->ip_src; sctp_hdr->src_port = sm->sctp_hdr->dest_port; sctp_hdr->dest_port = sm->sctp_hdr->src_port; } /* Calculate IP header checksum */ ip->ip_sum = in_cksum_hdr(ip); /* calculate SCTP header CRC32 */ sctp_hdr->checksum = 0; sctp_hdr->checksum = local_sctp_finalize_crc32(calculate_crc32c(0xffffffff, (unsigned char *) sctp_hdr, sctp_size)); memcpy(sm->ip_hdr, ip, ip_size); SN_LOG(SN_LOG_EVENT,SctpAliasLog("%s %s 0x%x (->%s:%u vtag=0x%x crc=0x%x)\n", - ((sndrply == SN_SEND_ABORT) ? "Sending" : "Replying"), - ((sndrply & SN_TX_ERROR) ? "ErrorM" : "AbortM"), - (include_error_cause ? ntohs(error_cause->code) : 0), - inet_ntoa_r(ip->ip_dst, INET_NTOA_BUF(addrbuf)), - ntohs(sctp_hdr->dest_port), - ntohl(sctp_hdr->v_tag), ntohl(sctp_hdr->checksum))); + ((sndrply == SN_SEND_ABORT) ? "Sending" : "Replying"), + ((sndrply & SN_TX_ERROR) ? "ErrorM" : "AbortM"), + (include_error_cause ? ntohs(error_cause->code) : 0), + inet_ntoa_r(ip->ip_dst, INET_NTOA_BUF(addrbuf)), + ntohs(sctp_hdr->dest_port), + ntohl(sctp_hdr->v_tag), ntohl(sctp_hdr->checksum))); } /* ---------------------------------------------------------------------- * PACKET PARSER CODE * ---------------------------------------------------------------------- */ /** @addtogroup packet_parser * * These functions parse the SCTP packet and fill a sctp_nat_msg structure * with the parsed contents. */ /** @ingroup packet_parser * @brief Parses SCTP packets for the key SCTP chunk that will be processed * * This module parses SCTP packets for the key SCTP chunk that will be processed * The module completes the sctp_nat_msg structure and either retrieves the * relevant (existing) stored association from the Hash Tables or creates a new * association entity with state SN_ID * * @param la Pointer to the relevant libalias instance * @param direction SN_TO_LOCAL | SN_TO_GLOBAL * @param pip * @param sm Pointer to sctp message information * @param passoc Pointer to the association this SCTP Message belongs to * * @return SN_PARSE_OK | SN_PARSE_ERROR_* */ static int sctp_PktParser(struct libalias *la, int direction, struct ip *pip, struct sctp_nat_msg *sm, struct sctp_nat_assoc **passoc) //sctp_PktParser(int direction, struct mbuf *ipak, int ip_hdr_len,struct sctp_nat_msg *sm, struct sctp_nat_assoc *assoc) { struct sctphdr *sctp_hdr; struct sctp_chunkhdr *chunk_hdr; struct sctp_paramhdr *param_hdr; struct in_addr ipv4addr; int bytes_left; /* bytes left in ip packet */ int chunk_length; int chunk_count; int partial_match = 0; // mbuf *mp; // int mlen; // mlen = SCTP_HEADER_LEN(i_pak); // mp = SCTP_HEADER_TO_CHAIN(i_pak); /* does nothing in bsd since header and chain not separate */ /* * Note, that if the VTag is zero, it must be an INIT * Also, I am only interested in the content of INIT and ADDIP chunks */ // no mbuf stuff from Paolo yet so ... sm->ip_hdr = pip; /* remove ip header length from the bytes_left */ bytes_left = ntohs(pip->ip_len) - (pip->ip_hl << 2); /* Check SCTP header length and move to first chunk */ if (bytes_left < sizeof(struct sctphdr)) { sm->sctp_hdr = NULL; return (SN_PARSE_ERROR_IPSHL); /* packet not long enough*/ } sm->sctp_hdr = sctp_hdr = (struct sctphdr *) ip_next(pip); bytes_left -= sizeof(struct sctphdr); /* Check for valid ports (zero valued ports would find partially initialised associations */ if (sctp_hdr->src_port == 0 || sctp_hdr->dest_port == 0) return (SN_PARSE_ERROR_PORT); /* Check length of first chunk */ if (bytes_left < SN_MIN_CHUNK_SIZE) /* malformed chunk - could cause endless loop*/ return (SN_PARSE_ERROR_CHHL); /* packet not long enough for this chunk */ /* First chunk */ chunk_hdr = SN_SCTP_FIRSTCHUNK(sctp_hdr); chunk_length = SCTP_SIZE32(ntohs(chunk_hdr->chunk_length)); if ((chunk_length < SN_MIN_CHUNK_SIZE) || (chunk_length > bytes_left)) /* malformed chunk - could cause endless loop*/ return (SN_PARSE_ERROR_CHHL); if ((chunk_hdr->chunk_flags & SCTP_HAD_NO_TCB) && ((chunk_hdr->chunk_type == SCTP_ABORT_ASSOCIATION) || (chunk_hdr->chunk_type == SCTP_SHUTDOWN_COMPLETE))) { /* T-Bit set */ if (direction == SN_TO_LOCAL) *passoc = FindSctpGlobalT(la, pip->ip_src, sctp_hdr->v_tag, sctp_hdr->dest_port, sctp_hdr->src_port); else *passoc = FindSctpLocalT(la, pip->ip_dst, sctp_hdr->v_tag, sctp_hdr->dest_port, sctp_hdr->src_port); } else { /* Proper v_tag settings */ if (direction == SN_TO_LOCAL) *passoc = FindSctpGlobal(la, pip->ip_src, sctp_hdr->v_tag, sctp_hdr->src_port, sctp_hdr->dest_port, &partial_match); else *passoc = FindSctpLocal(la, pip->ip_src, pip->ip_dst, sctp_hdr->v_tag, sctp_hdr->src_port, sctp_hdr->dest_port); } chunk_count = 1; /* Real packet parsing occurs below */ sm->msg = SN_SCTP_OTHER;/* Initialise to largest value*/ sm->chunk_length = 0; /* only care about length for key chunks */ while (IS_SCTP_CONTROL(chunk_hdr)) { switch (chunk_hdr->chunk_type) { case SCTP_INITIATION: if (chunk_length < sizeof(struct sctp_init_chunk)) /* malformed chunk*/ return (SN_PARSE_ERROR_CHHL); sm->msg = SN_SCTP_INIT; sm->sctpchnk.Init = (struct sctp_init *) ((char *) chunk_hdr + sizeof(struct sctp_chunkhdr)); sm->chunk_length = chunk_length; /* if no existing association, create a new one */ if (*passoc == NULL) { if (sctp_hdr->v_tag == 0) { //Init requires vtag=0 *passoc = (struct sctp_nat_assoc *) sn_malloc(sizeof(struct sctp_nat_assoc)); if (*passoc == NULL) {/* out of resources */ return (SN_PARSE_ERROR_AS_MALLOC); } /* Initialize association - sn_malloc initializes memory to zeros */ (*passoc)->state = SN_ID; LIST_INIT(&((*passoc)->Gaddr)); /* always initialise to avoid memory problems */ (*passoc)->TableRegister = SN_NULL_TBL; return (SN_PARSE_OK); } return (SN_PARSE_ERROR_VTAG); } return (SN_PARSE_ERROR_LOOKUP); case SCTP_INITIATION_ACK: if (chunk_length < sizeof(struct sctp_init_ack_chunk)) /* malformed chunk*/ return (SN_PARSE_ERROR_CHHL); sm->msg = SN_SCTP_INITACK; sm->sctpchnk.InitAck = (struct sctp_init_ack *) ((char *) chunk_hdr + sizeof(struct sctp_chunkhdr)); sm->chunk_length = chunk_length; return ((*passoc == NULL) ? (SN_PARSE_ERROR_LOOKUP) : (SN_PARSE_OK)); case SCTP_ABORT_ASSOCIATION: /* access only minimum sized chunk */ sm->msg = SN_SCTP_ABORT; sm->chunk_length = chunk_length; return ((*passoc == NULL) ? (SN_PARSE_ERROR_LOOKUP_ABORT) : (SN_PARSE_OK)); case SCTP_SHUTDOWN_ACK: if (chunk_length < sizeof(struct sctp_shutdown_ack_chunk)) /* malformed chunk*/ return (SN_PARSE_ERROR_CHHL); if (sm->msg > SN_SCTP_SHUTACK) { sm->msg = SN_SCTP_SHUTACK; sm->chunk_length = chunk_length; } break; case SCTP_SHUTDOWN_COMPLETE: /* minimum sized chunk */ if (sm->msg > SN_SCTP_SHUTCOMP) { sm->msg = SN_SCTP_SHUTCOMP; sm->chunk_length = chunk_length; } return ((*passoc == NULL) ? (SN_PARSE_ERROR_LOOKUP) : (SN_PARSE_OK)); case SCTP_ASCONF: if (sm->msg > SN_SCTP_ASCONF) { if (chunk_length < (sizeof(struct sctp_asconf_chunk) + sizeof(struct sctp_ipv4addr_param))) /* malformed chunk*/ return (SN_PARSE_ERROR_CHHL); //leave parameter searching to later, if required param_hdr = (struct sctp_paramhdr *) ((char *) chunk_hdr + sizeof(struct sctp_asconf_chunk)); /*compulsory IP parameter*/ if (ntohs(param_hdr->param_type) == SCTP_IPV4_ADDRESS) { if ((*passoc == NULL) && (direction == SN_TO_LOCAL)) { /* AddIP with no association */ /* try look up with the ASCONF packet's alternative address */ ipv4addr.s_addr = ((struct sctp_ipv4addr_param *) param_hdr)->addr; *passoc = FindSctpGlobal(la, ipv4addr, sctp_hdr->v_tag, sctp_hdr->src_port, sctp_hdr->dest_port, &partial_match); } param_hdr = (struct sctp_paramhdr *) ((char *) param_hdr + sizeof(struct sctp_ipv4addr_param)); /*asconf's compulsory address parameter */ sm->chunk_length = chunk_length - sizeof(struct sctp_asconf_chunk) - sizeof(struct sctp_ipv4addr_param); /* rest of chunk */ } else { if (chunk_length < (sizeof(struct sctp_asconf_chunk) + sizeof(struct sctp_ipv6addr_param))) /* malformed chunk*/ return (SN_PARSE_ERROR_CHHL); param_hdr = (struct sctp_paramhdr *) ((char *) param_hdr + sizeof(struct sctp_ipv6addr_param)); /*asconf's compulsory address parameter */ sm->chunk_length = chunk_length - sizeof(struct sctp_asconf_chunk) - sizeof(struct sctp_ipv6addr_param); /* rest of chunk */ } sm->msg = SN_SCTP_ASCONF; sm->sctpchnk.Asconf = param_hdr; if (*passoc == NULL) { /* AddIP with no association */ *passoc = (struct sctp_nat_assoc *) sn_malloc(sizeof(struct sctp_nat_assoc)); if (*passoc == NULL) {/* out of resources */ return (SN_PARSE_ERROR_AS_MALLOC); } /* Initialize association - sn_malloc initializes memory to zeros */ (*passoc)->state = SN_ID; LIST_INIT(&((*passoc)->Gaddr)); /* always initialise to avoid memory problems */ (*passoc)->TableRegister = SN_NULL_TBL; return (SN_PARSE_OK); } } break; case SCTP_ASCONF_ACK: if (sm->msg > SN_SCTP_ASCONFACK) { if (chunk_length < sizeof(struct sctp_asconf_ack_chunk)) /* malformed chunk*/ return (SN_PARSE_ERROR_CHHL); //leave parameter searching to later, if required param_hdr = (struct sctp_paramhdr *) ((char *) chunk_hdr + sizeof(struct sctp_asconf_ack_chunk)); sm->msg = SN_SCTP_ASCONFACK; sm->sctpchnk.Asconf = param_hdr; sm->chunk_length = chunk_length - sizeof(struct sctp_asconf_ack_chunk); } break; default: break; /* do nothing*/ } /* if no association is found exit - we need to find an Init or AddIP within sysctl_initialising_chunk_proc_limit */ if ((*passoc == NULL) && (chunk_count >= sysctl_initialising_chunk_proc_limit)) return (SN_PARSE_ERROR_LOOKUP); /* finished with this chunk, on to the next chunk*/ bytes_left-= chunk_length; /* Is this the end of the packet ? */ if (bytes_left == 0) return (*passoc == NULL) ? (SN_PARSE_ERROR_LOOKUP) : (SN_PARSE_OK); /* Are there enough bytes in packet to at least retrieve length of next chunk ? */ if (bytes_left < SN_MIN_CHUNK_SIZE) return (SN_PARSE_ERROR_CHHL); chunk_hdr = SN_SCTP_NEXTCHUNK(chunk_hdr); /* Is the chunk long enough to not cause endless look and are there enough bytes in packet to read the chunk ? */ chunk_length = SCTP_SIZE32(ntohs(chunk_hdr->chunk_length)); if ((chunk_length < SN_MIN_CHUNK_SIZE) || (chunk_length > bytes_left)) return (SN_PARSE_ERROR_CHHL); if (++chunk_count > sysctl_chunk_proc_limit) return (SN_PARSE_OK); /* limit for processing chunks, take what we get */ } if (*passoc == NULL) return (partial_match) ? (SN_PARSE_ERROR_PARTIALLOOKUP) : (SN_PARSE_ERROR_LOOKUP); else return (SN_PARSE_OK); } /** @ingroup packet_parser * @brief Extract Vtags from Asconf Chunk * * GetAsconfVtags scans an Asconf Chunk for the vtags parameter, and then * extracts the vtags. * * GetAsconfVtags is not called from within sctp_PktParser. It is called only * from within ID_process when an AddIP has been received. * * @param la Pointer to the relevant libalias instance * @param sm Pointer to sctp message information * @param l_vtag Pointer to the local vtag in the association this SCTP Message belongs to * @param g_vtag Pointer to the local vtag in the association this SCTP Message belongs to * @param direction SN_TO_LOCAL | SN_TO_GLOBAL * * @return 1 - success | 0 - fail */ static int GetAsconfVtags(struct libalias *la, struct sctp_nat_msg *sm, uint32_t *l_vtag, uint32_t *g_vtag, int direction) { /* To be removed when information is in the sctp headers */ #define SCTP_VTAG_PARAM 0xC007 struct sctp_vtag_param { struct sctp_paramhdr ph;/* type=SCTP_VTAG_PARAM */ uint32_t local_vtag; uint32_t remote_vtag; - } __attribute__((packed)); + } __attribute__((packed)); struct sctp_vtag_param *vtag_param; struct sctp_paramhdr *param; int bytes_left; int param_size; int param_count; param_count = 1; param = sm->sctpchnk.Asconf; param_size = SCTP_SIZE32(ntohs(param->param_length)); bytes_left = sm->chunk_length; /* step through Asconf parameters */ while((bytes_left >= param_size) && (bytes_left >= SN_VTAG_PARAM_SIZE)) { if (ntohs(param->param_type) == SCTP_VTAG_PARAM) { vtag_param = (struct sctp_vtag_param *) param; switch (direction) { /* The Internet draft is a little ambigious as to order of these vtags. We think it is this way around. If we are wrong, the order will need to be changed. */ case SN_TO_GLOBAL: *g_vtag = vtag_param->local_vtag; *l_vtag = vtag_param->remote_vtag; break; case SN_TO_LOCAL: *g_vtag = vtag_param->remote_vtag; *l_vtag = vtag_param->local_vtag; break; } return (1); /* found */ } bytes_left -= param_size; - if (bytes_left < SN_MIN_PARAM_SIZE) return (0); + if (bytes_left < SN_MIN_PARAM_SIZE) + return (0); param = SN_SCTP_NEXTPARAM(param); param_size = SCTP_SIZE32(ntohs(param->param_length)); if (++param_count > sysctl_param_proc_limit) { SN_LOG(SN_LOG_EVENT, logsctperror("Parameter parse limit exceeded (GetAsconfVtags)", sm->sctp_hdr->v_tag, sysctl_param_proc_limit, direction)); return (0); /* not found limit exceeded*/ } } return (0); /* not found */ } /** @ingroup packet_parser * @brief AddGlobalIPAddresses from Init,InitAck,or AddIP packets * * AddGlobalIPAddresses scans an SCTP chunk (in sm) for Global IP addresses, and * adds them. * * @param sm Pointer to sctp message information * @param assoc Pointer to the association this SCTP Message belongs to * @param direction SN_TO_LOCAL | SN_TO_GLOBAL * */ static void AddGlobalIPAddresses(struct sctp_nat_msg *sm, struct sctp_nat_assoc *assoc, int direction) { struct sctp_ipv4addr_param *ipv4_param; struct sctp_paramhdr *param = NULL; struct sctp_GlobalAddress *G_Addr; struct in_addr g_addr = {0}; int bytes_left = 0; int param_size; int param_count, addr_param_count = 0; switch (direction) { case SN_TO_GLOBAL: /* does not contain global addresses */ g_addr = sm->ip_hdr->ip_dst; bytes_left = 0; /* force exit */ break; case SN_TO_LOCAL: g_addr = sm->ip_hdr->ip_src; param_count = 1; switch (sm->msg) { case SN_SCTP_INIT: bytes_left = sm->chunk_length - sizeof(struct sctp_init_chunk); param = (struct sctp_paramhdr *)((char *)sm->sctpchnk.Init + sizeof(struct sctp_init)); break; case SN_SCTP_INITACK: bytes_left = sm->chunk_length - sizeof(struct sctp_init_ack_chunk); param = (struct sctp_paramhdr *)((char *)sm->sctpchnk.InitAck + sizeof(struct sctp_init_ack)); break; case SN_SCTP_ASCONF: bytes_left = sm->chunk_length; param = sm->sctpchnk.Asconf; break; } } if (bytes_left >= SN_MIN_PARAM_SIZE) param_size = SCTP_SIZE32(ntohs(param->param_length)); else param_size = bytes_left+1; /* force skip loop */ if ((assoc->state == SN_ID) && ((sm->msg == SN_SCTP_INIT) || (bytes_left < SN_MIN_PARAM_SIZE))) {/* add pkt address */ G_Addr = (struct sctp_GlobalAddress *) sn_malloc(sizeof(struct sctp_GlobalAddress)); if (G_Addr == NULL) {/* out of resources */ SN_LOG(SN_LOG_EVENT, logsctperror("AddGlobalIPAddress: No resources for adding global address - revert to no tracking", sm->sctp_hdr->v_tag, 0, direction)); assoc->num_Gaddr = 0; /* don't track any more for this assoc*/ sysctl_track_global_addresses=0; return; } G_Addr->g_addr = g_addr; if (!Add_Global_Address_to_List(assoc, G_Addr)) SN_LOG(SN_LOG_EVENT, logsctperror("AddGlobalIPAddress: Address already in list", sm->sctp_hdr->v_tag, assoc->num_Gaddr, direction)); } /* step through parameters */ while((bytes_left >= param_size) && (bytes_left >= sizeof(struct sctp_ipv4addr_param))) { if (assoc->num_Gaddr >= sysctl_track_global_addresses) { SN_LOG(SN_LOG_EVENT, logsctperror("AddGlobalIPAddress: Maximum Number of addresses reached", sm->sctp_hdr->v_tag, sysctl_track_global_addresses, direction)); return; } switch (ntohs(param->param_type)) { case SCTP_ADD_IP_ADDRESS: /* skip to address parameter - leave param_size so bytes left will be calculated properly*/ param = (struct sctp_paramhdr *) &((struct sctp_asconf_addrv4_param *) param)->addrp; /* FALLTHROUGH */ case SCTP_IPV4_ADDRESS: ipv4_param = (struct sctp_ipv4addr_param *) param; /* add addresses to association */ G_Addr = (struct sctp_GlobalAddress *) sn_malloc(sizeof(struct sctp_GlobalAddress)); if (G_Addr == NULL) {/* out of resources */ SN_LOG(SN_LOG_EVENT, logsctperror("AddGlobalIPAddress: No resources for adding global address - revert to no tracking", sm->sctp_hdr->v_tag, 0, direction)); assoc->num_Gaddr = 0; /* don't track any more for this assoc*/ sysctl_track_global_addresses=0; return; } /* add address */ addr_param_count++; if ((sm->msg == SN_SCTP_ASCONF) && (ipv4_param->addr == INADDR_ANY)) { /* use packet address */ G_Addr->g_addr = g_addr; if (!Add_Global_Address_to_List(assoc, G_Addr)) SN_LOG(SN_LOG_EVENT, logsctperror("AddGlobalIPAddress: Address already in list", sm->sctp_hdr->v_tag, assoc->num_Gaddr, direction)); return; /*shouldn't be any other addresses if the zero address is given*/ } else { G_Addr->g_addr.s_addr = ipv4_param->addr; if (!Add_Global_Address_to_List(assoc, G_Addr)) SN_LOG(SN_LOG_EVENT, logsctperror("AddGlobalIPAddress: Address already in list", sm->sctp_hdr->v_tag, assoc->num_Gaddr, direction)); } } bytes_left -= param_size; if (bytes_left < SN_MIN_PARAM_SIZE) break; param = SN_SCTP_NEXTPARAM(param); param_size = SCTP_SIZE32(ntohs(param->param_length)); if (++param_count > sysctl_param_proc_limit) { SN_LOG(SN_LOG_EVENT, logsctperror("Parameter parse limit exceeded (AddGlobalIPAddress)", sm->sctp_hdr->v_tag, sysctl_param_proc_limit, direction)); break; /* limit exceeded*/ } } if (addr_param_count == 0) { SN_LOG(SN_LOG_DETAIL, logsctperror("AddGlobalIPAddress: no address parameters to add", sm->sctp_hdr->v_tag, assoc->num_Gaddr, direction)); } } /** * @brief Add_Global_Address_to_List * * Adds a global IP address to an associations address list, if it is not * already there. The first address added us usually the packet's address, and * is most likely to be used, so it is added at the beginning. Subsequent * addresses are added after this one. * * @param assoc Pointer to the association this SCTP Message belongs to * @param G_addr Pointer to the global address to add * * @return 1 - success | 0 - fail */ -static int Add_Global_Address_to_List(struct sctp_nat_assoc *assoc, struct sctp_GlobalAddress *G_addr) +static int +Add_Global_Address_to_List(struct sctp_nat_assoc *assoc, struct sctp_GlobalAddress *G_addr) { struct sctp_GlobalAddress *iter_G_Addr = NULL, *first_G_Addr = NULL; first_G_Addr = LIST_FIRST(&(assoc->Gaddr)); if (first_G_Addr == NULL) { LIST_INSERT_HEAD(&(assoc->Gaddr), G_addr, list_Gaddr); /* add new address to beginning of list*/ } else { LIST_FOREACH(iter_G_Addr, &(assoc->Gaddr), list_Gaddr) { if (G_addr->g_addr.s_addr == iter_G_Addr->g_addr.s_addr) return (0); /* already exists, so don't add */ } LIST_INSERT_AFTER(first_G_Addr, G_addr, list_Gaddr); /* add address to end of list*/ } assoc->num_Gaddr++; return (1); /* success */ } /** @ingroup packet_parser * @brief RmGlobalIPAddresses from DelIP packets * * RmGlobalIPAddresses scans an ASCONF chunk for DelIP parameters to remove the * given Global IP addresses from the association. It will not delete the * the address if it is a list of one address. * * * @param sm Pointer to sctp message information * @param assoc Pointer to the association this SCTP Message belongs to * @param direction SN_TO_LOCAL | SN_TO_GLOBAL * */ static void RmGlobalIPAddresses(struct sctp_nat_msg *sm, struct sctp_nat_assoc *assoc, int direction) { struct sctp_asconf_addrv4_param *asconf_ipv4_param; struct sctp_paramhdr *param; struct sctp_GlobalAddress *G_Addr, *G_Addr_tmp; struct in_addr g_addr; int bytes_left; int param_size; int param_count; if (direction == SN_TO_GLOBAL) g_addr = sm->ip_hdr->ip_dst; else g_addr = sm->ip_hdr->ip_src; bytes_left = sm->chunk_length; param_count = 1; param = sm->sctpchnk.Asconf; if (bytes_left >= SN_MIN_PARAM_SIZE) { param_size = SCTP_SIZE32(ntohs(param->param_length)); } else { SN_LOG(SN_LOG_EVENT, logsctperror("RmGlobalIPAddress: truncated packet - cannot remove IP addresses", sm->sctp_hdr->v_tag, sysctl_track_global_addresses, direction)); return; } /* step through Asconf parameters */ while((bytes_left >= param_size) && (bytes_left >= sizeof(struct sctp_ipv4addr_param))) { if (ntohs(param->param_type) == SCTP_DEL_IP_ADDRESS) { asconf_ipv4_param = (struct sctp_asconf_addrv4_param *) param; if (asconf_ipv4_param->addrp.addr == INADDR_ANY) { /* remove all bar pkt address */ LIST_FOREACH_SAFE(G_Addr, &(assoc->Gaddr), list_Gaddr, G_Addr_tmp) { if (G_Addr->g_addr.s_addr != sm->ip_hdr->ip_src.s_addr) { if (assoc->num_Gaddr > 1) { /* only delete if more than one */ LIST_REMOVE(G_Addr, list_Gaddr); sn_free(G_Addr); assoc->num_Gaddr--; } else { SN_LOG(SN_LOG_EVENT, logsctperror("RmGlobalIPAddress: Request to remove last IP address (didn't)", sm->sctp_hdr->v_tag, assoc->num_Gaddr, direction)); } } } return; /*shouldn't be any other addresses if the zero address is given*/ } else { LIST_FOREACH_SAFE(G_Addr, &(assoc->Gaddr), list_Gaddr, G_Addr_tmp) { if (G_Addr->g_addr.s_addr == asconf_ipv4_param->addrp.addr) { if (assoc->num_Gaddr > 1) { /* only delete if more than one */ LIST_REMOVE(G_Addr, list_Gaddr); sn_free(G_Addr); assoc->num_Gaddr--; break; /* Since add only adds new addresses, there should be no double entries */ } else { SN_LOG(SN_LOG_EVENT, logsctperror("RmGlobalIPAddress: Request to remove last IP address (didn't)", sm->sctp_hdr->v_tag, assoc->num_Gaddr, direction)); } } } } } bytes_left -= param_size; - if (bytes_left == 0) return; + if (bytes_left == 0) + return; else if (bytes_left < SN_MIN_PARAM_SIZE) { SN_LOG(SN_LOG_EVENT, logsctperror("RmGlobalIPAddress: truncated packet - may not have removed all IP addresses", sm->sctp_hdr->v_tag, sysctl_track_global_addresses, direction)); return; } param = SN_SCTP_NEXTPARAM(param); param_size = SCTP_SIZE32(ntohs(param->param_length)); if (++param_count > sysctl_param_proc_limit) { SN_LOG(SN_LOG_EVENT, logsctperror("Parameter parse limit exceeded (RmGlobalIPAddress)", sm->sctp_hdr->v_tag, sysctl_param_proc_limit, direction)); return; /* limit exceeded*/ } } } /** @ingroup packet_parser * @brief Check that ASCONF was successful * * Each ASCONF configuration parameter carries a correlation ID which should be * matched with an ASCONFack. This is difficult for a NAT, since every * association could potentially have a number of outstanding ASCONF * configuration parameters, which should only be activated on receipt of the * ACK. * * Currently we only look for an ACK when the NAT is setting up a new * association (ie AddIP for a connection that the NAT does not know about * because the original Init went through a public interface or another NAT) * Since there is currently no connection on this path, there should be no other * ASCONF configuration parameters outstanding, so we presume that if there is * an ACK that it is responding to the AddIP and activate the new association. * * @param la Pointer to the relevant libalias instance * @param sm Pointer to sctp message information * @param direction SN_TO_LOCAL | SN_TO_GLOBAL * * @return 1 - success | 0 - fail */ static int IsASCONFack(struct libalias *la, struct sctp_nat_msg *sm, int direction) { struct sctp_paramhdr *param; int bytes_left; int param_size; int param_count; param_count = 1; param = sm->sctpchnk.Asconf; param_size = SCTP_SIZE32(ntohs(param->param_length)); if (param_size == 8) return (1); /*success - default acknowledgement of everything */ bytes_left = sm->chunk_length; if (bytes_left < param_size) return (0); /* not found */ /* step through Asconf parameters */ while(bytes_left >= SN_ASCONFACK_PARAM_SIZE) { if (ntohs(param->param_type) == SCTP_SUCCESS_REPORT) return (1); /* success - but can't match correlation IDs - should only be one */ /* check others just in case */ bytes_left -= param_size; - if (bytes_left >= SN_MIN_PARAM_SIZE) { + if (bytes_left >= SN_MIN_PARAM_SIZE) param = SN_SCTP_NEXTPARAM(param); - } else { + else return (0); - } + param_size = SCTP_SIZE32(ntohs(param->param_length)); - if (bytes_left < param_size) return (0); + if (bytes_left < param_size) + return (0); if (++param_count > sysctl_param_proc_limit) { SN_LOG(SN_LOG_EVENT, logsctperror("Parameter parse limit exceeded (IsASCONFack)", sm->sctp_hdr->v_tag, sysctl_param_proc_limit, direction)); return (0); /* not found limit exceeded*/ } } return (0); /* not success */ } /** @ingroup packet_parser * @brief Check to see if ASCONF contains an Add IP or Del IP parameter * * IsADDorDEL scans an ASCONF packet to see if it contains an AddIP or DelIP * parameter * * @param la Pointer to the relevant libalias instance * @param sm Pointer to sctp message information * @param direction SN_TO_LOCAL | SN_TO_GLOBAL * * @return SCTP_ADD_IP_ADDRESS | SCTP_DEL_IP_ADDRESS | 0 - fail */ static int IsADDorDEL(struct libalias *la, struct sctp_nat_msg *sm, int direction) { struct sctp_paramhdr *param; int bytes_left; int param_size; int param_count; param_count = 1; param = sm->sctpchnk.Asconf; param_size = SCTP_SIZE32(ntohs(param->param_length)); bytes_left = sm->chunk_length; if (bytes_left < param_size) return (0); /* not found */ /* step through Asconf parameters */ while(bytes_left >= SN_ASCONFACK_PARAM_SIZE) { if (ntohs(param->param_type) == SCTP_ADD_IP_ADDRESS) return (SCTP_ADD_IP_ADDRESS); else if (ntohs(param->param_type) == SCTP_DEL_IP_ADDRESS) return (SCTP_DEL_IP_ADDRESS); /* check others just in case */ bytes_left -= param_size; - if (bytes_left >= SN_MIN_PARAM_SIZE) { + if (bytes_left >= SN_MIN_PARAM_SIZE) param = SN_SCTP_NEXTPARAM(param); - } else { + else return (0); /*Neither found */ - } + param_size = SCTP_SIZE32(ntohs(param->param_length)); - if (bytes_left < param_size) return (0); + if (bytes_left < param_size) + return (0); if (++param_count > sysctl_param_proc_limit) { SN_LOG(SN_LOG_EVENT, logsctperror("Parameter parse limit exceeded IsADDorDEL)", sm->sctp_hdr->v_tag, sysctl_param_proc_limit, direction)); return (0); /* not found limit exceeded*/ } } return (0); /*Neither found */ } /* ---------------------------------------------------------------------- * STATE MACHINE CODE * ---------------------------------------------------------------------- */ /** @addtogroup state_machine * * The SCTP NAT State Machine functions will: * - Process an already parsed packet * - Use the existing NAT Hash Tables * - Determine the next state for the association * - Update the NAT Hash Tables and Timer Queues * - Return the appropriate action to take with the packet */ /** @ingroup state_machine * @brief Process SCTP message * * This function is the base state machine. It calls the processing engine for * each state. * * @param la Pointer to the relevant libalias instance * @param direction SN_TO_LOCAL | SN_TO_GLOBAL * @param sm Pointer to sctp message information * @param assoc Pointer to the association this SCTP Message belongs to * * @return SN_DROP_PKT | SN_NAT_PKT | SN_REPLY_ABORT | SN_REPLY_ERROR | SN_PROCESSING_ERROR */ static int ProcessSctpMsg(struct libalias *la, int direction, struct sctp_nat_msg *sm, struct sctp_nat_assoc *assoc) { int rtnval; switch (assoc->state) { case SN_ID: /* Idle */ rtnval = ID_process(la, direction, assoc, sm); if (rtnval != SN_NAT_PKT) { assoc->state = SN_RM;/* Mark for removal*/ } return (rtnval); case SN_INi: /* Initialising - Init */ return (INi_process(la, direction, assoc, sm)); case SN_INa: /* Initialising - AddIP */ return (INa_process(la, direction, assoc, sm)); case SN_UP: /* Association UP */ return (UP_process(la, direction, assoc, sm)); case SN_CL: /* Association Closing */ return (CL_process(la, direction, assoc, sm)); } return (SN_PROCESSING_ERROR); } /** @ingroup state_machine * @brief Process SCTP message while in the Idle state * * This function looks for an Incoming INIT or AddIP message. * * All other SCTP messages are invalid when in SN_ID, and are dropped. * * @param la Pointer to the relevant libalias instance * @param direction SN_TO_LOCAL | SN_TO_GLOBAL * @param sm Pointer to sctp message information * @param assoc Pointer to the association this SCTP Message belongs to * * @return SN_NAT_PKT | SN_DROP_PKT | SN_REPLY_ABORT | SN_REPLY_ERROR */ static int ID_process(struct libalias *la, int direction, struct sctp_nat_assoc *assoc, struct sctp_nat_msg *sm) { switch (sm->msg) { case SN_SCTP_ASCONF: /* a packet containing an ASCONF chunk with ADDIP */ if (!sysctl_accept_global_ootb_addip && (direction == SN_TO_LOCAL)) return (SN_DROP_PKT); /* if this Asconf packet does not contain the Vtag parameters it is of no use in Idle state */ if (!GetAsconfVtags(la, sm, &(assoc->l_vtag), &(assoc->g_vtag), direction)) return (SN_DROP_PKT); /* FALLTHROUGH */ case SN_SCTP_INIT: /* a packet containing an INIT chunk or an ASCONF AddIP */ if (sysctl_track_global_addresses) AddGlobalIPAddresses(sm, assoc, direction); switch (direction) { case SN_TO_GLOBAL: assoc->l_addr = sm->ip_hdr->ip_src; assoc->a_addr = FindAliasAddress(la, assoc->l_addr); assoc->l_port = sm->sctp_hdr->src_port; assoc->g_port = sm->sctp_hdr->dest_port; if (sm->msg == SN_SCTP_INIT) assoc->g_vtag = sm->sctpchnk.Init->initiate_tag; - if (AddSctpAssocGlobal(la, assoc)) /* DB clash *///**** need to add dst address + if (AddSctpAssocGlobal(la, assoc)) /* DB clash: need to add dst address */ return ((sm->msg == SN_SCTP_INIT) ? SN_REPLY_ABORT : SN_REPLY_ERROR); if (sm->msg == SN_SCTP_ASCONF) { if (AddSctpAssocLocal(la, assoc, sm->ip_hdr->ip_dst)) /* DB clash */ return (SN_REPLY_ERROR); assoc->TableRegister |= SN_WAIT_TOLOCAL; /* wait for tolocal ack */ } break; case SN_TO_LOCAL: assoc->l_addr = FindSctpRedirectAddress(la, sm); assoc->a_addr = sm->ip_hdr->ip_dst; assoc->l_port = sm->sctp_hdr->dest_port; assoc->g_port = sm->sctp_hdr->src_port; if (sm->msg == SN_SCTP_INIT) assoc->l_vtag = sm->sctpchnk.Init->initiate_tag; if (AddSctpAssocLocal(la, assoc, sm->ip_hdr->ip_src)) /* DB clash */ return ((sm->msg == SN_SCTP_INIT) ? SN_REPLY_ABORT : SN_REPLY_ERROR); if (sm->msg == SN_SCTP_ASCONF) { - if (AddSctpAssocGlobal(la, assoc)) /* DB clash */ //**** need to add src address + if (AddSctpAssocGlobal(la, assoc)) /* DB clash: need to add src address */ return (SN_REPLY_ERROR); assoc->TableRegister |= SN_WAIT_TOGLOBAL; /* wait for toglobal ack */ - } + } break; } assoc->state = (sm->msg == SN_SCTP_INIT) ? SN_INi : SN_INa; assoc->exp = SN_I_T(la); sctp_AddTimeOut(la,assoc); return (SN_NAT_PKT); default: /* Any other type of SCTP message is not valid in Idle */ return (SN_DROP_PKT); } return (SN_DROP_PKT);/* shouldn't get here very bad: log, drop and hope for the best */ } /** @ingroup state_machine * @brief Process SCTP message while waiting for an INIT-ACK message * * Only an INIT-ACK, resent INIT, or an ABORT SCTP packet are valid in this * state, all other packets are dropped. * * @param la Pointer to the relevant libalias instance * @param direction SN_TO_LOCAL | SN_TO_GLOBAL * @param sm Pointer to sctp message information * @param assoc Pointer to the association this SCTP Message belongs to * * @return SN_NAT_PKT | SN_DROP_PKT | SN_REPLY_ABORT */ static int INi_process(struct libalias *la, int direction, struct sctp_nat_assoc *assoc, struct sctp_nat_msg *sm) { switch (sm->msg) { case SN_SCTP_INIT: /* a packet containing a retransmitted INIT chunk */ sctp_ResetTimeOut(la, assoc, SN_I_T(la)); return (SN_NAT_PKT); case SN_SCTP_INITACK: /* a packet containing an INIT-ACK chunk */ switch (direction) { case SN_TO_LOCAL: if (assoc->num_Gaddr) /*If tracking global addresses for this association */ AddGlobalIPAddresses(sm, assoc, direction); assoc->l_vtag = sm->sctpchnk.Init->initiate_tag; if (AddSctpAssocLocal(la, assoc, sm->ip_hdr->ip_src)) { /* DB clash */ assoc->state = SN_RM;/* Mark for removal*/ return (SN_SEND_ABORT); } break; case SN_TO_GLOBAL: assoc->l_addr = sm->ip_hdr->ip_src; // Only if not set in Init! * assoc->g_vtag = sm->sctpchnk.Init->initiate_tag; if (AddSctpAssocGlobal(la, assoc)) { /* DB clash */ assoc->state = SN_RM;/* Mark for removal*/ return (SN_SEND_ABORT); } break; } assoc->state = SN_UP;/* association established for NAT */ sctp_ResetTimeOut(la,assoc, SN_U_T(la)); return (SN_NAT_PKT); case SN_SCTP_ABORT: /* a packet containing an ABORT chunk */ assoc->state = SN_RM;/* Mark for removal*/ return (SN_NAT_PKT); default: return (SN_DROP_PKT); } return (SN_DROP_PKT);/* shouldn't get here very bad: log, drop and hope for the best */ } /** @ingroup state_machine * @brief Process SCTP message while waiting for an AddIp-ACK message * * Only an AddIP-ACK, resent AddIP, or an ABORT message are valid, all other * SCTP packets are dropped * * @param la Pointer to the relevant libalias instance * @param direction SN_TO_LOCAL | SN_TO_GLOBAL * @param sm Pointer to sctp message information * @param assoc Pointer to the association this SCTP Message belongs to * * @return SN_NAT_PKT | SN_DROP_PKT */ static int INa_process(struct libalias *la, int direction,struct sctp_nat_assoc *assoc, struct sctp_nat_msg *sm) { switch (sm->msg) { case SN_SCTP_ASCONF: /* a packet containing an ASCONF chunk*/ sctp_ResetTimeOut(la,assoc, SN_I_T(la)); return (SN_NAT_PKT); case SN_SCTP_ASCONFACK: /* a packet containing an ASCONF chunk with a ADDIP-ACK */ switch (direction) { case SN_TO_LOCAL: if (!(assoc->TableRegister & SN_WAIT_TOLOCAL)) /* wrong direction */ return (SN_DROP_PKT); break; case SN_TO_GLOBAL: if (!(assoc->TableRegister & SN_WAIT_TOGLOBAL)) /* wrong direction */ return (SN_DROP_PKT); } if (IsASCONFack(la,sm,direction)) { assoc->TableRegister &= SN_BOTH_TBL; /* remove wait flags */ assoc->state = SN_UP; /* association established for NAT */ sctp_ResetTimeOut(la,assoc, SN_U_T(la)); return (SN_NAT_PKT); } else { assoc->state = SN_RM;/* Mark for removal*/ return (SN_NAT_PKT); } case SN_SCTP_ABORT: /* a packet containing an ABORT chunk */ assoc->state = SN_RM;/* Mark for removal*/ return (SN_NAT_PKT); default: return (SN_DROP_PKT); } return (SN_DROP_PKT);/* shouldn't get here very bad: log, drop and hope for the best */ } /** @ingroup state_machine * @brief Process SCTP messages while association is UP redirecting packets * * While in the SN_UP state, all packets for the particular association * are passed. Only a SHUT-ACK or an ABORT will cause a change of state. * * @param la Pointer to the relevant libalias instance * @param direction SN_TO_LOCAL | SN_TO_GLOBAL * @param sm Pointer to sctp message information * @param assoc Pointer to the association this SCTP Message belongs to * * @return SN_NAT_PKT | SN_DROP_PKT */ static int UP_process(struct libalias *la, int direction, struct sctp_nat_assoc *assoc, struct sctp_nat_msg *sm) { switch (sm->msg) { case SN_SCTP_SHUTACK: /* a packet containing a SHUTDOWN-ACK chunk */ assoc->state = SN_CL; sctp_ResetTimeOut(la,assoc, SN_C_T(la)); return (SN_NAT_PKT); case SN_SCTP_ABORT: /* a packet containing an ABORT chunk */ assoc->state = SN_RM;/* Mark for removal*/ return (SN_NAT_PKT); case SN_SCTP_ASCONF: /* a packet containing an ASCONF chunk*/ if ((direction == SN_TO_LOCAL) && assoc->num_Gaddr) /*If tracking global addresses for this association & from global side */ switch (IsADDorDEL(la,sm,direction)) { case SCTP_ADD_IP_ADDRESS: AddGlobalIPAddresses(sm, assoc, direction); break; case SCTP_DEL_IP_ADDRESS: RmGlobalIPAddresses(sm, assoc, direction); break; - } /* fall through to default */ + } + /* fall through to default */ default: sctp_ResetTimeOut(la,assoc, SN_U_T(la)); return (SN_NAT_PKT); /* forward packet */ } return (SN_DROP_PKT);/* shouldn't get here very bad: log, drop and hope for the best */ } /** @ingroup state_machine * @brief Process SCTP message while association is in the process of closing * * This function waits for a SHUT-COMP to close the association. Depending on * the setting of sysctl_holddown_timer it may not remove the association * immediately, but leave it up until SN_X_T(la). Only SHUT-COMP, SHUT-ACK, and * ABORT packets are permitted in this state. All other packets are dropped. * * @param la Pointer to the relevant libalias instance * @param direction SN_TO_LOCAL | SN_TO_GLOBAL * @param sm Pointer to sctp message information * @param assoc Pointer to the association this SCTP Message belongs to * * @return SN_NAT_PKT | SN_DROP_PKT */ static int CL_process(struct libalias *la, int direction,struct sctp_nat_assoc *assoc, struct sctp_nat_msg *sm) { switch (sm->msg) { case SN_SCTP_SHUTCOMP: /* a packet containing a SHUTDOWN-COMPLETE chunk */ assoc->state = SN_CL; /* Stay in Close state until timeout */ if (sysctl_holddown_timer > 0) sctp_ResetTimeOut(la, assoc, SN_X_T(la));/* allow to stay open for Tbit packets*/ else assoc->state = SN_RM;/* Mark for removal*/ return (SN_NAT_PKT); case SN_SCTP_SHUTACK: /* a packet containing a SHUTDOWN-ACK chunk */ assoc->state = SN_CL; /* Stay in Close state until timeout */ sctp_ResetTimeOut(la, assoc, SN_C_T(la)); return (SN_NAT_PKT); case SN_SCTP_ABORT: /* a packet containing an ABORT chunk */ assoc->state = SN_RM;/* Mark for removal*/ return (SN_NAT_PKT); default: return (SN_DROP_PKT); } return (SN_DROP_PKT);/* shouldn't get here very bad: log, drop and hope for the best */ } /* ---------------------------------------------------------------------- * HASH TABLE CODE * ---------------------------------------------------------------------- */ /** @addtogroup Hash * * The Hash functions facilitate searching the NAT Hash Tables for associations * as well as adding/removing associations from the table(s). */ /** @ingroup Hash * @brief Find the SCTP association given the local address, port and vtag * * Searches the local look-up table for the association entry matching the * provided local tuple * * @param la Pointer to the relevant libalias instance * @param l_addr local address * @param g_addr global address * @param l_vtag local Vtag * @param l_port local Port * @param g_port global Port * * @return pointer to association or NULL */ -static struct sctp_nat_assoc* +static struct sctp_nat_assoc * FindSctpLocal(struct libalias *la, struct in_addr l_addr, struct in_addr g_addr, uint32_t l_vtag, uint16_t l_port, uint16_t g_port) { u_int i; struct sctp_nat_assoc *assoc = NULL; struct sctp_GlobalAddress *G_Addr = NULL; if (l_vtag != 0) { /* an init packet, vtag==0 */ i = SN_TABLE_HASH(l_vtag, l_port, la->sctpNatTableSize); LIST_FOREACH(assoc, &la->sctpTableLocal[i], list_L) { if ((assoc->l_vtag == l_vtag) && (assoc->l_port == l_port) && (assoc->g_port == g_port)\ && (assoc->l_addr.s_addr == l_addr.s_addr)) { if (assoc->num_Gaddr) { LIST_FOREACH(G_Addr, &(assoc->Gaddr), list_Gaddr) { if (G_Addr->g_addr.s_addr == g_addr.s_addr) return (assoc); } } else { return (assoc); } } } } return (NULL); } /** @ingroup Hash * @brief Check for Global Clash * * Searches the global look-up table for the association entry matching the * provided global <(addresses):ports:vtag> tuple * * @param la Pointer to the relevant libalias instance * @param Cassoc association being checked for a clash * * @return pointer to association or NULL */ -static struct sctp_nat_assoc* -FindSctpGlobalClash(struct libalias *la, struct sctp_nat_assoc *Cassoc) +static struct sctp_nat_assoc * +FindSctpGlobalClash(struct libalias *la, struct sctp_nat_assoc *Cassoc) { u_int i; struct sctp_nat_assoc *assoc = NULL; struct sctp_GlobalAddress *G_Addr = NULL; struct sctp_GlobalAddress *G_AddrC = NULL; if (Cassoc->g_vtag != 0) { /* an init packet, vtag==0 */ i = SN_TABLE_HASH(Cassoc->g_vtag, Cassoc->g_port, la->sctpNatTableSize); LIST_FOREACH(assoc, &la->sctpTableGlobal[i], list_G) { if ((assoc->g_vtag == Cassoc->g_vtag) && (assoc->g_port == Cassoc->g_port) && (assoc->l_port == Cassoc->l_port)) { if (assoc->num_Gaddr) { LIST_FOREACH(G_AddrC, &(Cassoc->Gaddr), list_Gaddr) { LIST_FOREACH(G_Addr, &(assoc->Gaddr), list_Gaddr) { if (G_Addr->g_addr.s_addr == G_AddrC->g_addr.s_addr) return (assoc); } } } else { return (assoc); } } } } return (NULL); } /** @ingroup Hash * @brief Find the SCTP association given the global port and vtag * * Searches the global look-up table for the association entry matching the * provided global tuple * * If all but the global address match it sets partial_match to 1 to indicate a * partial match. If the NAT is tracking global IP addresses for this * association, the NAT may respond with an ERRORM to request the missing * address to be added. * * @param la Pointer to the relevant libalias instance * @param g_addr global address * @param g_vtag global vtag * @param g_port global port * @param l_port local port * * @return pointer to association or NULL */ -static struct sctp_nat_assoc* +static struct sctp_nat_assoc * FindSctpGlobal(struct libalias *la, struct in_addr g_addr, uint32_t g_vtag, uint16_t g_port, uint16_t l_port, int *partial_match) { u_int i; struct sctp_nat_assoc *assoc = NULL; struct sctp_GlobalAddress *G_Addr = NULL; *partial_match = 0; if (g_vtag != 0) { /* an init packet, vtag==0 */ i = SN_TABLE_HASH(g_vtag, g_port, la->sctpNatTableSize); LIST_FOREACH(assoc, &la->sctpTableGlobal[i], list_G) { if ((assoc->g_vtag == g_vtag) && (assoc->g_port == g_port) && (assoc->l_port == l_port)) { *partial_match = 1; if (assoc->num_Gaddr) { LIST_FOREACH(G_Addr, &(assoc->Gaddr), list_Gaddr) { if (G_Addr->g_addr.s_addr == g_addr.s_addr) return (assoc); } } else { return (assoc); } } } } return (NULL); } /** @ingroup Hash * @brief Find the SCTP association for a T-Flag message (given the global port and local vtag) * * Searches the local look-up table for a unique association entry matching the * provided global port and local vtag information * * @param la Pointer to the relevant libalias instance * @param g_addr global address * @param l_vtag local Vtag * @param g_port global Port * @param l_port local Port * * @return pointer to association or NULL */ -static struct sctp_nat_assoc* +static struct sctp_nat_assoc * FindSctpLocalT(struct libalias *la, struct in_addr g_addr, uint32_t l_vtag, uint16_t g_port, uint16_t l_port) { u_int i; struct sctp_nat_assoc *assoc = NULL, *lastmatch = NULL; struct sctp_GlobalAddress *G_Addr = NULL; int cnt = 0; if (l_vtag != 0) { /* an init packet, vtag==0 */ i = SN_TABLE_HASH(l_vtag, g_port, la->sctpNatTableSize); LIST_FOREACH(assoc, &la->sctpTableGlobal[i], list_G) { if ((assoc->g_vtag == l_vtag) && (assoc->g_port == g_port) && (assoc->l_port == l_port)) { if (assoc->num_Gaddr) { LIST_FOREACH(G_Addr, &(assoc->Gaddr), list_Gaddr) { if (G_Addr->g_addr.s_addr == g_addr.s_addr) return (assoc); /* full match */ } } else { - if (++cnt > 1) return (NULL); + if (++cnt > 1) + return (NULL); lastmatch = assoc; } } } } /* If there is more than one match we do not know which local address to send to */ return (cnt ? lastmatch : NULL); } /** @ingroup Hash * @brief Find the SCTP association for a T-Flag message (given the local port and global vtag) * * Searches the global look-up table for a unique association entry matching the * provided local port and global vtag information * * @param la Pointer to the relevant libalias instance * @param g_addr global address * @param g_vtag global vtag * @param l_port local port * @param g_port global port * * @return pointer to association or NULL */ -static struct sctp_nat_assoc* +static struct sctp_nat_assoc * FindSctpGlobalT(struct libalias *la, struct in_addr g_addr, uint32_t g_vtag, uint16_t l_port, uint16_t g_port) { u_int i; struct sctp_nat_assoc *assoc = NULL; struct sctp_GlobalAddress *G_Addr = NULL; if (g_vtag != 0) { /* an init packet, vtag==0 */ i = SN_TABLE_HASH(g_vtag, l_port, la->sctpNatTableSize); LIST_FOREACH(assoc, &la->sctpTableLocal[i], list_L) { if ((assoc->l_vtag == g_vtag) && (assoc->l_port == l_port) && (assoc->g_port == g_port)) { if (assoc->num_Gaddr) { LIST_FOREACH(G_Addr, &(assoc->Gaddr), list_Gaddr) { if (G_Addr->g_addr.s_addr == g_addr.s_addr) return (assoc); } } else { return (assoc); } } } } return (NULL); } /** @ingroup Hash * @brief Add the sctp association information to the local look up table * * Searches the local look-up table for an existing association with the same * details. If a match exists and is ONLY in the local look-up table then this * is a repeated INIT packet, we need to remove this association from the * look-up table and add the new association * * The new association is added to the head of the list and state is updated * * @param la Pointer to the relevant libalias instance * @param assoc pointer to sctp association * @param g_addr global address * * @return SN_ADD_OK | SN_ADD_CLASH */ static int AddSctpAssocLocal(struct libalias *la, struct sctp_nat_assoc *assoc, struct in_addr g_addr) { struct sctp_nat_assoc *found; LIBALIAS_LOCK_ASSERT(la); found = FindSctpLocal(la, assoc->l_addr, g_addr, assoc->l_vtag, assoc->l_port, assoc->g_port); /* * Note that if a different global address initiated this Init, * ie it wasn't resent as presumed: * - the local receiver if receiving it for the first time will establish * an association with the new global host * - if receiving an init from a different global address after sending a * lost initack it will send an initack to the new global host, the first * association attempt will then be blocked if retried. */ if (found != NULL) { if ((found->TableRegister == SN_LOCAL_TBL) && (found->g_port == assoc->g_port)) { /* resent message */ RmSctpAssoc(la, found); sctp_RmTimeOut(la, found); freeGlobalAddressList(found); sn_free(found); } else return (SN_ADD_CLASH); } LIST_INSERT_HEAD(&la->sctpTableLocal[SN_TABLE_HASH(assoc->l_vtag, assoc->l_port, la->sctpNatTableSize)], assoc, list_L); assoc->TableRegister |= SN_LOCAL_TBL; la->sctpLinkCount++; //increment link count if (assoc->TableRegister == SN_BOTH_TBL) { /* libalias log -- controlled by libalias */ if (la->packetAliasMode & PKT_ALIAS_LOG) SctpShowAliasStats(la); SN_LOG(SN_LOG_INFO, logsctpassoc(assoc, "^")); } return (SN_ADD_OK); } /** @ingroup Hash * @brief Add the sctp association information to the global look up table * * Searches the global look-up table for an existing association with the same * details. If a match exists and is ONLY in the global look-up table then this * is a repeated INIT packet, we need to remove this association from the * look-up table and add the new association * * The new association is added to the head of the list and state is updated * * @param la Pointer to the relevant libalias instance * @param assoc pointer to sctp association * * @return SN_ADD_OK | SN_ADD_CLASH */ static int AddSctpAssocGlobal(struct libalias *la, struct sctp_nat_assoc *assoc) { struct sctp_nat_assoc *found; LIBALIAS_LOCK_ASSERT(la); found = FindSctpGlobalClash(la, assoc); if (found != NULL) { - if ((found->TableRegister == SN_GLOBAL_TBL) && \ - (found->l_addr.s_addr == assoc->l_addr.s_addr) && (found->l_port == assoc->l_port)) { /* resent message */ + if ((found->TableRegister == SN_GLOBAL_TBL) && + (found->l_addr.s_addr == assoc->l_addr.s_addr) && + (found->l_port == assoc->l_port)) { /* resent message */ RmSctpAssoc(la, found); sctp_RmTimeOut(la, found); freeGlobalAddressList(found); sn_free(found); } else return (SN_ADD_CLASH); } LIST_INSERT_HEAD(&la->sctpTableGlobal[SN_TABLE_HASH(assoc->g_vtag, assoc->g_port, la->sctpNatTableSize)], assoc, list_G); assoc->TableRegister |= SN_GLOBAL_TBL; la->sctpLinkCount++; //increment link count if (assoc->TableRegister == SN_BOTH_TBL) { /* libalias log -- controlled by libalias */ if (la->packetAliasMode & PKT_ALIAS_LOG) SctpShowAliasStats(la); SN_LOG(SN_LOG_INFO, logsctpassoc(assoc, "^")); } return (SN_ADD_OK); } /** @ingroup Hash * @brief Remove the sctp association information from the look up table * * For each of the two (local/global) look-up tables, remove the association * from that table IF it has been registered in that table. * * NOTE: The calling code is responsible for freeing memory allocated to the * association structure itself * * NOTE: The association is NOT removed from the timer queue * * @param la Pointer to the relevant libalias instance * @param assoc pointer to sctp association */ static void RmSctpAssoc(struct libalias *la, struct sctp_nat_assoc *assoc) { // struct sctp_nat_assoc *found; if (assoc == NULL) { /* very bad, log and die*/ SN_LOG(SN_LOG_LOW, logsctperror("ERROR: alias_sctp:RmSctpAssoc(NULL)\n", 0, 0, SN_TO_NODIR)); return; } /* log if association is fully up and now closing */ if (assoc->TableRegister == SN_BOTH_TBL) { SN_LOG(SN_LOG_INFO, logsctpassoc(assoc, "$")); } LIBALIAS_LOCK_ASSERT(la); if (assoc->TableRegister & SN_LOCAL_TBL) { assoc->TableRegister ^= SN_LOCAL_TBL; la->sctpLinkCount--; //decrement link count LIST_REMOVE(assoc, list_L); } if (assoc->TableRegister & SN_GLOBAL_TBL) { assoc->TableRegister ^= SN_GLOBAL_TBL; la->sctpLinkCount--; //decrement link count LIST_REMOVE(assoc, list_G); } // sn_free(assoc); //Don't remove now, remove if needed later /* libalias logging -- controlled by libalias log definition */ if (la->packetAliasMode & PKT_ALIAS_LOG) SctpShowAliasStats(la); } /** * @ingroup Hash * @brief free the Global Address List memory * * freeGlobalAddressList deletes all global IP addresses in an associations * global IP address list. * * @param assoc */ static void freeGlobalAddressList(struct sctp_nat_assoc *assoc) { struct sctp_GlobalAddress *gaddr1=NULL,*gaddr2=NULL; /*free global address list*/ gaddr1 = LIST_FIRST(&(assoc->Gaddr)); while (gaddr1 != NULL) { gaddr2 = LIST_NEXT(gaddr1, list_Gaddr); sn_free(gaddr1); gaddr1 = gaddr2; } } /* ---------------------------------------------------------------------- * TIMER QUEUE CODE * ---------------------------------------------------------------------- */ /** @addtogroup Timer * * The timer queue management functions are designed to operate efficiently with * a minimum of interaction with the queues. * * Once a timeout is set in the queue it will not be altered in the queue unless * it has to be changed to a shorter time (usually only for aborts and closing). * On a queue timeout, the real expiry time is checked, and if not leq than the * timeout it is requeued (O(1)) at its later time. This is especially important * for normal packets sent during an association. When a timer expires, it is * updated to its new expiration time if necessary, or processed as a * timeout. This means that while in UP state, the timing queue is only altered * every U_T (every few minutes) for a particular association. */ /** @ingroup Timer * @brief Add an association timeout to the timer queue * * Determine the location in the queue to add the timeout and insert the * association into the list at that queue position * * @param la * @param assoc */ static void sctp_AddTimeOut(struct libalias *la, struct sctp_nat_assoc *assoc) { int add_loc; LIBALIAS_LOCK_ASSERT(la); add_loc = assoc->exp - la->sctpNatTimer.loc_time + la->sctpNatTimer.cur_loc; if (add_loc >= SN_TIMER_QUEUE_SIZE) add_loc -= SN_TIMER_QUEUE_SIZE; LIST_INSERT_HEAD(&la->sctpNatTimer.TimerQ[add_loc], assoc, timer_Q); assoc->exp_loc = add_loc; } /** @ingroup Timer * @brief Remove an association from timer queue * * This is an O(1) operation to remove the association pointer from its * current position in the timer queue * * @param la Pointer to the relevant libalias instance * @param assoc pointer to sctp association */ static void sctp_RmTimeOut(struct libalias *la, struct sctp_nat_assoc *assoc) { LIBALIAS_LOCK_ASSERT(la); LIST_REMOVE(assoc, timer_Q);/* Note this is O(1) */ } /** @ingroup Timer * @brief Reset timer in timer queue * * Reset the actual timeout for the specified association. If it is earlier than * the existing timeout, then remove and re-install the association into the * queue * * @param la Pointer to the relevant libalias instance * @param assoc pointer to sctp association * @param newexp New expiration time */ static void sctp_ResetTimeOut(struct libalias *la, struct sctp_nat_assoc *assoc, int newexp) { if (newexp < assoc->exp) { sctp_RmTimeOut(la, assoc); assoc->exp = newexp; sctp_AddTimeOut(la, assoc); } else { assoc->exp = newexp; } } /** @ingroup Timer * @brief Check timer Q against current time * * Loop through each entry in the timer queue since the last time we processed * the timer queue until now (the current time). For each association in the * event list, we remove it from that position in the timer queue and check if * it has really expired. If so we: * - Log the timer expiry * - Remove the association from the NAT tables * - Release the memory used by the association * * If the timer hasn't really expired we place the association into its new * correct position in the timer queue. * * @param la Pointer to the relevant libalias instance */ void sctp_CheckTimers(struct libalias *la) { struct sctp_nat_assoc *assoc; LIBALIAS_LOCK_ASSERT(la); while(la->timeStamp >= la->sctpNatTimer.loc_time) { while (!LIST_EMPTY(&la->sctpNatTimer.TimerQ[la->sctpNatTimer.cur_loc])) { assoc = LIST_FIRST(&la->sctpNatTimer.TimerQ[la->sctpNatTimer.cur_loc]); //SLIST_REMOVE_HEAD(&la->sctpNatTimer.TimerQ[la->sctpNatTimer.cur_loc], timer_Q); LIST_REMOVE(assoc, timer_Q); if (la->timeStamp >= assoc->exp) { /* state expired */ SN_LOG(((assoc->state == SN_CL) ? (SN_LOG_DEBUG) : (SN_LOG_INFO)), logsctperror("Timer Expired", assoc->g_vtag, assoc->state, SN_TO_NODIR)); RmSctpAssoc(la, assoc); freeGlobalAddressList(assoc); sn_free(assoc); } else {/* state not expired, reschedule timer*/ sctp_AddTimeOut(la, assoc); } } /* Goto next location in the timer queue*/ ++la->sctpNatTimer.loc_time; if (++la->sctpNatTimer.cur_loc >= SN_TIMER_QUEUE_SIZE) la->sctpNatTimer.cur_loc = 0; } } /* ---------------------------------------------------------------------- * LOGGING CODE * ---------------------------------------------------------------------- */ /** @addtogroup Logging * * The logging functions provide logging of different items ranging from logging * a simple message, through logging an association details to logging the * current state of the NAT tables */ /** @ingroup Logging * @brief Log sctp nat errors * * @param errormsg Error message to be logged * @param vtag Current Vtag * @param error Error number * @param direction Direction of packet */ static void -logsctperror(char* errormsg, uint32_t vtag, int error, int direction) +logsctperror(char *errormsg, uint32_t vtag, int error, int direction) { char dir; switch (direction) { case SN_TO_LOCAL: dir = 'L'; break; case SN_TO_GLOBAL: dir = 'G'; break; default: dir = '*'; break; } SctpAliasLog("->%c %s (vt=%u) %d\n", dir, errormsg, ntohl(vtag), error); } /** @ingroup Logging * @brief Log what the parser parsed * * @param direction Direction of packet * @param sm Pointer to sctp message information */ static void logsctpparse(int direction, struct sctp_nat_msg *sm) { char *ploc, *pstate; switch (direction) { case SN_TO_LOCAL: ploc = "TO_LOCAL -"; break; case SN_TO_GLOBAL: ploc = "TO_GLOBAL -"; break; default: ploc = ""; } switch (sm->msg) { case SN_SCTP_INIT: pstate = "Init"; break; case SN_SCTP_INITACK: pstate = "InitAck"; break; case SN_SCTP_ABORT: pstate = "Abort"; break; case SN_SCTP_SHUTACK: pstate = "ShutAck"; break; case SN_SCTP_SHUTCOMP: pstate = "ShutComp"; break; case SN_SCTP_ASCONF: pstate = "Asconf"; break; case SN_SCTP_ASCONFACK: pstate = "AsconfAck"; break; case SN_SCTP_OTHER: pstate = "Other"; break; default: pstate = "***ERROR***"; break; } SctpAliasLog("Parsed: %s %s\n", ploc, pstate); } /** @ingroup Logging * @brief Log an SCTP association's details * * @param assoc pointer to sctp association * @param s Character that indicates the state of processing for this packet */ -static void logsctpassoc(struct sctp_nat_assoc *assoc, char* s) +static void logsctpassoc(struct sctp_nat_assoc *assoc, char *s) { struct sctp_GlobalAddress *G_Addr = NULL; char *sp; char addrbuf[INET_ADDRSTRLEN]; switch (assoc->state) { case SN_ID: sp = "ID "; break; case SN_INi: sp = "INi "; break; case SN_INa: sp = "INa "; break; case SN_UP: sp = "UP "; break; case SN_CL: sp = "CL "; break; case SN_RM: sp = "RM "; break; default: sp = "***ERROR***"; break; } SctpAliasLog("%sAssoc: %s exp=%u la=%s lv=%u lp=%u gv=%u gp=%u tbl=%d\n", s, sp, assoc->exp, inet_ntoa_r(assoc->l_addr, addrbuf), ntohl(assoc->l_vtag), ntohs(assoc->l_port), ntohl(assoc->g_vtag), ntohs(assoc->g_port), assoc->TableRegister); /* list global addresses */ LIST_FOREACH(G_Addr, &(assoc->Gaddr), list_Gaddr) { SctpAliasLog("\t\tga=%s\n", inet_ntoa_r(G_Addr->g_addr, addrbuf)); } } /** @ingroup Logging * @brief Output Global table to log * * @param la Pointer to the relevant libalias instance */ static void logSctpGlobal(struct libalias *la) { u_int i; struct sctp_nat_assoc *assoc = NULL; SctpAliasLog("G->\n"); - for (i=0; i < la->sctpNatTableSize; i++) { + for (i = 0; i < la->sctpNatTableSize; i++) { LIST_FOREACH(assoc, &la->sctpTableGlobal[i], list_G) { logsctpassoc(assoc, " "); } } } /** @ingroup Logging * @brief Output Local table to log * * @param la Pointer to the relevant libalias instance */ static void logSctpLocal(struct libalias *la) { u_int i; struct sctp_nat_assoc *assoc = NULL; SctpAliasLog("L->\n"); - for (i=0; i < la->sctpNatTableSize; i++) { + for (i = 0; i < la->sctpNatTableSize; i++) { LIST_FOREACH(assoc, &la->sctpTableLocal[i], list_L) { logsctpassoc(assoc, " "); } } } /** @ingroup Logging * @brief Output timer queue to log * * @param la Pointer to the relevant libalias instance */ static void logTimerQ(struct libalias *la) { static char buf[50]; u_int i; struct sctp_nat_assoc *assoc = NULL; SctpAliasLog("t->\n"); - for (i=0; i < SN_TIMER_QUEUE_SIZE; i++) { + for (i = 0; i < SN_TIMER_QUEUE_SIZE; i++) { LIST_FOREACH(assoc, &la->sctpNatTimer.TimerQ[i], timer_Q) { snprintf(buf, 50, " l=%u ",i); //SctpAliasLog(la->logDesc," l=%d ",i); logsctpassoc(assoc, buf); } } } /** @ingroup Logging * @brief Sctp NAT logging function * * This function is based on a similar function in alias_db.c * * @param str/stream logging descriptor * @param format printf type string */ #ifdef _KERNEL static void SctpAliasLog(const char *format, ...) { char buffer[LIBALIAS_BUF_SIZE]; va_list ap; va_start(ap, format); vsnprintf(buffer, LIBALIAS_BUF_SIZE, format, ap); va_end(ap); - log(LOG_SECURITY | LOG_INFO, - "alias_sctp: %s", buffer); + log(LOG_SECURITY | LOG_INFO, "alias_sctp: %s", buffer); } #else static void SctpAliasLog(FILE *stream, const char *format, ...) { va_list ap; va_start(ap, format); vfprintf(stream, format, ap); va_end(ap); fflush(stream); } #endif diff --git a/sys/netinet/libalias/alias_sctp.h b/sys/netinet/libalias/alias_sctp.h index 37f44a96e752..de1090cedd84 100644 --- a/sys/netinet/libalias/alias_sctp.h +++ b/sys/netinet/libalias/alias_sctp.h @@ -1,189 +1,189 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2008 * Swinburne University of Technology, Melbourne, Australia. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* - * Alias_sctp forms part of the libalias kernel module to handle + * Alias_sctp forms part of the libalias kernel module to handle * Network Address Translation (NAT) for the SCTP protocol. * * This software was developed by David A. Hayes * with leadership and advice from Jason But * * The design is outlined in CAIA technical report number 080618A * (D. Hayes and J. But, "Alias_sctp Version 0.1: SCTP NAT implementation in IPFW") * * Development is part of the CAIA SONATA project, * proposed by Jason But and Grenville Armitage: * http://caia.swin.edu.au/urp/sonata/ * - * + * * This project has been made possible in part by a grant from * the Cisco University Research Program Fund at Community * Foundation Silicon Valley. * */ /* $FreeBSD$ */ #ifndef _ALIAS_SCTP_H_ #define _ALIAS_SCTP_H_ #include -#ifdef _KERNEL +#ifdef _KERNEL #include #include #include #include #include #include #include -#endif // #ifdef _KERNEL +#endif // #ifdef _KERNEL #include #include #include #include #include #include #include /** * These are defined in sctp_os_bsd.h, but it can't be included due to its local file * inclusion, so I'm defining them here. - * + * */ #include /* The packed define for 64 bit platforms */ #ifndef SCTP_PACKED #define SCTP_PACKED __attribute__((packed)) #endif //#ifndef SCTP_PACKED #ifndef SCTP_UNUSED #define SCTP_UNUSED __attribute__((unused)) #endif //#ifndef SCTP_UNUSED #include //#include --might be needed later for mbuf stuff #include #ifndef _KERNEL #include #include #endif //#ifdef _KERNEL #define LINK_SCTP IPPROTO_SCTP #define SN_TO_LOCAL 0 /**< packet traveling from global to local */ #define SN_TO_GLOBAL 1 /**< packet traveling from local to global */ #define SN_TO_NODIR 99 /**< used where direction is not important */ #define SN_NAT_PKT 0x0000 /**< Network Address Translate packet */ #define SN_DROP_PKT 0x0001 /**< drop packet (don't forward it) */ #define SN_PROCESSING_ERROR 0x0003 /**< Packet processing error */ #define SN_REPLY_ABORT 0x0010 /**< Reply with ABORT to sender (don't forward it) */ #define SN_SEND_ABORT 0x0020 /**< Send ABORT to destination */ #define SN_TX_ABORT 0x0030 /**< mask for transmitting abort */ #define SN_REFLECT_ERROR 0x0100 /**< Reply with ERROR to sender on OOTB packet Tbit set */ #define SN_REPLY_ERROR 0x0200 /**< Reply with ERROR to sender on ASCONF clash */ #define SN_TX_ERROR 0x0300 /**< mask for transmitting error */ #define PKT_ALIAS_RESPOND 0x1000 /**< Signal to libalias that there is a response packet to send */ /* * Data structures */ /** * @brief sctp association information * * Structure that contains information about a particular sctp association * currently under Network Address Translation. * Information is stored in network byte order (as is libalias)*** */ struct sctp_nat_assoc { uint32_t l_vtag; /**< local side verification tag */ uint16_t l_port; /**< local side port number */ uint32_t g_vtag; /**< global side verification tag */ uint16_t g_port; /**< global side port number */ - struct in_addr l_addr; /**< local ip address */ - struct in_addr a_addr; /**< alias ip address */ + struct in_addr l_addr; /**< local ip address */ + struct in_addr a_addr; /**< alias ip address */ int state; /**< current state of NAT association */ int TableRegister; /**< stores which look up tables association is registered in */ int exp; /**< timer expiration in seconds from uptime */ int exp_loc; /**< current location in timer_Q */ - int num_Gaddr; /**< number of global IP addresses in the list */ + int num_Gaddr; /**< number of global IP addresses in the list */ LIST_HEAD(sctpGlobalAddresshead,sctp_GlobalAddress) Gaddr; /**< List of global addresses */ LIST_ENTRY (sctp_nat_assoc) list_L; /**< Linked list of pointers for Local table*/ LIST_ENTRY (sctp_nat_assoc) list_G; /**< Linked list of pointers for Global table */ LIST_ENTRY (sctp_nat_assoc) timer_Q; /**< Linked list of pointers for timer Q */ -//Using libalias locking + //Using libalias locking }; struct sctp_GlobalAddress { struct in_addr g_addr; LIST_ENTRY (sctp_GlobalAddress) list_Gaddr; /**< Linked list of pointers for Global table */ }; /** * @brief SCTP chunk of interest * * The only chunks whose contents are of any interest are the INIT and ASCONF_AddIP */ union sctpChunkOfInt { - struct sctp_init *Init; /**< Pointer to Init Chunk */ + struct sctp_init *Init; /**< Pointer to Init Chunk */ struct sctp_init_ack *InitAck; /**< Pointer to Init Chunk */ - struct sctp_paramhdr *Asconf; /**< Pointer to ASCONF chunk */ + struct sctp_paramhdr *Asconf; /**< Pointer to ASCONF chunk */ }; /** * @brief SCTP message - * + * * Structure containing the relevant information from the SCTP message */ struct sctp_nat_msg { uint16_t msg; /**< one of the key messages defined above */ #ifdef INET6 // struct ip6_hdr *ip_hdr; /**< pointer to ip packet header */ /*no inet6 support yet*/ #else struct ip *ip_hdr; /**< pointer to ip packet header */ #endif //#ifdef INET6 struct sctphdr *sctp_hdr; /**< pointer to sctp common header */ union sctpChunkOfInt sctpchnk; /**< union of pointers to the chunk of interest */ int chunk_length; /**< length of chunk of interest */ }; /** * @brief sctp nat timer queue structure - * + * */ struct sctp_nat_timer { int loc_time; /**< time in seconds for the current location in the queue */ int cur_loc; /**< index of the current location in the circular queue */ LIST_HEAD(sctpTimerQ,sctp_nat_assoc) *TimerQ; /**< List of associations at this position in the timer Q */ }; #endif //#ifndef _ALIAS_SCTP_H diff --git a/sys/netinet/libalias/alias_skinny.c b/sys/netinet/libalias/alias_skinny.c index c18410ba4191..31b33696fc20 100644 --- a/sys/netinet/libalias/alias_skinny.c +++ b/sys/netinet/libalias/alias_skinny.c @@ -1,448 +1,446 @@ /*- * alias_skinny.c * * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2002, 2003 MarcusCom, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * Author: Joe Marcus Clarke * * $FreeBSD$ */ #ifdef _KERNEL #include #include #include #else #include #include #include #endif #include #include #include #include #ifdef _KERNEL #include #include #else #include "alias_local.h" #include "alias_mod.h" #endif static void AliasHandleSkinny(struct libalias *, struct ip *, struct alias_link *); static int fingerprint(struct libalias *la, struct alias_data *ah) { - if (ah->dport == NULL || ah->sport == NULL || ah->lnk == NULL) return (-1); if (la->skinnyPort != 0 && (ntohs(*ah->sport) == la->skinnyPort || ntohs(*ah->dport) == la->skinnyPort)) return (0); return (-1); } static int protohandler(struct libalias *la, struct ip *pip, struct alias_data *ah) { - - AliasHandleSkinny(la, pip, ah->lnk); + AliasHandleSkinny(la, pip, ah->lnk); return (0); } struct proto_handler handlers[] = { { .pri = 110, .dir = IN|OUT, .proto = TCP, .fingerprint = &fingerprint, .protohandler = &protohandler }, { EOH } }; static int mod_handler(module_t mod, int type, void *data) { int error; switch (type) { case MOD_LOAD: error = 0; LibAliasAttachHandlers(handlers); break; case MOD_UNLOAD: error = 0; LibAliasDetachHandlers(handlers); break; default: error = EINVAL; } return (error); } #ifdef _KERNEL static #endif moduledata_t alias_mod = { "alias_skinny", mod_handler, NULL }; -#ifdef _KERNEL +#ifdef _KERNEL DECLARE_MODULE(alias_skinny, alias_mod, SI_SUB_DRIVERS, SI_ORDER_SECOND); MODULE_VERSION(alias_skinny, 1); MODULE_DEPEND(alias_skinny, libalias, 1, 1, 1); #endif /* * alias_skinny.c handles the translation for the Cisco Skinny Station * protocol. Skinny typically uses TCP port 2000 to set up calls between * a Cisco Call Manager and a Cisco IP phone. When a phone comes on line, * it first needs to register with the Call Manager. To do this it sends * a registration message. This message contains the IP address of the * IP phone. This message must then be translated to reflect our global * IP address. Along with the registration message (and usually in the * same packet), the phone sends an IP port message. This message indicates * the TCP port over which it will communicate. * * When a call is placed from the phone, the Call Manager will send an * Open Receive Channel message to the phone to let the caller know someone * has answered. The phone then sends back an Open Receive Channel * Acknowledgement. In this packet, the phone sends its IP address again, * and the UDP port over which the voice traffic should flow. These values * need translation. Right after the Open Receive Channel Acknowledgement, * the Call Manager sends a Start Media Transmission message indicating the * call is connected. This message contains the IP address and UDP port * number of the remote (called) party. Once this message is translated, the * call can commence. The called part sends the first UDP packet to the * calling phone at the pre-arranged UDP port in the Open Receive Channel * Acknowledgement. * * Skinny is a Cisco-proprietary protocol and is a trademark of Cisco Systems, * Inc. All rights reserved. */ /* #define LIBALIAS_DEBUG 1 */ /* Message types that need translating */ -#define REG_MSG 0x00000001 -#define IP_PORT_MSG 0x00000002 -#define OPNRCVCH_ACK 0x00000022 -#define START_MEDIATX 0x0000008a +#define REG_MSG 0x00000001 +#define IP_PORT_MSG 0x00000002 +#define OPNRCVCH_ACK 0x00000022 +#define START_MEDIATX 0x0000008a struct skinny_header { u_int32_t len; u_int32_t reserved; u_int32_t msgId; }; struct RegisterMessage { u_int32_t msgId; char devName [16]; u_int32_t uid; u_int32_t instance; u_int32_t ipAddr; u_char devType; u_int32_t maxStreams; }; struct IpPortMessage { u_int32_t msgId; u_int32_t stationIpPort; /* Note: Skinny uses 32-bit port * numbers */ }; struct OpenReceiveChannelAck { u_int32_t msgId; u_int32_t status; u_int32_t ipAddr; u_int32_t port; u_int32_t passThruPartyID; }; struct StartMediaTransmission { u_int32_t msgId; u_int32_t conferenceID; u_int32_t passThruPartyID; u_int32_t remoteIpAddr; u_int32_t remotePort; u_int32_t MSPacket; u_int32_t payloadCap; u_int32_t precedence; u_int32_t silenceSuppression; u_short maxFramesPerPacket; u_int32_t G723BitRate; }; typedef enum { ClientToServer = 0, ServerToClient = 1 } ConvDirection; static int alias_skinny_reg_msg(struct RegisterMessage *reg_msg, struct ip *pip, struct tcphdr *tc, struct alias_link *lnk, ConvDirection direction) { (void)direction; - reg_msg->ipAddr = (u_int32_t) GetAliasAddress(lnk).s_addr; + reg_msg->ipAddr = (u_int32_t)GetAliasAddress(lnk).s_addr; tc->th_sum = 0; #ifdef _KERNEL tc->th_x2 = 1; #else tc->th_sum = TcpChecksum(pip); #endif return (0); } static int alias_skinny_startmedia(struct StartMediaTransmission *start_media, struct ip *pip, struct tcphdr *tc, struct alias_link *lnk, u_int32_t localIpAddr, ConvDirection direction) { struct in_addr dst, src; (void)pip; (void)tc; (void)lnk; (void)direction; dst.s_addr = start_media->remoteIpAddr; src.s_addr = localIpAddr; /* * XXX I should probably handle in bound global translations as * well. */ return (0); } static int alias_skinny_port_msg(struct IpPortMessage *port_msg, struct ip *pip, struct tcphdr *tc, struct alias_link *lnk, ConvDirection direction) { (void)direction; - port_msg->stationIpPort = (u_int32_t) ntohs(GetAliasPort(lnk)); + port_msg->stationIpPort = (u_int32_t)ntohs(GetAliasPort(lnk)); tc->th_sum = 0; #ifdef _KERNEL tc->th_x2 = 1; #else tc->th_sum = TcpChecksum(pip); #endif return (0); } static int alias_skinny_opnrcvch_ack(struct libalias *la, struct OpenReceiveChannelAck *opnrcvch_ack, struct ip *pip, struct tcphdr *tc, struct alias_link *lnk, u_int32_t * localIpAddr, ConvDirection direction) { struct in_addr null_addr; struct alias_link *opnrcv_lnk; u_int32_t localPort; (void)lnk; (void)direction; - *localIpAddr = (u_int32_t) opnrcvch_ack->ipAddr; + *localIpAddr = (u_int32_t)opnrcvch_ack->ipAddr; localPort = opnrcvch_ack->port; null_addr.s_addr = INADDR_ANY; opnrcv_lnk = FindUdpTcpOut(la, pip->ip_src, null_addr, htons((u_short) opnrcvch_ack->port), 0, IPPROTO_UDP, 1); - opnrcvch_ack->ipAddr = (u_int32_t) GetAliasAddress(opnrcv_lnk).s_addr; - opnrcvch_ack->port = (u_int32_t) ntohs(GetAliasPort(opnrcv_lnk)); + opnrcvch_ack->ipAddr = (u_int32_t)GetAliasAddress(opnrcv_lnk).s_addr; + opnrcvch_ack->port = (u_int32_t)ntohs(GetAliasPort(opnrcv_lnk)); tc->th_sum = 0; #ifdef _KERNEL tc->th_x2 = 1; #else tc->th_sum = TcpChecksum(pip); #endif return (0); } static void AliasHandleSkinny(struct libalias *la, struct ip *pip, struct alias_link *lnk) { size_t hlen, tlen, dlen; struct tcphdr *tc; u_int32_t msgId, t, len, lip; struct skinny_header *sd; size_t orig_len, skinny_hdr_len = sizeof(struct skinny_header); ConvDirection direction; lip = -1; tc = (struct tcphdr *)ip_next(pip); hlen = (pip->ip_hl + tc->th_off) << 2; tlen = ntohs(pip->ip_len); dlen = tlen - hlen; sd = (struct skinny_header *)tcp_next(tc); /* * XXX This direction is reserved for future use. I still need to * handle the scenario where the call manager is on the inside, and * the calling phone is on the global outside. */ - if (ntohs(tc->th_dport) == la->skinnyPort) { + if (ntohs(tc->th_dport) == la->skinnyPort) direction = ClientToServer; - } else if (ntohs(tc->th_sport) == la->skinnyPort) { + else if (ntohs(tc->th_sport) == la->skinnyPort) direction = ServerToClient; - } else { + else { #ifdef LIBALIAS_DEBUG fprintf(stderr, "PacketAlias/Skinny: Invalid port number, not a Skinny packet\n"); #endif return; } orig_len = dlen; /* * Skinny packets can contain many messages. We need to loop * through the packet using len to determine message boundaries. * This comes into play big time with port messages being in the * same packet as register messages. Also, open receive channel * acks are usually buried in a packet some 400 bytes long. */ while (dlen >= skinny_hdr_len) { len = (sd->len); msgId = (sd->msgId); t = len; if (t > orig_len || t > dlen) { #ifdef LIBALIAS_DEBUG fprintf(stderr, "PacketAlias/Skinny: Not a skinny packet, invalid length \n"); #endif return; } switch (msgId) { case REG_MSG: { struct RegisterMessage *reg_mesg; if (len < (int)sizeof(struct RegisterMessage)) { #ifdef LIBALIAS_DEBUG fprintf(stderr, "PacketAlias/Skinny: Not a skinny packet, bad registration message\n"); #endif return; } reg_mesg = (struct RegisterMessage *)&sd->msgId; #ifdef LIBALIAS_DEBUG fprintf(stderr, "PacketAlias/Skinny: Received a register message"); #endif alias_skinny_reg_msg(reg_mesg, pip, tc, lnk, direction); break; } case IP_PORT_MSG: { struct IpPortMessage *port_mesg; if (len < (int)sizeof(struct IpPortMessage)) { #ifdef LIBALIAS_DEBUG fprintf(stderr, "PacketAlias/Skinny: Not a skinny packet, port message\n"); #endif return; } #ifdef LIBALIAS_DEBUG fprintf(stderr, "PacketAlias/Skinny: Received ipport message\n"); #endif port_mesg = (struct IpPortMessage *)&sd->msgId; alias_skinny_port_msg(port_mesg, pip, tc, lnk, direction); break; } case OPNRCVCH_ACK: { struct OpenReceiveChannelAck *opnrcvchn_ack; if (len < (int)sizeof(struct OpenReceiveChannelAck)) { #ifdef LIBALIAS_DEBUG fprintf(stderr, "PacketAlias/Skinny: Not a skinny packet, packet,OpnRcvChnAckMsg\n"); #endif return; } #ifdef LIBALIAS_DEBUG fprintf(stderr, "PacketAlias/Skinny: Received open rcv channel msg\n"); #endif opnrcvchn_ack = (struct OpenReceiveChannelAck *)&sd->msgId; alias_skinny_opnrcvch_ack(la, opnrcvchn_ack, pip, tc, lnk, &lip, direction); break; } case START_MEDIATX: { struct StartMediaTransmission *startmedia_tx; if (len < (int)sizeof(struct StartMediaTransmission)) { #ifdef LIBALIAS_DEBUG fprintf(stderr, "PacketAlias/Skinny: Not a skinny packet,StartMediaTx Message\n"); #endif return; } if (lip == -1) { #ifdef LIBALIAS_DEBUG fprintf(stderr, "PacketAlias/Skinny: received a" " packet,StartMediaTx Message before" " packet,OpnRcvChnAckMsg\n" #endif return; } #ifdef LIBALIAS_DEBUG fprintf(stderr, "PacketAlias/Skinny: Received start media trans msg\n"); #endif startmedia_tx = (struct StartMediaTransmission *)&sd->msgId; alias_skinny_startmedia(startmedia_tx, pip, tc, lnk, lip, direction); break; } default: break; } /* Place the pointer at the next message in the packet. */ dlen -= len + (skinny_hdr_len - sizeof(msgId)); sd = (struct skinny_header *)(((char *)&sd->msgId) + len); } } diff --git a/sys/netinet/libalias/alias_smedia.c b/sys/netinet/libalias/alias_smedia.c index f28a0fc1f344..9b5a9d673ecf 100644 --- a/sys/netinet/libalias/alias_smedia.c +++ b/sys/netinet/libalias/alias_smedia.c @@ -1,542 +1,525 @@ /*- * alias_smedia.c * * SPDX-License-Identifier: BSD-2-Clause-FreeBSD AND BSD-2-Clause * * Copyright (c) 2000 Whistle Communications, Inc. * All rights reserved. * * Subject to the following obligations and disclaimer of warranty, use and * redistribution of this software, in source or object code forms, with or * without modifications are expressly permitted by Whistle Communications; * provided, however, that: * 1. Any and all reproductions of the source or object code must include the * copyright notice above and the following disclaimer of warranties; and * 2. No rights are granted, in any manner or form, to use Whistle * Communications, Inc. trademarks, including the mark "WHISTLE * COMMUNICATIONS" on advertising, endorsements, or otherwise except as * such appears in the above copyright notice or in the software. * * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY * OF SUCH DAMAGE. * * Copyright (c) 2000 Junichi SATOH * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * Authors: Erik Salander * Junichi SATOH * */ #include __FBSDID("$FreeBSD$"); /* Alias_smedia.c is meant to contain the aliasing code for streaming media protocols. It performs special processing for RSTP sessions under TCP. Specifically, when a SETUP request is sent by a client, or a 200 reply is sent by a server, it is intercepted and modified. The address is changed to the gateway machine and an aliasing port is used. More specifically, the "client_port" configuration parameter is parsed for SETUP requests. The "server_port" configuration parameter is parsed for 200 replies eminating from a server. This is intended to handle the unicast case. RTSP also allows a redirection of a stream to another client by using the "destination" configuration parameter. The destination config parm would indicate a different IP address. This function is NOT supported by the RTSP translation code below. The RTSP multicast functions without any address translation intervention. For this routine to work, the SETUP/200 must fit entirely into a single TCP packet. This is typically the case, but exceptions can easily be envisioned under the actual specifications. Probably the most troubling aspect of the approach taken here is that the new SETUP/200 will typically be a different length, and this causes a certain amount of bookkeeping to keep track of the changes of sequence and acknowledgment numbers, since the client machine is totally unaware of the modification to the TCP stream. Initial version: May, 2000 (eds) */ #ifdef _KERNEL #include #include #include #include #else #include #include #include #include #endif #include #include #include #include #ifdef _KERNEL #include #include #include #else #include "alias_local.h" #include "alias_mod.h" #endif #define RTSP_CONTROL_PORT_NUMBER_1 554 #define RTSP_CONTROL_PORT_NUMBER_2 7070 #define TFTP_PORT_NUMBER 69 static void -AliasHandleRtspOut(struct libalias *, struct ip *, struct alias_link *, - int maxpacketsize); +AliasHandleRtspOut(struct libalias *, struct ip *, struct alias_link *, + int maxpacketsize); static int fingerprint(struct libalias *la, struct alias_data *ah) { - if (ah->dport != NULL && ah->aport != NULL && ah->sport != NULL && - ntohs(*ah->dport) == TFTP_PORT_NUMBER) + ntohs(*ah->dport) == TFTP_PORT_NUMBER) return (0); if (ah->dport == NULL || ah->sport == NULL || ah->lnk == NULL || ah->maxpktsize == 0) return (-1); if (ntohs(*ah->dport) == RTSP_CONTROL_PORT_NUMBER_1 || ntohs(*ah->sport) == RTSP_CONTROL_PORT_NUMBER_1 || ntohs(*ah->dport) == RTSP_CONTROL_PORT_NUMBER_2 || ntohs(*ah->sport) == RTSP_CONTROL_PORT_NUMBER_2) return (0); return (-1); } static int protohandler(struct libalias *la, struct ip *pip, struct alias_data *ah) { - if (ntohs(*ah->dport) == TFTP_PORT_NUMBER) FindRtspOut(la, pip->ip_src, pip->ip_dst, - *ah->sport, *ah->aport, IPPROTO_UDP); - else AliasHandleRtspOut(la, pip, ah->lnk, ah->maxpktsize); + *ah->sport, *ah->aport, IPPROTO_UDP); + else AliasHandleRtspOut(la, pip, ah->lnk, ah->maxpktsize); return (0); } struct proto_handler handlers[] = { { .pri = 100, .dir = OUT, .proto = TCP|UDP, .fingerprint = &fingerprint, .protohandler = &protohandler }, { EOH } }; static int mod_handler(module_t mod, int type, void *data) { int error; switch (type) { case MOD_LOAD: error = 0; LibAliasAttachHandlers(handlers); break; case MOD_UNLOAD: error = 0; LibAliasDetachHandlers(handlers); break; default: error = EINVAL; } return (error); } #ifdef _KERNEL static #endif moduledata_t alias_mod = { "alias_smedia", mod_handler, NULL }; -#ifdef _KERNEL +#ifdef _KERNEL DECLARE_MODULE(alias_smedia, alias_mod, SI_SUB_DRIVERS, SI_ORDER_SECOND); MODULE_VERSION(alias_smedia, 1); MODULE_DEPEND(alias_smedia, libalias, 1, 1, 1); #endif -#define RTSP_CONTROL_PORT_NUMBER_1 554 -#define RTSP_CONTROL_PORT_NUMBER_2 7070 -#define RTSP_PORT_GROUP 2 +#define RTSP_CONTROL_PORT_NUMBER_1 554 +#define RTSP_CONTROL_PORT_NUMBER_2 7070 +#define RTSP_PORT_GROUP 2 #define ISDIGIT(a) (((a) >= '0') && ((a) <= '9')) static int search_string(char *data, int dlen, const char *search_str) { int i, j, k; int search_str_len; search_str_len = strlen(search_str); for (i = 0; i < dlen - search_str_len; i++) { for (j = i, k = 0; j < dlen - search_str_len; j++, k++) { if (data[j] != search_str[k] && - data[j] != search_str[k] - ('a' - 'A')) { + data[j] != search_str[k] - ('a' - 'A')) break; - } - if (k == search_str_len - 1) { + if (k == search_str_len - 1) return (j + 1); - } } } return (-1); } static int alias_rtsp_out(struct libalias *la, struct ip *pip, struct alias_link *lnk, char *data, const char *port_str) { int hlen, tlen, dlen; struct tcphdr *tc; int i, j, pos, state, port_dlen, new_dlen, delta; u_short p[2], new_len; u_short sport, eport, base_port; u_short salias = 0, ealias = 0, base_alias = 0; const char *transport_str = "transport:"; char newdata[2048], *port_data, *port_newdata, stemp[80]; int links_created = 0, pkt_updated = 0; struct alias_link *rtsp_lnk = NULL; struct in_addr null_addr; /* Calculate data length of TCP packet */ tc = (struct tcphdr *)ip_next(pip); hlen = (pip->ip_hl + tc->th_off) << 2; tlen = ntohs(pip->ip_len); dlen = tlen - hlen; /* Find keyword, "Transport: " */ pos = search_string(data, dlen, transport_str); - if (pos < 0) { + if (pos < 0) return (-1); - } + port_data = data + pos; port_dlen = dlen - pos; memcpy(newdata, data, pos); port_newdata = newdata + pos; while (port_dlen > (int)strlen(port_str)) { /* Find keyword, appropriate port string */ pos = search_string(port_data, port_dlen, port_str); - if (pos < 0) { + if (pos < 0) break; - } + memcpy(port_newdata, port_data, pos + 1); port_newdata += (pos + 1); p[0] = p[1] = 0; sport = eport = 0; state = 0; for (i = pos; i < port_dlen; i++) { switch (state) { case 0: - if (port_data[i] == '=') { + if (port_data[i] == '=') state++; - } break; case 1: - if (ISDIGIT(port_data[i])) { + if (ISDIGIT(port_data[i])) p[0] = p[0] * 10 + port_data[i] - '0'; - } else { - if (port_data[i] == ';') { - state = 3; - } - if (port_data[i] == '-') { - state++; - } - } + else if (port_data[i] == ';') + state = 3; + else if (port_data[i] == '-') + state++; break; case 2: - if (ISDIGIT(port_data[i])) { + if (ISDIGIT(port_data[i])) p[1] = p[1] * 10 + port_data[i] - '0'; - } else { + else state++; - } break; case 3: base_port = p[0]; sport = htons(p[0]); eport = htons(p[1]); if (!links_created) { links_created = 1; /* * Find an even numbered port * number base that satisfies the * contiguous number of ports we * need */ null_addr.s_addr = 0; if (0 == (salias = FindNewPortGroup(la, null_addr, FindAliasAddress(la, pip->ip_src), sport, 0, RTSP_PORT_GROUP, IPPROTO_UDP, 1))) { #ifdef LIBALIAS_DEBUG fprintf(stderr, "PacketAlias/RTSP: Cannot find contiguous RTSP data ports\n"); #endif } else { base_alias = ntohs(salias); for (j = 0; j < RTSP_PORT_GROUP; j++) { /* * Establish link * to port found in * RTSP packet */ rtsp_lnk = FindRtspOut(la, GetOriginalAddress(lnk), null_addr, htons(base_port + j), htons(base_alias + j), IPPROTO_UDP); if (rtsp_lnk != NULL) { #ifndef NO_FW_PUNCH /* * Punch * hole in * firewall */ PunchFWHole(rtsp_lnk); #endif } else { #ifdef LIBALIAS_DEBUG fprintf(stderr, "PacketAlias/RTSP: Cannot allocate RTSP data ports\n"); #endif break; } } } ealias = htons(base_alias + (RTSP_PORT_GROUP - 1)); } if (salias && rtsp_lnk) { pkt_updated = 1; /* Copy into IP packet */ sprintf(stemp, "%d", ntohs(salias)); memcpy(port_newdata, stemp, strlen(stemp)); port_newdata += strlen(stemp); if (eport != 0) { *port_newdata = '-'; port_newdata++; /* Copy into IP packet */ sprintf(stemp, "%d", ntohs(ealias)); memcpy(port_newdata, stemp, strlen(stemp)); port_newdata += strlen(stemp); } *port_newdata = ';'; port_newdata++; } state++; break; } if (state > 3) { break; } } port_data += i; port_dlen -= i; } if (!pkt_updated) return (-1); memcpy(port_newdata, port_data, port_dlen); port_newdata += port_dlen; *port_newdata = '\0'; /* Create new packet */ new_dlen = port_newdata - newdata; memcpy(data, newdata, new_dlen); SetAckModified(lnk); tc = (struct tcphdr *)ip_next(pip); delta = GetDeltaSeqOut(tc->th_seq, lnk); AddSeq(lnk, delta + new_dlen - dlen, pip->ip_hl, pip->ip_len, tc->th_seq, tc->th_off); new_len = htons(hlen + new_dlen); - DifferentialChecksum(&pip->ip_sum, - &new_len, - &pip->ip_len, - 1); + DifferentialChecksum(&pip->ip_sum, &new_len, &pip->ip_len, 1); pip->ip_len = new_len; tc->th_sum = 0; #ifdef _KERNEL tc->th_x2 = 1; #else tc->th_sum = TcpChecksum(pip); #endif return (0); } /* Support the protocol used by early versions of RealPlayer */ static int alias_pna_out(struct libalias *la, struct ip *pip, struct alias_link *lnk, char *data, int dlen) { struct alias_link *pna_links; u_short msg_id, msg_len; char *work; u_short alias_port, port; struct tcphdr *tc; work = data; work += 5; while (work + 4 < data + dlen) { memcpy(&msg_id, work, 2); work += 2; memcpy(&msg_len, work, 2); work += 2; - if (ntohs(msg_id) == 0) { - /* end of options */ + if (ntohs(msg_id) == 0) /* end of options */ return (0); - } + if ((ntohs(msg_id) == 1) || (ntohs(msg_id) == 7)) { memcpy(&port, work, 2); pna_links = FindUdpTcpOut(la, pip->ip_src, GetDestAddress(lnk), port, 0, IPPROTO_UDP, 1); if (pna_links != NULL) { #ifndef NO_FW_PUNCH /* Punch hole in firewall */ PunchFWHole(pna_links); #endif tc = (struct tcphdr *)ip_next(pip); alias_port = GetAliasPort(pna_links); memcpy(work, &alias_port, 2); /* Compute TCP checksum for revised packet */ tc->th_sum = 0; #ifdef _KERNEL tc->th_x2 = 1; #else tc->th_sum = TcpChecksum(pip); #endif } } work += ntohs(msg_len); } return (0); } static void AliasHandleRtspOut(struct libalias *la, struct ip *pip, struct alias_link *lnk, int maxpacketsize) { int hlen, tlen, dlen; struct tcphdr *tc; char *data; const char *setup = "SETUP", *pna = "PNA", *str200 = "200"; const char *okstr = "OK", *client_port_str = "client_port"; const char *server_port_str = "server_port"; int i, parseOk; (void)maxpacketsize; tc = (struct tcphdr *)ip_next(pip); hlen = (pip->ip_hl + tc->th_off) << 2; tlen = ntohs(pip->ip_len); dlen = tlen - hlen; data = (char *)pip; data += hlen; /* When aliasing a client, check for the SETUP request */ if ((ntohs(tc->th_dport) == RTSP_CONTROL_PORT_NUMBER_1) || (ntohs(tc->th_dport) == RTSP_CONTROL_PORT_NUMBER_2)) { - if (dlen >= (int)strlen(setup)) { - if (memcmp(data, setup, strlen(setup)) == 0) { - alias_rtsp_out(la, pip, lnk, data, client_port_str); - return; - } - } - if (dlen >= (int)strlen(pna)) { - if (memcmp(data, pna, strlen(pna)) == 0) { - alias_pna_out(la, pip, lnk, data, dlen); - } + if (dlen >= (int)strlen(setup) && + memcmp(data, setup, strlen(setup)) == 0) { + alias_rtsp_out(la, pip, lnk, data, client_port_str); + return; } + + if (dlen >= (int)strlen(pna) && + memcmp(data, pna, strlen(pna)) == 0) + alias_pna_out(la, pip, lnk, data, dlen); } else { /* * When aliasing a server, check for the 200 reply * Accommodate varying number of blanks between 200 & OK */ if (dlen >= (int)strlen(str200)) { for (parseOk = 0, i = 0; i <= dlen - (int)strlen(str200); - i++) { + i++) if (memcmp(&data[i], str200, strlen(str200)) == 0) { parseOk = 1; break; } - } + if (parseOk) { i += strlen(str200); /* skip string found */ while (data[i] == ' ') /* skip blank(s) */ i++; - if ((dlen - i) >= (int)strlen(okstr)) { + if ((dlen - i) >= (int)strlen(okstr)) if (memcmp(&data[i], okstr, strlen(okstr)) == 0) alias_rtsp_out(la, pip, lnk, data, server_port_str); - } } } } } diff --git a/sys/netinet/libalias/alias_util.c b/sys/netinet/libalias/alias_util.c index 0f1557533b0b..85566e0b2d4f 100644 --- a/sys/netinet/libalias/alias_util.c +++ b/sys/netinet/libalias/alias_util.c @@ -1,177 +1,177 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2001 Charles Mott * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); /* Alias_util.c contains general utilities used by other functions in the packet aliasing module. At the moment, there are functions for computing IP header and TCP packet checksums. The checksum routines are based upon example code in a Unix networking text written by Stevens (sorry, I can't remember the title -- but at least this is a good author). Initial Version: August, 1996 (cjm) Version 1.7: January 9, 1997 Added differential checksum update function. */ #ifdef _KERNEL #include #include #else #include #include #endif #include #include #include #include #ifdef _KERNEL #include #include #else #include "alias.h" #include "alias_local.h" #endif /* * Note: the checksum routines assume that the actual checksum word has * been zeroed out. If the checksum word is filled with the proper value, * then these routines will give a result of zero (useful for testing * purposes); */ u_short -LibAliasInternetChecksum(struct libalias *la __unused, u_short * ptr, - int nbytes) +LibAliasInternetChecksum(struct libalias *la __unused, u_short *ptr, + int nbytes) { int sum, oddbyte; LIBALIAS_LOCK(la); sum = 0; while (nbytes > 1) { sum += *ptr++; nbytes -= 2; } if (nbytes == 1) { oddbyte = 0; - ((u_char *) & oddbyte)[0] = *(u_char *) ptr; - ((u_char *) & oddbyte)[1] = 0; + ((u_char *)&oddbyte)[0] = *(u_char *)ptr; + ((u_char *)&oddbyte)[1] = 0; sum += oddbyte; } sum = (sum >> 16) + (sum & 0xffff); sum += (sum >> 16); LIBALIAS_UNLOCK(la); return (~sum); } -#ifndef _KERNEL +#ifndef _KERNEL u_short IpChecksum(struct ip *pip) { - return (LibAliasInternetChecksum(NULL, (u_short *) pip, + return (LibAliasInternetChecksum(NULL, (u_short *)pip, (pip->ip_hl << 2))); } u_short TcpChecksum(struct ip *pip) { u_short *ptr; struct tcphdr *tc; int nhdr, ntcp, nbytes; int sum, oddbyte; nhdr = pip->ip_hl << 2; ntcp = ntohs(pip->ip_len) - nhdr; tc = (struct tcphdr *)ip_next(pip); - ptr = (u_short *) tc; + ptr = (u_short *)tc; /* Add up TCP header and data */ nbytes = ntcp; sum = 0; while (nbytes > 1) { sum += *ptr++; nbytes -= 2; } if (nbytes == 1) { oddbyte = 0; - ((u_char *) & oddbyte)[0] = *(u_char *) ptr; - ((u_char *) & oddbyte)[1] = 0; + ((u_char *)&oddbyte)[0] = *(u_char *)ptr; + ((u_char *)&oddbyte)[1] = 0; sum += oddbyte; } /* "Pseudo-header" data */ ptr = (void *)&pip->ip_dst; sum += *ptr++; sum += *ptr; ptr = (void *)&pip->ip_src; sum += *ptr++; sum += *ptr; - sum += htons((u_short) ntcp); - sum += htons((u_short) pip->ip_p); + sum += htons((u_short)ntcp); + sum += htons((u_short)pip->ip_p); /* Roll over carry bits */ sum = (sum >> 16) + (sum & 0xffff); sum += (sum >> 16); /* Return checksum */ - return ((u_short) ~ sum); + return ((u_short)~sum); } #endif /* not _KERNEL */ void -DifferentialChecksum(u_short * cksum, void *newp, void *oldp, int n) +DifferentialChecksum(u_short *cksum, void *newp, void *oldp, int n) { int i; int accumulate; u_short *new = newp; u_short *old = oldp; accumulate = *cksum; for (i = 0; i < n; i++) { accumulate -= *new++; accumulate += *old++; } if (accumulate < 0) { accumulate = -accumulate; accumulate = (accumulate >> 16) + (accumulate & 0xffff); accumulate += accumulate >> 16; - *cksum = (u_short) ~ accumulate; + *cksum = (u_short)~accumulate; } else { accumulate = (accumulate >> 16) + (accumulate & 0xffff); accumulate += accumulate >> 16; - *cksum = (u_short) accumulate; + *cksum = (u_short)accumulate; } }