#include <sys/cdefs.h>
#ifdef _KERNEL
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/sysctl.h>
#else
#include <sys/types.h>
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <dlfcn.h>
#include <errno.h>
#include <string.h>
#endif
#include <netinet/in_systm.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#ifdef _KERNEL
#include <netinet/libalias/alias.h>
#include <netinet/libalias/alias_local.h>
#include <netinet/libalias/alias_mod.h>
#else
#include <err.h>
#include "alias.h"
#include "alias_local.h"
#include "alias_mod.h"
#endif
#ifdef SYSCTL_NODE
SYSCTL_DECL(_net_inet);
SYSCTL_DECL(_net_inet_ip);
SYSCTL_NODE(_net_inet_ip, OID_AUTO, alias, CTLFLAG_RW | CTLFLAG_MPSAFE, NULL,
"Libalias sysctl API");
#endif
static __inline int
twowords(void *p)
{
uint8_t *c = p;
#if BYTE_ORDER == LITTLE_ENDIAN
uint16_t s1 = ((uint16_t)c[1] << 8) + (uint16_t)c[0];
uint16_t s2 = ((uint16_t)c[3] << 8) + (uint16_t)c[2];
#else
uint16_t s1 = ((uint16_t)c[0] << 8) + (uint16_t)c[1];
uint16_t s2 = ((uint16_t)c[2] << 8) + (uint16_t)c[3];
#endif
return (s1 + s2);
}
static void TcpMonitorIn(uint16_t, struct alias_link *);
static void TcpMonitorOut(uint16_t, struct alias_link *);
static void
TcpMonitorIn(uint16_t th_flags, struct alias_link *lnk)
{
switch (GetStateIn(lnk)) {
case ALIAS_TCP_STATE_NOT_CONNECTED:
if (th_flags & TH_RST)
SetStateIn(lnk, ALIAS_TCP_STATE_DISCONNECTED);
else if (th_flags & TH_SYN)
SetStateIn(lnk, ALIAS_TCP_STATE_CONNECTED);
break;
case ALIAS_TCP_STATE_CONNECTED:
if (th_flags & (TH_FIN | TH_RST))
SetStateIn(lnk, ALIAS_TCP_STATE_DISCONNECTED);
break;
}
}
static void
TcpMonitorOut(uint16_t th_flags, struct alias_link *lnk)
{
switch (GetStateOut(lnk)) {
case ALIAS_TCP_STATE_NOT_CONNECTED:
if (th_flags & TH_RST)
SetStateOut(lnk, ALIAS_TCP_STATE_DISCONNECTED);
else if (th_flags & TH_SYN)
SetStateOut(lnk, ALIAS_TCP_STATE_CONNECTED);
break;
case ALIAS_TCP_STATE_CONNECTED:
if (th_flags & (TH_FIN | TH_RST))
SetStateOut(lnk, ALIAS_TCP_STATE_DISCONNECTED);
break;
}
}
static int IcmpAliasIn1(struct libalias *, struct ip *);
static int IcmpAliasIn2(struct libalias *, struct ip *);
static int IcmpAliasIn(struct libalias *, struct ip *);
static int IcmpAliasOut1(struct libalias *, struct ip *, int create);
static int IcmpAliasOut2(struct libalias *, struct ip *);
static int IcmpAliasOut(struct libalias *, struct ip *, int create);
static int ProtoAliasIn(struct libalias *la, struct in_addr ip_src,
struct ip *pip, u_char ip_p, u_short *ip_sum);
static int ProtoAliasOut(struct libalias *la, struct ip *pip,
struct in_addr ip_dst, u_char ip_p, u_short *ip_sum,
int create);
static int UdpAliasIn(struct libalias *, struct ip *);
static int UdpAliasOut(struct libalias *, struct ip *, int, int create);
static int TcpAliasIn(struct libalias *, struct ip *);
static int TcpAliasOut(struct libalias *, struct ip *, int, int create);
static int
IcmpAliasIn1(struct libalias *la, struct ip *pip)
{
struct alias_link *lnk;
struct icmp *ic;
int ret;
LIBALIAS_LOCK_ASSERT(la);
ic = (struct icmp *)ip_next(pip);
ret = FindIcmpIn(la, pip->ip_src, pip->ip_dst, ic->icmp_id, 1, &lnk);
if (ret == PKT_ALIAS_OK) {
u_short original_id;
int accumulate;
original_id = GetOriginalPort(lnk);
accumulate = ic->icmp_id;
accumulate -= original_id;
ADJUST_CHECKSUM(accumulate, ic->icmp_cksum);
ic->icmp_id = original_id;
{
struct in_addr original_address;
original_address = GetOriginalAddress(lnk);
DifferentialChecksum(&pip->ip_sum,
&original_address, &pip->ip_dst, 2);
pip->ip_dst = original_address;
}
}
return (ret);
}
static int
IcmpAliasIn2(struct libalias *la, struct ip *pip)
{
struct ip *ip;
struct icmp *ic, *ic2;
struct udphdr *ud;
struct tcphdr *tc;
struct alias_link *lnk;
int ret;
LIBALIAS_LOCK_ASSERT(la);
ic = (struct icmp *)ip_next(pip);
ip = &ic->icmp_ip;
ud = (struct udphdr *)ip_next(ip);
tc = (struct tcphdr *)ip_next(ip);
ic2 = (struct icmp *)ip_next(ip);
if (ip->ip_p == IPPROTO_UDP) {
ret = FindUdpTcpIn(la, ip->ip_dst, ip->ip_src,
ud->uh_dport, ud->uh_sport,
IPPROTO_UDP, 0, &lnk);
if (ret != PKT_ALIAS_OK)
return (ret);
} else if (ip->ip_p == IPPROTO_TCP) {
ret = FindUdpTcpIn(la, ip->ip_dst, ip->ip_src,
tc->th_dport, tc->th_sport,
IPPROTO_TCP, 0, &lnk);
if (ret != PKT_ALIAS_OK)
return (ret);
} else if (ip->ip_p == IPPROTO_ICMP) {
if (ic2->icmp_type == ICMP_ECHO ||
ic2->icmp_type == ICMP_TSTAMP) {
ret = FindIcmpIn(la, ip->ip_dst, ip->ip_src,
ic2->icmp_id, 0, &lnk);
if (ret != PKT_ALIAS_OK)
return (ret);
} else
lnk = NULL;
} else
lnk = NULL;
if (lnk != NULL) {
if (ip->ip_p == IPPROTO_UDP || ip->ip_p == IPPROTO_TCP) {
int accumulate, accumulate2;
struct in_addr original_address;
u_short original_port;
original_address = GetOriginalAddress(lnk);
original_port = GetOriginalPort(lnk);
accumulate = twowords(&ip->ip_src);
accumulate -= twowords(&original_address);
accumulate += ud->uh_sport;
accumulate -= original_port;
accumulate2 = accumulate;
accumulate2 += ip->ip_sum;
ADJUST_CHECKSUM(accumulate, ip->ip_sum);
accumulate2 -= ip->ip_sum;
ADJUST_CHECKSUM(accumulate2, ic->icmp_cksum);
DifferentialChecksum(&pip->ip_sum,
&original_address, &pip->ip_dst, 2);
pip->ip_dst = original_address;
ip->ip_src = original_address;
ud->uh_sport = original_port;
} else if (ip->ip_p == IPPROTO_ICMP) {
int accumulate, accumulate2;
struct in_addr original_address;
u_short original_id;
original_address = GetOriginalAddress(lnk);
original_id = GetOriginalPort(lnk);
accumulate = twowords(&ip->ip_src);
accumulate -= twowords(&original_address);
accumulate += ic2->icmp_id;
accumulate -= original_id;
accumulate2 = accumulate;
accumulate2 += ip->ip_sum;
ADJUST_CHECKSUM(accumulate, ip->ip_sum);
accumulate2 -= ip->ip_sum;
ADJUST_CHECKSUM(accumulate2, ic->icmp_cksum);
DifferentialChecksum(&pip->ip_sum,
&original_address, &pip->ip_dst, 2);
pip->ip_dst = original_address;
ip->ip_src = original_address;
ic2->icmp_id = original_id;
}
return (PKT_ALIAS_OK);
}
return (PKT_ALIAS_IGNORED);
}
static int
IcmpAliasIn(struct libalias *la, struct ip *pip)
{
struct icmp *ic;
int iresult;
size_t dlen;
LIBALIAS_LOCK_ASSERT(la);
dlen = ntohs(pip->ip_len) - (pip->ip_hl << 2);
if (dlen < ICMP_MINLEN)
return (PKT_ALIAS_IGNORED);
if (la->packetAliasMode & PKT_ALIAS_PROXY_ONLY)
return (PKT_ALIAS_OK);
ic = (struct icmp *)ip_next(pip);
iresult = PKT_ALIAS_IGNORED;
switch (ic->icmp_type) {
case ICMP_ECHOREPLY:
case ICMP_TSTAMPREPLY:
if (ic->icmp_code == 0) {
iresult = IcmpAliasIn1(la, pip);
}
break;
case ICMP_UNREACH:
case ICMP_SOURCEQUENCH:
case ICMP_TIMXCEED:
case ICMP_PARAMPROB:
if (dlen < ICMP_ADVLENMIN ||
dlen < (size_t)ICMP_ADVLEN(ic))
return (PKT_ALIAS_IGNORED);
iresult = IcmpAliasIn2(la, pip);
break;
case ICMP_ECHO:
case ICMP_TSTAMP:
iresult = IcmpAliasIn1(la, pip);
break;
}
return (iresult);
}
static int
IcmpAliasOut1(struct libalias *la, struct ip *pip, int create)
{
struct alias_link *lnk;
struct icmp *ic;
int ret;
LIBALIAS_LOCK_ASSERT(la);
ic = (struct icmp *)ip_next(pip);
ret = FindIcmpOut(la, pip->ip_src, pip->ip_dst, ic->icmp_id, create,
&lnk);
if (ret == PKT_ALIAS_OK) {
u_short alias_id;
int accumulate;
alias_id = GetAliasPort(lnk);
accumulate = ic->icmp_id;
accumulate -= alias_id;
ADJUST_CHECKSUM(accumulate, ic->icmp_cksum);
ic->icmp_id = alias_id;
{
struct in_addr alias_address;
alias_address = GetAliasAddress(lnk);
DifferentialChecksum(&pip->ip_sum,
&alias_address, &pip->ip_src, 2);
pip->ip_src = alias_address;
}
}
return (ret);
}
static int
IcmpAliasOut2(struct libalias *la, struct ip *pip)
{
struct ip *ip;
struct icmp *ic, *ic2;
struct udphdr *ud;
struct tcphdr *tc;
struct alias_link *lnk;
int ret;
LIBALIAS_LOCK_ASSERT(la);
ic = (struct icmp *)ip_next(pip);
ip = &ic->icmp_ip;
ud = (struct udphdr *)ip_next(ip);
tc = (struct tcphdr *)ip_next(ip);
ic2 = (struct icmp *)ip_next(ip);
if (ip->ip_p == IPPROTO_UDP) {
ret = FindUdpTcpOut(la, ip->ip_dst, ip->ip_src,
ud->uh_dport, ud->uh_sport,
IPPROTO_UDP, 0, &lnk);
if (ret != PKT_ALIAS_OK)
return (ret);
} else if (ip->ip_p == IPPROTO_TCP) {
ret = FindUdpTcpOut(la, ip->ip_dst, ip->ip_src,
tc->th_dport, tc->th_sport,
IPPROTO_TCP, 0, &lnk);
if (ret != PKT_ALIAS_OK)
return (ret);
} else if (ip->ip_p == IPPROTO_ICMP) {
if (ic2->icmp_type == ICMP_ECHO ||
ic2->icmp_type == ICMP_TSTAMP) {
ret = FindIcmpOut(la, ip->ip_dst, ip->ip_src,
ic2->icmp_id, 0, &lnk);
if (ret != PKT_ALIAS_OK)
return (ret);
} else
lnk = NULL;
} else
lnk = NULL;
if (lnk != NULL) {
if (ip->ip_p == IPPROTO_UDP || ip->ip_p == IPPROTO_TCP) {
int accumulate;
struct in_addr alias_address;
u_short alias_port;
alias_address = GetAliasAddress(lnk);
alias_port = GetAliasPort(lnk);
accumulate = twowords(&ip->ip_dst);
accumulate -= twowords(&alias_address);
accumulate += ud->uh_dport;
accumulate -= alias_port;
ADJUST_CHECKSUM(accumulate, ic->icmp_cksum);
if (pip->ip_src.s_addr == ip->ip_dst.s_addr) {
DifferentialChecksum(&pip->ip_sum,
&alias_address, &pip->ip_src, 2);
pip->ip_src = alias_address;
}
ip->ip_dst = alias_address;
ud->uh_dport = alias_port;
} else if (ip->ip_p == IPPROTO_ICMP) {
int accumulate;
struct in_addr alias_address;
u_short alias_id;
alias_address = GetAliasAddress(lnk);
alias_id = GetAliasPort(lnk);
accumulate = twowords(&ip->ip_dst);
accumulate -= twowords(&alias_address);
accumulate += ic2->icmp_id;
accumulate -= alias_id;
ADJUST_CHECKSUM(accumulate, ic->icmp_cksum);
if (pip->ip_src.s_addr == ip->ip_dst.s_addr) {
DifferentialChecksum(&pip->ip_sum,
&alias_address, &pip->ip_src, 2);
pip->ip_src = alias_address;
}
ip->ip_dst = alias_address;
ic2->icmp_id = alias_id;
}
return (PKT_ALIAS_OK);
}
return (PKT_ALIAS_IGNORED);
}
static int
IcmpAliasOut(struct libalias *la, struct ip *pip, int create)
{
int iresult;
struct icmp *ic;
LIBALIAS_LOCK_ASSERT(la);
(void)create;
if (la->packetAliasMode & PKT_ALIAS_PROXY_ONLY)
return (PKT_ALIAS_OK);
ic = (struct icmp *)ip_next(pip);
iresult = PKT_ALIAS_IGNORED;
switch (ic->icmp_type) {
case ICMP_ECHO:
case ICMP_TSTAMP:
if (ic->icmp_code == 0) {
iresult = IcmpAliasOut1(la, pip, create);
}
break;
case ICMP_UNREACH:
case ICMP_SOURCEQUENCH:
case ICMP_TIMXCEED:
case ICMP_PARAMPROB:
iresult = IcmpAliasOut2(la, pip);
break;
case ICMP_ECHOREPLY:
case ICMP_TSTAMPREPLY:
iresult = IcmpAliasOut1(la, pip, create);
}
return (iresult);
}
static int
ProtoAliasIn(struct libalias *la, struct in_addr ip_src,
struct ip *pip, u_char ip_p, u_short *ip_sum)
{
struct alias_link *lnk;
int ret;
LIBALIAS_LOCK_ASSERT(la);
if (la->packetAliasMode & PKT_ALIAS_PROXY_ONLY)
return (PKT_ALIAS_OK);
ret = FindProtoIn(la, ip_src, pip->ip_dst, ip_p, &lnk);
if (ret == PKT_ALIAS_OK) {
struct in_addr original_address;
original_address = GetOriginalAddress(lnk);
DifferentialChecksum(ip_sum,
&original_address, &pip->ip_dst, 2);
pip->ip_dst = original_address;
}
return (ret);
}
static int
ProtoAliasOut(struct libalias *la, struct ip *pip,
struct in_addr ip_dst, u_char ip_p, u_short *ip_sum, int create)
{
struct alias_link *lnk;
int ret;
LIBALIAS_LOCK_ASSERT(la);
if (la->packetAliasMode & PKT_ALIAS_PROXY_ONLY)
return (PKT_ALIAS_OK);
if (!create)
return (PKT_ALIAS_IGNORED);
ret = FindProtoOut(la, pip->ip_src, ip_dst, ip_p, &lnk);
if (ret == PKT_ALIAS_OK) {
struct in_addr alias_address;
alias_address = GetAliasAddress(lnk);
DifferentialChecksum(ip_sum,
&alias_address, &pip->ip_src, 2);
pip->ip_src = alias_address;
}
return (ret);
}
#define MF_ISSET(_pip) (ntohs((_pip)->ip_off) & IP_MF)
#define FRAG_NO_HDR(_pip) (ntohs((_pip)->ip_off) & IP_OFFMASK)
static struct udphdr *
ValidateUdpLength(struct ip *pip)
{
struct udphdr *ud;
size_t dlen;
#ifdef _KERNEL
KASSERT(!FRAG_NO_HDR(pip), ("header-less fragment isn't expected here"));
#endif
dlen = ntohs(pip->ip_len) - (pip->ip_hl << 2);
if (dlen < sizeof(struct udphdr))
return (NULL);
ud = (struct udphdr *)ip_next(pip);
if (!MF_ISSET(pip) && dlen < ntohs(ud->uh_ulen))
return (NULL);
return (ud);
}
static int
UdpAliasIn(struct libalias *la, struct ip *pip)
{
struct udphdr *ud;
struct alias_link *lnk;
int ret;
LIBALIAS_LOCK_ASSERT(la);
ud = ValidateUdpLength(pip);
if (ud == NULL)
return (PKT_ALIAS_IGNORED);
ret = FindUdpTcpIn(la, pip->ip_src, pip->ip_dst,
ud->uh_sport, ud->uh_dport,
IPPROTO_UDP, !(la->packetAliasMode & PKT_ALIAS_PROXY_ONLY), &lnk);
if (ret != PKT_ALIAS_OK)
return (ret);
{
struct in_addr alias_address;
struct in_addr original_address;
struct in_addr proxy_address;
u_short alias_port;
u_short proxy_port;
int accumulate;
int error;
struct alias_data ad = {
.lnk = lnk,
.oaddr = &original_address,
.aaddr = &alias_address,
.aport = &alias_port,
.sport = &ud->uh_sport,
.dport = &ud->uh_dport,
.maxpktsize = 0
};
alias_address = GetAliasAddress(lnk);
original_address = GetOriginalAddress(lnk);
proxy_address = GetProxyAddress(lnk);
alias_port = ud->uh_dport;
ud->uh_dport = GetOriginalPort(lnk);
proxy_port = GetProxyPort(lnk);
error = find_handler(IN, UDP, la, pip, &ad);
if (error < 0)
return (PKT_ALIAS_IGNORED);
if (ud->uh_sum != 0) {
accumulate = alias_port;
accumulate -= ud->uh_dport;
accumulate += twowords(&alias_address);
accumulate -= twowords(&original_address);
if (proxy_port != 0) {
accumulate += ud->uh_sport;
accumulate -= proxy_port;
}
if (proxy_address.s_addr != 0) {
accumulate += twowords(&pip->ip_src);
accumulate -= twowords(&proxy_address);
}
ADJUST_CHECKSUM(accumulate, ud->uh_sum);
}
if (proxy_port != 0)
ud->uh_sport = proxy_port;
if (proxy_address.s_addr != 0) {
DifferentialChecksum(&pip->ip_sum,
&proxy_address, &pip->ip_src, 2);
pip->ip_src = proxy_address;
}
DifferentialChecksum(&pip->ip_sum,
&original_address, &pip->ip_dst, 2);
pip->ip_dst = original_address;
return (PKT_ALIAS_OK);
}
}
static int
UdpAliasOut(struct libalias *la, struct ip *pip, int maxpacketsize, int create)
{
struct udphdr *ud;
struct alias_link *lnk;
struct in_addr dest_address;
struct in_addr proxy_server_address;
u_short dest_port;
u_short proxy_server_port;
int proxy_type, ret;
LIBALIAS_LOCK_ASSERT(la);
ud = ValidateUdpLength(pip);
if (ud == NULL)
return (PKT_ALIAS_IGNORED);
proxy_type = ProxyCheck(la, &proxy_server_address, &proxy_server_port,
pip->ip_src, pip->ip_dst, ud->uh_dport, pip->ip_p);
if (proxy_type == 0 && (la->packetAliasMode & PKT_ALIAS_PROXY_ONLY))
return (PKT_ALIAS_OK);
dest_port = ud->uh_dport;
dest_address = pip->ip_dst;
if (proxy_type != 0) {
int accumulate;
accumulate = twowords(&pip->ip_dst);
accumulate -= twowords(&proxy_server_address);
ADJUST_CHECKSUM(accumulate, pip->ip_sum);
if (ud->uh_sum != 0) {
accumulate = twowords(&pip->ip_dst);
accumulate -= twowords(&proxy_server_address);
accumulate += ud->uh_dport;
accumulate -= proxy_server_port;
ADJUST_CHECKSUM(accumulate, ud->uh_sum);
}
pip->ip_dst = proxy_server_address;
ud->uh_dport = proxy_server_port;
}
ret = FindUdpTcpOut(la, pip->ip_src, pip->ip_dst,
ud->uh_sport, ud->uh_dport,
IPPROTO_UDP, create, &lnk);
if (ret != PKT_ALIAS_OK)
return (ret);
{
u_short alias_port;
struct in_addr alias_address;
struct alias_data ad = {
.lnk = lnk,
.oaddr = NULL,
.aaddr = &alias_address,
.aport = &alias_port,
.sport = &ud->uh_sport,
.dport = &ud->uh_dport,
.maxpktsize = 0
};
if (proxy_type != 0) {
SetProxyPort(lnk, dest_port);
SetProxyAddress(lnk, dest_address);
ProxyModify(la, lnk, pip, maxpacketsize, proxy_type);
ud = (struct udphdr *)ip_next(pip);
}
alias_address = GetAliasAddress(lnk);
alias_port = GetAliasPort(lnk);
find_handler(OUT, UDP, la, pip, &ad);
if (ud->uh_sum != 0) {
int accumulate;
accumulate = ud->uh_sport;
accumulate -= alias_port;
accumulate += twowords(&pip->ip_src);
accumulate -= twowords(&alias_address);
ADJUST_CHECKSUM(accumulate, ud->uh_sum);
}
ud->uh_sport = alias_port;
DifferentialChecksum(&pip->ip_sum,
&alias_address, &pip->ip_src, 2);
pip->ip_src = alias_address;
return (PKT_ALIAS_OK);
}
}
static int
TcpAliasIn(struct libalias *la, struct ip *pip)
{
struct tcphdr *tc;
struct alias_link *lnk;
size_t dlen;
int ret;
LIBALIAS_LOCK_ASSERT(la);
dlen = ntohs(pip->ip_len) - (pip->ip_hl << 2);
if (dlen < sizeof(struct tcphdr))
return (PKT_ALIAS_IGNORED);
tc = (struct tcphdr *)ip_next(pip);
ret = FindUdpTcpIn(la, pip->ip_src, pip->ip_dst,
tc->th_sport, tc->th_dport,
IPPROTO_TCP,
!(la->packetAliasMode & PKT_ALIAS_PROXY_ONLY),
&lnk);
if (ret == PKT_ALIAS_OK) {
struct in_addr alias_address;
struct in_addr original_address;
struct in_addr proxy_address;
u_short alias_port;
u_short proxy_port;
int accumulate;
struct alias_data ad = {
.lnk = lnk,
.oaddr = NULL,
.aaddr = NULL,
.aport = NULL,
.sport = &tc->th_sport,
.dport = &tc->th_dport,
.maxpktsize = 0
};
find_handler(IN, TCP, la, pip, &ad);
alias_address = GetAliasAddress(lnk);
original_address = GetOriginalAddress(lnk);
proxy_address = GetProxyAddress(lnk);
alias_port = tc->th_dport;
tc->th_dport = GetOriginalPort(lnk);
proxy_port = GetProxyPort(lnk);
#if 0
struct alias_data ad = {
.lnk = lnk,
.oaddr = &original_address,
.aaddr = &alias_address,
.aport = &alias_port,
.sport = &ud->uh_sport,
.dport = &ud->uh_dport,
.maxpktsize = 0
};
error = find_handler(la, pip, &ad);
if (error == EHDNOF)
printf("Protocol handler not found\n");
#endif
accumulate = alias_port;
accumulate -= tc->th_dport;
accumulate += twowords(&alias_address);
accumulate -= twowords(&original_address);
if (proxy_port != 0) {
accumulate += tc->th_sport;
tc->th_sport = proxy_port;
accumulate -= tc->th_sport;
accumulate += twowords(&pip->ip_src);
accumulate -= twowords(&proxy_address);
}
if (GetAckModified(lnk) == 1) {
int delta;
tc = (struct tcphdr *)ip_next(pip);
delta = GetDeltaAckIn(tc->th_ack, lnk);
if (delta != 0) {
accumulate += twowords(&tc->th_ack);
tc->th_ack = htonl(ntohl(tc->th_ack) - delta);
accumulate -= twowords(&tc->th_ack);
}
}
ADJUST_CHECKSUM(accumulate, tc->th_sum);
accumulate = twowords(&pip->ip_dst);
pip->ip_dst = original_address;
accumulate -= twowords(&pip->ip_dst);
if (proxy_address.s_addr != 0) {
accumulate += twowords(&pip->ip_src);
pip->ip_src = proxy_address;
accumulate -= twowords(&pip->ip_src);
}
ADJUST_CHECKSUM(accumulate, pip->ip_sum);
tc = (struct tcphdr *)ip_next(pip);
TcpMonitorIn(__tcp_get_flags(tc), lnk);
return (PKT_ALIAS_OK);
}
return (ret);
}
static int
TcpAliasOut(struct libalias *la, struct ip *pip, int maxpacketsize, int create)
{
int proxy_type, ret;
u_short dest_port;
u_short proxy_server_port;
size_t dlen;
struct in_addr dest_address;
struct in_addr proxy_server_address;
struct tcphdr *tc;
struct alias_link *lnk;
LIBALIAS_LOCK_ASSERT(la);
dlen = ntohs(pip->ip_len) - (pip->ip_hl << 2);
if (dlen < sizeof(struct tcphdr))
return (PKT_ALIAS_IGNORED);
tc = (struct tcphdr *)ip_next(pip);
if (create)
proxy_type = ProxyCheck(la, &proxy_server_address,
&proxy_server_port, pip->ip_src, pip->ip_dst,
tc->th_dport, pip->ip_p);
else
proxy_type = 0;
if (proxy_type == 0 && (la->packetAliasMode & PKT_ALIAS_PROXY_ONLY))
return (PKT_ALIAS_OK);
dest_port = tc->th_dport;
dest_address = pip->ip_dst;
if (proxy_type != 0) {
int accumulate;
accumulate = tc->th_dport;
tc->th_dport = proxy_server_port;
accumulate -= tc->th_dport;
accumulate += twowords(&pip->ip_dst);
accumulate -= twowords(&proxy_server_address);
ADJUST_CHECKSUM(accumulate, tc->th_sum);
accumulate = twowords(&pip->ip_dst);
pip->ip_dst = proxy_server_address;
accumulate -= twowords(&pip->ip_dst);
ADJUST_CHECKSUM(accumulate, pip->ip_sum);
}
ret = FindUdpTcpOut(la, pip->ip_src, pip->ip_dst,
tc->th_sport, tc->th_dport,
IPPROTO_TCP, create, &lnk);
if (ret != PKT_ALIAS_OK)
return (ret);
{
u_short alias_port;
struct in_addr alias_address;
int accumulate;
struct alias_data ad = {
.lnk = lnk,
.oaddr = NULL,
.aaddr = &alias_address,
.aport = &alias_port,
.sport = &tc->th_sport,
.dport = &tc->th_dport,
.maxpktsize = maxpacketsize
};
if (proxy_type != 0) {
SetProxyPort(lnk, dest_port);
SetProxyAddress(lnk, dest_address);
ProxyModify(la, lnk, pip, maxpacketsize, proxy_type);
tc = (struct tcphdr *)ip_next(pip);
}
alias_port = GetAliasPort(lnk);
alias_address = GetAliasAddress(lnk);
tc = (struct tcphdr *)ip_next(pip);
TcpMonitorOut(__tcp_get_flags(tc), lnk);
find_handler(OUT, TCP, la, pip, &ad);
accumulate = tc->th_sport;
tc->th_sport = alias_port;
accumulate -= tc->th_sport;
accumulate += twowords(&pip->ip_src);
accumulate -= twowords(&alias_address);
if (GetAckModified(lnk) == 1) {
int delta;
tc = (struct tcphdr *)ip_next(pip);
delta = GetDeltaSeqOut(tc->th_seq, lnk);
if (delta != 0) {
accumulate += twowords(&tc->th_seq);
tc->th_seq = htonl(ntohl(tc->th_seq) + delta);
accumulate -= twowords(&tc->th_seq);
}
}
ADJUST_CHECKSUM(accumulate, tc->th_sum);
accumulate = twowords(&pip->ip_src);
pip->ip_src = alias_address;
accumulate -= twowords(&pip->ip_src);
ADJUST_CHECKSUM(accumulate, pip->ip_sum);
return (PKT_ALIAS_OK);
}
}
static int FragmentIn(struct libalias *la, struct in_addr ip_src,
struct ip *pip, u_short ip_id, u_short *ip_sum);
static int FragmentOut(struct libalias *, struct ip *pip,
u_short *ip_sum);
static int
FragmentIn(struct libalias *la, struct in_addr ip_src, struct ip *pip,
u_short ip_id, u_short *ip_sum)
{
struct alias_link *lnk;
LIBALIAS_LOCK_ASSERT(la);
lnk = FindFragmentIn2(la, ip_src, pip->ip_dst, ip_id);
if (lnk != NULL) {
struct in_addr original_address;
GetFragmentAddr(lnk, &original_address);
DifferentialChecksum(ip_sum,
&original_address, &pip->ip_dst, 2);
pip->ip_dst = original_address;
return (PKT_ALIAS_OK);
}
return (PKT_ALIAS_UNRESOLVED_FRAGMENT);
}
static int
FragmentOut(struct libalias *la, struct ip *pip, u_short *ip_sum)
{
struct in_addr alias_address;
LIBALIAS_LOCK_ASSERT(la);
alias_address = FindAliasAddress(la, pip->ip_src);
DifferentialChecksum(ip_sum,
&alias_address, &pip->ip_src, 2);
pip->ip_src = alias_address;
return (PKT_ALIAS_OK);
}
int
LibAliasSaveFragment(struct libalias *la, void *ptr)
{
int iresult;
struct alias_link *lnk;
struct ip *pip;
LIBALIAS_LOCK(la);
pip = (struct ip *)ptr;
lnk = AddFragmentPtrLink(la, pip->ip_src, pip->ip_id);
iresult = PKT_ALIAS_ERROR;
if (lnk != NULL) {
SetFragmentPtr(lnk, ptr);
iresult = PKT_ALIAS_OK;
}
LIBALIAS_UNLOCK(la);
return (iresult);
}
void *
LibAliasGetFragment(struct libalias *la, void *ptr)
{
struct alias_link *lnk;
void *fptr;
struct ip *pip;
LIBALIAS_LOCK(la);
pip = (struct ip *)ptr;
lnk = FindFragmentPtr(la, pip->ip_src, pip->ip_id);
if (lnk != NULL) {
GetFragmentPtr(lnk, &fptr);
SetFragmentPtr(lnk, NULL);
SetExpire(lnk, 0);
} else
fptr = NULL;
LIBALIAS_UNLOCK(la);
return (fptr);
}
void
LibAliasFragmentIn(struct libalias *la,
void *ptr,
void *ptr_fragment
)
{
struct ip *pip;
struct ip *fpip;
LIBALIAS_LOCK(la);
(void)la;
pip = (struct ip *)ptr;
fpip = (struct ip *)ptr_fragment;
DifferentialChecksum(&fpip->ip_sum,
&pip->ip_dst, &fpip->ip_dst, 2);
fpip->ip_dst = pip->ip_dst;
LIBALIAS_UNLOCK(la);
}
static int
LibAliasOutLocked(struct libalias *la, struct ip *pip,
int maxpacketsize, int create);
static int
LibAliasInLocked(struct libalias *la, struct ip *pip,
int maxpacketsize);
int
LibAliasIn(struct libalias *la, void *ptr, int maxpacketsize)
{
int res;
LIBALIAS_LOCK(la);
res = LibAliasInLocked(la, (struct ip *)ptr, maxpacketsize);
LIBALIAS_UNLOCK(la);
return (res);
}
static int
LibAliasInLocked(struct libalias *la, struct ip *pip, int maxpacketsize)
{
struct in_addr alias_addr;
int iresult;
if (la->packetAliasMode & PKT_ALIAS_REVERSE) {
la->packetAliasMode &= ~PKT_ALIAS_REVERSE;
iresult = LibAliasOutLocked(la, pip, maxpacketsize, 1);
la->packetAliasMode |= PKT_ALIAS_REVERSE;
goto getout;
}
HouseKeeping(la);
alias_addr = pip->ip_dst;
if (ntohs(pip->ip_len) > maxpacketsize
|| (pip->ip_hl << 2) > maxpacketsize) {
iresult = PKT_ALIAS_IGNORED;
goto getout;
}
if (FRAG_NO_HDR(pip)) {
iresult = FragmentIn(la, pip->ip_src, pip, pip->ip_id,
&pip->ip_sum);
goto getout;
}
iresult = PKT_ALIAS_IGNORED;
switch (pip->ip_p) {
case IPPROTO_ICMP:
iresult = IcmpAliasIn(la, pip);
break;
case IPPROTO_UDP:
iresult = UdpAliasIn(la, pip);
break;
case IPPROTO_TCP:
iresult = TcpAliasIn(la, pip);
break;
#ifdef _KERNEL
case IPPROTO_SCTP:
iresult = SctpAlias(la, pip, SN_TO_LOCAL);
break;
#endif
case IPPROTO_GRE: {
int error;
struct alias_data ad = {
.lnk = NULL,
.oaddr = NULL,
.aaddr = NULL,
.aport = NULL,
.sport = NULL,
.dport = NULL,
.maxpktsize = 0
};
error = find_handler(IN, IP, la, pip, &ad);
if (error == 0)
iresult = PKT_ALIAS_OK;
else
iresult = ProtoAliasIn(la, pip->ip_src,
pip, pip->ip_p, &pip->ip_sum);
break;
}
default:
iresult = ProtoAliasIn(la, pip->ip_src, pip,
pip->ip_p, &pip->ip_sum);
break;
}
if (MF_ISSET(pip)) {
struct alias_link *lnk;
lnk = FindFragmentIn1(la, pip->ip_src, alias_addr, pip->ip_id);
if (lnk != NULL) {
iresult = PKT_ALIAS_FOUND_HEADER_FRAGMENT;
SetFragmentAddr(lnk, pip->ip_dst);
} else {
iresult = PKT_ALIAS_ERROR;
}
}
getout:
return (iresult);
}
#define UNREG_ADDR_A_LOWER 0x0a000000
#define UNREG_ADDR_A_UPPER 0x0affffff
#define UNREG_ADDR_B_LOWER 0xac100000
#define UNREG_ADDR_B_UPPER 0xac1fffff
#define UNREG_ADDR_C_LOWER 0xc0a80000
#define UNREG_ADDR_C_UPPER 0xc0a8ffff
#define UNREG_ADDR_CGN_LOWER 0x64400000
#define UNREG_ADDR_CGN_UPPER 0x647fffff
int
LibAliasOut(struct libalias *la, void *ptr, int maxpacketsize)
{
int res;
LIBALIAS_LOCK(la);
res = LibAliasOutLocked(la, (struct ip *)ptr, maxpacketsize, 1);
LIBALIAS_UNLOCK(la);
return (res);
}
int
LibAliasOutTry(struct libalias *la, void *ptr, int maxpacketsize, int create)
{
int res;
LIBALIAS_LOCK(la);
res = LibAliasOutLocked(la, (struct ip *)ptr, maxpacketsize, create);
LIBALIAS_UNLOCK(la);
return (res);
}
static int
LibAliasOutLocked(struct libalias *la,
struct ip *pip,
int maxpacketsize,
int create
)
{
int iresult;
struct in_addr addr_save;
if (la->packetAliasMode & PKT_ALIAS_REVERSE) {
la->packetAliasMode &= ~PKT_ALIAS_REVERSE;
iresult = LibAliasInLocked(la, pip, maxpacketsize);
la->packetAliasMode |= PKT_ALIAS_REVERSE;
goto getout;
}
HouseKeeping(la);
if (ntohs(pip->ip_len) > maxpacketsize
|| (pip->ip_hl << 2) > maxpacketsize) {
iresult = PKT_ALIAS_IGNORED;
goto getout;
}
addr_save = GetDefaultAliasAddress(la);
if (la->packetAliasMode & PKT_ALIAS_UNREGISTERED_ONLY ||
la->packetAliasMode & PKT_ALIAS_UNREGISTERED_CGN) {
u_long addr;
int iclass;
iclass = 0;
addr = ntohl(pip->ip_src.s_addr);
if (addr >= UNREG_ADDR_C_LOWER && addr <= UNREG_ADDR_C_UPPER)
iclass = 3;
else if (addr >= UNREG_ADDR_B_LOWER && addr <= UNREG_ADDR_B_UPPER)
iclass = 2;
else if (addr >= UNREG_ADDR_A_LOWER && addr <= UNREG_ADDR_A_UPPER)
iclass = 1;
else if (addr >= UNREG_ADDR_CGN_LOWER && addr <= UNREG_ADDR_CGN_UPPER &&
la->packetAliasMode & PKT_ALIAS_UNREGISTERED_CGN)
iclass = 4;
if (iclass == 0) {
SetDefaultAliasAddress(la, pip->ip_src);
}
} else if (la->packetAliasMode & PKT_ALIAS_PROXY_ONLY) {
SetDefaultAliasAddress(la, pip->ip_src);
}
if (FRAG_NO_HDR(pip)) {
iresult = FragmentOut(la, pip, &pip->ip_sum);
goto getout_restore;
}
iresult = PKT_ALIAS_IGNORED;
switch (pip->ip_p) {
case IPPROTO_ICMP:
iresult = IcmpAliasOut(la, pip, create);
break;
case IPPROTO_UDP:
iresult = UdpAliasOut(la, pip, maxpacketsize, create);
break;
case IPPROTO_TCP:
iresult = TcpAliasOut(la, pip, maxpacketsize, create);
break;
#ifdef _KERNEL
case IPPROTO_SCTP:
iresult = SctpAlias(la, pip, SN_TO_GLOBAL);
break;
#endif
case IPPROTO_GRE: {
int error;
struct alias_data ad = {
.lnk = NULL,
.oaddr = NULL,
.aaddr = NULL,
.aport = NULL,
.sport = NULL,
.dport = NULL,
.maxpktsize = 0
};
error = find_handler(OUT, IP, la, pip, &ad);
if (error == 0)
iresult = PKT_ALIAS_OK;
else
iresult = ProtoAliasOut(la, pip,
pip->ip_dst, pip->ip_p, &pip->ip_sum, create);
break;
}
default:
iresult = ProtoAliasOut(la, pip,
pip->ip_dst, pip->ip_p, &pip->ip_sum, create);
break;
}
getout_restore:
SetDefaultAliasAddress(la, addr_save);
getout:
return (iresult);
}
int
LibAliasUnaliasOut(struct libalias *la,
void *ptr,
int maxpacketsize
)
{
struct ip *pip;
struct icmp *ic;
struct udphdr *ud;
struct tcphdr *tc;
struct alias_link *lnk;
int iresult = PKT_ALIAS_IGNORED;
LIBALIAS_LOCK(la);
pip = (struct ip *)ptr;
if (ntohs(pip->ip_len) > maxpacketsize
|| (pip->ip_hl << 2) > maxpacketsize)
goto getout;
ud = (struct udphdr *)ip_next(pip);
tc = (struct tcphdr *)ip_next(pip);
ic = (struct icmp *)ip_next(pip);
if (pip->ip_p == IPPROTO_UDP) {
iresult = FindUdpTcpIn(la, pip->ip_dst, pip->ip_src,
ud->uh_dport, ud->uh_sport,
IPPROTO_UDP, 0, &lnk);
if (iresult != PKT_ALIAS_OK)
goto getout;
} else if (pip->ip_p == IPPROTO_TCP) {
iresult = FindUdpTcpIn(la, pip->ip_dst, pip->ip_src,
tc->th_dport, tc->th_sport,
IPPROTO_TCP, 0, &lnk);
if (iresult != PKT_ALIAS_OK)
goto getout;
} else if (pip->ip_p == IPPROTO_ICMP) {
iresult = FindIcmpIn(la, pip->ip_dst, pip->ip_src,
ic->icmp_id, 0, &lnk);
if (iresult != PKT_ALIAS_OK)
goto getout;
} else
lnk = NULL;
if (lnk != NULL) {
if (pip->ip_p == IPPROTO_UDP || pip->ip_p == IPPROTO_TCP) {
int accumulate;
struct in_addr original_address;
u_short original_port;
original_address = GetOriginalAddress(lnk);
original_port = GetOriginalPort(lnk);
accumulate = twowords(&pip->ip_src);
accumulate -= twowords(&original_address);
if (pip->ip_p == IPPROTO_UDP) {
accumulate += ud->uh_sport;
accumulate -= original_port;
ADJUST_CHECKSUM(accumulate, ud->uh_sum);
} else {
accumulate += tc->th_sport;
accumulate -= original_port;
ADJUST_CHECKSUM(accumulate, tc->th_sum);
}
DifferentialChecksum(&pip->ip_sum,
&original_address, &pip->ip_src, 2);
pip->ip_src = original_address;
if (pip->ip_p == IPPROTO_UDP)
ud->uh_sport = original_port;
else
tc->th_sport = original_port;
iresult = PKT_ALIAS_OK;
} else if (pip->ip_p == IPPROTO_ICMP) {
int accumulate;
struct in_addr original_address;
u_short original_id;
original_address = GetOriginalAddress(lnk);
original_id = GetOriginalPort(lnk);
accumulate = twowords(&pip->ip_src);
accumulate -= twowords(&original_address);
accumulate += ic->icmp_id;
accumulate -= original_id;
ADJUST_CHECKSUM(accumulate, ic->icmp_cksum);
DifferentialChecksum(&pip->ip_sum,
&original_address, &pip->ip_src, 2);
pip->ip_src = original_address;
ic->icmp_id = original_id;
iresult = PKT_ALIAS_OK;
}
}
getout:
LIBALIAS_UNLOCK(la);
return (iresult);
}
#ifndef _KERNEL
int
LibAliasRefreshModules(void)
{
char buf[256], conf[] = "/etc/libalias.conf";
FILE *fd;
int i, len;
fd = fopen(conf, "r");
if (fd == NULL)
err(1, "fopen(%s)", conf);
LibAliasUnLoadAllModule();
for (;;) {
fgets(buf, 256, fd);
if (feof(fd))
break;
len = strlen(buf);
if (len > 1) {
for (i = 0; i < len; i++)
if (!isspace(buf[i]))
break;
if (buf[i] == '#')
continue;
buf[len - 1] = '\0';
LibAliasLoadModule(buf);
}
}
fclose(fd);
return (0);
}
int
LibAliasLoadModule(char *path)
{
struct dll *t;
void *handle;
struct proto_handler *m;
const char *error;
moduledata_t *p;
handle = dlopen (path, RTLD_LAZY);
if (!handle) {
fprintf(stderr, "%s\n", dlerror());
return (EINVAL);
}
p = dlsym(handle, "alias_mod");
if ((error = dlerror()) != NULL) {
fprintf(stderr, "%s\n", dlerror());
return (EINVAL);
}
t = malloc(sizeof(struct dll));
if (t == NULL)
return (ENOMEM);
strncpy(t->name, p->name, DLL_LEN);
t->handle = handle;
if (attach_dll(t) == EEXIST) {
free(t);
fprintf(stderr, "dll conflict\n");
return (EEXIST);
}
m = dlsym(t->handle, "handlers");
if ((error = dlerror()) != NULL) {
fprintf(stderr, "%s\n", error);
return (EINVAL);
}
LibAliasAttachHandlers(m);
return (0);
}
int
LibAliasUnLoadAllModule(void)
{
struct dll *t;
struct proto_handler *p;
while ((p = first_handler()) != NULL) {
LibAliasDetachHandlers(p);
}
while ((t = walk_dll_chain()) != NULL) {
dlclose(t->handle);
free(t);
}
return (1);
}
#endif
#ifdef _KERNEL
struct mbuf *
m_megapullup(struct mbuf *m, int len)
{
struct mbuf *mcl;
if (len > m->m_pkthdr.len)
goto bad;
if (m->m_next == NULL && M_WRITABLE(m))
return (m);
if (len <= MJUMPAGESIZE)
mcl = m_get2(len, M_NOWAIT, MT_DATA, M_PKTHDR);
else if (len <= MJUM9BYTES)
mcl = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, MJUM9BYTES);
else if (len <= MJUM16BYTES)
mcl = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, MJUM16BYTES);
else
goto bad;
if (mcl == NULL)
goto bad;
m_align(mcl, len);
m_move_pkthdr(mcl, m);
m_copydata(m, 0, len, mtod(mcl, caddr_t));
mcl->m_len = mcl->m_pkthdr.len = len;
m_freem(m);
return (mcl);
bad:
m_freem(m);
return (NULL);
}
#endif