diff --git a/sys/netinet/libalias/HISTORY b/sys/netinet/libalias/HISTORY index 8965290a4f27..87d5b6f8ed76 100644 --- a/sys/netinet/libalias/HISTORY +++ b/sys/netinet/libalias/HISTORY @@ -1,149 +1,150 @@ $FreeBSD$ Version 1.0: August 11, 1996 (cjm) Version 1.1: August 20, 1996 (cjm) - Host accepts incoming connections for ports 0 to 1023. Version 1.2: September 7, 1996 (cjm) - Fragment handling error in alias_db.c corrected. Version 1.3: September 15, 1996 (cjm) - Generalized mechanism for handling incoming connections (no more 0 to 1023 restriction). - Increased ICMP support (will handle traceroute now). - Improved TCP close connection logic. Version 1.4: September 16, 1996 (cjm) Version 1.5: September 17, 1996 (cjm) - Corrected error in handling incoming UDP packets with zero checksum. Version 1.6: September 18, 1996 - Simplified ICMP data storage. Will now handle tracert from Win95 and NT as well as FreeBSD traceroute, which uses UDP packets to non-existent ports. Version 1.7: January 9, 1997 (cjm) - Reduced malloc() activity for ICMP echo and timestamp requests. - Added handling for out-of-order IP fragments. - Switched to differential checksum computation for IP headers (TCP, UDP and ICMP checksums were already differential). - Accepts FTP data connections from other than port 20. This allows one ftp connections from two hosts which are both running packet aliasing. - Checksum error on FTP transfers. Problem in code located by Martin Renters and Brian Somers. Version 1.8: January 14, 1997 (cjm) - Fixed data type error in function StartPoint() in alias_db.c (this bug did not exist before v1.7) Problem in code located by Ari Suutari. Version 1.9: February 1, 1997 (Eivind Eklund ) - Added support for IRC DCC (ee) - Changed the aliasing routines to use ANSI style throughout (ee) - Minor API changes for integration with other programs than PPP (ee) - Fixed minor security hole in alias_ftp.c for other applications of the aliasing software. Hole could _not_ manifest in ppp+pktAlias, but could potentially manifest in other applications of the aliasing. (ee) - Connections initiated from packet aliasing host machine will not have their port number aliased unless it conflicts with an aliasing port already being used. (There is an option to disable this for debugging) (cjm) - Sockets will be allocated in cases where there might be port interference with the host machine. This can be disabled in cases where the ppp host will be acting purely as a masquerading router and not generate any traffic of its own. (cjm) Version 2.0: March, 1997 (cjm) - Aliasing links are cleared only when a host interface address changes. - PacketAliasPermanentLink() API added. - Option for only aliasing private, unregistered IP addresses added. - Substantial rework to the aliasing lookup engine. Version 2.1: May, 1997 (cjm) - Continuing rework to the aliasing lookup engine to support multiple incoming addresses and static NAT. PacketAliasRedirectPort() and PacketAliasRedirectAddr() added to API. - Now supports outgoing as well as incoming ICMP error messages. Version 2.2: July, 1997 (cjm) - Rationalized API function names to all begin with "PacketAlias..." Old function names are retained for backwards compatibility. - Packet aliasing engine will now free memory of fragments which are never resolved after a timeout period. Once a fragment is resolved, it becomes the users responsibility to free the memory. Version 2.3: August 11, 1997 (cjm) - Problem associated with socket file descriptor accumulation in alias_db.c corrected. The sockets had to be closed when a binding failed. Problem in code located by Gordon Burditt. Version 2.4: September 1, 1997 (cjm) - PKT_ALIAS_UNREGISTERED_ONLY option repaired. This part of the code was incorrectly re-implemented in version 2.1. Version 2.5: December, 1997 (ee) - Added PKT_ALIAS_PUNCH_FW mode for firewall bypass of FTP/IRC DCC data connections. Also added improved TCP connection monitoring. Version 2.6: May, 1998 (amurai) - Added supporting routine for NetBios over TCP/IP. Version 3.0: January 1, 1999 - Transparent proxying support added. - PPTP redirecting support added based on patches contributed by Dru Nelson . Version 3.1: May, 2000 (Erik Salander, erik@whistle.com) - Added support to alias 227 replies, allows aliasing for FTP servers in passive mode. - Added support for PPTP aliasing. Version 3.2: July, 2000 (Erik Salander, erik@whistle.com and Junichi Satoh, junichi@junichi.org) - Added support for streaming media (RTSP and PNA) aliasing. Version 3.3: May 2021 (donner) - Dropped LibAliasCheckNewLink - - Refactor the database handling, perfomance improvements. + - Refactor the database handling + - Switch to more effienct SPLAY trees diff --git a/sys/netinet/libalias/alias_db.c b/sys/netinet/libalias/alias_db.c index b056f55b56a5..783a8ca3baa8 100644 --- a/sys/netinet/libalias/alias_db.c +++ b/sys/netinet/libalias/alias_db.c @@ -1,2913 +1,2491 @@ /*- * 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 #endif #include #include #ifdef _KERNEL #include #include #include #include #else #include "alias.h" #include "alias_local.h" #include "alias_mod.h" #endif +#include "alias_db.h" + static LIST_HEAD(, libalias) instancehead = LIST_HEAD_INITIALIZER(instancehead); int LibAliasTime; -/* - Constants (note: constants are also defined - near relevant functions or structs) -*/ - -/* 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 - -/* Matches any/unknown address in FindLinkIn/Out() and AddLink(). */ -static struct in_addr const ANY_ADDR = { INADDR_ANY }; - -/* 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. -*/ - -/* used to save changes to ACK/sequence numbers */ -struct ack_data_record { - u_long ack_old; - u_long ack_new; - int delta; - int active; -}; - -/* 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; -}; - -/* 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]; - /* Which firewall record is used for this hole? */ - int fwhole; -}; - -/* LSNAT server pool (circular list) */ -struct server { - struct in_addr addr; - u_short port; - struct server *next; -}; - -/* Main data structure */ -struct alias_link { - struct libalias *la; - /* 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; - /* 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 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 */ -#ifndef NO_USE_SOCKETS - int sockfd; /* socket descriptor */ -#endif - /* Linked list of pointers for input and output lookup tables */ - union { - struct { - LIST_ENTRY(alias_link) in; - LIST_ENTRY(alias_link) out; - } all; - struct { - LIST_ENTRY(alias_link) list; - } pptp; - }; - struct { - TAILQ_ENTRY(alias_link) list; - int time; /* Expire time for link */ - } expire; - /* Auxiliary data */ - union { - char *frag_ptr; - struct in_addr frag_addr; - struct tcp_dat *tcp; - } data; -}; - -/* Clean up procedure. */ -static void finishoff(void); - /* Kernel module definition. */ #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(); 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 struct group_in * -StartPointIn(struct libalias *, struct in_addr, u_short, int, 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); - -#define INGUARD \ - if (grp->alias_port != alias_port || \ - grp->link_type != link_type || \ - grp->alias_addr.s_addr != alias_addr.s_addr) \ - continue; +SPLAY_GENERATE(splay_out, alias_link, all.out, cmp_out); +SPLAY_GENERATE(splay_in, group_in, in, cmp_in); static struct group_in * StartPointIn(struct libalias *la, struct in_addr alias_addr, u_short alias_port, int link_type, int create) { - u_int n; - struct group_in *grp, *tmp; - - n = alias_addr.s_addr; - n += alias_port; - n += link_type; - n %= LINK_TABLE_IN_SIZE; + struct group_in *grp; + struct group_in needle = { + .alias_addr = alias_addr, + .alias_port = alias_port, + .link_type = link_type + }; - LIST_FOREACH_SAFE(grp, &la->groupTableIn[n], group_in, tmp) { - /* Auto cleanup */ - if (LIST_EMPTY(&grp->full) && LIST_EMPTY(&grp->partial)) { - LIST_REMOVE(grp, group_in); - free(grp); - } else { - INGUARD; - return (grp); - } - } - if (!create || (grp = malloc(sizeof(*grp))) == NULL) + grp = SPLAY_FIND(splay_in, &la->linkSplayIn, &needle); + if (grp != NULL || !create || (grp = malloc(sizeof(*grp))) == NULL) return (grp); grp->alias_addr = alias_addr; grp->alias_port = alias_port; grp->link_type = link_type; LIST_INIT(&grp->full); LIST_INIT(&grp->partial); - LIST_INSERT_HEAD(&la->groupTableIn[n], grp, group_in); + SPLAY_INSERT(splay_in, &la->linkSplayIn, grp); return (grp); } -#undef INGUARD - -static u_int -StartPointOut(struct in_addr src_addr, struct in_addr dst_addr, - u_short src_port, u_short dst_port, int link_type) -{ - u_int n; - - n = src_addr.s_addr; - n += dst_addr.s_addr; - n += src_port; - n += dst_port; - n += link_type; - - return (n % LINK_TABLE_OUT_SIZE); -} static int SeqDiff(u_long x, u_long y) { /* Return the difference between two TCP sequence numbers * This function is encapsulated in case there are any unusual * arithmetic conditions that need to be considered. */ return (ntohl(y) - ntohl(x)); } #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 */ 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; 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); #ifndef _KERNEL 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 - CleanupLink() - look for a stale link - 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 -static u_short GetSocket(struct libalias *, u_short, int *, int); -#endif -static void CleanupAliasData(struct libalias *, int); -static void CleanupLink(struct libalias *, struct alias_link **, int); -static void DeleteLink(struct alias_link **, int); -static struct alias_link * -UseLink(struct libalias *, 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, int); - -static struct alias_link * -FindLinkOut(struct libalias *, struct in_addr, struct in_addr, u_short, u_short, int, int); - -static struct alias_link * -FindLinkIn(struct libalias *, struct in_addr, struct in_addr, u_short, u_short, int, int); - -static u_short _RandomPort(struct libalias *la); - -#define GET_NEW_PORT_MAX_ATTEMPTS 20 - /* get random port in network byte order */ static u_short _RandomPort(struct libalias *la) { u_short port; port = la->aliasPortLower + arc4random_uniform(la->aliasPortLength); return ntohs(port); } /* GetNewPort() allocates port numbers. Note that if a port number is already in use, that does not mean that it cannot be used by another link concurrently. This is because GetNewPort() looks for unused triplets: (dest addr, dest port, alias port). */ static int GetNewPort(struct libalias *la, struct alias_link *lnk, int alias_port_param) { int i; int max_trials; u_short port; 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. * * The aliasing port is automatically selected by one of * two methods below: * * When this parameter is GET_ALIAS_PORT, it indicates to get * a randomly selected port number. */ if (alias_port_param >= 0 && alias_port_param < 0x10000) { lnk->alias_port = (u_short) alias_port_param; return (0); } if (alias_port_param != GET_ALIAS_PORT) { #ifdef LIBALIAS_DEBUG fprintf(stderr, "PacketAlias/GetNewPort(): "); fprintf(stderr, "input parameter error\n"); #endif return (-1); } max_trials = GET_NEW_PORT_MAX_ATTEMPTS; /* * 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 = (la->packetAliasMode & PKT_ALIAS_SAME_PORTS) ? lnk->src_port : _RandomPort(la); /* Port number search */ for (i = 0; i < max_trials; i++, port = _RandomPort(la)) { struct group_in *grp; struct alias_link *search_result; grp = StartPointIn(la, lnk->alias_addr, port, lnk->link_type, 0); if (grp == NULL) break; LIST_FOREACH(search_result, &grp->full, all.in) { if (lnk->dst_addr.s_addr == search_result->dst_addr.s_addr && lnk->dst_port == search_result->dst_port) break; /* found match */ } if (search_result == NULL) break; } if (i >= max_trials) { #ifdef LIBALIAS_DEBUG fprintf(stderr, "PacketAlias/GetNewPort(): "); fprintf(stderr, "could not find free port\n"); #endif return (-1); } #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, &lnk->sockfd, lnk->link_type)) { return (-1); } } #endif lnk->alias_port = port; return (0); } #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; 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 = src_port; } else { port = _RandomPort(la); } /* Port number search */ for (i = 0; i < max_trials; i++, port = _RandomPort(la)) { struct alias_link *search_result; if (align) port &= htons(0xfffe); for (j = 0; j < port_count; j++) { u_short port_j = ntohs(port) + j; if ((search_result = FindLinkIn(la, dst_addr, alias_addr, dst_port, htons(port_j), link_type, 0)) != NULL) break; } /* Found a good range, return base */ if (j == port_count) return (port); } #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, int deletePermanent) { struct alias_link *lnk, *lnk_tmp; - u_int i; LIBALIAS_LOCK_ASSERT(la); /* permanent entries may stay */ TAILQ_FOREACH_SAFE(lnk, &la->checkExpire, expire.list, lnk_tmp) DeleteLink(&lnk, deletePermanent); - - for (i = 0; i < LINK_TABLE_IN_SIZE; i++) { - struct group_in *grp, *grp_tmp; - - LIST_FOREACH_SAFE(grp, &la->groupTableIn[i], group_in, grp_tmp) - if (LIST_EMPTY(&grp->full) && LIST_EMPTY(&grp->partial)) { - LIST_REMOVE(grp, group_in); - free(grp); - } - } } - static void CleanupLink(struct libalias *la, struct alias_link **lnk, int deletePermanent) { LIBALIAS_LOCK_ASSERT(la); if (lnk == NULL || *lnk == NULL) return; if (LibAliasTime - (*lnk)->timestamp > (*lnk)->expire.time) { DeleteLink(lnk, deletePermanent); if ((*lnk) == NULL) return; } /* move to end, swap may fail on a single entry list */ TAILQ_REMOVE(&la->checkExpire, (*lnk), expire.list); TAILQ_INSERT_TAIL(&la->checkExpire, (*lnk), expire.list); } static struct alias_link * UseLink(struct libalias *la, struct alias_link *lnk) { CleanupLink(la, &lnk, 0); if (lnk != NULL) lnk->timestamp = LibAliasTime; return (lnk); } static void DeleteLink(struct alias_link **plnk, int deletePermanent) { struct alias_link *lnk = *plnk; struct libalias *la = lnk->la; LIBALIAS_LOCK_ASSERT(la); /* Don't do anything if the link is marked permanent */ if (!deletePermanent && (lnk->flags & LINK_PERMANENT)) return; #ifndef NO_FW_PUNCH /* Delete associated firewall hole, if any */ ClearFWHole(lnk); #endif switch (lnk->link_type) { case LINK_PPTP: LIST_REMOVE(lnk, pptp.list); break; - default: + default: { + struct group_in *grp; + /* 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); } else { /* Adjust output table pointers */ - LIST_REMOVE(lnk, all.out); + SPLAY_REMOVE(splay_out, &la->linkSplayOut, lnk); } /* Adjust input table pointers */ LIST_REMOVE(lnk, all.in); + + /* Remove intermediate node, if empty */ + grp = StartPointIn(la, lnk->alias_addr, lnk->alias_port, lnk->link_type, 0); + if (grp != NULL && + LIST_EMPTY(&grp->full) && + LIST_EMPTY(&grp->partial)) { + SPLAY_REMOVE(splay_in, &la->linkSplayIn, grp); + free(grp); + } + } break; } /* remove from housekeeping */ TAILQ_REMOVE(&la->checkExpire, lnk, expire.list); #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 */ switch (lnk->link_type) { case LINK_ICMP: la->icmpLinkCount--; break; case LINK_UDP: la->udpLinkCount--; break; case LINK_TCP: la->tcpLinkCount--; free(lnk->data.tcp); break; case LINK_PPTP: la->pptpLinkCount--; break; case LINK_FRAGMENT_ID: la->fragmentIdLinkCount--; break; case LINK_FRAGMENT_PTR: la->fragmentPtrLinkCount--; if (lnk->data.frag_ptr != NULL) free(lnk->data.frag_ptr); break; case LINK_ADDR: break; default: la->protoLinkCount--; break; } /* Free memory */ free(lnk); *plnk = NULL; /* 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) { #ifdef LIBALIAS_DEBUG fprintf(stderr, "PacketAlias/AddLink(): "); fprintf(stderr, "malloc() call failed.\n"); #endif return (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 lnk->sockfd = -1; #endif lnk->flags = 0; lnk->pflags = 0; lnk->timestamp = LibAliasTime; /* 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_FRAGMENT_ID: lnk->expire.time = FRAGMENT_ID_EXPIRE_TIME; break; case LINK_FRAGMENT_PTR: lnk->expire.time = FRAGMENT_PTR_EXPIRE_TIME; 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) { case LINK_ICMP: la->icmpLinkCount++; break; case LINK_UDP: la->udpLinkCount++; break; case LINK_TCP: { struct tcp_dat *aux_tcp; int i; aux_tcp = malloc(sizeof(struct tcp_dat)); if (aux_tcp == NULL) { #ifdef LIBALIAS_DEBUG fprintf(stderr, "PacketAlias/AddLink: "); fprintf(stderr, " cannot allocate auxiliary TCP data\n"); #endif free(lnk); return (NULL); } 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; } 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; } switch (link_type) { case LINK_PPTP: LIST_INSERT_HEAD(&la->pptpList, lnk, pptp.list); break; default: { struct group_in *grp; grp = StartPointIn(la, alias_addr, lnk->alias_port, link_type, 1); if (grp == NULL) { free(lnk); return (NULL); } /* 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, all.out); + SPLAY_INSERT(splay_out, &la->linkSplayOut, lnk); /* Set up pointers for input lookup table */ if (lnk->flags & LINK_PARTIALLY_SPECIFIED) LIST_INSERT_HEAD(&grp->partial, lnk, all.in); else LIST_INSERT_HEAD(&grp->full, lnk, all.in); } break; } /* Include the element into the housekeeping list */ TAILQ_INSERT_TAIL(&la->checkExpire, lnk, expire.list); 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, int link_type, int deletePermanent) { 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, deletePermanent); return (new_lnk); } - -#define OUTGUARD \ - if (lnk->src_port != src_port || \ - lnk->src_addr.s_addr != src_addr.s_addr || \ - lnk->dst_addr.s_addr != dst_addr.s_addr || \ - lnk->dst_port != dst_port || \ - lnk->link_type != link_type) \ - continue; - static struct alias_link * _SearchLinkOut(struct libalias *la, struct in_addr src_addr, struct in_addr dst_addr, u_short src_port, u_short dst_port, int link_type) { - u_int i; struct alias_link *lnk; + struct alias_link needle = { + .src_addr = src_addr, + .dst_addr = dst_addr, + .src_port = src_port, + .dst_port = dst_port, + .link_type = link_type + }; - i = StartPointOut(src_addr, dst_addr, src_port, dst_port, link_type); - LIST_FOREACH(lnk, &la->linkTableOut[i], all.out) { - OUTGUARD; - return (UseLink(la, lnk)); - } - - return (NULL); + lnk = SPLAY_FIND(splay_out, &la->linkSplayOut, &needle); + return (UseLink(la, lnk)); } -#undef OUTGUARD - 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 = _SearchLinkOut(la, src_addr, dst_addr, src_port, dst_port, link_type); if (lnk != NULL || !replace_partial_links) return (lnk); /* Search for partially specified links. */ if (dst_port != 0 && dst_addr.s_addr != INADDR_ANY) { lnk = _SearchLinkOut(la, src_addr, dst_addr, src_port, 0, link_type); if (lnk == NULL) lnk = _SearchLinkOut(la, src_addr, ANY_ADDR, src_port, dst_port, link_type); } if (lnk == NULL && (dst_port != 0 || dst_addr.s_addr != INADDR_ANY)) { lnk = _SearchLinkOut(la, src_addr, ANY_ADDR, src_port, 0, link_type); } if (lnk != NULL) { lnk = ReLink(lnk, src_addr, dst_addr, lnk->alias_addr, src_port, dst_port, lnk->alias_port, link_type, 0); } 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, ANY_ADDR, 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; struct group_in *grp; struct alias_link *lnk; struct alias_link *lnk_unknown_all; struct alias_link *lnk_unknown_dst_addr; struct alias_link *lnk_unknown_dst_port; struct in_addr src_addr; u_short src_port; LIBALIAS_LOCK_ASSERT(la); /* Initialize pointers */ lnk_unknown_all = NULL; lnk_unknown_dst_addr = NULL; lnk_unknown_dst_port = NULL; /* If either the dest addr or port is unknown, the search * loop will have to know about this. */ flags_in = 0; if (dst_addr.s_addr == INADDR_ANY) flags_in |= LINK_UNKNOWN_DEST_ADDR; if (dst_port == 0) flags_in |= LINK_UNKNOWN_DEST_PORT; /* Search loop */ grp = StartPointIn(la, alias_addr, alias_port, link_type, 0); if (grp == NULL) return (NULL); switch (flags_in) { case 0: LIST_FOREACH(lnk, &grp->full, all.in) { if (lnk->dst_addr.s_addr == dst_addr.s_addr && lnk->dst_port == dst_port) return (UseLink(la, lnk)); } break; case LINK_UNKNOWN_DEST_PORT: LIST_FOREACH(lnk, &grp->full, all.in) { if(lnk->dst_addr.s_addr == dst_addr.s_addr) { lnk_unknown_dst_port = lnk; break; } } break; case LINK_UNKNOWN_DEST_ADDR: LIST_FOREACH(lnk, &grp->full, all.in) { if(lnk->dst_port == dst_port) { lnk_unknown_dst_addr = lnk; break; } } break; case LINK_PARTIALLY_SPECIFIED: lnk_unknown_all = LIST_FIRST(&grp->full); break; } if (lnk_unknown_dst_port == NULL) { LIST_FOREACH(lnk, &grp->partial, all.in) { int flags = (flags_in | lnk->flags) & LINK_PARTIALLY_SPECIFIED; if (flags == LINK_PARTIALLY_SPECIFIED && lnk_unknown_all == NULL) lnk_unknown_all = lnk; if (flags == LINK_UNKNOWN_DEST_ADDR && lnk->dst_port == dst_port && lnk_unknown_dst_addr == NULL) lnk_unknown_dst_addr = lnk; if (flags == LINK_UNKNOWN_DEST_PORT && lnk->dst_addr.s_addr == dst_addr.s_addr) { lnk_unknown_dst_port = lnk; break; } } } lnk = (lnk_unknown_dst_port != NULL) ? lnk_unknown_dst_port : (lnk_unknown_dst_addr != NULL) ? lnk_unknown_dst_addr : lnk_unknown_all; if (lnk == NULL || !replace_partial_links) return (lnk); 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; } else { lnk = ReLink(lnk, src_addr, dst_addr, alias_addr, src_port, dst_port, alias_port, link_type, 0); } 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, ANY_ADDR, 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, ANY_ADDR, 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, 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, ANY_ADDR, dst_addr, ANY_ADDR, 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, ANY_ADDR, 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) { struct alias_link *lnk; LIBALIAS_LOCK_ASSERT(la); LIST_FOREACH(lnk, &la->pptpList, pptp.list) if (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 (UseLink(la, lnk)); } 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 *lnk; LIBALIAS_LOCK_ASSERT(la); LIST_FOREACH(lnk, &la->pptpList, pptp.list) if (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 (UseLink(la, lnk)); } 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 *lnk; LIBALIAS_LOCK_ASSERT(la); LIST_FOREACH(lnk, &la->pptpList, pptp.list) if (lnk->dst_port == dst_call_id && lnk->dst_addr.s_addr == dst_addr.s_addr && lnk->alias_addr.s_addr == alias_addr.s_addr) break; return (UseLink(la, 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); LIST_FOREACH(lnk, &la->pptpList, pptp.list) if (lnk->alias_port == alias_call_id && lnk->dst_addr.s_addr == dst_addr.s_addr && lnk->alias_addr.s_addr == alias_addr.s_addr) break; 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, ANY_ADDR, alias_addr, 0, 0, LINK_ADDR, 0); if (lnk == NULL) { 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, ANY_ADDR, 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 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 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 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) { 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) { 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) { 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) { 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, u_long th_seq, u_int th_off) { 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, 0); } 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 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) { LIBALIAS_LOCK_ASSERT(lnk->la); ReLink(lnk, lnk->src_addr, lnk->dst_addr, lnk->alias_addr, lnk->src_port, cid, lnk->alias_port, lnk->link_type, 1); } /* 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) { static unsigned int packets = 0; static unsigned int packet_limit = 1000; LIBALIAS_LOCK_ASSERT(la); packets++; /* * User space time/gettimeofday/... is very expensive. * Kernel space cache trashing is unnecessary. * * Save system time (seconds) in global variable LibAliasTime * for use by other functions. This is done so as not to * unnecessarily waste timeline by making system calls. * * Reduce the amount of house keeping work substantially by * sampling over the packets. */ if (packets % packet_limit == 0) { time_t now; #ifdef _KERNEL now = time_uptime; #else now = time(NULL); #endif if (now != LibAliasTime) { /* retry three times a second */ packet_limit = packets / 3; packets = 0; LibAliasTime = now; } } /* Do a cleanup for the first packets of the new second only */ if (packets < (la->udpLinkCount + la->tcpLinkCount)) { struct alias_link * lnk = TAILQ_FIRST(&la->checkExpire); CleanupLink(la, &lnk, 0); } } /* 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 if ((la->logDesc = fopen("/var/log/alias.log", "w"))) fprintf(la->logDesc, "PacketAlias/InitPacketAliasLog: Packet alias logging enabled.\n"); #endif 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; switch (lnk->link_type) { case LINK_PPTP: server = NULL; break; default: server = malloc(sizeof(struct server)); break; } if (server != NULL) { struct server *head; server->addr = addr; server->port = port; head = lnk->server; if (head == NULL) { server->next = server; /* not usable for outgoing connections */ - LIST_REMOVE(lnk, all.out); + SPLAY_REMOVE(splay_out, &la->linkSplayOut, lnk); } else { struct server *s; 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, ANY_ADDR, 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) { LIBALIAS_LOCK(la); (void)la; DeleteLink(&lnk, 1); 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, 0); la->aliasAddress = addr; LIBALIAS_UNLOCK(la); } void LibAliasSetAliasPortRange(struct libalias *la, u_short port_low, u_short port_high) { LIBALIAS_LOCK(la); if (port_low) { 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; } else { /* Set default values */ la->aliasPortLower = 0x8000; la->aliasPortLength = 0x8000; } 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; - 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 */ if (LIST_EMPTY(&instancehead)) atexit(finishoff); #endif LIST_INSERT_HEAD(&instancehead, la, instancelist); #ifdef _KERNEL LibAliasTime = time_uptime; #else LibAliasTime = time(NULL); #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->groupTableIn[i]); + SPLAY_INIT(&la->linkSplayIn); + SPLAY_INIT(&la->linkSplayOut); LIST_INIT(&la->pptpList); TAILQ_INIT(&la->checkExpire); #ifdef _KERNEL AliasSctpInit(la); #endif LIBALIAS_LOCK_INIT(la); LIBALIAS_LOCK(la); } else { LIBALIAS_LOCK(la); CleanupAliasData(la, 1); #ifdef _KERNEL AliasSctpTerm(la); AliasSctpInit(la); #endif } la->aliasAddress.s_addr = INADDR_ANY; la->targetAddress.s_addr = INADDR_ANY; la->aliasPortLower = 0x8000; la->aliasPortLength = 0x8000; 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->packetAliasMode = PKT_ALIAS_SAME_PORTS #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 CleanupAliasData(la, 1); 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); if (flags & mask & PKT_ALIAS_LOG) { /* Enable logging */ if (InitPacketAliasLog(la) == ENOMEM) goto getout; } else if (~flags & mask & PKT_ALIAS_LOG) /* _Disable_ logging */ UninitPacketAliasLog(la); #ifndef NO_FW_PUNCH if (flags & mask & PKT_ALIAS_PUNCH_FW) /* Start punching holes in the firewall? */ InitPunchFW(la); 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 */ la->packetAliasMode = (flags & mask) | (la->packetAliasMode & ~mask); res = la->packetAliasMode; getout: LIBALIAS_UNLOCK(la); return (res); } /* never used and never worked, to be removed in FreeBSD 14 */ int LibAliasCheckNewLink(struct libalias *la) { (void)la; return (0); } #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; cmd->addr.s_addr = addr; return fill_cmd(cmd1, opcode, F_INSN_SIZE(ipfw_insn_u32), 0, 0); } static ipfw_insn * fill_one_port(ipfw_insn * cmd1, enum ipfw_opcodes opcode, u_int16_t port) { ipfw_insn_u16 *cmd = (ipfw_insn_u16 *)cmd1; cmd->ports[0] = cmd->ports[1] = port; return fill_cmd(cmd1, opcode, F_INSN_SIZE(ipfw_insn_u16), 0, 0); } static int fill_rule(void *buf, int bufsize, int rulenum, enum ipfw_opcodes action, int proto, struct in_addr sa, u_int16_t sp, struct in_addr da, u_int16_t dp) { struct ip_fw *rule = (struct ip_fw *)buf; ipfw_insn *cmd = (ipfw_insn *)rule->cmd; bzero(buf, bufsize); rule->rulenum = rulenum; cmd = fill_cmd(cmd, O_PROTO, F_INSN_SIZE(ipfw_insn), 0, proto); cmd = fill_ip(cmd, O_IP_SRC, sa.s_addr); cmd = fill_one_port(cmd, O_IP_SRCPORT, sp); cmd = fill_ip(cmd, O_IP_DST, da.s_addr); cmd = fill_one_port(cmd, O_IP_DSTPORT, dp); rule->act_ofs = (u_int32_t *)cmd - (u_int32_t *)rule->cmd; cmd = fill_cmd(cmd, action, F_INSN_SIZE(ipfw_insn), 0, 0); rule->cmd_len = (u_int32_t *)cmd - (u_int32_t *)rule->cmd; return ((char *)cmd - (char *)buf); } -static void ClearAllFWHoles(struct libalias *la); - -#define fw_setfield(la, field, num) \ -do { \ - (field)[(num) - la->fireWallBaseNum] = 1; \ -} /*lint -save -e717 */ while(0)/* lint -restore */ - -#define fw_clrfield(la, field, num) \ -do { \ - (field)[(num) - la->fireWallBaseNum] = 0; \ -} /*lint -save -e717 */ while(0)/* lint -restore */ - -#define fw_tstfield(la, field, num) ((field)[(num) - la->fireWallBaseNum]) - static void InitPunchFW(struct libalias *la) { la->fireWallField = malloc(la->fireWallNumNums); if (la->fireWallField) { memset(la->fireWallField, 0, la->fireWallNumNums); if (la->fireWallFD < 0) { la->fireWallFD = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); } ClearAllFWHoles(la); la->fireWallActiveNum = la->fireWallBaseNum; } } static void UninitPunchFW(struct libalias *la) { ClearAllFWHoles(la); if (la->fireWallFD >= 0) close(la->fireWallFD); la->fireWallFD = -1; if (la->fireWallField) free(la->fireWallField); la->fireWallField = NULL; la->packetAliasMode &= ~PKT_ALIAS_PUNCH_FW; } /* Make a certain link go through the firewall */ void PunchFWHole(struct alias_link *lnk) { struct libalias *la; int r; /* Result code */ struct ip_fw rule; /* On-the-fly built rule */ int fwhole; /* Where to punch hole */ la = lnk->la; /* Don't do anything unless we are asked to */ if (!(la->packetAliasMode & PKT_ALIAS_PUNCH_FW) || la->fireWallFD < 0 || lnk->link_type != LINK_TCP) return; memset(&rule, 0, sizeof rule); /** Build rule **/ /* Find empty slot */ for (fwhole = la->fireWallActiveNum; fwhole < la->fireWallBaseNum + la->fireWallNumNums && fw_tstfield(la, la->fireWallField, fwhole); fwhole++); if (fwhole == la->fireWallBaseNum + la->fireWallNumNums) { for (fwhole = la->fireWallBaseNum; fwhole < la->fireWallActiveNum && fw_tstfield(la, la->fireWallField, fwhole); fwhole++); if (fwhole == la->fireWallActiveNum) { /* No rule point empty - we can't punch more holes. */ la->fireWallActiveNum = la->fireWallBaseNum; #ifdef 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 */ lnk->data.tcp->fwhole = fwhole; fw_setfield(la, la->fireWallField, fwhole); } /* Remove a hole in a firewall associated with a particular alias lnk. Calling this too often is harmless. */ static void ClearFWHole(struct alias_link *lnk) { struct libalias *la; la = lnk->la; if (lnk->link_type == LINK_TCP) { int fwhole = lnk->data.tcp->fwhole; /* Where is the firewall hole? */ struct ip_fw rule; if (fwhole < 0) return; memset(&rule, 0, sizeof rule); /* useless for ipfw2 */ 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) { /* 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 */ lnk = FindLinkIn(la, sm->ip_hdr->ip_src, sm->ip_hdr->ip_dst, NO_DEST_PORT, 0, LINK_SCTP, 1); if (lnk != NULL) /* redirect proto */ return (lnk->src_addr); } return (redir); /* address redirect */ } } diff --git a/sys/netinet/libalias/alias_db.h b/sys/netinet/libalias/alias_db.h new file mode 100644 index 000000000000..971ca305c1a6 --- /dev/null +++ b/sys/netinet/libalias/alias_db.h @@ -0,0 +1,443 @@ +/*- + * 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. + */ + +/* + 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. +*/ + +#ifndef _ALIAS_DB_H_ +#define _ALIAS_DB_H_ + + +/* + Constants (note: constants are also defined + near relevant functions or structs) +*/ + +/* 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 + +/* Matches any/unknown address in FindLinkIn/Out() and AddLink(). */ +static struct in_addr const ANY_ADDR = { INADDR_ANY }; + +/* 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. +*/ + +/* used to save changes to ACK/sequence numbers */ +struct ack_data_record { + u_long ack_old; + u_long ack_new; + int delta; + int active; +}; + +/* 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; +}; + +/* 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]; + /* Which firewall record is used for this hole? */ + int fwhole; +}; + +/* LSNAT server pool (circular list) */ +struct server { + struct in_addr addr; + u_short port; + struct server *next; +}; + +/* Main data structure */ +struct alias_link { + struct libalias *la; + /* 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; + /* 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 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 */ +#ifndef NO_USE_SOCKETS + int sockfd; /* socket descriptor */ +#endif + /* Linked list of pointers for input and output lookup tables */ + union { + struct { + SPLAY_ENTRY(alias_link) out; + LIST_ENTRY (alias_link) in; + } all; + struct { + LIST_ENTRY (alias_link) list; + } pptp; + }; + struct { + TAILQ_ENTRY(alias_link) list; + int time; /* Expire time for link */ + } expire; + /* Auxiliary data */ + union { + char *frag_ptr; + struct in_addr frag_addr; + struct tcp_dat *tcp; + } data; +}; + +/* Clean up procedure. */ +static void finishoff(void); + +/* 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 struct group_in * +StartPointIn(struct libalias *, struct in_addr, u_short, int, 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); + + +/* Splay handling */ +static inline int +cmp_out(struct alias_link *a, struct alias_link *b) { + int i = a->src_port - b->src_port; + if (i != 0) return (i); + if (a->src_addr.s_addr > b->src_addr.s_addr) return (1); + if (a->src_addr.s_addr < b->src_addr.s_addr) return (-1); + if (a->dst_addr.s_addr > b->dst_addr.s_addr) return (1); + if (a->dst_addr.s_addr < b->dst_addr.s_addr) return (-1); + i = a->dst_port - b->dst_port; + if (i != 0) return (i); + i = a->link_type - b->link_type; + return (i); +} +SPLAY_PROTOTYPE(splay_out, alias_link, all.out, cmp_out); + +static inline int +cmp_in(struct group_in *a, struct group_in *b) { + int i = a->alias_port - b->alias_port; + if (i != 0) return (i); + i = a->link_type - b->link_type; + if (i != 0) return (i); + if (a->alias_addr.s_addr > b->alias_addr.s_addr) return (1); + if (a->alias_addr.s_addr < b->alias_addr.s_addr) return (-1); + return (0); +} +SPLAY_PROTOTYPE(splay_in, group_in, in, cmp_in); + +/* 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 + CleanupLink() - look for a stale link + 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 +static u_short GetSocket(struct libalias *, u_short, int *, int); +#endif +static void CleanupAliasData(struct libalias *, int); +static void CleanupLink(struct libalias *, struct alias_link **, int); +static void DeleteLink(struct alias_link **, int); +static struct alias_link * +UseLink(struct libalias *, 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, int); + +static struct alias_link * +FindLinkOut(struct libalias *, struct in_addr, struct in_addr, u_short, u_short, int, int); + +static struct alias_link * +FindLinkIn(struct libalias *, struct in_addr, struct in_addr, u_short, u_short, int, int); + +static u_short _RandomPort(struct libalias *la); + +#define GET_NEW_PORT_MAX_ATTEMPTS 20 + + +#ifndef NO_FW_PUNCH + +static void ClearAllFWHoles(struct libalias *la); + +#define fw_setfield(la, field, num) \ +do { \ + (field)[(num) - la->fireWallBaseNum] = 1; \ +} /*lint -save -e717 */ while(0)/* lint -restore */ + +#define fw_clrfield(la, field, num) \ +do { \ + (field)[(num) - la->fireWallBaseNum] = 0; \ +} /*lint -save -e717 */ while(0)/* lint -restore */ + +#define fw_tstfield(la, field, num) ((field)[(num) - la->fireWallBaseNum]) + +#endif /* !NO_FW_PUNCH */ + +#endif /* _ALIAS_DB_H_ */ diff --git a/sys/netinet/libalias/alias_local.h b/sys/netinet/libalias/alias_local.h index 5628adac203e..938744924dcc 100644 --- a/sys/netinet/libalias/alias_local.h +++ b/sys/netinet/libalias/alias_local.h @@ -1,384 +1,382 @@ /*- * 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 #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 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 group_in { struct in_addr alias_addr; u_short alias_port; int link_type; - LIST_ENTRY(group_in) group_in; + SPLAY_ENTRY(group_in) in; LIST_HEAD(, alias_link) full, partial; }; struct libalias { LIST_ENTRY(libalias) instancelist; /* 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; /* 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 (, group_in) groupTableIn[LINK_TABLE_IN_SIZE]; + SPLAY_HEAD(splay_out, alias_link) linkSplayOut; + SPLAY_HEAD(splay_in, group_in) linkSplayIn; LIST_HEAD (, alias_link) pptpList; /* HouseKeeping */ TAILQ_HEAD (, alias_link) checkExpire; /* Link statistics */ unsigned int icmpLinkCount; unsigned int udpLinkCount; unsigned int tcpLinkCount; unsigned int pptpLinkCount; unsigned int protoLinkCount; unsigned int fragmentIdLinkCount; unsigned int fragmentPtrLinkCount; unsigned int sockCount; /* log descriptor */ #ifdef _KERNEL char *logDesc; #else FILE *logDesc; #endif #ifndef NO_FW_PUNCH /* 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 /* 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 /* 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 */ LIST_HEAD(sctpNatTableL, sctp_nat_assoc) *sctpTableLocal; /* 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. */ struct mtx mutex; #endif }; /* Macros */ #ifdef _KERNEL #define LIBALIAS_LOCK_INIT(l) \ 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 */ /* System time in seconds for current packet */ extern int LibAliasTime; /* * 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); struct alias_link * FindFragmentPtr(struct libalias *la, struct in_addr _dst_addr, u_short _ip_id); struct alias_link * FindProtoIn(struct libalias *la, struct in_addr _dst_addr, struct in_addr _alias_addr, u_char _proto); struct alias_link * FindProtoOut(struct libalias *la, struct in_addr _src_addr, struct in_addr _dst_addr, u_char _proto); struct alias_link * FindUdpTcpIn(struct libalias *la, struct in_addr _dst_addr, struct in_addr _alias_addr, u_short _dst_port, u_short _alias_port, u_char _proto, int _create); struct alias_link * FindUdpTcpOut(struct libalias *la, struct in_addr _src_addr, struct in_addr _dst_addr, u_short _src_port, u_short _dst_port, u_char _proto, int _create); struct alias_link * AddPptp(struct libalias *la, struct in_addr _src_addr, struct in_addr _dst_addr, struct in_addr _alias_addr, u_int16_t _src_call_id); struct alias_link * FindPptpOutByCallId(struct libalias *la, struct in_addr _src_addr, struct in_addr _dst_addr, u_int16_t _src_call_id); struct alias_link * FindPptpInByCallId(struct libalias *la, struct in_addr _dst_addr, struct in_addr _alias_addr, u_int16_t _dst_call_id); struct alias_link * FindPptpOutByPeerCallId(struct libalias *la, struct in_addr _src_addr, struct in_addr _dst_addr, u_int16_t _dst_call_id); struct alias_link * FindPptpInByPeerCallId(struct libalias *la, struct in_addr _dst_addr, struct in_addr _alias_addr, u_int16_t _alias_call_id); struct alias_link * FindRtspOut(struct libalias *la, struct in_addr _src_addr, struct in_addr _dst_addr, u_short _src_port, u_short _alias_port, u_char _proto); struct in_addr FindOriginalAddress(struct libalias *la, struct in_addr _alias_addr); struct in_addr FindAliasAddress(struct libalias *la, struct in_addr _original_addr); 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); 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); void SetDefaultAliasAddress(struct libalias *la, struct in_addr _alias_addr); u_short GetOriginalPort(struct alias_link *_lnk); u_short GetAliasPort(struct alias_link *_lnk); struct in_addr GetProxyAddress(struct alias_link *_lnk); void SetProxyAddress(struct alias_link *_lnk, struct in_addr _addr); u_short GetProxyPort(struct alias_link *_lnk); void SetProxyPort(struct alias_link *_lnk, u_short _port); void SetAckModified(struct alias_link *_lnk); int GetAckModified(struct alias_link *_lnk); int GetDeltaAckIn(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, u_short ip_len, u_long th_seq, u_int th_off); void SetExpire (struct alias_link *_lnk, int _expire); 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 *); /* Transparent proxy routines */ int ProxyCheck(struct libalias *la, struct in_addr *proxy_server_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_ */