mirror of
https://git.FreeBSD.org/src.git
synced 2024-12-24 11:29:10 +00:00
e83aaae350
Requested by: Charles Mott <cmott@scientech.com>
2813 lines
73 KiB
C
2813 lines
73 KiB
C
/* -*- mode: c; tab-width: 8; c-basic-indent: 4; -*- */
|
|
|
|
/*-
|
|
* Copyright (c) 2001 Charles Mott <cm@linktel.net>
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
__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.
|
|
*/
|
|
|
|
|
|
/* System include files */
|
|
#include <errno.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
|
|
#include <sys/queue.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/time.h>
|
|
#include <sys/types.h>
|
|
|
|
/* BSD network include files */
|
|
#include <netinet/in_systm.h>
|
|
#include <netinet/in.h>
|
|
#include <netinet/ip.h>
|
|
#include <netinet/tcp.h>
|
|
#include <arpa/inet.h>
|
|
|
|
#include "alias.h"
|
|
#include "alias_local.h"
|
|
|
|
|
|
|
|
/*
|
|
Constants (note: constants are also defined
|
|
near relevant functions or structs)
|
|
*/
|
|
|
|
/* Sizes of input and output link tables */
|
|
#define LINK_TABLE_OUT_SIZE 101
|
|
#define LINK_TABLE_IN_SIZE 4001
|
|
|
|
/* Parameters used for cleanup of expired links */
|
|
#define ALIAS_CLEANUP_INTERVAL_SECS 60
|
|
#define ALIAS_CLEANUP_MAX_SPOKES 30
|
|
|
|
/* Timeouts (in seconds) for different link types */
|
|
#define ICMP_EXPIRE_TIME 60
|
|
#define UDP_EXPIRE_TIME 60
|
|
#define PROTO_EXPIRE_TIME 60
|
|
#define FRAGMENT_ID_EXPIRE_TIME 10
|
|
#define FRAGMENT_PTR_EXPIRE_TIME 30
|
|
|
|
/* TCP link expire time for different cases */
|
|
/* When the link has been used and closed - minimal grace time to
|
|
allow ACKs and potential re-connect in FTP (XXX - is this allowed?) */
|
|
#ifndef TCP_EXPIRE_DEAD
|
|
# define TCP_EXPIRE_DEAD 10
|
|
#endif
|
|
|
|
/* When the link has been used and closed on one side - the other side
|
|
is allowed to still send data */
|
|
#ifndef TCP_EXPIRE_SINGLEDEAD
|
|
# define TCP_EXPIRE_SINGLEDEAD 90
|
|
#endif
|
|
|
|
/* When the link isn't yet up */
|
|
#ifndef TCP_EXPIRE_INITIAL
|
|
# define TCP_EXPIRE_INITIAL 300
|
|
#endif
|
|
|
|
/* When the link is up */
|
|
#ifndef TCP_EXPIRE_CONNECTED
|
|
# define TCP_EXPIRE_CONNECTED 86400
|
|
#endif
|
|
|
|
|
|
/* Dummy port number codes used for FindLinkIn/Out() and AddLink().
|
|
These constants can be anything except zero, which indicates an
|
|
unknown port number. */
|
|
|
|
#define NO_DEST_PORT 1
|
|
#define NO_SRC_PORT 1
|
|
|
|
|
|
|
|
/* Data Structures
|
|
|
|
The fundamental data structure used in this program is
|
|
"struct alias_link". Whenever a TCP connection is made,
|
|
a UDP datagram is sent out, or an ICMP echo request is made,
|
|
a link record is made (if it has not already been created).
|
|
The link record is identified by the source address/port
|
|
and the destination address/port. In the case of an ICMP
|
|
echo request, the source port is treated as being equivalent
|
|
with the 16-bit ID number of the ICMP packet.
|
|
|
|
The link record also can store some auxiliary data. For
|
|
TCP connections that have had sequence and acknowledgment
|
|
modifications, data space is available to track these changes.
|
|
A state field is used to keep track in changes to the TCP
|
|
connection state. ID numbers of fragments can also be
|
|
stored in the auxiliary space. Pointers to unresolved
|
|
fragments can also be stored.
|
|
|
|
The link records support two independent chainings. Lookup
|
|
tables for input and out tables hold the initial pointers
|
|
the link chains. On input, the lookup table indexes on alias
|
|
port and link type. On output, the lookup table indexes on
|
|
source address, destination address, source port, destination
|
|
port and link type.
|
|
*/
|
|
|
|
struct ack_data_record /* used to save changes to ACK/sequence numbers */
|
|
{
|
|
u_long ack_old;
|
|
u_long ack_new;
|
|
int delta;
|
|
int active;
|
|
};
|
|
|
|
struct tcp_state /* Information about TCP connection */
|
|
{
|
|
int in; /* State for outside -> inside */
|
|
int out; /* State for inside -> outside */
|
|
int index; /* Index to ACK data array */
|
|
int ack_modified; /* Indicates whether ACK and sequence numbers */
|
|
/* been modified */
|
|
};
|
|
|
|
#define N_LINK_TCP_DATA 3 /* Number of distinct ACK number changes
|
|
saved for a modified TCP stream */
|
|
struct tcp_dat
|
|
{
|
|
struct tcp_state state;
|
|
struct ack_data_record ack[N_LINK_TCP_DATA];
|
|
int fwhole; /* Which firewall record is used for this hole? */
|
|
};
|
|
|
|
struct server /* LSNAT server pool (circular list) */
|
|
{
|
|
struct in_addr addr;
|
|
u_short port;
|
|
struct server *next;
|
|
};
|
|
|
|
struct alias_link /* Main data structure */
|
|
{
|
|
struct in_addr src_addr; /* Address and port information */
|
|
struct in_addr dst_addr;
|
|
struct in_addr alias_addr;
|
|
struct in_addr proxy_addr;
|
|
u_short src_port;
|
|
u_short dst_port;
|
|
u_short alias_port;
|
|
u_short proxy_port;
|
|
struct server *server;
|
|
|
|
int link_type; /* Type of link: TCP, UDP, ICMP, proto, frag */
|
|
|
|
/* values for link_type */
|
|
#define LINK_ICMP IPPROTO_ICMP
|
|
#define LINK_UDP IPPROTO_UDP
|
|
#define LINK_TCP IPPROTO_TCP
|
|
#define LINK_FRAGMENT_ID (IPPROTO_MAX + 1)
|
|
#define LINK_FRAGMENT_PTR (IPPROTO_MAX + 2)
|
|
#define LINK_ADDR (IPPROTO_MAX + 3)
|
|
#define LINK_PPTP (IPPROTO_MAX + 4)
|
|
|
|
int flags; /* indicates special characteristics */
|
|
int pflags; /* protocol-specific flags */
|
|
|
|
/* flag bits */
|
|
#define LINK_UNKNOWN_DEST_PORT 0x01
|
|
#define LINK_UNKNOWN_DEST_ADDR 0x02
|
|
#define LINK_PERMANENT 0x04
|
|
#define LINK_PARTIALLY_SPECIFIED 0x03 /* logical-or of first two bits */
|
|
#define LINK_UNFIREWALLED 0x08
|
|
|
|
int timestamp; /* Time link was last accessed */
|
|
int expire_time; /* Expire time for link */
|
|
|
|
int sockfd; /* socket descriptor */
|
|
|
|
LIST_ENTRY(alias_link) list_out; /* Linked list of pointers for */
|
|
LIST_ENTRY(alias_link) list_in; /* input and output lookup tables */
|
|
|
|
union /* Auxiliary data */
|
|
{
|
|
char *frag_ptr;
|
|
struct in_addr frag_addr;
|
|
struct tcp_dat *tcp;
|
|
} data;
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/* Global Variables
|
|
|
|
The global variables listed here are only accessed from
|
|
within alias_db.c and so are prefixed with the static
|
|
designation.
|
|
*/
|
|
|
|
int packetAliasMode; /* Mode flags */
|
|
/* - documented in alias.h */
|
|
|
|
static struct in_addr aliasAddress; /* Address written onto source */
|
|
/* field of IP packet. */
|
|
|
|
static struct in_addr targetAddress; /* IP address incoming packets */
|
|
/* are sent to if no aliasing */
|
|
/* link already exists */
|
|
|
|
static struct in_addr nullAddress; /* Used as a dummy parameter for */
|
|
/* some function calls */
|
|
static LIST_HEAD(, alias_link)
|
|
linkTableOut[LINK_TABLE_OUT_SIZE]; /* Lookup table of pointers to */
|
|
/* chains of link records. Each */
|
|
static LIST_HEAD(, alias_link) /* link record is doubly indexed */
|
|
linkTableIn[LINK_TABLE_IN_SIZE]; /* into input and output lookup */
|
|
/* tables. */
|
|
|
|
static int icmpLinkCount; /* Link statistics */
|
|
static int udpLinkCount;
|
|
static int tcpLinkCount;
|
|
static int pptpLinkCount;
|
|
static int protoLinkCount;
|
|
static int fragmentIdLinkCount;
|
|
static int fragmentPtrLinkCount;
|
|
static int sockCount;
|
|
|
|
static int cleanupIndex; /* Index to chain of link table */
|
|
/* being inspected for old links */
|
|
|
|
static int timeStamp; /* System time in seconds for */
|
|
/* current packet */
|
|
|
|
static int lastCleanupTime; /* Last time IncrementalCleanup() */
|
|
/* was called */
|
|
|
|
static int houseKeepingResidual; /* used by HouseKeeping() */
|
|
|
|
static int deleteAllLinks; /* If equal to zero, DeleteLink() */
|
|
/* will not remove permanent links */
|
|
|
|
static FILE *monitorFile; /* File descriptor for link */
|
|
/* statistics monitoring file */
|
|
|
|
static int newDefaultLink; /* Indicates if a new aliasing */
|
|
/* link has been created after a */
|
|
/* call to PacketAliasIn/Out(). */
|
|
|
|
#ifndef NO_FW_PUNCH
|
|
static int fireWallFD = -1; /* File descriptor to be able to */
|
|
/* control firewall. Opened by */
|
|
/* PacketAliasSetMode on first */
|
|
/* setting the PKT_ALIAS_PUNCH_FW */
|
|
/* flag. */
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Internal utility routines (used only in alias_db.c)
|
|
|
|
Lookup table starting points:
|
|
StartPointIn() -- link table initial search point for
|
|
incoming packets
|
|
StartPointOut() -- link table initial search point for
|
|
outgoing packets
|
|
|
|
Miscellaneous:
|
|
SeqDiff() -- difference between two TCP sequences
|
|
ShowAliasStats() -- send alias statistics to a monitor file
|
|
*/
|
|
|
|
|
|
/* Local prototypes */
|
|
static u_int StartPointIn(struct in_addr, u_short, int);
|
|
|
|
static u_int StartPointOut(struct in_addr, struct in_addr,
|
|
u_short, u_short, int);
|
|
|
|
static int SeqDiff(u_long, u_long);
|
|
|
|
static void ShowAliasStats(void);
|
|
|
|
#ifndef NO_FW_PUNCH
|
|
/* Firewall control */
|
|
static void InitPunchFW(void);
|
|
static void UninitPunchFW(void);
|
|
static void ClearFWHole(struct alias_link *link);
|
|
#endif
|
|
|
|
/* Log file control */
|
|
static void InitPacketAliasLog(void);
|
|
static void UninitPacketAliasLog(void);
|
|
|
|
static u_int
|
|
StartPointIn(struct in_addr alias_addr,
|
|
u_short alias_port,
|
|
int link_type)
|
|
{
|
|
u_int n;
|
|
|
|
n = alias_addr.s_addr;
|
|
if (link_type != LINK_PPTP)
|
|
n += alias_port;
|
|
n += link_type;
|
|
return(n % LINK_TABLE_IN_SIZE);
|
|
}
|
|
|
|
|
|
static u_int
|
|
StartPointOut(struct in_addr src_addr, struct in_addr dst_addr,
|
|
u_short src_port, u_short dst_port, int link_type)
|
|
{
|
|
u_int n;
|
|
|
|
n = src_addr.s_addr;
|
|
n += dst_addr.s_addr;
|
|
if (link_type != LINK_PPTP) {
|
|
n += src_port;
|
|
n += dst_port;
|
|
}
|
|
n += link_type;
|
|
|
|
return(n % LINK_TABLE_OUT_SIZE);
|
|
}
|
|
|
|
|
|
static int
|
|
SeqDiff(u_long x, u_long y)
|
|
{
|
|
/* Return the difference between two TCP sequence numbers */
|
|
|
|
/*
|
|
This function is encapsulated in case there are any unusual
|
|
arithmetic conditions that need to be considered.
|
|
*/
|
|
|
|
return (ntohl(y) - ntohl(x));
|
|
}
|
|
|
|
|
|
static void
|
|
ShowAliasStats(void)
|
|
{
|
|
/* Used for debugging */
|
|
|
|
if (monitorFile)
|
|
{
|
|
fprintf(monitorFile, "icmp=%d, udp=%d, tcp=%d, pptp=%d, proto=%d, frag_id=%d frag_ptr=%d",
|
|
icmpLinkCount,
|
|
udpLinkCount,
|
|
tcpLinkCount,
|
|
pptpLinkCount,
|
|
protoLinkCount,
|
|
fragmentIdLinkCount,
|
|
fragmentPtrLinkCount);
|
|
|
|
fprintf(monitorFile, " / tot=%d (sock=%d)\n",
|
|
icmpLinkCount + udpLinkCount
|
|
+ tcpLinkCount
|
|
+ pptpLinkCount
|
|
+ protoLinkCount
|
|
+ fragmentIdLinkCount
|
|
+ fragmentPtrLinkCount,
|
|
sockCount);
|
|
|
|
fflush(monitorFile);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Internal routines for finding, deleting and adding links
|
|
|
|
Port Allocation:
|
|
GetNewPort() -- find and reserve new alias port number
|
|
GetSocket() -- try to allocate a socket for a given port
|
|
|
|
Link creation and deletion:
|
|
CleanupAliasData() - remove all link chains from lookup table
|
|
IncrementalCleanup() - look for stale links in a single chain
|
|
DeleteLink() - remove link
|
|
AddLink() - add link
|
|
ReLink() - change link
|
|
|
|
Link search:
|
|
FindLinkOut() - find link for outgoing packets
|
|
FindLinkIn() - find link for incoming packets
|
|
|
|
Port search:
|
|
FindNewPortGroup() - find an available group of ports
|
|
*/
|
|
|
|
/* Local prototypes */
|
|
static int GetNewPort(struct alias_link *, int);
|
|
|
|
static u_short GetSocket(u_short, int *, int);
|
|
|
|
static void CleanupAliasData(void);
|
|
|
|
static void IncrementalCleanup(void);
|
|
|
|
static void DeleteLink(struct alias_link *);
|
|
|
|
static struct alias_link *
|
|
AddLink(struct in_addr, struct in_addr, struct in_addr,
|
|
u_short, u_short, int, int);
|
|
|
|
static struct alias_link *
|
|
ReLink(struct alias_link *,
|
|
struct in_addr, struct in_addr, struct in_addr,
|
|
u_short, u_short, int, int);
|
|
|
|
static struct alias_link *
|
|
FindLinkOut(struct in_addr, struct in_addr, u_short, u_short, int, int);
|
|
|
|
static struct alias_link *
|
|
FindLinkIn(struct in_addr, struct in_addr, u_short, u_short, int, int);
|
|
|
|
|
|
#define ALIAS_PORT_BASE 0x08000
|
|
#define ALIAS_PORT_MASK 0x07fff
|
|
#define ALIAS_PORT_MASK_EVEN 0x07ffe
|
|
#define GET_NEW_PORT_MAX_ATTEMPTS 20
|
|
|
|
#define GET_ALIAS_PORT -1
|
|
#define GET_ALIAS_ID GET_ALIAS_PORT
|
|
|
|
#define FIND_EVEN_ALIAS_BASE 1
|
|
|
|
/* GetNewPort() allocates port numbers. Note that if a port number
|
|
is already in use, that does not mean that it cannot be used by
|
|
another link concurrently. This is because GetNewPort() looks for
|
|
unused triplets: (dest addr, dest port, alias port). */
|
|
|
|
static int
|
|
GetNewPort(struct alias_link *link, int alias_port_param)
|
|
{
|
|
int i;
|
|
int max_trials;
|
|
u_short port_sys;
|
|
u_short port_net;
|
|
|
|
/*
|
|
Description of alias_port_param for GetNewPort(). When
|
|
this parameter is zero or positive, it precisely specifies
|
|
the port number. GetNewPort() will return this number
|
|
without check that it is in use.
|
|
|
|
When this parameter is GET_ALIAS_PORT, it indicates to get a randomly
|
|
selected port number.
|
|
*/
|
|
|
|
if (alias_port_param == GET_ALIAS_PORT)
|
|
{
|
|
/*
|
|
* The aliasing port is automatically selected
|
|
* by one of two methods below:
|
|
*/
|
|
max_trials = GET_NEW_PORT_MAX_ATTEMPTS;
|
|
|
|
if (packetAliasMode & PKT_ALIAS_SAME_PORTS)
|
|
{
|
|
/*
|
|
* When the PKT_ALIAS_SAME_PORTS option is
|
|
* chosen, the first try will be the
|
|
* actual source port. If this is already
|
|
* in use, the remainder of the trials
|
|
* will be random.
|
|
*/
|
|
port_net = link->src_port;
|
|
port_sys = ntohs(port_net);
|
|
}
|
|
else
|
|
{
|
|
/* First trial and all subsequent are random. */
|
|
port_sys = random() & ALIAS_PORT_MASK;
|
|
port_sys += ALIAS_PORT_BASE;
|
|
port_net = htons(port_sys);
|
|
}
|
|
}
|
|
else if (alias_port_param >= 0 && alias_port_param < 0x10000)
|
|
{
|
|
link->alias_port = (u_short) alias_port_param;
|
|
return(0);
|
|
}
|
|
else
|
|
{
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "PacketAlias/GetNewPort(): ");
|
|
fprintf(stderr, "input parameter error\n");
|
|
#endif
|
|
return(-1);
|
|
}
|
|
|
|
|
|
/* Port number search */
|
|
for (i=0; i<max_trials; i++)
|
|
{
|
|
int go_ahead;
|
|
struct alias_link *search_result;
|
|
|
|
search_result = FindLinkIn(link->dst_addr, link->alias_addr,
|
|
link->dst_port, port_net,
|
|
link->link_type, 0);
|
|
|
|
if (search_result == NULL)
|
|
go_ahead = 1;
|
|
else if (!(link->flags & LINK_PARTIALLY_SPECIFIED)
|
|
&& (search_result->flags & LINK_PARTIALLY_SPECIFIED))
|
|
go_ahead = 1;
|
|
else
|
|
go_ahead = 0;
|
|
|
|
if (go_ahead)
|
|
{
|
|
if ((packetAliasMode & PKT_ALIAS_USE_SOCKETS)
|
|
&& (link->flags & LINK_PARTIALLY_SPECIFIED)
|
|
&& ((link->link_type == LINK_TCP) ||
|
|
(link->link_type == LINK_UDP)))
|
|
{
|
|
if (GetSocket(port_net, &link->sockfd, link->link_type))
|
|
{
|
|
link->alias_port = port_net;
|
|
return(0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
link->alias_port = port_net;
|
|
return(0);
|
|
}
|
|
}
|
|
|
|
port_sys = random() & ALIAS_PORT_MASK;
|
|
port_sys += ALIAS_PORT_BASE;
|
|
port_net = htons(port_sys);
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "PacketAlias/GetnewPort(): ");
|
|
fprintf(stderr, "could not find free port\n");
|
|
#endif
|
|
|
|
return(-1);
|
|
}
|
|
|
|
|
|
static u_short
|
|
GetSocket(u_short port_net, int *sockfd, int link_type)
|
|
{
|
|
int err;
|
|
int sock;
|
|
struct sockaddr_in sock_addr;
|
|
|
|
if (link_type == LINK_TCP)
|
|
sock = socket(AF_INET, SOCK_STREAM, 0);
|
|
else if (link_type == LINK_UDP)
|
|
sock = socket(AF_INET, SOCK_DGRAM, 0);
|
|
else
|
|
{
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "PacketAlias/GetSocket(): ");
|
|
fprintf(stderr, "incorrect link type\n");
|
|
#endif
|
|
return(0);
|
|
}
|
|
|
|
if (sock < 0)
|
|
{
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "PacketAlias/GetSocket(): ");
|
|
fprintf(stderr, "socket() error %d\n", *sockfd);
|
|
#endif
|
|
return(0);
|
|
}
|
|
|
|
sock_addr.sin_family = AF_INET;
|
|
sock_addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
|
sock_addr.sin_port = port_net;
|
|
|
|
err = bind(sock,
|
|
(struct sockaddr *) &sock_addr,
|
|
sizeof(sock_addr));
|
|
if (err == 0)
|
|
{
|
|
sockCount++;
|
|
*sockfd = sock;
|
|
return(1);
|
|
}
|
|
else
|
|
{
|
|
close(sock);
|
|
return(0);
|
|
}
|
|
}
|
|
|
|
|
|
/* 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 in_addr dst_addr,
|
|
struct in_addr alias_addr,
|
|
u_short src_port,
|
|
u_short dst_port,
|
|
u_short port_count,
|
|
u_char proto,
|
|
u_char align)
|
|
{
|
|
int i, j;
|
|
int max_trials;
|
|
u_short port_sys;
|
|
int link_type;
|
|
|
|
/*
|
|
* Get link_type from protocol
|
|
*/
|
|
|
|
switch (proto)
|
|
{
|
|
case IPPROTO_UDP:
|
|
link_type = LINK_UDP;
|
|
break;
|
|
case IPPROTO_TCP:
|
|
link_type = LINK_TCP;
|
|
break;
|
|
default:
|
|
return (0);
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* The aliasing port is automatically selected
|
|
* by one of two methods below:
|
|
*/
|
|
max_trials = GET_NEW_PORT_MAX_ATTEMPTS;
|
|
|
|
if (packetAliasMode & PKT_ALIAS_SAME_PORTS) {
|
|
/*
|
|
* When the ALIAS_SAME_PORTS option is
|
|
* chosen, the first try will be the
|
|
* actual source port. If this is already
|
|
* in use, the remainder of the trials
|
|
* will be random.
|
|
*/
|
|
port_sys = ntohs(src_port);
|
|
|
|
} else {
|
|
|
|
/* First trial and all subsequent are random. */
|
|
if (align == FIND_EVEN_ALIAS_BASE)
|
|
port_sys = random() & ALIAS_PORT_MASK_EVEN;
|
|
else
|
|
port_sys = random() & ALIAS_PORT_MASK;
|
|
|
|
port_sys += ALIAS_PORT_BASE;
|
|
}
|
|
|
|
/* Port number search */
|
|
for (i = 0; i < max_trials; i++) {
|
|
|
|
struct alias_link *search_result;
|
|
|
|
for (j = 0; j < port_count; j++)
|
|
if (0 != (search_result = FindLinkIn(dst_addr, alias_addr,
|
|
dst_port, htons(port_sys + j),
|
|
link_type, 0)))
|
|
break;
|
|
|
|
/* Found a good range, return base */
|
|
if (j == port_count)
|
|
return (htons(port_sys));
|
|
|
|
/* Find a new base to try */
|
|
if (align == FIND_EVEN_ALIAS_BASE)
|
|
port_sys = random() & ALIAS_PORT_MASK_EVEN;
|
|
else
|
|
port_sys = random() & ALIAS_PORT_MASK;
|
|
|
|
port_sys += ALIAS_PORT_BASE;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "PacketAlias/FindNewPortGroup(): ");
|
|
fprintf(stderr, "could not find free port(s)\n");
|
|
#endif
|
|
|
|
return(0);
|
|
}
|
|
|
|
static void
|
|
CleanupAliasData(void)
|
|
{
|
|
struct alias_link *link;
|
|
int i, icount;
|
|
|
|
icount = 0;
|
|
for (i=0; i<LINK_TABLE_OUT_SIZE; i++)
|
|
{
|
|
link = LIST_FIRST(&linkTableOut[i]);
|
|
while (link != NULL)
|
|
{
|
|
struct alias_link *link_next;
|
|
link_next = LIST_NEXT(link, list_out);
|
|
icount++;
|
|
DeleteLink(link);
|
|
link = link_next;
|
|
}
|
|
}
|
|
|
|
cleanupIndex =0;
|
|
}
|
|
|
|
|
|
static void
|
|
IncrementalCleanup(void)
|
|
{
|
|
int icount;
|
|
struct alias_link *link;
|
|
|
|
icount = 0;
|
|
link = LIST_FIRST(&linkTableOut[cleanupIndex++]);
|
|
while (link != NULL)
|
|
{
|
|
int idelta;
|
|
struct alias_link *link_next;
|
|
|
|
link_next = LIST_NEXT(link, list_out);
|
|
idelta = timeStamp - link->timestamp;
|
|
switch (link->link_type)
|
|
{
|
|
case LINK_TCP:
|
|
if (idelta > link->expire_time)
|
|
{
|
|
struct tcp_dat *tcp_aux;
|
|
|
|
tcp_aux = link->data.tcp;
|
|
if (tcp_aux->state.in != ALIAS_TCP_STATE_CONNECTED
|
|
|| tcp_aux->state.out != ALIAS_TCP_STATE_CONNECTED)
|
|
{
|
|
DeleteLink(link);
|
|
icount++;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
if (idelta > link->expire_time)
|
|
{
|
|
DeleteLink(link);
|
|
icount++;
|
|
}
|
|
break;
|
|
}
|
|
link = link_next;
|
|
}
|
|
|
|
if (cleanupIndex == LINK_TABLE_OUT_SIZE)
|
|
cleanupIndex = 0;
|
|
}
|
|
|
|
static void
|
|
DeleteLink(struct alias_link *link)
|
|
{
|
|
|
|
/* Don't do anything if the link is marked permanent */
|
|
if (deleteAllLinks == 0 && link->flags & LINK_PERMANENT)
|
|
return;
|
|
|
|
#ifndef NO_FW_PUNCH
|
|
/* Delete associated firewall hole, if any */
|
|
ClearFWHole(link);
|
|
#endif
|
|
|
|
/* Free memory allocated for LSNAT server pool */
|
|
if (link->server != NULL) {
|
|
struct server *head, *curr, *next;
|
|
|
|
head = curr = link->server;
|
|
do {
|
|
next = curr->next;
|
|
free(curr);
|
|
} while ((curr = next) != head);
|
|
}
|
|
|
|
/* Adjust output table pointers */
|
|
LIST_REMOVE(link, list_out);
|
|
|
|
/* Adjust input table pointers */
|
|
LIST_REMOVE(link, list_in);
|
|
|
|
/* Close socket, if one has been allocated */
|
|
if (link->sockfd != -1)
|
|
{
|
|
sockCount--;
|
|
close(link->sockfd);
|
|
}
|
|
|
|
/* Link-type dependent cleanup */
|
|
switch(link->link_type)
|
|
{
|
|
case LINK_ICMP:
|
|
icmpLinkCount--;
|
|
break;
|
|
case LINK_UDP:
|
|
udpLinkCount--;
|
|
break;
|
|
case LINK_TCP:
|
|
tcpLinkCount--;
|
|
free(link->data.tcp);
|
|
break;
|
|
case LINK_PPTP:
|
|
pptpLinkCount--;
|
|
break;
|
|
case LINK_FRAGMENT_ID:
|
|
fragmentIdLinkCount--;
|
|
break;
|
|
case LINK_FRAGMENT_PTR:
|
|
fragmentPtrLinkCount--;
|
|
if (link->data.frag_ptr != NULL)
|
|
free(link->data.frag_ptr);
|
|
break;
|
|
case LINK_ADDR:
|
|
break;
|
|
default:
|
|
protoLinkCount--;
|
|
break;
|
|
}
|
|
|
|
/* Free memory */
|
|
free(link);
|
|
|
|
/* Write statistics, if logging enabled */
|
|
if (packetAliasMode & PKT_ALIAS_LOG)
|
|
{
|
|
ShowAliasStats();
|
|
}
|
|
}
|
|
|
|
|
|
static struct alias_link *
|
|
AddLink(struct in_addr src_addr,
|
|
struct in_addr dst_addr,
|
|
struct in_addr alias_addr,
|
|
u_short src_port,
|
|
u_short dst_port,
|
|
int alias_port_param, /* if less than zero, alias */
|
|
int link_type) /* port will be automatically */
|
|
{ /* chosen. If greater than */
|
|
u_int start_point; /* zero, equal to alias port */
|
|
struct alias_link *link;
|
|
|
|
link = malloc(sizeof(struct alias_link));
|
|
if (link != NULL)
|
|
{
|
|
/* Basic initialization */
|
|
link->src_addr = src_addr;
|
|
link->dst_addr = dst_addr;
|
|
link->alias_addr = alias_addr;
|
|
link->proxy_addr.s_addr = INADDR_ANY;
|
|
link->src_port = src_port;
|
|
link->dst_port = dst_port;
|
|
link->proxy_port = 0;
|
|
link->server = NULL;
|
|
link->link_type = link_type;
|
|
link->sockfd = -1;
|
|
link->flags = 0;
|
|
link->pflags = 0;
|
|
link->timestamp = timeStamp;
|
|
|
|
/* Expiration time */
|
|
switch (link_type)
|
|
{
|
|
case LINK_ICMP:
|
|
link->expire_time = ICMP_EXPIRE_TIME;
|
|
break;
|
|
case LINK_UDP:
|
|
link->expire_time = UDP_EXPIRE_TIME;
|
|
break;
|
|
case LINK_TCP:
|
|
link->expire_time = TCP_EXPIRE_INITIAL;
|
|
break;
|
|
case LINK_PPTP:
|
|
link->flags |= LINK_PERMANENT; /* no timeout. */
|
|
break;
|
|
case LINK_FRAGMENT_ID:
|
|
link->expire_time = FRAGMENT_ID_EXPIRE_TIME;
|
|
break;
|
|
case LINK_FRAGMENT_PTR:
|
|
link->expire_time = FRAGMENT_PTR_EXPIRE_TIME;
|
|
break;
|
|
case LINK_ADDR:
|
|
break;
|
|
default:
|
|
link->expire_time = PROTO_EXPIRE_TIME;
|
|
break;
|
|
}
|
|
|
|
/* Determine alias flags */
|
|
if (dst_addr.s_addr == INADDR_ANY)
|
|
link->flags |= LINK_UNKNOWN_DEST_ADDR;
|
|
if (dst_port == 0)
|
|
link->flags |= LINK_UNKNOWN_DEST_PORT;
|
|
|
|
/* Determine alias port */
|
|
if (GetNewPort(link, alias_port_param) != 0)
|
|
{
|
|
free(link);
|
|
return(NULL);
|
|
}
|
|
|
|
/* Link-type dependent initialization */
|
|
switch(link_type)
|
|
{
|
|
struct tcp_dat *aux_tcp;
|
|
|
|
case LINK_ICMP:
|
|
icmpLinkCount++;
|
|
break;
|
|
case LINK_UDP:
|
|
udpLinkCount++;
|
|
break;
|
|
case LINK_TCP:
|
|
aux_tcp = malloc(sizeof(struct tcp_dat));
|
|
if (aux_tcp != NULL)
|
|
{
|
|
int i;
|
|
|
|
tcpLinkCount++;
|
|
aux_tcp->state.in = ALIAS_TCP_STATE_NOT_CONNECTED;
|
|
aux_tcp->state.out = ALIAS_TCP_STATE_NOT_CONNECTED;
|
|
aux_tcp->state.index = 0;
|
|
aux_tcp->state.ack_modified = 0;
|
|
for (i=0; i<N_LINK_TCP_DATA; i++)
|
|
aux_tcp->ack[i].active = 0;
|
|
aux_tcp->fwhole = -1;
|
|
link->data.tcp = aux_tcp;
|
|
}
|
|
else
|
|
{
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "PacketAlias/AddLink: ");
|
|
fprintf(stderr, " cannot allocate auxiliary TCP data\n");
|
|
#endif
|
|
free(link);
|
|
return (NULL);
|
|
}
|
|
break;
|
|
case LINK_PPTP:
|
|
pptpLinkCount++;
|
|
break;
|
|
case LINK_FRAGMENT_ID:
|
|
fragmentIdLinkCount++;
|
|
break;
|
|
case LINK_FRAGMENT_PTR:
|
|
fragmentPtrLinkCount++;
|
|
break;
|
|
case LINK_ADDR:
|
|
break;
|
|
default:
|
|
protoLinkCount++;
|
|
break;
|
|
}
|
|
|
|
/* Set up pointers for output lookup table */
|
|
start_point = StartPointOut(src_addr, dst_addr,
|
|
src_port, dst_port, link_type);
|
|
LIST_INSERT_HEAD(&linkTableOut[start_point], link, list_out);
|
|
|
|
/* Set up pointers for input lookup table */
|
|
start_point = StartPointIn(alias_addr, link->alias_port, link_type);
|
|
LIST_INSERT_HEAD(&linkTableIn[start_point], link, list_in);
|
|
}
|
|
else
|
|
{
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "PacketAlias/AddLink(): ");
|
|
fprintf(stderr, "malloc() call failed.\n");
|
|
#endif
|
|
}
|
|
|
|
if (packetAliasMode & PKT_ALIAS_LOG)
|
|
{
|
|
ShowAliasStats();
|
|
}
|
|
|
|
return(link);
|
|
}
|
|
|
|
static struct alias_link *
|
|
ReLink(struct alias_link *old_link,
|
|
struct in_addr src_addr,
|
|
struct in_addr dst_addr,
|
|
struct in_addr alias_addr,
|
|
u_short src_port,
|
|
u_short dst_port,
|
|
int alias_port_param, /* if less than zero, alias */
|
|
int link_type) /* port will be automatically */
|
|
{ /* chosen. If greater than */
|
|
struct alias_link *new_link; /* zero, equal to alias port */
|
|
|
|
new_link = AddLink(src_addr, dst_addr, alias_addr,
|
|
src_port, dst_port, alias_port_param,
|
|
link_type);
|
|
#ifndef NO_FW_PUNCH
|
|
if (new_link != NULL &&
|
|
old_link->link_type == LINK_TCP &&
|
|
old_link->data.tcp->fwhole > 0) {
|
|
PunchFWHole(new_link);
|
|
}
|
|
#endif
|
|
DeleteLink(old_link);
|
|
return new_link;
|
|
}
|
|
|
|
static struct alias_link *
|
|
_FindLinkOut(struct in_addr src_addr,
|
|
struct in_addr dst_addr,
|
|
u_short src_port,
|
|
u_short dst_port,
|
|
int link_type,
|
|
int replace_partial_links)
|
|
{
|
|
u_int i;
|
|
struct alias_link *link;
|
|
|
|
i = StartPointOut(src_addr, dst_addr, src_port, dst_port, link_type);
|
|
LIST_FOREACH(link, &linkTableOut[i], list_out)
|
|
{
|
|
if (link->src_addr.s_addr == src_addr.s_addr
|
|
&& link->server == NULL
|
|
&& link->dst_addr.s_addr == dst_addr.s_addr
|
|
&& link->dst_port == dst_port
|
|
&& link->src_port == src_port
|
|
&& link->link_type == link_type)
|
|
{
|
|
link->timestamp = timeStamp;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Search for partially specified links. */
|
|
if (link == NULL && replace_partial_links)
|
|
{
|
|
if (dst_port != 0 && dst_addr.s_addr != INADDR_ANY)
|
|
{
|
|
link = _FindLinkOut(src_addr, dst_addr, src_port, 0,
|
|
link_type, 0);
|
|
if (link == NULL)
|
|
link = _FindLinkOut(src_addr, nullAddress, src_port,
|
|
dst_port, link_type, 0);
|
|
}
|
|
if (link == NULL &&
|
|
(dst_port != 0 || dst_addr.s_addr != INADDR_ANY))
|
|
{
|
|
link = _FindLinkOut(src_addr, nullAddress, src_port, 0,
|
|
link_type, 0);
|
|
}
|
|
if (link != NULL)
|
|
{
|
|
link = ReLink(link,
|
|
src_addr, dst_addr, link->alias_addr,
|
|
src_port, dst_port, link->alias_port,
|
|
link_type);
|
|
}
|
|
}
|
|
|
|
return(link);
|
|
}
|
|
|
|
static struct alias_link *
|
|
FindLinkOut(struct in_addr src_addr,
|
|
struct in_addr dst_addr,
|
|
u_short src_port,
|
|
u_short dst_port,
|
|
int link_type,
|
|
int replace_partial_links)
|
|
{
|
|
struct alias_link *link;
|
|
|
|
link = _FindLinkOut(src_addr, dst_addr, src_port, dst_port,
|
|
link_type, replace_partial_links);
|
|
|
|
if (link == NULL)
|
|
{
|
|
/* The following allows permanent links to be
|
|
specified as using the default source address
|
|
(i.e. device interface address) without knowing
|
|
in advance what that address is. */
|
|
if (aliasAddress.s_addr != 0 &&
|
|
src_addr.s_addr == aliasAddress.s_addr)
|
|
{
|
|
link = _FindLinkOut(nullAddress, dst_addr, src_port, dst_port,
|
|
link_type, replace_partial_links);
|
|
}
|
|
}
|
|
|
|
return(link);
|
|
}
|
|
|
|
|
|
static struct alias_link *
|
|
_FindLinkIn(struct in_addr dst_addr,
|
|
struct in_addr alias_addr,
|
|
u_short dst_port,
|
|
u_short alias_port,
|
|
int link_type,
|
|
int replace_partial_links)
|
|
{
|
|
int flags_in;
|
|
u_int start_point;
|
|
struct alias_link *link;
|
|
struct alias_link *link_fully_specified;
|
|
struct alias_link *link_unknown_all;
|
|
struct alias_link *link_unknown_dst_addr;
|
|
struct alias_link *link_unknown_dst_port;
|
|
|
|
/* Initialize pointers */
|
|
link_fully_specified = NULL;
|
|
link_unknown_all = NULL;
|
|
link_unknown_dst_addr = NULL;
|
|
link_unknown_dst_port = NULL;
|
|
|
|
/* If either the dest addr or port is unknown, the search
|
|
loop will have to know about this. */
|
|
|
|
flags_in = 0;
|
|
if (dst_addr.s_addr == INADDR_ANY)
|
|
flags_in |= LINK_UNKNOWN_DEST_ADDR;
|
|
if (dst_port == 0)
|
|
flags_in |= LINK_UNKNOWN_DEST_PORT;
|
|
|
|
/* Search loop */
|
|
start_point = StartPointIn(alias_addr, alias_port, link_type);
|
|
LIST_FOREACH(link, &linkTableIn[start_point], list_in)
|
|
{
|
|
int flags;
|
|
|
|
flags = flags_in | link->flags;
|
|
if (!(flags & LINK_PARTIALLY_SPECIFIED))
|
|
{
|
|
if (link->alias_addr.s_addr == alias_addr.s_addr
|
|
&& link->alias_port == alias_port
|
|
&& link->dst_addr.s_addr == dst_addr.s_addr
|
|
&& link->dst_port == dst_port
|
|
&& link->link_type == link_type)
|
|
{
|
|
link_fully_specified = link;
|
|
break;
|
|
}
|
|
}
|
|
else if ((flags & LINK_UNKNOWN_DEST_ADDR)
|
|
&& (flags & LINK_UNKNOWN_DEST_PORT))
|
|
{
|
|
if (link->alias_addr.s_addr == alias_addr.s_addr
|
|
&& link->alias_port == alias_port
|
|
&& link->link_type == link_type)
|
|
{
|
|
if (link_unknown_all == NULL)
|
|
link_unknown_all = link;
|
|
}
|
|
}
|
|
else if (flags & LINK_UNKNOWN_DEST_ADDR)
|
|
{
|
|
if (link->alias_addr.s_addr == alias_addr.s_addr
|
|
&& link->alias_port == alias_port
|
|
&& link->link_type == link_type
|
|
&& link->dst_port == dst_port)
|
|
{
|
|
if (link_unknown_dst_addr == NULL)
|
|
link_unknown_dst_addr = link;
|
|
}
|
|
}
|
|
else if (flags & LINK_UNKNOWN_DEST_PORT)
|
|
{
|
|
if (link->alias_addr.s_addr == alias_addr.s_addr
|
|
&& link->alias_port == alias_port
|
|
&& link->link_type == link_type
|
|
&& link->dst_addr.s_addr == dst_addr.s_addr)
|
|
{
|
|
if (link_unknown_dst_port == NULL)
|
|
link_unknown_dst_port = link;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if (link_fully_specified != NULL)
|
|
{
|
|
link_fully_specified->timestamp = timeStamp;
|
|
link = link_fully_specified;
|
|
}
|
|
else if (link_unknown_dst_port != NULL)
|
|
link = link_unknown_dst_port;
|
|
else if (link_unknown_dst_addr != NULL)
|
|
link = link_unknown_dst_addr;
|
|
else if (link_unknown_all != NULL)
|
|
link = link_unknown_all;
|
|
else
|
|
return (NULL);
|
|
|
|
if (replace_partial_links &&
|
|
(link->flags & LINK_PARTIALLY_SPECIFIED || link->server != NULL))
|
|
{
|
|
struct in_addr src_addr;
|
|
u_short src_port;
|
|
|
|
if (link->server != NULL) { /* LSNAT link */
|
|
src_addr = link->server->addr;
|
|
src_port = link->server->port;
|
|
link->server = link->server->next;
|
|
} else {
|
|
src_addr = link->src_addr;
|
|
src_port = link->src_port;
|
|
}
|
|
|
|
link = ReLink(link,
|
|
src_addr, dst_addr, alias_addr,
|
|
src_port, dst_port, alias_port,
|
|
link_type);
|
|
}
|
|
|
|
return (link);
|
|
}
|
|
|
|
static struct alias_link *
|
|
FindLinkIn(struct in_addr dst_addr,
|
|
struct in_addr alias_addr,
|
|
u_short dst_port,
|
|
u_short alias_port,
|
|
int link_type,
|
|
int replace_partial_links)
|
|
{
|
|
struct alias_link *link;
|
|
|
|
link = _FindLinkIn(dst_addr, alias_addr, dst_port, alias_port,
|
|
link_type, replace_partial_links);
|
|
|
|
if (link == NULL)
|
|
{
|
|
/* The following allows permanent links to be
|
|
specified as using the default aliasing address
|
|
(i.e. device interface address) without knowing
|
|
in advance what that address is. */
|
|
if (aliasAddress.s_addr != 0 &&
|
|
alias_addr.s_addr == aliasAddress.s_addr)
|
|
{
|
|
link = _FindLinkIn(dst_addr, nullAddress, dst_port, alias_port,
|
|
link_type, replace_partial_links);
|
|
}
|
|
}
|
|
|
|
return(link);
|
|
}
|
|
|
|
|
|
|
|
|
|
/* External routines for finding/adding links
|
|
|
|
-- "external" means outside alias_db.c, but within alias*.c --
|
|
|
|
FindIcmpIn(), FindIcmpOut()
|
|
FindFragmentIn1(), FindFragmentIn2()
|
|
AddFragmentPtrLink(), FindFragmentPtr()
|
|
FindProtoIn(), FindProtoOut()
|
|
FindUdpTcpIn(), FindUdpTcpOut()
|
|
AddPptp(), FindPptpOutByCallId(), FindPptpInByCallId(),
|
|
FindPptpOutByPeerCallId(), FindPptpInByPeerCallId()
|
|
FindOriginalAddress(), FindAliasAddress()
|
|
|
|
(prototypes in alias_local.h)
|
|
*/
|
|
|
|
|
|
struct alias_link *
|
|
FindIcmpIn(struct in_addr dst_addr,
|
|
struct in_addr alias_addr,
|
|
u_short id_alias,
|
|
int create)
|
|
{
|
|
struct alias_link *link;
|
|
|
|
link = FindLinkIn(dst_addr, alias_addr,
|
|
NO_DEST_PORT, id_alias,
|
|
LINK_ICMP, 0);
|
|
if (link == NULL && create && !(packetAliasMode & PKT_ALIAS_DENY_INCOMING))
|
|
{
|
|
struct in_addr target_addr;
|
|
|
|
target_addr = FindOriginalAddress(alias_addr);
|
|
link = AddLink(target_addr, dst_addr, alias_addr,
|
|
id_alias, NO_DEST_PORT, id_alias,
|
|
LINK_ICMP);
|
|
}
|
|
|
|
return (link);
|
|
}
|
|
|
|
|
|
struct alias_link *
|
|
FindIcmpOut(struct in_addr src_addr,
|
|
struct in_addr dst_addr,
|
|
u_short id,
|
|
int create)
|
|
{
|
|
struct alias_link * link;
|
|
|
|
link = FindLinkOut(src_addr, dst_addr,
|
|
id, NO_DEST_PORT,
|
|
LINK_ICMP, 0);
|
|
if (link == NULL && create)
|
|
{
|
|
struct in_addr alias_addr;
|
|
|
|
alias_addr = FindAliasAddress(src_addr);
|
|
link = AddLink(src_addr, dst_addr, alias_addr,
|
|
id, NO_DEST_PORT, GET_ALIAS_ID,
|
|
LINK_ICMP);
|
|
}
|
|
|
|
return(link);
|
|
}
|
|
|
|
|
|
struct alias_link *
|
|
FindFragmentIn1(struct in_addr dst_addr,
|
|
struct in_addr alias_addr,
|
|
u_short ip_id)
|
|
{
|
|
struct alias_link *link;
|
|
|
|
link = FindLinkIn(dst_addr, alias_addr,
|
|
NO_DEST_PORT, ip_id,
|
|
LINK_FRAGMENT_ID, 0);
|
|
|
|
if (link == NULL)
|
|
{
|
|
link = AddLink(nullAddress, dst_addr, alias_addr,
|
|
NO_SRC_PORT, NO_DEST_PORT, ip_id,
|
|
LINK_FRAGMENT_ID);
|
|
}
|
|
|
|
return(link);
|
|
}
|
|
|
|
|
|
struct alias_link *
|
|
FindFragmentIn2(struct in_addr dst_addr, /* Doesn't add a link if one */
|
|
struct in_addr alias_addr, /* is not found. */
|
|
u_short ip_id)
|
|
{
|
|
return FindLinkIn(dst_addr, alias_addr,
|
|
NO_DEST_PORT, ip_id,
|
|
LINK_FRAGMENT_ID, 0);
|
|
}
|
|
|
|
|
|
struct alias_link *
|
|
AddFragmentPtrLink(struct in_addr dst_addr,
|
|
u_short ip_id)
|
|
{
|
|
return AddLink(nullAddress, dst_addr, nullAddress,
|
|
NO_SRC_PORT, NO_DEST_PORT, ip_id,
|
|
LINK_FRAGMENT_PTR);
|
|
}
|
|
|
|
|
|
struct alias_link *
|
|
FindFragmentPtr(struct in_addr dst_addr,
|
|
u_short ip_id)
|
|
{
|
|
return FindLinkIn(dst_addr, nullAddress,
|
|
NO_DEST_PORT, ip_id,
|
|
LINK_FRAGMENT_PTR, 0);
|
|
}
|
|
|
|
|
|
struct alias_link *
|
|
FindProtoIn(struct in_addr dst_addr,
|
|
struct in_addr alias_addr,
|
|
u_char proto)
|
|
{
|
|
struct alias_link *link;
|
|
|
|
link = FindLinkIn(dst_addr, alias_addr,
|
|
NO_DEST_PORT, 0,
|
|
proto, 1);
|
|
|
|
if (link == NULL && !(packetAliasMode & PKT_ALIAS_DENY_INCOMING))
|
|
{
|
|
struct in_addr target_addr;
|
|
|
|
target_addr = FindOriginalAddress(alias_addr);
|
|
link = AddLink(target_addr, dst_addr, alias_addr,
|
|
NO_SRC_PORT, NO_DEST_PORT, 0,
|
|
proto);
|
|
}
|
|
|
|
return (link);
|
|
}
|
|
|
|
|
|
struct alias_link *
|
|
FindProtoOut(struct in_addr src_addr,
|
|
struct in_addr dst_addr,
|
|
u_char proto)
|
|
{
|
|
struct alias_link *link;
|
|
|
|
link = FindLinkOut(src_addr, dst_addr,
|
|
NO_SRC_PORT, NO_DEST_PORT,
|
|
proto, 1);
|
|
|
|
if (link == NULL)
|
|
{
|
|
struct in_addr alias_addr;
|
|
|
|
alias_addr = FindAliasAddress(src_addr);
|
|
link = AddLink(src_addr, dst_addr, alias_addr,
|
|
NO_SRC_PORT, NO_DEST_PORT, 0,
|
|
proto);
|
|
}
|
|
|
|
return (link);
|
|
}
|
|
|
|
|
|
struct alias_link *
|
|
FindUdpTcpIn(struct in_addr dst_addr,
|
|
struct in_addr alias_addr,
|
|
u_short dst_port,
|
|
u_short alias_port,
|
|
u_char proto,
|
|
int create)
|
|
{
|
|
int link_type;
|
|
struct alias_link *link;
|
|
|
|
switch (proto)
|
|
{
|
|
case IPPROTO_UDP:
|
|
link_type = LINK_UDP;
|
|
break;
|
|
case IPPROTO_TCP:
|
|
link_type = LINK_TCP;
|
|
break;
|
|
default:
|
|
return NULL;
|
|
break;
|
|
}
|
|
|
|
link = FindLinkIn(dst_addr, alias_addr,
|
|
dst_port, alias_port,
|
|
link_type, create);
|
|
|
|
if (link == NULL && create && !(packetAliasMode & PKT_ALIAS_DENY_INCOMING))
|
|
{
|
|
struct in_addr target_addr;
|
|
|
|
target_addr = FindOriginalAddress(alias_addr);
|
|
link = AddLink(target_addr, dst_addr, alias_addr,
|
|
alias_port, dst_port, alias_port,
|
|
link_type);
|
|
}
|
|
|
|
return(link);
|
|
}
|
|
|
|
|
|
struct alias_link *
|
|
FindUdpTcpOut(struct in_addr src_addr,
|
|
struct in_addr dst_addr,
|
|
u_short src_port,
|
|
u_short dst_port,
|
|
u_char proto,
|
|
int create)
|
|
{
|
|
int link_type;
|
|
struct alias_link *link;
|
|
|
|
switch (proto)
|
|
{
|
|
case IPPROTO_UDP:
|
|
link_type = LINK_UDP;
|
|
break;
|
|
case IPPROTO_TCP:
|
|
link_type = LINK_TCP;
|
|
break;
|
|
default:
|
|
return NULL;
|
|
break;
|
|
}
|
|
|
|
link = FindLinkOut(src_addr, dst_addr, src_port, dst_port, link_type, create);
|
|
|
|
if (link == NULL && create)
|
|
{
|
|
struct in_addr alias_addr;
|
|
|
|
alias_addr = FindAliasAddress(src_addr);
|
|
link = AddLink(src_addr, dst_addr, alias_addr,
|
|
src_port, dst_port, GET_ALIAS_PORT,
|
|
link_type);
|
|
}
|
|
|
|
return(link);
|
|
}
|
|
|
|
|
|
struct alias_link *
|
|
AddPptp(struct in_addr src_addr,
|
|
struct in_addr dst_addr,
|
|
struct in_addr alias_addr,
|
|
u_int16_t src_call_id)
|
|
{
|
|
struct alias_link *link;
|
|
|
|
link = AddLink(src_addr, dst_addr, alias_addr,
|
|
src_call_id, 0, GET_ALIAS_PORT,
|
|
LINK_PPTP);
|
|
|
|
return (link);
|
|
}
|
|
|
|
|
|
struct alias_link *
|
|
FindPptpOutByCallId(struct in_addr src_addr,
|
|
struct in_addr dst_addr,
|
|
u_int16_t src_call_id)
|
|
{
|
|
u_int i;
|
|
struct alias_link *link;
|
|
|
|
i = StartPointOut(src_addr, dst_addr, 0, 0, LINK_PPTP);
|
|
LIST_FOREACH(link, &linkTableOut[i], list_out)
|
|
if (link->link_type == LINK_PPTP &&
|
|
link->src_addr.s_addr == src_addr.s_addr &&
|
|
link->dst_addr.s_addr == dst_addr.s_addr &&
|
|
link->src_port == src_call_id)
|
|
break;
|
|
|
|
return (link);
|
|
}
|
|
|
|
|
|
struct alias_link *
|
|
FindPptpOutByPeerCallId(struct in_addr src_addr,
|
|
struct in_addr dst_addr,
|
|
u_int16_t dst_call_id)
|
|
{
|
|
u_int i;
|
|
struct alias_link *link;
|
|
|
|
i = StartPointOut(src_addr, dst_addr, 0, 0, LINK_PPTP);
|
|
LIST_FOREACH(link, &linkTableOut[i], list_out)
|
|
if (link->link_type == LINK_PPTP &&
|
|
link->src_addr.s_addr == src_addr.s_addr &&
|
|
link->dst_addr.s_addr == dst_addr.s_addr &&
|
|
link->dst_port == dst_call_id)
|
|
break;
|
|
|
|
return (link);
|
|
}
|
|
|
|
|
|
struct alias_link *
|
|
FindPptpInByCallId(struct in_addr dst_addr,
|
|
struct in_addr alias_addr,
|
|
u_int16_t dst_call_id)
|
|
{
|
|
u_int i;
|
|
struct alias_link *link;
|
|
|
|
i = StartPointIn(alias_addr, 0, LINK_PPTP);
|
|
LIST_FOREACH(link, &linkTableIn[i], list_in)
|
|
if (link->link_type == LINK_PPTP &&
|
|
link->dst_addr.s_addr == dst_addr.s_addr &&
|
|
link->alias_addr.s_addr == alias_addr.s_addr &&
|
|
link->dst_port == dst_call_id)
|
|
break;
|
|
|
|
return (link);
|
|
}
|
|
|
|
|
|
struct alias_link *
|
|
FindPptpInByPeerCallId(struct in_addr dst_addr,
|
|
struct in_addr alias_addr,
|
|
u_int16_t alias_call_id)
|
|
{
|
|
struct alias_link *link;
|
|
|
|
link = FindLinkIn(dst_addr, alias_addr,
|
|
0/* any */, alias_call_id,
|
|
LINK_PPTP, 0);
|
|
|
|
|
|
return (link);
|
|
}
|
|
|
|
|
|
struct alias_link *
|
|
FindRtspOut(struct in_addr src_addr,
|
|
struct in_addr dst_addr,
|
|
u_short src_port,
|
|
u_short alias_port,
|
|
u_char proto)
|
|
{
|
|
int link_type;
|
|
struct alias_link *link;
|
|
|
|
switch (proto)
|
|
{
|
|
case IPPROTO_UDP:
|
|
link_type = LINK_UDP;
|
|
break;
|
|
case IPPROTO_TCP:
|
|
link_type = LINK_TCP;
|
|
break;
|
|
default:
|
|
return NULL;
|
|
break;
|
|
}
|
|
|
|
link = FindLinkOut(src_addr, dst_addr, src_port, 0, link_type, 1);
|
|
|
|
if (link == NULL)
|
|
{
|
|
struct in_addr alias_addr;
|
|
|
|
alias_addr = FindAliasAddress(src_addr);
|
|
link = AddLink(src_addr, dst_addr, alias_addr,
|
|
src_port, 0, alias_port,
|
|
link_type);
|
|
}
|
|
|
|
return(link);
|
|
}
|
|
|
|
|
|
struct in_addr
|
|
FindOriginalAddress(struct in_addr alias_addr)
|
|
{
|
|
struct alias_link *link;
|
|
|
|
link = FindLinkIn(nullAddress, alias_addr,
|
|
0, 0, LINK_ADDR, 0);
|
|
if (link == NULL)
|
|
{
|
|
newDefaultLink = 1;
|
|
if (targetAddress.s_addr == INADDR_ANY)
|
|
return alias_addr;
|
|
else if (targetAddress.s_addr == INADDR_NONE)
|
|
return aliasAddress;
|
|
else
|
|
return targetAddress;
|
|
}
|
|
else
|
|
{
|
|
if (link->server != NULL) { /* LSNAT link */
|
|
struct in_addr src_addr;
|
|
|
|
src_addr = link->server->addr;
|
|
link->server = link->server->next;
|
|
return (src_addr);
|
|
} else if (link->src_addr.s_addr == INADDR_ANY)
|
|
return aliasAddress;
|
|
else
|
|
return link->src_addr;
|
|
}
|
|
}
|
|
|
|
|
|
struct in_addr
|
|
FindAliasAddress(struct in_addr original_addr)
|
|
{
|
|
struct alias_link *link;
|
|
|
|
link = FindLinkOut(original_addr, nullAddress,
|
|
0, 0, LINK_ADDR, 0);
|
|
if (link == NULL)
|
|
{
|
|
return aliasAddress;
|
|
}
|
|
else
|
|
{
|
|
if (link->alias_addr.s_addr == INADDR_ANY)
|
|
return aliasAddress;
|
|
else
|
|
return link->alias_addr;
|
|
}
|
|
}
|
|
|
|
|
|
/* External routines for getting or changing link data
|
|
(external to alias_db.c, but internal to alias*.c)
|
|
|
|
SetFragmentData(), GetFragmentData()
|
|
SetFragmentPtr(), GetFragmentPtr()
|
|
SetStateIn(), SetStateOut(), GetStateIn(), GetStateOut()
|
|
GetOriginalAddress(), GetDestAddress(), GetAliasAddress()
|
|
GetOriginalPort(), GetAliasPort()
|
|
SetAckModified(), GetAckModified()
|
|
GetDeltaAckIn(), GetDeltaSeqOut(), AddSeq()
|
|
SetProtocolFlags(), GetProtocolFlags()
|
|
SetDestCallId()
|
|
*/
|
|
|
|
|
|
void
|
|
SetFragmentAddr(struct alias_link *link, struct in_addr src_addr)
|
|
{
|
|
link->data.frag_addr = src_addr;
|
|
}
|
|
|
|
|
|
void
|
|
GetFragmentAddr(struct alias_link *link, struct in_addr *src_addr)
|
|
{
|
|
*src_addr = link->data.frag_addr;
|
|
}
|
|
|
|
|
|
void
|
|
SetFragmentPtr(struct alias_link *link, char *fptr)
|
|
{
|
|
link->data.frag_ptr = fptr;
|
|
}
|
|
|
|
|
|
void
|
|
GetFragmentPtr(struct alias_link *link, char **fptr)
|
|
{
|
|
*fptr = link->data.frag_ptr;
|
|
}
|
|
|
|
|
|
void
|
|
SetStateIn(struct alias_link *link, int state)
|
|
{
|
|
/* TCP input state */
|
|
switch (state) {
|
|
case ALIAS_TCP_STATE_DISCONNECTED:
|
|
if (link->data.tcp->state.out != ALIAS_TCP_STATE_CONNECTED)
|
|
link->expire_time = TCP_EXPIRE_DEAD;
|
|
else
|
|
link->expire_time = TCP_EXPIRE_SINGLEDEAD;
|
|
break;
|
|
case ALIAS_TCP_STATE_CONNECTED:
|
|
if (link->data.tcp->state.out == ALIAS_TCP_STATE_CONNECTED)
|
|
link->expire_time = TCP_EXPIRE_CONNECTED;
|
|
break;
|
|
default:
|
|
abort();
|
|
}
|
|
link->data.tcp->state.in = state;
|
|
}
|
|
|
|
|
|
void
|
|
SetStateOut(struct alias_link *link, int state)
|
|
{
|
|
/* TCP output state */
|
|
switch (state) {
|
|
case ALIAS_TCP_STATE_DISCONNECTED:
|
|
if (link->data.tcp->state.in != ALIAS_TCP_STATE_CONNECTED)
|
|
link->expire_time = TCP_EXPIRE_DEAD;
|
|
else
|
|
link->expire_time = TCP_EXPIRE_SINGLEDEAD;
|
|
break;
|
|
case ALIAS_TCP_STATE_CONNECTED:
|
|
if (link->data.tcp->state.in == ALIAS_TCP_STATE_CONNECTED)
|
|
link->expire_time = TCP_EXPIRE_CONNECTED;
|
|
break;
|
|
default:
|
|
abort();
|
|
}
|
|
link->data.tcp->state.out = state;
|
|
}
|
|
|
|
|
|
int
|
|
GetStateIn(struct alias_link *link)
|
|
{
|
|
/* TCP input state */
|
|
return link->data.tcp->state.in;
|
|
}
|
|
|
|
|
|
int
|
|
GetStateOut(struct alias_link *link)
|
|
{
|
|
/* TCP output state */
|
|
return link->data.tcp->state.out;
|
|
}
|
|
|
|
|
|
struct in_addr
|
|
GetOriginalAddress(struct alias_link *link)
|
|
{
|
|
if (link->src_addr.s_addr == INADDR_ANY)
|
|
return aliasAddress;
|
|
else
|
|
return(link->src_addr);
|
|
}
|
|
|
|
|
|
struct in_addr
|
|
GetDestAddress(struct alias_link *link)
|
|
{
|
|
return(link->dst_addr);
|
|
}
|
|
|
|
|
|
struct in_addr
|
|
GetAliasAddress(struct alias_link *link)
|
|
{
|
|
if (link->alias_addr.s_addr == INADDR_ANY)
|
|
return aliasAddress;
|
|
else
|
|
return link->alias_addr;
|
|
}
|
|
|
|
|
|
struct in_addr
|
|
GetDefaultAliasAddress()
|
|
{
|
|
return aliasAddress;
|
|
}
|
|
|
|
|
|
void
|
|
SetDefaultAliasAddress(struct in_addr alias_addr)
|
|
{
|
|
aliasAddress = alias_addr;
|
|
}
|
|
|
|
|
|
u_short
|
|
GetOriginalPort(struct alias_link *link)
|
|
{
|
|
return(link->src_port);
|
|
}
|
|
|
|
|
|
u_short
|
|
GetAliasPort(struct alias_link *link)
|
|
{
|
|
return(link->alias_port);
|
|
}
|
|
|
|
#ifndef NO_FW_PUNCH
|
|
static u_short
|
|
GetDestPort(struct alias_link *link)
|
|
{
|
|
return(link->dst_port);
|
|
}
|
|
#endif
|
|
|
|
void
|
|
SetAckModified(struct alias_link *link)
|
|
{
|
|
/* Indicate that ACK numbers have been modified in a TCP connection */
|
|
link->data.tcp->state.ack_modified = 1;
|
|
}
|
|
|
|
|
|
struct in_addr
|
|
GetProxyAddress(struct alias_link *link)
|
|
{
|
|
return link->proxy_addr;
|
|
}
|
|
|
|
|
|
void
|
|
SetProxyAddress(struct alias_link *link, struct in_addr addr)
|
|
{
|
|
link->proxy_addr = addr;
|
|
}
|
|
|
|
|
|
u_short
|
|
GetProxyPort(struct alias_link *link)
|
|
{
|
|
return link->proxy_port;
|
|
}
|
|
|
|
|
|
void
|
|
SetProxyPort(struct alias_link *link, u_short port)
|
|
{
|
|
link->proxy_port = port;
|
|
}
|
|
|
|
|
|
int
|
|
GetAckModified(struct alias_link *link)
|
|
{
|
|
/* See if ACK numbers have been modified */
|
|
return link->data.tcp->state.ack_modified;
|
|
}
|
|
|
|
|
|
int
|
|
GetDeltaAckIn(struct ip *pip, struct alias_link *link)
|
|
{
|
|
/*
|
|
Find out how much the ACK number has been altered for an incoming
|
|
TCP packet. To do this, a circular list of ACK numbers where the TCP
|
|
packet size was altered is searched.
|
|
*/
|
|
|
|
int i;
|
|
struct tcphdr *tc;
|
|
int delta, ack_diff_min;
|
|
u_long ack;
|
|
|
|
tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2));
|
|
ack = tc->th_ack;
|
|
|
|
delta = 0;
|
|
ack_diff_min = -1;
|
|
for (i=0; i<N_LINK_TCP_DATA; i++)
|
|
{
|
|
struct ack_data_record x;
|
|
|
|
x = link->data.tcp->ack[i];
|
|
if (x.active == 1)
|
|
{
|
|
int ack_diff;
|
|
|
|
ack_diff = SeqDiff(x.ack_new, ack);
|
|
if (ack_diff >= 0)
|
|
{
|
|
if (ack_diff_min >= 0)
|
|
{
|
|
if (ack_diff < ack_diff_min)
|
|
{
|
|
delta = x.delta;
|
|
ack_diff_min = ack_diff;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
delta = x.delta;
|
|
ack_diff_min = ack_diff;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return (delta);
|
|
}
|
|
|
|
|
|
int
|
|
GetDeltaSeqOut(struct ip *pip, struct alias_link *link)
|
|
{
|
|
/*
|
|
Find out how much the sequence number has been altered for an outgoing
|
|
TCP packet. To do this, a circular list of ACK numbers where the TCP
|
|
packet size was altered is searched.
|
|
*/
|
|
|
|
int i;
|
|
struct tcphdr *tc;
|
|
int delta, seq_diff_min;
|
|
u_long seq;
|
|
|
|
tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2));
|
|
seq = tc->th_seq;
|
|
|
|
delta = 0;
|
|
seq_diff_min = -1;
|
|
for (i=0; i<N_LINK_TCP_DATA; i++)
|
|
{
|
|
struct ack_data_record x;
|
|
|
|
x = link->data.tcp->ack[i];
|
|
if (x.active == 1)
|
|
{
|
|
int seq_diff;
|
|
|
|
seq_diff = SeqDiff(x.ack_old, seq);
|
|
if (seq_diff >= 0)
|
|
{
|
|
if (seq_diff_min >= 0)
|
|
{
|
|
if (seq_diff < seq_diff_min)
|
|
{
|
|
delta = x.delta;
|
|
seq_diff_min = seq_diff;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
delta = x.delta;
|
|
seq_diff_min = seq_diff;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return (delta);
|
|
}
|
|
|
|
|
|
void
|
|
AddSeq(struct ip *pip, struct alias_link *link, int delta)
|
|
{
|
|
/*
|
|
When a TCP packet has been altered in length, save this
|
|
information in a circular list. If enough packets have
|
|
been altered, then this list will begin to overwrite itself.
|
|
*/
|
|
|
|
struct tcphdr *tc;
|
|
struct ack_data_record x;
|
|
int hlen, tlen, dlen;
|
|
int i;
|
|
|
|
tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2));
|
|
|
|
hlen = (pip->ip_hl + tc->th_off) << 2;
|
|
tlen = ntohs(pip->ip_len);
|
|
dlen = tlen - hlen;
|
|
|
|
x.ack_old = htonl(ntohl(tc->th_seq) + dlen);
|
|
x.ack_new = htonl(ntohl(tc->th_seq) + dlen + delta);
|
|
x.delta = delta;
|
|
x.active = 1;
|
|
|
|
i = link->data.tcp->state.index;
|
|
link->data.tcp->ack[i] = x;
|
|
|
|
i++;
|
|
if (i == N_LINK_TCP_DATA)
|
|
link->data.tcp->state.index = 0;
|
|
else
|
|
link->data.tcp->state.index = i;
|
|
}
|
|
|
|
void
|
|
SetExpire(struct alias_link *link, int expire)
|
|
{
|
|
if (expire == 0)
|
|
{
|
|
link->flags &= ~LINK_PERMANENT;
|
|
DeleteLink(link);
|
|
}
|
|
else if (expire == -1)
|
|
{
|
|
link->flags |= LINK_PERMANENT;
|
|
}
|
|
else if (expire > 0)
|
|
{
|
|
link->expire_time = expire;
|
|
}
|
|
else
|
|
{
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "PacketAlias/SetExpire(): ");
|
|
fprintf(stderr, "error in expire parameter\n");
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void
|
|
ClearCheckNewLink(void)
|
|
{
|
|
newDefaultLink = 0;
|
|
}
|
|
|
|
void
|
|
SetProtocolFlags(struct alias_link *link, int pflags)
|
|
{
|
|
|
|
link->pflags = pflags;;
|
|
}
|
|
|
|
int
|
|
GetProtocolFlags(struct alias_link *link)
|
|
{
|
|
|
|
return (link->pflags);
|
|
}
|
|
|
|
void
|
|
SetDestCallId(struct alias_link *link, u_int16_t cid)
|
|
{
|
|
|
|
deleteAllLinks = 1;
|
|
link = ReLink(link, link->src_addr, link->dst_addr, link->alias_addr,
|
|
link->src_port, cid, link->alias_port, link->link_type);
|
|
deleteAllLinks = 0;
|
|
}
|
|
|
|
|
|
/* Miscellaneous Functions
|
|
|
|
HouseKeeping()
|
|
InitPacketAliasLog()
|
|
UninitPacketAliasLog()
|
|
*/
|
|
|
|
/*
|
|
Whenever an outgoing or incoming packet is handled, HouseKeeping()
|
|
is called to find and remove timed-out aliasing links. Logic exists
|
|
to sweep through the entire table and linked list structure
|
|
every 60 seconds.
|
|
|
|
(prototype in alias_local.h)
|
|
*/
|
|
|
|
void
|
|
HouseKeeping(void)
|
|
{
|
|
int i, n, n100;
|
|
struct timeval tv;
|
|
struct timezone tz;
|
|
|
|
/*
|
|
* Save system time (seconds) in global variable timeStamp for
|
|
* use by other functions. This is done so as not to unnecessarily
|
|
* waste timeline by making system calls.
|
|
*/
|
|
gettimeofday(&tv, &tz);
|
|
timeStamp = tv.tv_sec;
|
|
|
|
/* Compute number of spokes (output table link chains) to cover */
|
|
n100 = LINK_TABLE_OUT_SIZE * 100 + houseKeepingResidual;
|
|
n100 *= timeStamp - lastCleanupTime;
|
|
n100 /= ALIAS_CLEANUP_INTERVAL_SECS;
|
|
|
|
n = n100/100;
|
|
|
|
/* Handle different cases */
|
|
if (n > ALIAS_CLEANUP_MAX_SPOKES)
|
|
{
|
|
n = ALIAS_CLEANUP_MAX_SPOKES;
|
|
lastCleanupTime = timeStamp;
|
|
houseKeepingResidual = 0;
|
|
|
|
for (i=0; i<n; i++)
|
|
IncrementalCleanup();
|
|
}
|
|
else if (n > 0)
|
|
{
|
|
lastCleanupTime = timeStamp;
|
|
houseKeepingResidual = n100 - 100*n;
|
|
|
|
for (i=0; i<n; i++)
|
|
IncrementalCleanup();
|
|
}
|
|
else if (n < 0)
|
|
{
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "PacketAlias/HouseKeeping(): ");
|
|
fprintf(stderr, "something unexpected in time values\n");
|
|
#endif
|
|
lastCleanupTime = timeStamp;
|
|
houseKeepingResidual = 0;
|
|
}
|
|
}
|
|
|
|
|
|
/* Init the log file and enable logging */
|
|
static void
|
|
InitPacketAliasLog(void)
|
|
{
|
|
if ((~packetAliasMode & PKT_ALIAS_LOG)
|
|
&& (monitorFile = fopen("/var/log/alias.log", "w")))
|
|
{
|
|
packetAliasMode |= PKT_ALIAS_LOG;
|
|
fprintf(monitorFile,
|
|
"PacketAlias/InitPacketAliasLog: Packet alias logging enabled.\n");
|
|
}
|
|
}
|
|
|
|
|
|
/* Close the log-file and disable logging. */
|
|
static void
|
|
UninitPacketAliasLog(void)
|
|
{
|
|
if (monitorFile) {
|
|
fclose(monitorFile);
|
|
monitorFile = NULL;
|
|
}
|
|
packetAliasMode &= ~PKT_ALIAS_LOG;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Outside world interfaces
|
|
|
|
-- "outside world" means other than alias*.c routines --
|
|
|
|
PacketAliasRedirectPort()
|
|
PacketAliasAddServer()
|
|
PacketAliasRedirectProto()
|
|
PacketAliasRedirectAddr()
|
|
PacketAliasRedirectDelete()
|
|
PacketAliasSetAddress()
|
|
PacketAliasInit()
|
|
PacketAliasUninit()
|
|
PacketAliasSetMode()
|
|
|
|
(prototypes in alias.h)
|
|
*/
|
|
|
|
/* Redirection from a specific public addr:port to a
|
|
private addr:port */
|
|
struct alias_link *
|
|
PacketAliasRedirectPort(struct in_addr src_addr, u_short src_port,
|
|
struct in_addr dst_addr, u_short dst_port,
|
|
struct in_addr alias_addr, u_short alias_port,
|
|
u_char proto)
|
|
{
|
|
int link_type;
|
|
struct alias_link *link;
|
|
|
|
switch(proto)
|
|
{
|
|
case IPPROTO_UDP:
|
|
link_type = LINK_UDP;
|
|
break;
|
|
case IPPROTO_TCP:
|
|
link_type = LINK_TCP;
|
|
break;
|
|
default:
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "PacketAliasRedirectPort(): ");
|
|
fprintf(stderr, "only TCP and UDP protocols allowed\n");
|
|
#endif
|
|
return NULL;
|
|
}
|
|
|
|
link = AddLink(src_addr, dst_addr, alias_addr,
|
|
src_port, dst_port, alias_port,
|
|
link_type);
|
|
|
|
if (link != NULL)
|
|
{
|
|
link->flags |= LINK_PERMANENT;
|
|
}
|
|
#ifdef DEBUG
|
|
else
|
|
{
|
|
fprintf(stderr, "PacketAliasRedirectPort(): "
|
|
"call to AddLink() failed\n");
|
|
}
|
|
#endif
|
|
|
|
return link;
|
|
}
|
|
|
|
/* Add server to the pool of servers */
|
|
int
|
|
PacketAliasAddServer(struct alias_link *link, struct in_addr addr, u_short port)
|
|
{
|
|
struct server *server;
|
|
|
|
server = malloc(sizeof(struct server));
|
|
|
|
if (server != NULL) {
|
|
struct server *head;
|
|
|
|
server->addr = addr;
|
|
server->port = port;
|
|
|
|
head = link->server;
|
|
if (head == NULL)
|
|
server->next = server;
|
|
else {
|
|
struct server *s;
|
|
|
|
for (s = head; s->next != head; s = s->next);
|
|
s->next = server;
|
|
server->next = head;
|
|
}
|
|
link->server = server;
|
|
return (0);
|
|
} else
|
|
return (-1);
|
|
}
|
|
|
|
/* Redirect packets of a given IP protocol from a specific
|
|
public address to a private address */
|
|
struct alias_link *
|
|
PacketAliasRedirectProto(struct in_addr src_addr,
|
|
struct in_addr dst_addr,
|
|
struct in_addr alias_addr,
|
|
u_char proto)
|
|
{
|
|
struct alias_link *link;
|
|
|
|
link = AddLink(src_addr, dst_addr, alias_addr,
|
|
NO_SRC_PORT, NO_DEST_PORT, 0,
|
|
proto);
|
|
|
|
if (link != NULL)
|
|
{
|
|
link->flags |= LINK_PERMANENT;
|
|
}
|
|
#ifdef DEBUG
|
|
else
|
|
{
|
|
fprintf(stderr, "PacketAliasRedirectProto(): "
|
|
"call to AddLink() failed\n");
|
|
}
|
|
#endif
|
|
|
|
return link;
|
|
}
|
|
|
|
/* Static address translation */
|
|
struct alias_link *
|
|
PacketAliasRedirectAddr(struct in_addr src_addr,
|
|
struct in_addr alias_addr)
|
|
{
|
|
struct alias_link *link;
|
|
|
|
link = AddLink(src_addr, nullAddress, alias_addr,
|
|
0, 0, 0,
|
|
LINK_ADDR);
|
|
|
|
if (link != NULL)
|
|
{
|
|
link->flags |= LINK_PERMANENT;
|
|
}
|
|
#ifdef DEBUG
|
|
else
|
|
{
|
|
fprintf(stderr, "PacketAliasRedirectAddr(): "
|
|
"call to AddLink() failed\n");
|
|
}
|
|
#endif
|
|
|
|
return link;
|
|
}
|
|
|
|
|
|
void
|
|
PacketAliasRedirectDelete(struct alias_link *link)
|
|
{
|
|
/* This is a dangerous function to put in the API,
|
|
because an invalid pointer can crash the program. */
|
|
|
|
deleteAllLinks = 1;
|
|
DeleteLink(link);
|
|
deleteAllLinks = 0;
|
|
}
|
|
|
|
|
|
void
|
|
PacketAliasSetAddress(struct in_addr addr)
|
|
{
|
|
if (packetAliasMode & PKT_ALIAS_RESET_ON_ADDR_CHANGE
|
|
&& aliasAddress.s_addr != addr.s_addr)
|
|
CleanupAliasData();
|
|
|
|
aliasAddress = addr;
|
|
}
|
|
|
|
|
|
void
|
|
PacketAliasSetTarget(struct in_addr target_addr)
|
|
{
|
|
targetAddress = target_addr;
|
|
}
|
|
|
|
|
|
void
|
|
PacketAliasInit(void)
|
|
{
|
|
int i;
|
|
struct timeval tv;
|
|
struct timezone tz;
|
|
static int firstCall = 1;
|
|
|
|
if (firstCall == 1)
|
|
{
|
|
gettimeofday(&tv, &tz);
|
|
timeStamp = tv.tv_sec;
|
|
lastCleanupTime = tv.tv_sec;
|
|
houseKeepingResidual = 0;
|
|
|
|
for (i=0; i<LINK_TABLE_OUT_SIZE; i++)
|
|
LIST_INIT(&linkTableOut[i]);
|
|
for (i=0; i<LINK_TABLE_IN_SIZE; i++)
|
|
LIST_INIT(&linkTableIn[i]);
|
|
|
|
atexit(PacketAliasUninit);
|
|
firstCall = 0;
|
|
}
|
|
else
|
|
{
|
|
deleteAllLinks = 1;
|
|
CleanupAliasData();
|
|
deleteAllLinks = 0;
|
|
}
|
|
|
|
aliasAddress.s_addr = INADDR_ANY;
|
|
targetAddress.s_addr = INADDR_ANY;
|
|
|
|
icmpLinkCount = 0;
|
|
udpLinkCount = 0;
|
|
tcpLinkCount = 0;
|
|
pptpLinkCount = 0;
|
|
protoLinkCount = 0;
|
|
fragmentIdLinkCount = 0;
|
|
fragmentPtrLinkCount = 0;
|
|
sockCount = 0;
|
|
|
|
cleanupIndex =0;
|
|
|
|
packetAliasMode = PKT_ALIAS_SAME_PORTS
|
|
| PKT_ALIAS_USE_SOCKETS
|
|
| PKT_ALIAS_RESET_ON_ADDR_CHANGE;
|
|
}
|
|
|
|
void
|
|
PacketAliasUninit(void) {
|
|
deleteAllLinks = 1;
|
|
CleanupAliasData();
|
|
deleteAllLinks = 0;
|
|
UninitPacketAliasLog();
|
|
#ifndef NO_FW_PUNCH
|
|
UninitPunchFW();
|
|
#endif
|
|
}
|
|
|
|
|
|
/* Change mode for some operations */
|
|
unsigned int
|
|
PacketAliasSetMode(
|
|
unsigned int flags, /* Which state to bring flags to */
|
|
unsigned int mask /* Mask of which flags to affect (use 0 to do a
|
|
probe for flag values) */
|
|
)
|
|
{
|
|
/* Enable logging? */
|
|
if (flags & mask & PKT_ALIAS_LOG)
|
|
{
|
|
InitPacketAliasLog(); /* Do the enable */
|
|
} else
|
|
/* _Disable_ logging? */
|
|
if (~flags & mask & PKT_ALIAS_LOG) {
|
|
UninitPacketAliasLog();
|
|
}
|
|
|
|
#ifndef NO_FW_PUNCH
|
|
/* Start punching holes in the firewall? */
|
|
if (flags & mask & PKT_ALIAS_PUNCH_FW) {
|
|
InitPunchFW();
|
|
} else
|
|
/* Stop punching holes in the firewall? */
|
|
if (~flags & mask & PKT_ALIAS_PUNCH_FW) {
|
|
UninitPunchFW();
|
|
}
|
|
#endif
|
|
|
|
/* Other flags can be set/cleared without special action */
|
|
packetAliasMode = (flags & mask) | (packetAliasMode & ~mask);
|
|
return packetAliasMode;
|
|
}
|
|
|
|
|
|
int
|
|
PacketAliasCheckNewLink(void)
|
|
{
|
|
return newDefaultLink;
|
|
}
|
|
|
|
|
|
#ifndef NO_FW_PUNCH
|
|
|
|
/*****************
|
|
Code to support firewall punching. This shouldn't really be in this
|
|
file, but making variables global is evil too.
|
|
****************/
|
|
|
|
/* Firewall include files */
|
|
#include <net/if.h>
|
|
#include <netinet/ip_fw.h>
|
|
#include <string.h>
|
|
#include <err.h>
|
|
|
|
static void ClearAllFWHoles(void);
|
|
|
|
static int fireWallBaseNum; /* The first firewall entry free for our use */
|
|
static int fireWallNumNums; /* How many entries can we use? */
|
|
static int fireWallActiveNum; /* Which entry did we last use? */
|
|
static char *fireWallField; /* bool array for entries */
|
|
|
|
#define fw_setfield(field, num) \
|
|
do { \
|
|
(field)[(num) - fireWallBaseNum] = 1; \
|
|
} /*lint -save -e717 */ while(0) /*lint -restore */
|
|
#define fw_clrfield(field, num) \
|
|
do { \
|
|
(field)[(num) - fireWallBaseNum] = 0; \
|
|
} /*lint -save -e717 */ while(0) /*lint -restore */
|
|
#define fw_tstfield(field, num) ((field)[(num) - fireWallBaseNum])
|
|
|
|
static void
|
|
InitPunchFW(void) {
|
|
fireWallField = malloc(fireWallNumNums);
|
|
if (fireWallField) {
|
|
memset(fireWallField, 0, fireWallNumNums);
|
|
if (fireWallFD < 0) {
|
|
fireWallFD = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
|
|
}
|
|
ClearAllFWHoles();
|
|
fireWallActiveNum = fireWallBaseNum;
|
|
}
|
|
}
|
|
|
|
static void
|
|
UninitPunchFW(void) {
|
|
ClearAllFWHoles();
|
|
if (fireWallFD >= 0)
|
|
close(fireWallFD);
|
|
fireWallFD = -1;
|
|
if (fireWallField)
|
|
free(fireWallField);
|
|
fireWallField = NULL;
|
|
packetAliasMode &= ~PKT_ALIAS_PUNCH_FW;
|
|
}
|
|
|
|
/* Make a certain link go through the firewall */
|
|
void
|
|
PunchFWHole(struct alias_link *link) {
|
|
int r; /* Result code */
|
|
struct ip_fw rule; /* On-the-fly built rule */
|
|
int fwhole; /* Where to punch hole */
|
|
|
|
/* Don't do anything unless we are asked to */
|
|
if ( !(packetAliasMode & PKT_ALIAS_PUNCH_FW) ||
|
|
fireWallFD < 0 ||
|
|
link->link_type != LINK_TCP)
|
|
return;
|
|
|
|
memset(&rule, 0, sizeof rule);
|
|
|
|
/** Build rule **/
|
|
|
|
/* Find empty slot */
|
|
for (fwhole = fireWallActiveNum;
|
|
fwhole < fireWallBaseNum + fireWallNumNums &&
|
|
fw_tstfield(fireWallField, fwhole);
|
|
fwhole++)
|
|
;
|
|
if (fwhole == fireWallBaseNum + fireWallNumNums) {
|
|
for (fwhole = fireWallBaseNum;
|
|
fwhole < fireWallActiveNum &&
|
|
fw_tstfield(fireWallField, fwhole);
|
|
fwhole++)
|
|
;
|
|
if (fwhole == fireWallActiveNum) {
|
|
/* No rule point empty - we can't punch more holes. */
|
|
fireWallActiveNum = fireWallBaseNum;
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "libalias: Unable to create firewall hole!\n");
|
|
#endif
|
|
return;
|
|
}
|
|
}
|
|
/* Start next search at next position */
|
|
fireWallActiveNum = fwhole+1;
|
|
|
|
/* Build generic part of the two rules */
|
|
rule.fw_number = fwhole;
|
|
IP_FW_SETNSRCP(&rule, 1); /* Number of source ports. */
|
|
IP_FW_SETNDSTP(&rule, 1); /* Number of destination ports. */
|
|
rule.fw_flg = IP_FW_F_ACCEPT | IP_FW_F_IN | IP_FW_F_OUT;
|
|
rule.fw_prot = IPPROTO_TCP;
|
|
rule.fw_smsk.s_addr = INADDR_BROADCAST;
|
|
rule.fw_dmsk.s_addr = INADDR_BROADCAST;
|
|
|
|
/* Build and apply specific part of the rules */
|
|
rule.fw_src = GetOriginalAddress(link);
|
|
rule.fw_dst = GetDestAddress(link);
|
|
rule.fw_uar.fw_pts[0] = ntohs(GetOriginalPort(link));
|
|
rule.fw_uar.fw_pts[1] = ntohs(GetDestPort(link));
|
|
|
|
/* Skip non-bound links - XXX should not be strictly necessary,
|
|
but seems to leave hole if not done. Leak of non-bound links?
|
|
(Code should be left even if the problem is fixed - it is a
|
|
clear optimization) */
|
|
if (rule.fw_uar.fw_pts[0] != 0 && rule.fw_uar.fw_pts[1] != 0) {
|
|
r = setsockopt(fireWallFD, IPPROTO_IP, IP_FW_ADD, &rule, sizeof rule);
|
|
#ifdef DEBUG
|
|
if (r)
|
|
err(1, "alias punch inbound(1) setsockopt(IP_FW_ADD)");
|
|
#endif
|
|
rule.fw_src = GetDestAddress(link);
|
|
rule.fw_dst = GetOriginalAddress(link);
|
|
rule.fw_uar.fw_pts[0] = ntohs(GetDestPort(link));
|
|
rule.fw_uar.fw_pts[1] = ntohs(GetOriginalPort(link));
|
|
r = setsockopt(fireWallFD, IPPROTO_IP, IP_FW_ADD, &rule, sizeof rule);
|
|
#ifdef DEBUG
|
|
if (r)
|
|
err(1, "alias punch inbound(2) setsockopt(IP_FW_ADD)");
|
|
#endif
|
|
}
|
|
/* Indicate hole applied */
|
|
link->data.tcp->fwhole = fwhole;
|
|
fw_setfield(fireWallField, fwhole);
|
|
}
|
|
|
|
/* Remove a hole in a firewall associated with a particular alias
|
|
link. Calling this too often is harmless. */
|
|
static void
|
|
ClearFWHole(struct alias_link *link) {
|
|
if (link->link_type == LINK_TCP) {
|
|
int fwhole = link->data.tcp->fwhole; /* Where is the firewall hole? */
|
|
struct ip_fw rule;
|
|
|
|
if (fwhole < 0)
|
|
return;
|
|
|
|
memset(&rule, 0, sizeof rule);
|
|
rule.fw_number = fwhole;
|
|
while (!setsockopt(fireWallFD, IPPROTO_IP, IP_FW_DEL, &rule, sizeof rule))
|
|
;
|
|
fw_clrfield(fireWallField, fwhole);
|
|
link->data.tcp->fwhole = -1;
|
|
}
|
|
}
|
|
|
|
/* Clear out the entire range dedicated to firewall holes. */
|
|
static void
|
|
ClearAllFWHoles(void) {
|
|
struct ip_fw rule; /* On-the-fly built rule */
|
|
int i;
|
|
|
|
if (fireWallFD < 0)
|
|
return;
|
|
|
|
memset(&rule, 0, sizeof rule);
|
|
for (i = fireWallBaseNum; i < fireWallBaseNum + fireWallNumNums; i++) {
|
|
rule.fw_number = i;
|
|
while (!setsockopt(fireWallFD, IPPROTO_IP, IP_FW_DEL, &rule, sizeof rule))
|
|
;
|
|
}
|
|
memset(fireWallField, 0, fireWallNumNums);
|
|
}
|
|
#endif
|
|
|
|
void
|
|
PacketAliasSetFWBase(unsigned int base, unsigned int num) {
|
|
#ifndef NO_FW_PUNCH
|
|
fireWallBaseNum = base;
|
|
fireWallNumNums = num;
|
|
#endif
|
|
}
|