#include <sys/cdefs.h>
#ifdef _KERNEL
#include <sys/param.h>
#include <sys/limits.h>
#include <sys/kernel.h>
#include <sys/module.h>
#else
#include <errno.h>
#include <limits.h>
#include <sys/types.h>
#include <stdio.h>
#endif
#include <netinet/tcp.h>
#ifdef _KERNEL
#include <netinet/libalias/alias.h>
#include <netinet/libalias/alias_local.h>
#include <netinet/libalias/alias_mod.h>
#else
#include "alias.h"
#include "alias_local.h"
#include "alias_mod.h"
#endif
#define PPTP_CONTROL_PORT_NUMBER 1723
static void
AliasHandlePptpOut(struct libalias *, struct ip *, struct alias_link *);
static void
AliasHandlePptpIn(struct libalias *, struct ip *, struct alias_link *);
static int
AliasHandlePptpGreOut(struct libalias *, struct ip *);
static int
AliasHandlePptpGreIn(struct libalias *, struct ip *);
static int
fingerprint(struct libalias *la, struct alias_data *ah)
{
if (ah->dport == NULL || ah->sport == NULL || ah->lnk == NULL)
return (-1);
if (ntohs(*ah->dport) == PPTP_CONTROL_PORT_NUMBER
|| ntohs(*ah->sport) == PPTP_CONTROL_PORT_NUMBER)
return (0);
return (-1);
}
static int
fingerprintgre(struct libalias *la, struct alias_data *ah)
{
return (0);
}
static int
protohandlerin(struct libalias *la, struct ip *pip, struct alias_data *ah)
{
AliasHandlePptpIn(la, pip, ah->lnk);
return (0);
}
static int
protohandlerout(struct libalias *la, struct ip *pip, struct alias_data *ah)
{
AliasHandlePptpOut(la, pip, ah->lnk);
return (0);
}
static int
protohandlergrein(struct libalias *la, struct ip *pip, struct alias_data *ah)
{
if (la->packetAliasMode & PKT_ALIAS_PROXY_ONLY ||
AliasHandlePptpGreIn(la, pip) == 0)
return (0);
return (-1);
}
static int
protohandlergreout(struct libalias *la, struct ip *pip, struct alias_data *ah)
{
if (AliasHandlePptpGreOut(la, pip) == 0)
return (0);
return (-1);
}
struct proto_handler handlers[] = {
{
.pri = 200,
.dir = IN,
.proto = TCP,
.fingerprint = &fingerprint,
.protohandler = &protohandlerin
},
{
.pri = 210,
.dir = OUT,
.proto = TCP,
.fingerprint = &fingerprint,
.protohandler = &protohandlerout
},
{
.pri = INT_MAX,
.dir = IN,
.proto = IP,
.fingerprint = &fingerprintgre,
.protohandler = &protohandlergrein
},
{
.pri = INT_MAX,
.dir = OUT,
.proto = IP,
.fingerprint = &fingerprintgre,
.protohandler = &protohandlergreout
},
{ EOH }
};
static int
mod_handler(module_t mod, int type, void *data)
{
int error;
switch (type) {
case MOD_LOAD:
error = 0;
LibAliasAttachHandlers(handlers);
break;
case MOD_UNLOAD:
error = 0;
LibAliasDetachHandlers(handlers);
break;
default:
error = EINVAL;
}
return (error);
}
#ifdef _KERNEL
static
#endif
moduledata_t alias_mod = {
"alias_pptp", mod_handler, NULL
};
#ifdef _KERNEL
DECLARE_MODULE(alias_pptp, alias_mod, SI_SUB_DRIVERS, SI_ORDER_SECOND);
MODULE_VERSION(alias_pptp, 1);
MODULE_DEPEND(alias_pptp, libalias, 1, 1, 1);
#endif
struct grehdr {
u_int16_t gh_flags;
u_int16_t gh_protocol;
u_int16_t gh_length;
u_int16_t gh_call_id;
u_int32_t gh_seq_no;
u_int32_t gh_ack_no;
};
typedef struct grehdr GreHdr;
#define PPTP_GRE_PROTO 0x880b
#define PPTP_INIT_VALUE ((0x2001 << 16) | PPTP_GRE_PROTO)
#define PPTP_INIT_MASK 0xef7fffff
#define PPTP_MAGIC 0x1a2b3c4d
#define PPTP_CTRL_MSG_TYPE 1
enum {
PPTP_StartCtrlConnRequest = 1,
PPTP_StartCtrlConnReply = 2,
PPTP_StopCtrlConnRequest = 3,
PPTP_StopCtrlConnReply = 4,
PPTP_EchoRequest = 5,
PPTP_EchoReply = 6,
PPTP_OutCallRequest = 7,
PPTP_OutCallReply = 8,
PPTP_InCallRequest = 9,
PPTP_InCallReply = 10,
PPTP_InCallConn = 11,
PPTP_CallClearRequest = 12,
PPTP_CallDiscNotify = 13,
PPTP_WanErrorNotify = 14,
PPTP_SetLinkInfo = 15
};
struct pptpMsgHead {
u_int16_t length;
u_int16_t msgType;
u_int32_t magic;
u_int16_t type;
u_int16_t resv0;
};
typedef struct pptpMsgHead *PptpMsgHead;
struct pptpCodes {
u_int8_t resCode;
u_int8_t errCode;
};
typedef struct pptpCodes *PptpCode;
struct pptpCallIds {
u_int16_t cid1;
u_int16_t cid2;
};
typedef struct pptpCallIds *PptpCallId;
static PptpCallId AliasVerifyPptp(struct ip *, u_int16_t *);
static void
AliasHandlePptpOut(struct libalias *la,
struct ip *pip,
struct alias_link *lnk)
{
struct alias_link *pptp_lnk;
PptpCallId cptr;
PptpCode codes;
u_int16_t ctl_type;
struct tcphdr *tc;
if ((cptr = AliasVerifyPptp(pip, &ctl_type)) == NULL)
return;
switch (ctl_type) {
case PPTP_OutCallRequest:
case PPTP_OutCallReply:
case PPTP_InCallRequest:
case PPTP_InCallReply:
pptp_lnk = AddPptp(la, GetOriginalAddress(lnk), GetDestAddress(lnk),
GetAliasAddress(lnk), cptr->cid1);
break;
case PPTP_CallClearRequest:
case PPTP_CallDiscNotify:
pptp_lnk = FindPptpOutByCallId(la, GetOriginalAddress(lnk),
GetDestAddress(lnk), cptr->cid1);
break;
default:
return;
}
if (pptp_lnk != NULL) {
int accumulate = cptr->cid1;
cptr->cid1 = GetAliasPort(pptp_lnk);
tc = (struct tcphdr *)ip_next(pip);
accumulate -= cptr->cid1;
ADJUST_CHECKSUM(accumulate, tc->th_sum);
switch (ctl_type) {
case PPTP_OutCallReply:
case PPTP_InCallReply:
codes = (PptpCode)(cptr + 1);
if (codes->resCode == 1)
SetDestCallId(pptp_lnk, cptr->cid2);
else
SetExpire(pptp_lnk, 0);
break;
case PPTP_CallDiscNotify:
SetExpire(pptp_lnk, 0);
break;
}
}
}
static void
AliasHandlePptpIn(struct libalias *la,
struct ip *pip,
struct alias_link *lnk)
{
struct alias_link *pptp_lnk;
PptpCallId cptr;
u_int16_t *pcall_id;
u_int16_t ctl_type;
struct tcphdr *tc;
if ((cptr = AliasVerifyPptp(pip, &ctl_type)) == NULL)
return;
switch (ctl_type) {
case PPTP_InCallConn:
case PPTP_WanErrorNotify:
case PPTP_SetLinkInfo:
pcall_id = &cptr->cid1;
break;
case PPTP_OutCallReply:
case PPTP_InCallReply:
pcall_id = &cptr->cid2;
break;
case PPTP_CallDiscNotify:
pptp_lnk = FindPptpInByCallId(la, GetDestAddress(lnk),
GetAliasAddress(lnk), cptr->cid1);
if (pptp_lnk != NULL)
SetExpire(pptp_lnk, 0);
return;
default:
return;
}
pptp_lnk = FindPptpInByPeerCallId(la, GetDestAddress(lnk),
GetAliasAddress(lnk), *pcall_id);
if (pptp_lnk != NULL) {
int accumulate = *pcall_id;
*pcall_id = GetOriginalPort(pptp_lnk);
tc = (struct tcphdr *)ip_next(pip);
accumulate -= *pcall_id;
ADJUST_CHECKSUM(accumulate, tc->th_sum);
if (ctl_type == PPTP_OutCallReply ||
ctl_type == PPTP_InCallReply) {
PptpCode codes = (PptpCode)(cptr + 1);
if (codes->resCode == 1)
SetDestCallId(pptp_lnk, cptr->cid1);
else
SetExpire(pptp_lnk, 0);
}
}
}
static PptpCallId
AliasVerifyPptp(struct ip *pip, u_int16_t * ptype)
{
int hlen, tlen, dlen;
PptpMsgHead hptr;
struct tcphdr *tc;
tc = (struct tcphdr *)ip_next(pip);
hlen = (pip->ip_hl + tc->th_off) << 2;
tlen = ntohs(pip->ip_len);
dlen = tlen - hlen;
if (dlen < (int)(sizeof(struct pptpMsgHead) + sizeof(struct pptpCallIds)))
return (NULL);
hptr = (PptpMsgHead)tcp_next(tc);
*ptype = ntohs(hptr->type);
if ((ntohs(hptr->msgType) != PPTP_CTRL_MSG_TYPE) ||
(ntohl(hptr->magic) != PPTP_MAGIC))
return (NULL);
if ((*ptype == PPTP_OutCallReply || *ptype == PPTP_InCallReply) &&
(dlen < (int)(sizeof(struct pptpMsgHead) + sizeof(struct pptpCallIds) +
sizeof(struct pptpCodes))))
return (NULL);
else
return ((PptpCallId)(hptr + 1));
}
static int
AliasHandlePptpGreOut(struct libalias *la, struct ip *pip)
{
GreHdr *gr;
struct alias_link *lnk;
gr = (GreHdr *)ip_next(pip);
if ((ntohl(*((u_int32_t *)gr)) & PPTP_INIT_MASK) != PPTP_INIT_VALUE)
return (-1);
lnk = FindPptpOutByPeerCallId(la, pip->ip_src, pip->ip_dst, gr->gh_call_id);
if (lnk != NULL) {
struct in_addr alias_addr = GetAliasAddress(lnk);
DifferentialChecksum(&pip->ip_sum,
&alias_addr, &pip->ip_src, 2);
pip->ip_src = alias_addr;
}
return (0);
}
static int
AliasHandlePptpGreIn(struct libalias *la, struct ip *pip)
{
GreHdr *gr;
struct alias_link *lnk;
gr = (GreHdr *)ip_next(pip);
if ((ntohl(*((u_int32_t *)gr)) & PPTP_INIT_MASK) != PPTP_INIT_VALUE)
return (-1);
lnk = FindPptpInByPeerCallId(la, pip->ip_src, pip->ip_dst, gr->gh_call_id);
if (lnk != NULL) {
struct in_addr src_addr = GetOriginalAddress(lnk);
gr->gh_call_id = GetOriginalPort(lnk);
DifferentialChecksum(&pip->ip_sum,
&src_addr, &pip->ip_dst, 2);
pip->ip_dst = src_addr;
}
return (0);
}