/*
* ecp.c
*
* Written by Archie Cobbs <archie@freebsd.org>
* Copyright (c) 1998-1999 Whistle Communications, Inc. All rights reserved.
* See ``COPYRIGHT.whistle''
*/
#include "ppp.h"
#include "bund.h"
#include "ecp.h"
#include "fsm.h"
/*
* DEFINITIONS
*/
#define ECP_MAXFAILURE 7
#define ECP_KNOWN_CODES ( (1 << CODE_CONFIGREQ) \
| (1 << CODE_CONFIGACK) \
| (1 << CODE_CONFIGNAK) \
| (1 << CODE_CONFIGREJ) \
| (1 << CODE_TERMREQ) \
| (1 << CODE_TERMACK) \
| (1 << CODE_CODEREJ) \
| (1 << CODE_RESETREQ) \
| (1 << CODE_RESETACK) )
#define ECP_OVERHEAD 2
#define ECP_PEER_REJECTED(p,x) ((p)->peer_reject & (1<<(x)))
#define ECP_SELF_REJECTED(p,x) ((p)->self_reject & (1<<(x)))
#define ECP_PEER_REJ(p,x) do{(p)->peer_reject |= (1<<(x));}while(0)
#define ECP_SELF_REJ(p,x) do{(p)->self_reject |= (1<<(x));}while(0)
/* Set menu options */
enum
{
SET_KEY,
SET_ACCEPT,
SET_DENY,
SET_ENABLE,
SET_DISABLE,
SET_YES,
SET_NO,
};
/*
* INTERNAL FUNCTIONS
*/
static void EcpConfigure(Fsm fp);
static u_char *EcpBuildConfigReq(Fsm fp, u_char *cp);
static void EcpDecodeConfig(Fsm fp, FsmOption a, int num, int mode);
static void EcpLayerUp(Fsm fp);
static void EcpLayerDown(Fsm fp);
static void EcpFailure(Fsm f, enum fsmfail reason);
static void EcpRecvResetReq(Fsm fp, int id, Mbuf bp);
static void EcpRecvResetAck(Fsm fp, int id, Mbuf bp);
static int EcpSetCommand(int ac, char *av[], void *arg);
static EncType EcpFindType(int type, int *indexp);
static const char *EcpTypeName(int type);
/*
* GLOBAL VARIABLES
*/
const struct cmdtab EcpSetCmds[] =
{
{ "key string", "Set encryption key",
EcpSetCommand, NULL, (void *) SET_KEY },
{ "accept [opt ...]", "Accept option",
EcpSetCommand, NULL, (void *) SET_ACCEPT },
{ "deny [opt ...]", "Deny option",
EcpSetCommand, NULL, (void *) SET_DENY },
{ "enable [opt ...]", "Enable option",
EcpSetCommand, NULL, (void *) SET_ENABLE },
{ "disable [opt ...]", "Disable option",
EcpSetCommand, NULL, (void *) SET_DISABLE },
{ "yes [opt ...]", "Enable and accept option",
EcpSetCommand, NULL, (void *) SET_YES },
{ "no [opt ...]", "Disable and deny option",
EcpSetCommand, NULL, (void *) SET_NO },
{ NULL },
};
/*
* INTERNAL VARIABLES
*/
/* These should be listed in order of preference */
static const EncType gEncTypes[] =
{
#ifdef ENCRYPTION_DES
&gDesEncType,
#endif
};
#define ECP_NUM_PROTOS (sizeof(gEncTypes) / sizeof(*gEncTypes))
/* Corresponding option list */
static const struct confinfo *gConfList;
/* Initializer for struct fsm fields */
static const struct fsmtype gEcpFsmType =
{
"ECP",
PROTO_ECP,
ECP_KNOWN_CODES,
LG_ECP, LG_ECP2,
FALSE,
NULL,
EcpLayerUp,
EcpLayerDown,
NULL,
NULL,
EcpBuildConfigReq,
EcpDecodeConfig,
EcpConfigure,
NULL,
NULL,
NULL,
NULL,
NULL,
EcpFailure,
EcpRecvResetReq,
EcpRecvResetAck,
};
/* Names for different types of encryption */
static const struct ecpname
{
u_char type;
const char *name;
}
gEcpTypeNames[] =
{
{ ECP_TY_OUI, "OUI" },
{ ECP_TY_DES, "DES" },
{ 0, NULL },
};
/*
* EcpInit()
*/
void
EcpInit(void)
{
EcpState ecp = &bund->ecp;
/* Init ECP state for this bundle */
memset(ecp, 0, sizeof(*ecp));
FsmInit(&ecp->fsm, &gEcpFsmType);
ecp->fsm.conf.maxfailure = ECP_MAXFAILURE;
/* Construct options list if we haven't done so already */
if (gConfList == NULL)
{
struct confinfo *ci;
int k;
ci = Malloc(MB_CRYPT, (ECP_NUM_PROTOS + 1) * sizeof(*ci));
for (k = 0; k < ECP_NUM_PROTOS; k++)
{
ci[k].option = k;
ci[k].peered = TRUE;
ci[k].name = gEncTypes[k]->name;
}
ci[k].name = NULL;
gConfList = (const struct confinfo *) ci;
}
}
/*
* EcpConfigure()
*/
static void
EcpConfigure(Fsm fp)
{
EcpState const ecp = &bund->ecp;
int k;
/* Reset state */
memset(&ecp->stat, 0, sizeof(ecp->stat));
for (k = 0; k < ECP_NUM_PROTOS; k++)
{
EncType const et = gEncTypes[k];
if (et->Configure)
(*et->Configure)();
}
ecp->xmit = NULL;
ecp->recv = NULL;
ecp->self_reject = 0;
ecp->peer_reject = 0;
}
/*
* EcpDataOutput()
*
* Encrypt a frame. Consumes the original packet.
*/
Mbuf
EcpDataOutput(Mbuf plain, int *protop)
{
EcpState const ecp = &bund->ecp;
Mbuf cypher, header;
/* Prepend protocol field */
assert(ecp->fsm.state == ST_OPENED);
header = mballoc(MB_CRYPT, 2);
header->cnt = 0;
if (!PROT_COMPRESSIBLE(*protop))
MBDATA(header)[header->cnt++] = *protop >> 8;
MBDATA(header)[header->cnt++] = *protop & 0xff;
header->next = plain;
plain = header;
/* Encrypt packet */
LogDumpBp(LG_ECP2, plain, "[%s] xmit plain proto %s",
bund->name, ProtoName(*protop));
if (!ecp->xmit)
{
Log(LG_ERR, ("%s: no encryption for xmit", Pref(&ecp->fsm)));
PFREE(plain);
return(NULL);
}
cypher = (*ecp->xmit->Encrypt)(plain);
LogDumpBp(LG_ECP2, cypher, "[%s] xmit cypher", bund->name);
/* Return result, with new protocol number */
ecp->stat.outPackets++;
*protop = PROTO_CRYPT;
return(cypher);
}
/*
* EcpDataInput()
*
* Decrypt incoming packet. If packet got garbled, return NULL.
* In any case, we consume the packet passed to us.
*/
Mbuf
EcpDataInput(Mbuf cypher, int *protop)
{
EcpState const ecp = &bund->ecp;
u_int16_t proto;
Mbuf plain;
assert(ecp->fsm.state == ST_OPENED);
LogDumpBp(LG_ECP2, cypher, "[%s] recv cypher", bund->name);
/* Decrypt packet */
if (!ecp->recv)
{
Log(LG_ERR, ("%s: no encryption for recv", Pref(&ecp->fsm)));
PFREE(cypher);
return(NULL);
}
LogDumpBp(LG_ECP2, cypher, "[%s] recv cypher", bund->name);
plain = (*ecp->recv->Decrypt)(cypher);
/* Encrypted ok? */
if (plain == NULL)
{
Log(LG_ECP, ("%s: decryption failed", Pref(&ecp->fsm)));
ecp->stat.inPacketDrops++;
return(NULL);
}
LogDumpBp(LG_ECP2, plain, "[%s] recv plain", bund->name);
ecp->stat.inPackets++;
/* Extract protocol number */
for (proto = 0; !(proto & 1) && plain->cnt > 0; plain->offset++, plain->cnt--)
proto = (proto << 8) + *MBDATA(plain);
*protop = proto;
/* Done */
return(plain);
}
/*
* EcpUp()
*/
void
EcpUp(void)
{
FsmUp(&bund->ecp.fsm);
}
/*
* EcpDown()
*/
void
EcpDown(void)
{
FsmDown(&bund->ecp.fsm);
}
/*
* EcpOpen()
*/
void
EcpOpen(void)
{
FsmOpen(&bund->ecp.fsm);
}
/*
* EcpClose()
*/
void
EcpClose(void)
{
FsmClose(&bund->ecp.fsm);
}
/*
* EcpFailure()
*
* This is fatal to the entire link if encryption is required.
*/
static void
EcpFailure(Fsm f, enum fsmfail reason)
{
if (Enabled(&bund->conf.options, BUND_CONF_CRYPT_REQD))
FsmFailure(&bund->ipcp.fsm, FAIL_CANT_ENCRYPT);
}
/*
* EcpStat()
*/
int
EcpStat(int ac, char *av[], void *arg)
{
EcpState const ecp = &bund->ecp;
printf("%s [%s]\n", Pref(&ecp->fsm), FsmStateName(ecp->fsm.state));
printf("Enabled protocols:\n");
OptStat(&ecp->options, gConfList);
printf("Incoming encryption:\n");
printf("\tProtocol : %5s\n", ecp->xmit ? ecp->xmit->name : "none");
printf("\tRecv pkts : %5d\n", ecp->stat.inPackets);
printf("\tRecv drops: %5d\n", ecp->stat.inPacketDrops);
printf("Outgoing encryption:\n");
printf("\tProtocol : %5s\n", ecp->recv ? ecp->recv->name : "none");
printf("\tXmit pkts : %5d\n", ecp->stat.outPackets);
return(0);
}
/*
* EcpSendResetReq()
*/
void
EcpSendResetReq(Fsm fp)
{
EcpState const ecp = &bund->ecp;
EncType const et = ecp->recv;
Mbuf bp = NULL;
assert(et);
if (et->SendResetReq)
bp = (*et->SendResetReq)();
Log(LG_ECP, ("%s: SendResetReq", Pref(fp)));
FsmOutputMbuf(fp, CODE_RESETREQ, fp->reqid, bp);
}
/*
* EcpRecvResetReq()
*/
void
EcpRecvResetReq(Fsm fp, int id, Mbuf bp)
{
EcpState const ecp = &bund->ecp;
EncType const et = ecp->xmit;
bp = (et && et->RecvResetReq) ? (*et->RecvResetReq)(id, bp) : NULL;
Log(fp->log, ("%s: SendResetAck", Pref(fp)));
FsmOutputMbuf(fp, CODE_RESETACK, fp->reqid, bp);
}
/*
* EcpRecvResetAck()
*/
static void
EcpRecvResetAck(Fsm fp, int id, Mbuf bp)
{
EcpState const ecp = &bund->ecp;
EncType const et = ecp->recv;
if (et && et->RecvResetAck)
(*et->RecvResetAck)(id, bp);
}
/*
* EcpInput()
*/
void
EcpInput(Mbuf bp, int linkNum)
{
FsmInput(&bund->ecp.fsm, bp, linkNum);
}
/*
* EcpBuildConfigReq()
*/
static u_char *
EcpBuildConfigReq(Fsm fp, u_char *cp)
{
EcpState const ecp = &bund->ecp;
int type;
/* Put in all options that peer hasn't rejected */
for (ecp->recv = NULL, type = 0; type < ECP_NUM_PROTOS; type++)
{
EncType const et = gEncTypes[type];
if (Enabled(&ecp->options, type) && !ECP_PEER_REJECTED(ecp, type))
{
cp = (*et->BuildConfigReq)(cp);
if (!ecp->recv)
ecp->recv = et;
}
}
return(cp);
}
/*
* EcpLayerUp()
*
* Called when ECP has reached the OPENED state
*/
static void
EcpLayerUp(Fsm fp)
{
EcpState const ecp = &bund->ecp;
Log(LG_ECP, (" Encrypt = %s, Decrypt = %s",
ecp->xmit ? ecp->xmit->name : "none",
ecp->recv ? ecp->recv->name : "none"));
/* Initialize */
if (ecp->xmit && ecp->xmit->Init)
(*ecp->xmit->Init)(TRUE);
if (ecp->recv && ecp->recv->Init)
(*ecp->recv->Init)(FALSE);
/* Update interface MTU */
BundUpdateParams();
}
/*
* EcpLayerDown()
*
* Called when ECP leaves the OPENED state
*/
static void
EcpLayerDown(Fsm fp)
{
EcpState const ecp = &bund->ecp;
if (ecp->xmit && ecp->xmit->Cleanup)
(ecp->xmit->Cleanup)(TRUE);
if (ecp->recv && ecp->recv->Cleanup)
(ecp->recv->Cleanup)(FALSE);
}
/*
* EcpDecodeConfig()
*/
static void
EcpDecodeConfig(Fsm fp, FsmOption list, int num, int mode)
{
EcpState const ecp = &bund->ecp;
u_int ackSizeSave, rejSizeSave;
int k, rej;
/* Decode each config option */
for (k = 0; k < num; k++)
{
FsmOption const opt = &list[k];
int index;
EncType et;
Log(LG_ECP, (" %s", EcpTypeName(opt->type)));
if ((et = EcpFindType(opt->type, &index)) == NULL)
{
if (mode == MODE_REQ)
{
Log(LG_ECP, (" Not supported"));
FsmRej(fp, opt);
}
continue;
}
switch (mode)
{
case MODE_REQ:
ackSizeSave = gAckSize;
rejSizeSave = gRejSize;
rej = (!Acceptable(&ecp->options, index)
|| ECP_SELF_REJECTED(ecp, index)
|| (ecp->xmit && ecp->xmit != et));
if (rej)
{
(*et->DecodeConfig)(fp, opt, MODE_NOP);
FsmRej(fp, opt);
break;
}
(*et->DecodeConfig)(fp, opt, mode);
if (gRejSize != rejSizeSave) /* we rejected it */
{
ECP_SELF_REJ(ecp, index);
break;
}
if (gAckSize != ackSizeSave) /* we accepted it */
ecp->xmit = et;
break;
case MODE_NAK:
(*et->DecodeConfig)(fp, opt, mode);
break;
case MODE_REJ:
ECP_PEER_REJ(ecp, index);
break;
case MODE_NOP:
(*et->DecodeConfig)(fp, opt, mode);
break;
}
}
}
/*
* EcpSubtractBloat()
*
* Given that "size" is our MTU, return the maximum length frame
* we can encrypt without the result being longer than "size".
*/
int
EcpSubtractBloat(int size)
{
EcpState const ecp = &bund->ecp;
/* Account for ECP's protocol number overhead */
if (OPEN_STATE(ecp->fsm.state))
size -= ECP_OVERHEAD;
/* Check transmit encryption */
if (OPEN_STATE(ecp->fsm.state) && ecp->xmit && ecp->xmit->SubtractBloat)
size = (*ecp->xmit->SubtractBloat)(size);
/* Done */
return(size);
}
/*
* EcpSetCommand()
*/
static int
EcpSetCommand(int ac, char *av[], void *arg)
{
EcpState const ecp = &bund->ecp;
if (ac == 0)
return(-1);
switch ((intptr_t)arg)
{
case SET_KEY:
if (ac != 1)
return(-1);
snprintf(ecp->key, sizeof(ecp->key), "%s", av[0]);
break;
case SET_ACCEPT:
AcceptCommand(ac, av, &ecp->options, gConfList);
break;
case SET_DENY:
DenyCommand(ac, av, &ecp->options, gConfList);
break;
case SET_ENABLE:
EnableCommand(ac, av, &ecp->options, gConfList);
break;
case SET_DISABLE:
DisableCommand(ac, av, &ecp->options, gConfList);
break;
case SET_YES:
YesCommand(ac, av, &ecp->options, gConfList);
break;
case SET_NO:
NoCommand(ac, av, &ecp->options, gConfList);
break;
default:
assert(0);
}
return(0);
}
/*
* EcpFindType()
*/
static EncType
EcpFindType(int type, int *indexp)
{
int k;
for (k = 0; k < ECP_NUM_PROTOS; k++)
if (gEncTypes[k]->type == type)
{
if (indexp)
*indexp = k;
return(gEncTypes[k]);
}
return(NULL);
}
/*
* EcpTypeName()
*/
static const char *
EcpTypeName(int type)
{
const struct ecpname *p;
static char buf[20];
for (p = gEcpTypeNames; p->name; p++)
if (p->type == type)
return(p->name);
snprintf(buf, sizeof(buf), "UNKNOWN[%d]", type);
return(buf);
}
syntax highlighted by Code2HTML, v. 0.9.1