#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>
#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 PROTO_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
static int iChatAVHack = 1;
#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 server
{
struct in_addr addr;
u_short port;
struct server *next;
};
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;
struct server *server;
int 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;
#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
#define LINK_LAST_LINE_CRLF_TERMED 0x10
#define LINK_CONE 0x20
int timestamp;
int expire_time;
int sockfd;
LIST_ENTRY(alias_link) list_out;
LIST_ENTRY(alias_link) list_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 LIST_HEAD(, alias_link)
linkTableOut[LINK_TABLE_OUT_SIZE];
static LIST_HEAD(, alias_link)
linkTableIn[LINK_TABLE_IN_SIZE];
static int icmpLinkCount;
static int udpLinkCount;
static int tcpLinkCount;
static int pptpLinkCount;
static int protoLinkCount;
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 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;
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 (ntohl(y) - ntohl(x));
}
static void
ShowAliasStats(void)
{
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);
}
}
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
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)
&& ((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);
}
}
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;
switch (proto)
{
case IPPROTO_UDP:
link_type = LINK_UDP;
break;
case IPPROTO_TCP:
link_type = LINK_TCP;
break;
default:
return (0);
break;
}
max_trials = GET_NEW_PORT_MAX_ATTEMPTS;
if (packetAliasMode & PKT_ALIAS_SAME_PORTS) {
port_sys = ntohs(src_port);
} else {
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;
}
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;
if (j == port_count)
return (htons(port_sys));
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)
{
if (deleteAllLinks == 0 && link->flags & LINK_PERMANENT)
return;
#ifndef NO_FW_PUNCH
ClearFWHole(link);
#endif
if (link->server != NULL) {
struct server *head, *curr, *next;
head = curr = link->server;
do {
next = curr->next;
free(curr);
} while ((curr = next) != head);
}
LIST_REMOVE(link, list_out);
LIST_REMOVE(link, list_in);
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--;
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;
}
#ifdef DEBUG
if ((packetAliasMode & PKT_ALIAS_LOG) != 0 &&
!IN_MULTICAST(link->src_addr.s_addr) &&
!IN_MULTICAST(link->dst_addr.s_addr))
{
char src[16];
char dst[16];
char alias[16];
char *proto;
switch(link->link_type)
{
case LINK_TCP:
proto = " [TCP]";
break;
case LINK_UDP:
proto = " [UDP]";
break;
default:
proto = "";
}
fprintf(monitorFile, "Deleted%s %s:%d<->%s:%d to %s:%d<->%s:%d\n",
proto,
inet_ntop(AF_INET, &link->src_addr, src, sizeof(src)), link->src_port,
inet_ntop(AF_INET, &link->dst_addr, dst, sizeof(dst)), link->dst_port,
inet_ntop(AF_INET, &link->alias_addr, alias, sizeof(alias)), link->alias_port,
dst, link->dst_port);
fflush(monitorFile);
}
#else
if (packetAliasMode & PKT_ALIAS_LOG)
ShowAliasStats();
#endif
free(link);
}
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;
link = malloc(sizeof(struct alias_link));
if (link != NULL)
{
link->src_addr = src_addr;
link->dst_addr = dst_addr;
link->alias_addr = alias_addr;
link->proxy_addr.s_addr = INADDR_ANY;
link->src_port = src_port;
link->dst_port = dst_port;
link->proxy_port = 0;
link->server = NULL;
link->link_type = link_type;
link->sockfd = -1;
link->flags = 0;
link->timestamp = timeStamp;
switch (link_type)
{
case LINK_ICMP:
link->expire_time = ICMP_EXPIRE_TIME;
break;
case LINK_UDP:
if (dst_addr.s_addr == 0 && dst_port == 0)
link->expire_time = UDP_EXPIRE_TIME * 5;
else
link->expire_time = UDP_EXPIRE_TIME;
break;
case LINK_TCP:
link->expire_time = TCP_EXPIRE_INITIAL;
break;
case LINK_PPTP:
link->flags |= LINK_PERMANENT;
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;
}
if (dst_addr.s_addr == INADDR_ANY)
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);
}
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;
}
start_point = StartPointOut(src_addr, dst_addr,
src_port, dst_port, link_type);
LIST_INSERT_HEAD(&linkTableOut[start_point], link, list_out);
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
}
#ifdef DEBUG
if ((packetAliasMode & PKT_ALIAS_LOG) != 0 &&
!IN_MULTICAST(link->src_addr.s_addr) &&
!IN_MULTICAST(link->dst_addr.s_addr))
{
char src[16];
char dst[16];
char alias[16];
char *proto;
switch(link->link_type)
{
case LINK_TCP:
proto = " [TCP]";
break;
case LINK_UDP:
proto = " [UDP]";
break;
default:
proto = "";
}
fprintf(monitorFile, "Added %s %s:%d<->%s:%d to %s:%d<->%s:%d\n",
proto,
inet_ntop(AF_INET, &link->src_addr, src, sizeof(src)), link->src_port,
inet_ntop(AF_INET, &link->dst_addr, dst, sizeof(dst)), link->dst_port,
inet_ntop(AF_INET, &link->alias_addr, alias, sizeof(alias)), link->alias_port,
dst, link->dst_port);
}
#else
if (packetAliasMode & PKT_ALIAS_LOG)
ShowAliasStats();
#endif
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->fwhole > 0) {
PunchFWHole(new_link);
}
#endif
if ((old_link->flags & LINK_CONE) == 0)
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;
}
}
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)
{
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;
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 == INADDR_ANY)
flags_in |= LINK_UNKNOWN_DEST_ADDR;
if (dst_port == 0)
flags_in |= LINK_UNKNOWN_DEST_PORT;
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) {
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)
{
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);
}
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,
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 *
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;
struct in_addr dst_addr2 = dst_addr;
u_short dst_port2 = dst_port;
alias_addr = FindAliasAddress(src_addr);
if (iChatAVHack && link_type == LINK_UDP && dst_port == htons(5678)) {
dst_addr2.s_addr = 0;
dst_port2 = 0;
}
link = AddLink(src_addr, dst_addr2, alias_addr,
src_port, dst_port2, GET_ALIAS_PORT,
link_type);
if (link != NULL &&
(link->flags & (LINK_UNKNOWN_DEST_ADDR | LINK_UNKNOWN_DEST_PORT)) != 0)
{
link->flags |= LINK_CONE;
link = ReLink(link, link->src_addr, dst_addr, link->alias_addr,
link->src_port, dst_port, link->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, 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) {
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;
}
}
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;
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)
{
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)
{
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 == 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)
{
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
SetLastLineCrlfTermed(struct alias_link *link, int yes)
{
if (yes)
link->flags |= LINK_LAST_LINE_CRLF_TERMED;
else
link->flags &= ~LINK_LAST_LINE_CRLF_TERMED;
}
int
GetLastLineCrlfTermed(struct alias_link *link)
{
return (link->flags & LINK_LAST_LINE_CRLF_TERMED);
}
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;
}
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
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);
}
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;
}
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++)
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
}
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 <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) - fireWallBaseNum] = 1; \
} while(0)
#define fw_clrfield(field, num) \
do { \
(field)[(num) - fireWallBaseNum] = 0; \
} while(0)
#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;
}
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)
return;
memset(&rule, 0, sizeof rule);
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) {
fireWallActiveNum = fireWallBaseNum;
#ifdef DEBUG
fprintf(stderr, "libalias: Unable to create firewall hole!\n");
#endif
return;
}
}
fireWallActiveNum = fwhole+1;
rule.fw_number = fwhole;
IP_FW_SETNSRCP(&rule, 1);
IP_FW_SETNDSTP(&rule, 1);
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;
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) {
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
void
PacketAliasSetFWBase(unsigned int base, unsigned int num) {
#ifndef NO_FW_PUNCH
fireWallBaseNum = base;
fireWallNumNums = num;
#endif
}