#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/errno.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/types.h>
#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"
#define LINK_TABLE_OUT_SIZE 101
#define LINK_TABLE_IN_SIZE 4001
#define ALIAS_CLEANUP_INTERVAL_SECS 60
#define ALIAS_CLEANUP_MAX_SPOKES 30
#define ICMP_EXPIRE_TIME 60
#define UDP_EXPIRE_TIME 60
#define FRAGMENT_ID_EXPIRE_TIME 10
#define FRAGMENT_PTR_EXPIRE_TIME 30
#ifndef TCP_EXPIRE_DEAD
# define TCP_EXPIRE_DEAD 10
#endif
#ifndef TCP_EXPIRE_SINGLEDEAD
# define TCP_EXPIRE_SINGLEDEAD 90
#endif
#ifndef TCP_EXPIRE_INITIAL
# define TCP_EXPIRE_INITIAL 300
#endif
#ifndef TCP_EXPIRE_CONNECTED
# define TCP_EXPIRE_CONNECTED 86400
#endif
#define NO_DEST_PORT 1
#define NO_SRC_PORT 1
struct ack_data_record
{
u_long ack_old;
u_long ack_new;
int delta;
int active;
};
struct tcp_state
{
int in;
int out;
int index;
int ack_modified;
};
#define N_LINK_TCP_DATA 3
struct tcp_dat
{
struct tcp_state state;
struct ack_data_record ack[N_LINK_TCP_DATA];
int fwhole;
};
struct alias_link
{
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;
int link_type;
#define LINK_ICMP 1
#define LINK_UDP 2
#define LINK_TCP 3
#define LINK_FRAGMENT_ID 4
#define LINK_FRAGMENT_PTR 5
#define LINK_ADDR 6
int flags;
#define LINK_UNKNOWN_DEST_PORT 0x01
#define LINK_UNKNOWN_DEST_ADDR 0x02
#define LINK_PERMANENT 0x04
#define LINK_PARTIALLY_SPECIFIED 0x03
#define LINK_UNFIREWALLED 0x08
int timestamp;
int expire_time;
int sockfd;
u_int start_point_out;
u_int start_point_in;
struct alias_link *next_out;
struct alias_link *last_out;
struct alias_link *next_in;
struct alias_link *last_in;
union
{
char *frag_ptr;
struct in_addr frag_addr;
struct tcp_dat *tcp;
} data;
};
int packetAliasMode;
static struct in_addr aliasAddress;
static struct in_addr targetAddress;
static struct in_addr nullAddress;
static struct alias_link *
linkTableOut[LINK_TABLE_OUT_SIZE];
static struct alias_link *
linkTableIn[LINK_TABLE_IN_SIZE];
static int icmpLinkCount;
static int udpLinkCount;
static int tcpLinkCount;
static int fragmentIdLinkCount;
static int fragmentPtrLinkCount;
static int sockCount;
static int cleanupIndex;
static int timeStamp;
static int lastCleanupTime;
static int houseKeepingResidual;
static int deleteAllLinks;
static FILE *monitorFile;
static int newDefaultLink;
#ifndef NO_FW_PUNCH
static int fireWallFD = -1;
#endif
static int pptpAliasFlag;
static struct in_addr pptpAliasAddr;
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
static void InitPunchFW(void);
static void UninitPunchFW(void);
static void ClearFWHole(struct alias_link *link);
#endif
static void InitPacketAliasLog(void);
static void UninitPacketAliasLog(void);
static u_int
StartPointIn(struct in_addr alias_addr,
u_short alias_port,
int link_type)
{
u_int n;
n = alias_addr.s_addr;
n += alias_port;
n += link_type;
return(n % LINK_TABLE_IN_SIZE);
}
static u_int
StartPointOut(struct in_addr src_addr, struct in_addr dst_addr,
u_short src_port, u_short dst_port, int link_type)
{
u_int n;
n = src_addr.s_addr;
n += dst_addr.s_addr;
n += src_port;
n += dst_port;
n += link_type;
return(n % LINK_TABLE_OUT_SIZE);
}
static int
SeqDiff(u_long x, u_long y)
{
return (ntohl(y) - ntohl(x));
}
static void
ShowAliasStats(void)
{
if (monitorFile)
{
fprintf(monitorFile, "icmp=%d, udp=%d, tcp=%d, frag_id=%d frag_ptr=%d",
icmpLinkCount,
udpLinkCount,
tcpLinkCount,
fragmentIdLinkCount,
fragmentPtrLinkCount);
fprintf(monitorFile, " / tot=%d (sock=%d)\n",
icmpLinkCount + udpLinkCount
+ tcpLinkCount
+ fragmentIdLinkCount
+ fragmentPtrLinkCount,
sockCount);
fflush(monitorFile);
}
}
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);
static struct alias_link *
FindLinkIn(struct in_addr, struct in_addr, u_short, u_short, int, int);
#define ALIAS_PORT_BASE 0x08000
#define ALIAS_PORT_MASK 0x07fff
#define GET_NEW_PORT_MAX_ATTEMPTS 20
#define GET_ALIAS_PORT -1
#define GET_ALIAS_ID GET_ALIAS_PORT
static int
GetNewPort(struct alias_link *link, int alias_port_param)
{
int i;
int max_trials;
u_short port_sys;
u_short port_net;
if (alias_port_param == GET_ALIAS_PORT)
{
max_trials = GET_NEW_PORT_MAX_ATTEMPTS;
if (packetAliasMode & PKT_ALIAS_SAME_PORTS)
{
port_net = link->src_port;
port_sys = ntohs(port_net);
}
else
{
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);
}
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))
{
if (GetSocket(port_net, &link->sockfd, link->link_type))
{
link->alias_port = port_net;
return(0);
}
}
else
{
link->alias_port = port_net;
return(0);
}
}
port_sys = random() & ALIAS_PORT_MASK;
port_sys += ALIAS_PORT_BASE;
port_net = htons(port_sys);
}
#ifdef DEBUG
fprintf(stderr, "PacketAlias/GetnewPort(): ");
fprintf(stderr, "could not find free port\n");
#endif
return(-1);
}
static u_short
GetSocket(u_short port_net, int *sockfd, int link_type)
{
int err;
int sock;
struct sockaddr_in sock_addr;
if (link_type == LINK_TCP)
sock = socket(AF_INET, SOCK_STREAM, 0);
else if (link_type == LINK_UDP)
sock = socket(AF_INET, SOCK_DGRAM, 0);
else
{
#ifdef DEBUG
fprintf(stderr, "PacketAlias/GetSocket(): ");
fprintf(stderr, "incorrect link type\n");
#endif
return(0);
}
if (sock < 0)
{
#ifdef DEBUG
fprintf(stderr, "PacketAlias/GetSocket(): ");
fprintf(stderr, "socket() error %d\n", *sockfd);
#endif
return(0);
}
sock_addr.sin_family = AF_INET;
sock_addr.sin_addr.s_addr = htonl(INADDR_ANY);
sock_addr.sin_port = port_net;
err = bind(sock,
(struct sockaddr *) &sock_addr,
sizeof(sock_addr));
if (err == 0)
{
sockCount++;
*sockfd = sock;
return(1);
}
else
{
close(sock);
return(0);
}
}
static void
CleanupAliasData(void)
{
struct alias_link *link;
int i, icount;
icount = 0;
for (i=0; i<LINK_TABLE_OUT_SIZE; i++)
{
link = linkTableOut[i];
while (link != NULL)
{
struct alias_link *link_next;
link_next = link->next_out;
icount++;
DeleteLink(link);
link = link_next;
}
}
cleanupIndex =0;
}
static void
IncrementalCleanup(void)
{
int icount;
struct alias_link *link;
icount = 0;
link = linkTableOut[cleanupIndex++];
while (link != NULL)
{
int idelta;
struct alias_link *link_next;
link_next = link->next_out;
idelta = timeStamp - link->timestamp;
switch (link->link_type)
{
case LINK_ICMP:
case LINK_UDP:
case LINK_FRAGMENT_ID:
case LINK_FRAGMENT_PTR:
if (idelta > link->expire_time)
{
DeleteLink(link);
icount++;
}
break;
case LINK_TCP:
if (idelta > link->expire_time)
{
struct tcp_dat *tcp_aux;
tcp_aux = link->data.tcp;
if (tcp_aux->state.in != ALIAS_TCP_STATE_CONNECTED
|| tcp_aux->state.out != ALIAS_TCP_STATE_CONNECTED)
{
DeleteLink(link);
icount++;
}
}
break;
}
link = link_next;
}
if (cleanupIndex == LINK_TABLE_OUT_SIZE)
cleanupIndex = 0;
}
void
DeleteLink(struct alias_link *link)
{
struct alias_link *link_last;
struct alias_link *link_next;
if (deleteAllLinks == 0 && link->flags & LINK_PERMANENT)
return;
#ifndef NO_FW_PUNCH
ClearFWHole(link);
#endif
link_last = link->last_out;
link_next = link->next_out;
if (link_last != NULL)
link_last->next_out = link_next;
else
linkTableOut[link->start_point_out] = link_next;
if (link_next != NULL)
link_next->last_out = link_last;
link_last = link->last_in;
link_next = link->next_in;
if (link_last != NULL)
link_last->next_in = link_next;
else
linkTableIn[link->start_point_in] = link_next;
if (link_next != NULL)
link_next->last_in = link_last;
if (link->sockfd != -1)
{
sockCount--;
close(link->sockfd);
}
switch(link->link_type)
{
case LINK_ICMP:
icmpLinkCount--;
break;
case LINK_UDP:
udpLinkCount--;
break;
case LINK_TCP:
tcpLinkCount--;
if (link->data.tcp != NULL)
free(link->data.tcp);
break;
case LINK_FRAGMENT_ID:
fragmentIdLinkCount--;
break;
case LINK_FRAGMENT_PTR:
fragmentPtrLinkCount--;
if (link->data.frag_ptr != NULL)
free(link->data.frag_ptr);
break;
}
free(link);
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,
int link_type)
{
u_int start_point;
struct alias_link *link;
struct alias_link *first_link;
link = malloc(sizeof(struct alias_link));
if (link != NULL)
{
if (src_addr.s_addr == aliasAddress.s_addr)
src_addr.s_addr = 0;
if (alias_addr.s_addr == aliasAddress.s_addr)
alias_addr.s_addr = 0;
link->src_addr = src_addr;
link->dst_addr = dst_addr;
link->alias_addr = alias_addr;
link->proxy_addr.s_addr = 0;
link->src_port = src_port;
link->dst_port = dst_port;
link->proxy_port = 0;
link->link_type = link_type;
link->sockfd = -1;
link->flags = 0;
link->timestamp = timeStamp;
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_FRAGMENT_ID:
link->expire_time = FRAGMENT_ID_EXPIRE_TIME;
break;
case LINK_FRAGMENT_PTR:
link->expire_time = FRAGMENT_PTR_EXPIRE_TIME;
break;
}
if (dst_addr.s_addr == 0)
link->flags |= LINK_UNKNOWN_DEST_ADDR;
if (dst_port == 0)
link->flags |= LINK_UNKNOWN_DEST_PORT;
if (GetNewPort(link, alias_port_param) != 0)
{
free(link);
return(NULL);
}
start_point = StartPointOut(src_addr, dst_addr,
src_port, dst_port, link_type);
first_link = linkTableOut[start_point];
link->last_out = NULL;
link->next_out = first_link;
link->start_point_out = start_point;
if (first_link != NULL)
first_link->last_out = link;
linkTableOut[start_point] = link;
start_point = StartPointIn(alias_addr, link->alias_port, link_type);
first_link = linkTableIn[start_point];
link->last_in = NULL;
link->next_in = first_link;
link->start_point_in = start_point;
if (first_link != NULL)
first_link->last_in = link;
linkTableIn[start_point] = link;
switch(link_type)
{
struct tcp_dat *aux_tcp;
case LINK_ICMP:
icmpLinkCount++;
break;
case LINK_UDP:
udpLinkCount++;
break;
case LINK_TCP:
aux_tcp = malloc(sizeof(struct tcp_dat));
link->data.tcp = aux_tcp;
if (aux_tcp != NULL)
{
int i;
tcpLinkCount++;
aux_tcp->state.in = ALIAS_TCP_STATE_NOT_CONNECTED;
aux_tcp->state.out = ALIAS_TCP_STATE_NOT_CONNECTED;
aux_tcp->state.index = 0;
aux_tcp->state.ack_modified = 0;
for (i=0; i<N_LINK_TCP_DATA; i++)
aux_tcp->ack[i].active = 0;
aux_tcp->fwhole = -1;
}
else
{
#ifdef DEBUG
fprintf(stderr, "PacketAlias/AddLink: ");
fprintf(stderr, " cannot allocate auxiliary TCP data\n");
#endif
}
break;
case LINK_FRAGMENT_ID:
fragmentIdLinkCount++;
break;
case LINK_FRAGMENT_PTR:
fragmentPtrLinkCount++;
break;
}
}
else
{
#ifdef DEBUG
fprintf(stderr, "PacketAlias/AddLink(): ");
fprintf(stderr, "malloc() call failed.\n");
#endif
}
if (packetAliasMode & PKT_ALIAS_LOG)
{
ShowAliasStats();
}
return(link);
}
static struct alias_link *
ReLink(struct alias_link *old_link,
struct in_addr src_addr,
struct in_addr dst_addr,
struct in_addr alias_addr,
u_short src_port,
u_short dst_port,
int alias_port_param,
int link_type)
{
struct alias_link *new_link;
new_link = AddLink(src_addr, dst_addr, alias_addr,
src_port, dst_port, alias_port_param,
link_type);
#ifndef NO_FW_PUNCH
if (new_link != NULL &&
old_link->link_type == LINK_TCP &&
old_link->data.tcp &&
old_link->data.tcp->fwhole > 0) {
PunchFWHole(new_link);
}
#endif
DeleteLink(old_link);
return new_link;
}
static struct alias_link *
FindLinkOut(struct in_addr src_addr,
struct in_addr dst_addr,
u_short src_port,
u_short dst_port,
int link_type)
{
u_int i;
struct alias_link *link;
if (src_addr.s_addr == aliasAddress.s_addr)
src_addr.s_addr = 0;
i = StartPointOut(src_addr, dst_addr, src_port, dst_port, link_type);
link = linkTableOut[i];
while (link != NULL)
{
if (link->src_addr.s_addr == src_addr.s_addr
&& link->dst_addr.s_addr == dst_addr.s_addr
&& link->dst_port == dst_port
&& link->src_port == src_port
&& link->link_type == link_type)
{
link->timestamp = timeStamp;
break;
}
link = link->next_out;
}
return(link);
}
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;
link_fully_specified = NULL;
link_unknown_all = NULL;
link_unknown_dst_addr = NULL;
link_unknown_dst_port = NULL;
flags_in = 0;
if (dst_addr.s_addr == 0)
flags_in |= LINK_UNKNOWN_DEST_ADDR;
if (dst_port == 0)
flags_in |= LINK_UNKNOWN_DEST_PORT;
if (alias_addr.s_addr == aliasAddress.s_addr)
alias_addr.s_addr = 0;
start_point = StartPointIn(alias_addr, alias_port, link_type);
link = linkTableIn[start_point];
while (link != NULL)
{
int flags;
flags = flags_in | link->flags;
if (!(flags & LINK_PARTIALLY_SPECIFIED))
{
if (link->alias_addr.s_addr == alias_addr.s_addr
&& link->alias_port == alias_port
&& link->dst_addr.s_addr == dst_addr.s_addr
&& link->dst_port == dst_port
&& link->link_type == link_type)
{
link_fully_specified = link;
break;
}
}
else if ((flags & LINK_UNKNOWN_DEST_ADDR)
&& (flags & LINK_UNKNOWN_DEST_PORT))
{
if (link->alias_addr.s_addr == alias_addr.s_addr
&& link->alias_port == alias_port
&& link->link_type == link_type)
{
if (link_unknown_all == NULL)
link_unknown_all = link;
}
}
else if (flags & LINK_UNKNOWN_DEST_ADDR)
{
if (link->alias_addr.s_addr == alias_addr.s_addr
&& link->alias_port == alias_port
&& link->link_type == link_type
&& link->dst_port == dst_port)
{
if (link_unknown_dst_addr == NULL)
link_unknown_dst_addr = link;
}
}
else if (flags & LINK_UNKNOWN_DEST_PORT)
{
if (link->alias_addr.s_addr == alias_addr.s_addr
&& link->alias_port == alias_port
&& link->link_type == link_type
&& link->dst_addr.s_addr == dst_addr.s_addr)
{
if (link_unknown_dst_port == NULL)
link_unknown_dst_port = link;
}
}
link = link->next_in;
}
if (link_fully_specified != NULL)
{
return link_fully_specified;
}
else if (link_unknown_dst_port != NULL)
{
return replace_partial_links
? ReLink(link_unknown_dst_port,
link_unknown_dst_port->src_addr, dst_addr, alias_addr,
link_unknown_dst_port->src_port, dst_port, alias_port,
link_type)
: link_unknown_dst_port;
}
else if (link_unknown_dst_addr != NULL)
{
return replace_partial_links
? ReLink(link_unknown_dst_addr,
link_unknown_dst_addr->src_addr, dst_addr, alias_addr,
link_unknown_dst_addr->src_port, dst_port, alias_port,
link_type)
: link_unknown_dst_addr;
}
else if (link_unknown_all != NULL)
{
return replace_partial_links
? ReLink(link_unknown_all,
link_unknown_all->src_addr, dst_addr, alias_addr,
link_unknown_all->src_port, dst_port, alias_port,
link_type)
: link_unknown_all;
}
else
{
return(NULL);
}
}
struct alias_link *
FindIcmpIn(struct in_addr dst_addr,
struct in_addr alias_addr,
u_short id_alias)
{
return FindLinkIn(dst_addr, alias_addr,
NO_DEST_PORT, id_alias,
LINK_ICMP, 0);
}
struct alias_link *
FindIcmpOut(struct in_addr src_addr,
struct in_addr dst_addr,
u_short id)
{
struct alias_link * link;
link = FindLinkOut(src_addr, dst_addr,
id, NO_DEST_PORT,
LINK_ICMP);
if (link == NULL)
{
struct in_addr alias_addr;
alias_addr = FindAliasAddress(src_addr);
link = AddLink(src_addr, dst_addr, alias_addr,
id, NO_DEST_PORT, GET_ALIAS_ID,
LINK_ICMP);
}
return(link);
}
struct alias_link *
FindFragmentIn1(struct in_addr dst_addr,
struct in_addr alias_addr,
u_short ip_id)
{
struct alias_link *link;
link = FindLinkIn(dst_addr, alias_addr,
NO_DEST_PORT, ip_id,
LINK_FRAGMENT_ID, 0);
if (link == NULL)
{
link = AddLink(nullAddress, dst_addr, alias_addr,
NO_SRC_PORT, NO_DEST_PORT, ip_id,
LINK_FRAGMENT_ID);
}
return(link);
}
struct alias_link *
FindFragmentIn2(struct in_addr dst_addr,
struct in_addr alias_addr,
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 *
FindUdpTcpIn(struct in_addr dst_addr,
struct in_addr alias_addr,
u_short dst_port,
u_short alias_port,
u_char proto)
{
int link_type;
struct alias_link *link;
switch (proto)
{
case IPPROTO_UDP:
link_type = LINK_UDP;
break;
case IPPROTO_TCP:
link_type = LINK_TCP;
break;
default:
return NULL;
break;
}
link = FindLinkIn(dst_addr, alias_addr,
dst_port, alias_port,
link_type, 1);
if (!(packetAliasMode & PKT_ALIAS_DENY_INCOMING)
&& !(packetAliasMode & PKT_ALIAS_PROXY_ONLY)
&& link == NULL)
{
struct in_addr target_addr;
target_addr = FindOriginalAddress(alias_addr);
link = AddLink(target_addr, dst_addr, alias_addr,
alias_port, dst_port, alias_port,
link_type);
}
return(link);
}
struct alias_link *
FindUdpTcpOut(struct in_addr src_addr,
struct in_addr dst_addr,
u_short src_port,
u_short dst_port,
u_char proto)
{
int link_type;
struct alias_link *link;
switch (proto)
{
case IPPROTO_UDP:
link_type = LINK_UDP;
break;
case IPPROTO_TCP:
link_type = LINK_TCP;
break;
default:
return NULL;
break;
}
link = FindLinkOut(src_addr, dst_addr, src_port, dst_port, link_type);
if (link == NULL)
{
struct in_addr alias_addr;
alias_addr = FindAliasAddress(src_addr);
link = AddLink(src_addr, dst_addr, alias_addr,
src_port, dst_port, GET_ALIAS_PORT,
link_type);
}
return(link);
}
struct in_addr
FindOriginalAddress(struct in_addr alias_addr)
{
struct alias_link *link;
link = FindLinkIn(nullAddress, alias_addr,
0, 0, LINK_ADDR, 0);
if (link == NULL)
{
newDefaultLink = 1;
if (targetAddress.s_addr != 0)
return targetAddress;
else
return alias_addr;
}
else
{
if (link->src_addr.s_addr == 0)
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);
if (link == NULL)
{
return aliasAddress;
}
else
{
if (link->alias_addr.s_addr == 0)
return aliasAddress;
else
return link->alias_addr;
}
}
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)
{
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;
}
link->data.tcp->state.in = state;
break;
case ALIAS_TCP_STATE_CONNECTED:
link->expire_time = TCP_EXPIRE_CONNECTED;
case ALIAS_TCP_STATE_NOT_CONNECTED:
link->data.tcp->state.in = state;
break;
default:
abort();
}
}
void
SetStateOut(struct alias_link *link, int 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;
}
link->data.tcp->state.out = state;
break;
case ALIAS_TCP_STATE_CONNECTED:
link->expire_time = TCP_EXPIRE_CONNECTED;
case ALIAS_TCP_STATE_NOT_CONNECTED:
link->data.tcp->state.out = state;
break;
default:
abort();
}
}
int
GetStateIn(struct alias_link *link)
{
return link->data.tcp->state.in;
}
int
GetStateOut(struct alias_link *link)
{
return link->data.tcp->state.out;
}
struct in_addr
GetOriginalAddress(struct alias_link *link)
{
if (link->src_addr.s_addr == 0)
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 == 0)
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);
}
u_short
GetDestPort(struct alias_link *link)
{
return(link->dst_port);
}
void
SetAckModified(struct alias_link *link)
{
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)
{
return link->data.tcp->state.ack_modified;
}
int
GetDeltaAckIn(struct ip *pip, struct alias_link *link)
{
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)
{
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)
{
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
HouseKeeping(void)
{
int i, n, n100;
struct timeval tv;
struct timezone tz;
gettimeofday(&tv, &tz);
timeStamp = tv.tv_sec;
n100 = LINK_TABLE_OUT_SIZE * 100 + houseKeepingResidual;
n100 *= timeStamp - lastCleanupTime;
n100 /= ALIAS_CLEANUP_INTERVAL_SECS;
n = n100/100;
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;
}
}
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");
}
}
static void
UninitPacketAliasLog(void)
{
if (monitorFile) {
fclose(monitorFile);
monitorFile = NULL;
}
packetAliasMode &= ~PKT_ALIAS_LOG;
}
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;
}
int
PacketAliasPptp(struct in_addr src_addr)
{
pptpAliasAddr = src_addr;
pptpAliasFlag = src_addr.s_addr != INADDR_NONE;
return 1;
}
int GetPptpAlias (struct in_addr* alias_addr)
{
if (pptpAliasFlag)
*alias_addr = pptpAliasAddr;
return pptpAliasFlag;
}
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)
{
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++)
linkTableOut[i] = NULL;
for (i=0; i<LINK_TABLE_IN_SIZE; i++)
linkTableIn[i] = NULL;
atexit(PacketAliasUninit);
firstCall = 0;
}
else
{
deleteAllLinks = 1;
CleanupAliasData();
deleteAllLinks = 0;
}
aliasAddress.s_addr = 0;
targetAddress.s_addr = 0;
icmpLinkCount = 0;
udpLinkCount = 0;
tcpLinkCount = 0;
fragmentIdLinkCount = 0;
fragmentPtrLinkCount = 0;
sockCount = 0;
cleanupIndex =0;
packetAliasMode = PKT_ALIAS_SAME_PORTS
| PKT_ALIAS_USE_SOCKETS
| PKT_ALIAS_RESET_ON_ADDR_CHANGE;
pptpAliasFlag = 0;
}
void
PacketAliasUninit(void) {
deleteAllLinks = 1;
CleanupAliasData();
deleteAllLinks = 0;
UninitPacketAliasLog();
#ifndef NO_FW_PUNCH
UninitPunchFW();
#endif
}
unsigned int
PacketAliasSetMode(
unsigned int flags,
unsigned int mask
)
{
if (flags & mask & PKT_ALIAS_LOG)
{
InitPacketAliasLog();
} else
if (~flags & mask & PKT_ALIAS_LOG) {
UninitPacketAliasLog();
}
#ifndef NO_FW_PUNCH
if (flags & mask & PKT_ALIAS_PUNCH_FW) {
InitPunchFW();
} else
if (~flags & mask & PKT_ALIAS_PUNCH_FW) {
UninitPunchFW();
}
#endif
packetAliasMode = (flags & mask) | (packetAliasMode & ~mask);
return packetAliasMode;
}
int
PacketAliasCheckNewLink(void)
{
return newDefaultLink;
}
#ifndef NO_FW_PUNCH
#include <sys/queue.h>
#include <net/if.h>
#include <netinet/ip_fw.h>
#include <string.h>
#include <err.h>
static void ClearAllFWHoles(void);
static int fireWallBaseNum;
static int fireWallNumNums;
static int fireWallActiveNum;
static char *fireWallField;
#define fw_setfield(field, num) \
do { \
(field)[num] = 1; \
} while(0)
#define fw_clrfield(field, num) \
do { \
(field)[num] = 0; \
} while(0)
#define fw_tstfield(field, num) ((field)[num])
void
PacketAliasSetFWBase(unsigned int base, unsigned int num) {
fireWallBaseNum = base;
fireWallNumNums = num;
}
static void
InitPunchFW(void) {
fireWallField = malloc(fireWallNumNums);
if (fireWallField) {
memset(fireWallField, 0, fireWallNumNums);
if (fireWallFD < 0) {
fireWallFD = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
}
ClearAllFWHoles();
fireWallActiveNum = fireWallBaseNum;
}
}
static void
UninitPunchFW(void) {
ClearAllFWHoles();
if (fireWallFD >= 0)
close(fireWallFD);
fireWallFD = -1;
if (fireWallField)
free(fireWallField);
fireWallField = NULL;
packetAliasMode &= ~PKT_ALIAS_PUNCH_FW;
}
void
PunchFWHole(struct alias_link *link) {
int r;
struct ip_fw rule;
int fwhole;
if ( !(packetAliasMode & PKT_ALIAS_PUNCH_FW) ||
fireWallFD < 0 ||
link->link_type != LINK_TCP ||
!link->data.tcp)
return;
memset(&rule, 0, sizeof rule);
for (fwhole = fireWallActiveNum;
fwhole < fireWallBaseNum + fireWallNumNums &&
fw_tstfield(fireWallField, fwhole);
fwhole++)
;
if (fwhole >= fireWallBaseNum + fireWallNumNums ||
fw_tstfield(fireWallField, fwhole)) {
for (fwhole = fireWallBaseNum;
fwhole < fireWallActiveNum &&
fw_tstfield(fireWallField, fwhole);
fwhole++)
;
if (fwhole == fireWallActiveNum) {
fireWallActiveNum = fireWallBaseNum;
#ifdef DEBUG
fprintf(stderr, "libalias: Unable to create firewall hole!\n");
#endif
return;
}
}
fireWallActiveNum = fwhole+1;
rule.fw_number = fwhole;
rule.fw_nports = 1;
rule.fw_flg = IP_FW_F_ACCEPT;
rule.fw_prot = IPPROTO_TCP;
rule.fw_smsk.s_addr = INADDR_BROADCAST;
rule.fw_dmsk.s_addr = INADDR_BROADCAST;
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));
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
}
link->data.tcp->fwhole = fwhole;
fw_setfield(fireWallField, fwhole);
}
static void
ClearFWHole(struct alias_link *link) {
if (link->link_type == LINK_TCP && link->data.tcp) {
int fwhole = link->data.tcp->fwhole;
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;
}
}
static void
ClearAllFWHoles(void) {
struct ip_fw 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