/* * ccp.c * * Written by Archie Cobbs * Copyright (c) 1995-1999 Whistle Communications, Inc. All rights reserved. * See ``COPYRIGHT.whistle'' */ #include "defs.h" #include "ppp.h" #include "ccp.h" #include "fsm.h" #include "ngfunc.h" #include #include #include /* * DEFINITIONS */ #define CCP_MAXFAILURE 7 #define CCP_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) ) /* Set menu options */ enum { SET_ACCEPT, SET_DENY, SET_ENABLE, SET_DISABLE, SET_YES, SET_NO }; /* * INTERNAL FUNCTIONS */ static void CcpConfigure(Fsm fp); static void CcpUnConfigure(Fsm fp); static u_char *CcpBuildConfigReq(Fsm fp, u_char *cp); static void CcpDecodeConfig(Fsm f, FsmOption a, int num, int mode); static void CcpLayerUp(Fsm fp); static void CcpLayerDown(Fsm fp); static void CcpFailure(Fsm f, enum fsmfail reason); static void CcpRecvResetReq(Fsm fp, int id, Mbuf bp); static void CcpRecvResetAck(Fsm fp, int id, Mbuf bp); static int CcpCheckEncryption(Bund b); static int CcpSetCommand(Context ctx, int ac, const char *const av[], const void *arg); static CompType CcpFindComp(int type, int *indexp); static const char *CcpTypeName(int type, char *buf, size_t len); static void CcpNgCtrlEvent(int type, void *cookie); static void CcpNgDataEvent(int type, void *cookie); /* * GLOBAL VARIABLES */ const struct cmdtab CcpSetCmds[] = { { "accept [opt ...]", "Accept option", CcpSetCommand, NULL, 2, (void *) SET_ACCEPT }, { "deny [opt ...]", "Deny option", CcpSetCommand, NULL, 2, (void *) SET_DENY }, { "enable [opt ...]", "Enable option", CcpSetCommand, NULL, 2, (void *) SET_ENABLE }, { "disable [opt ...]", "Disable option", CcpSetCommand, NULL, 2, (void *) SET_DISABLE }, { "yes [opt ...]", "Enable and accept option", CcpSetCommand, NULL, 2, (void *) SET_YES }, { "no [opt ...]", "Disable and deny option", CcpSetCommand, NULL, 2, (void *) SET_NO }, { NULL, NULL, NULL, NULL, 0, NULL }, }; /* * INTERNAL VARIABLES */ /* These should be listed in order of preference */ static const CompType gCompTypes[] = { #ifdef CCP_MPPC &gCompMppcInfo, #endif #ifdef CCP_DEFLATE &gCompDeflateInfo, #endif #ifdef CCP_PRED1 &gCompPred1Info, #endif }; #define CCP_NUM_PROTOS (sizeof(gCompTypes) / sizeof(*gCompTypes)) /* Corresponding option list */ static const struct confinfo *gConfList; /* FSM Initializer */ static const struct fsmtype gCcpFsmType = { "CCP", PROTO_CCP, CCP_KNOWN_CODES, FALSE, LG_CCP, LG_CCP2, NULL, CcpLayerUp, CcpLayerDown, NULL, NULL, CcpBuildConfigReq, CcpDecodeConfig, CcpConfigure, CcpUnConfigure, NULL, NULL, NULL, NULL, CcpFailure, CcpRecvResetReq, CcpRecvResetAck, NULL, NULL, NULL, NULL }; /* Names for different types of compression */ static const struct ccpname { u_char type; const char *name; } gCcpTypeNames[] = { { CCP_TY_OUI, "OUI" }, { CCP_TY_PRED1, "PRED1" }, { CCP_TY_PRED2, "PRED2" }, { CCP_TY_PUDDLE, "PUDDLE" }, { CCP_TY_HWPPC, "HWPPC" }, { CCP_TY_STAC, "STAC" }, { CCP_TY_MPPC, "MPPC" }, { CCP_TY_GAND, "GAND" }, { CCP_TY_V42BIS, "V42BIS" }, { CCP_TY_BSD, "BSD" }, { CCP_TY_LZS_DCP, "LZS-DCP" }, { CCP_TY_MVRCA, "MVRCA" }, { CCP_TY_DCE, "DCE" }, { CCP_TY_DEFLATE, "DEFLATE" }, { CCP_TY_V44, "V.44/LZJH" }, { 0, NULL }, }; int gCcpCsock = -1; /* Socket node control socket */ int gCcpDsock = -1; /* Socket node data socket */ static EventRef gCcpCtrlEvent; static EventRef gCcpDataEvent; int CcpsInit(void) { char name[NG_NODESIZ]; /* Create a netgraph socket node */ snprintf(name, sizeof(name), "mpd%d-cso", gPid); if (NgMkSockNode(name, &gCcpCsock, &gCcpDsock) < 0) { Perror("CcpsInit(): can't create %s node", NG_SOCKET_NODE_TYPE); return(-1); } (void) fcntl(gCcpCsock, F_SETFD, 1); (void) fcntl(gCcpDsock, F_SETFD, 1); /* Listen for happenings on our node */ EventRegister(&gCcpCtrlEvent, EVENT_READ, gCcpCsock, EVENT_RECURRING, CcpNgCtrlEvent, NULL); EventRegister(&gCcpDataEvent, EVENT_READ, gCcpDsock, EVENT_RECURRING, CcpNgDataEvent, NULL); return (0); } void CcpsShutdown(void) { EventUnRegister(&gCcpCtrlEvent); close(gCcpCsock); gCcpCsock = -1; EventUnRegister(&gCcpDataEvent); close(gCcpDsock); gCcpDsock = -1; } /* * CcpInit() */ void CcpInit(Bund b) { CcpState ccp = &b->ccp; /* Init CCP state for this bundle */ memset(ccp, 0, sizeof(*ccp)); FsmInit(&ccp->fsm, &gCcpFsmType, b); ccp->fsm.conf.maxfailure = CCP_MAXFAILURE; /* Construct options list if we haven't done so already */ if (gConfList == NULL) { struct confinfo *ci; unsigned k; ci = Malloc(MB_COMP, (CCP_NUM_PROTOS + 1) * sizeof(*ci)); for (k = 0; k < CCP_NUM_PROTOS; k++) { ci[k].option = k; ci[k].peered = TRUE; ci[k].name = gCompTypes[k]->name; } /* Terminate list */ ci[k].name = NULL; gConfList = (const struct confinfo *) ci; } } /* * CcpInst() */ void CcpInst(Bund b, Bund bt) { CcpState ccp = &b->ccp; /* Init CCP state for this bundle */ memcpy(ccp, &bt->ccp, sizeof(*ccp)); FsmInst(&ccp->fsm, &bt->ccp.fsm, b); } /* * CcpConfigure() */ static void CcpConfigure(Fsm fp) { Bund b = (Bund)fp->arg; CcpState const ccp = &b->ccp; unsigned k; /* Reset state */ ccp->self_reject = 0; ccp->peer_reject = 0; ccp->crypt_check = 0; ccp->xmit = NULL; ccp->recv = NULL; for (k = 0; k < CCP_NUM_PROTOS; k++) { CompType const ct = gCompTypes[k]; if (ct->Configure) { if ((*ct->Configure)(b)) { if (Enabled(&ccp->options, k)) { Log(LG_CCP, ("[%s] CCP: Protocol %s disabled " "as useless for this setup", b->name, ct->name)); } CCP_SELF_REJ(ccp, k); }; } } } /* * CcpUnConfigure() */ static void CcpUnConfigure(Fsm fp) { Bund b = (Bund)fp->arg; CcpState const ccp = &b->ccp; unsigned k; /* Reset state */ ccp->self_reject = 0; ccp->peer_reject = 0; ccp->crypt_check = 0; ccp->xmit = NULL; ccp->recv = NULL; for (k = 0; k < CCP_NUM_PROTOS; k++) { CompType const ct = gCompTypes[k]; if (ct->UnConfigure) (*ct->UnConfigure)(b); } } /* * CcpNgCtrlEvent() * */ void CcpNgCtrlEvent(int type, void *cookie) { Bund b = NULL; union { u_char buf[2048]; struct ng_mesg msg; } u; char raddr[NG_PATHSIZ]; int i, len; ng_ID_t id; (void)cookie; (void)type; /* Read message */ if ((len = NgRecvMsg(gCcpCsock, &u.msg, sizeof(u), raddr)) < 0) { Perror("CcpNgCtrlEvent: can't read message"); return; } if (sscanf(raddr, "[%x]:", &id) != 1) { Log(LG_ERR, ("CcpNgCtrlEvent: can't decode sender id: '%s'", raddr)); return; } for (i = 0; i < gNumBundles; i++) { if (gBundles[i] && !gBundles[i]->dead && gBundles[i]->ccp.decomp_node_id == id) { b = gBundles[i]; break; } } if (!b) return; /* Examine message */ switch (u.msg.header.typecookie) { #ifdef USE_NG_MPPC case NGM_MPPC_COOKIE: #endif #ifdef USE_NG_DEFLATE case NGM_DEFLATE_COOKIE: #endif #ifdef USE_NG_PRED1 case NGM_PRED1_COOKIE: #endif CcpRecvMsg(b, &u.msg, len); return; default: /* Unknown message */ Log(LG_ERR, ("CcpNgCtrlEvent: rec'd unknown ctrl message, cookie=%d cmd=%d", u.msg.header.typecookie, u.msg.header.cmd)); break; } } /* * CcpNgDataEvent() */ static void CcpNgDataEvent(int type, void *cookie) { Bund b; struct sockaddr_ng naddr; socklen_t nsize; Mbuf bp; int num = 0; char *bundname, *rest; int id; (void)cookie; (void)type; while (1) { /* Protect from bundle shutdown and DoS */ if (num > 100) return; bp = mballoc(4096); /* Read data */ nsize = sizeof(naddr); if ((bp->cnt = recvfrom(gCcpDsock, MBDATA(bp), MBSPACE(bp), MSG_DONTWAIT, (struct sockaddr *)&naddr, &nsize)) < 0) { mbfree(bp); if (errno == EAGAIN) return; Log(LG_BUND|LG_ERR, ("CcpNgDataEvent: socket read: %s", strerror(errno))); return; } num++; /* Debugging */ LogDumpBp(LG_FRAME, bp, "CcpNgDataEvent: rec'd %zu bytes frame on %s hook", MBLEN(bp), naddr.sg_data); bundname = ((struct sockaddr_ng *)&naddr)->sg_data; if (bundname[0] != 'c' && bundname[0] != 'd') { Log(LG_ERR, ("CCP: Packet from unknown hook \"%s\"", bundname)); mbfree(bp); continue; } bundname++; id = strtol(bundname, &rest, 10); if (rest[0] != 0 || !gBundles[id] || gBundles[id]->dead) { Log(LG_ERR, ("CCP: Packet from unexisting bundle \"%s\"", bundname)); mbfree(bp); continue; } b = gBundles[id]; /* Packet requiring compression */ if (bundname[0] == 'c') { bp = CcpDataOutput(b, bp); } else { /* Packet requiring decompression */ bp = CcpDataInput(b, bp); } if (bp) NgFuncWriteFrame(gCcpDsock, naddr.sg_data, b->name, bp); } } /* * CcpRecvMsg() */ void CcpRecvMsg(Bund b, struct ng_mesg *msg, int len) { CcpState const ccp = &b->ccp; Fsm const fp = &ccp->fsm; (void)len; switch (msg->header.typecookie) { #ifdef USE_NG_MPPC case NGM_MPPC_COOKIE: switch (msg->header.cmd) { case NGM_MPPC_RESETREQ: { CcpSendResetReq(b); return; } default: break; } break; #endif #ifdef USE_NG_DEFLATE case NGM_DEFLATE_COOKIE: switch (msg->header.cmd) { case NGM_DEFLATE_RESETREQ: { CcpSendResetReq(b); return; } default: break; } break; #endif #ifdef USE_NG_PRED1 case NGM_PRED1_COOKIE: switch (msg->header.cmd) { case NGM_PRED1_RESETREQ: { CcpSendResetReq(b); return; } default: break; } break; #endif default: break; } /* Unknown! */ Log(LG_ERR, ("[%s] %s: rec'd unknown netgraph message: cookie=%d, cmd=%d", Pref(fp), Fsm(fp), msg->header.typecookie, msg->header.cmd)); } /* * CcpUp() */ void CcpUp(Bund b) { FsmUp(&b->ccp.fsm); } /* * CcpDown() */ void CcpDown(Bund b) { FsmDown(&b->ccp.fsm); } /* * CcpOpen() */ void CcpOpen(Bund b) { FsmOpen(&b->ccp.fsm); } /* * CcpClose() */ void CcpClose(Bund b) { FsmClose(&b->ccp.fsm); } /* * CcpOpenCmd() */ int CcpOpenCmd(Context ctx) { if (ctx->bund->tmpl) Error("impossible to open template"); FsmOpen(&ctx->bund->ccp.fsm); return (0); } /* * CcpCloseCmd() */ int CcpCloseCmd(Context ctx) { if (ctx->bund->tmpl) Error("impossible to close template"); FsmClose(&ctx->bund->ccp.fsm); return (0); } /* * CcpFailure() * * If we fail, just shut down and stop trying. However, if encryption * was required and MPPE encryption was enabled, then die here as well. */ static void CcpFailure(Fsm fp, enum fsmfail reason) { Bund b = (Bund)fp->arg; (void)reason; CcpCheckEncryption(b); } /* * CcpStat() */ int CcpStat(Context ctx, int ac, const char *const av[], const void *arg) { CcpState const ccp = &ctx->bund->ccp; char buf[64]; Printf("[%s] %s [%s]\r\n", Pref(&ccp->fsm), Fsm(&ccp->fsm), FsmStateName(ccp->fsm.state)); Printf("Enabled protocols:\r\n"); OptStat(ctx, &ccp->options, gConfList); #ifdef CCP_MPPC MppcStat(ctx, ac, av, arg); #endif Printf("Outgoing compression:\r\n"); Printf("\tProto\t: %s (%s)\r\n", !ccp->xmit ? "none" : ccp->xmit->name, (ccp->xmit && ccp->xmit->Describe) ? (*ccp->xmit->Describe)(ctx->bund, COMP_DIR_XMIT, buf, sizeof(buf)) : ""); if (ccp->xmit && ccp->xmit->Stat) ccp->xmit->Stat(ctx, COMP_DIR_XMIT); Printf("\tResets\t: %d\r\n", ccp->xmit_resets); Printf("Incoming decompression:\r\n"); Printf("\tProto\t: %s (%s)\r\n", !ccp->recv ? "none" : ccp->recv->name, (ccp->recv && ccp->recv->Describe) ? (*ccp->recv->Describe)(ctx->bund, COMP_DIR_RECV, buf, sizeof(buf)) : ""); if (ccp->recv && ccp->recv->Stat) ccp->recv->Stat(ctx, COMP_DIR_RECV); Printf("\tResets\t: %d\r\n", ccp->recv_resets); return(0); } /* * CcpSendResetReq() */ void CcpSendResetReq(Bund b) { CcpState const ccp = &b->ccp; CompType const ct = ccp->recv; Fsm const fp = &ccp->fsm; Mbuf bp = NULL; if (ct == NULL) { Log(LG_ERR, ("[%s] %s: CcpSendResetReq() call from undefined decompressor!", Pref(fp), Fsm(fp))); return; } ccp->recv_resets++; if (ct->SendResetReq) bp = (*ct->SendResetReq)(b); Log(LG_CCP, ("[%s] %s: SendResetReq #%d link %d (%s)", Pref(fp), Fsm(fp), fp->reqid, 0, FsmStateName(fp->state))); FsmOutputMbuf(fp, CODE_RESETREQ, fp->reqid++, bp); } /* * CcpRecvResetReq() */ static void CcpRecvResetReq(Fsm fp, int id, Mbuf bp) { Bund b = (Bund)fp->arg; CcpState const ccp = &b->ccp; CompType const ct = ccp->xmit; int noAck = 0; ccp->xmit_resets++; bp = (ct && ct->RecvResetReq) ? (*ct->RecvResetReq)(b, id, bp, &noAck) : NULL; if (!noAck) { Log(LG_CCP, ("[%s] %s: SendResetAck #%d link %d (%s)", Pref(fp), Fsm(fp), id, 0, FsmStateName(fp->state))); FsmOutputMbuf(fp, CODE_RESETACK, id, bp); } } /* * CcpRecvResetAck() */ static void CcpRecvResetAck(Fsm fp, int id, Mbuf bp) { Bund b = (Bund)fp->arg; CcpState const ccp = &b->ccp; CompType const ct = ccp->recv; if (ct && ct->RecvResetAck) (*ct->RecvResetAck)(b, id, bp); } /* * CcpInput() */ void CcpInput(Bund b, Mbuf bp) { FsmInput(&b->ccp.fsm, bp); } /* * CcpDataOutput() * * Compress a frame. Consumes the original packet. */ Mbuf CcpDataOutput(Bund b, Mbuf plain) { CcpState const ccp = &b->ccp; Mbuf comp; LogDumpBp(LG_FRAME, plain, "[%s] %s: xmit plain", Pref(&ccp->fsm), Fsm(&ccp->fsm)); /* Compress packet */ if ((!ccp->xmit) || (!ccp->xmit->Compress)) { Log(LG_ERR, ("[%s] %s: no encryption for xmit", Pref(&ccp->fsm), Fsm(&ccp->fsm))); mbfree(plain); return(NULL); } comp = (*ccp->xmit->Compress)(b, plain); LogDumpBp(LG_FRAME, comp, "[%s] %s: xmit comp", Pref(&ccp->fsm), Fsm(&ccp->fsm)); return(comp); } /* * CcpDataInput() * * Decompress incoming packet. If packet got garbled, return NULL. * In any case, we consume the packet passed to us. */ Mbuf CcpDataInput(Bund b, Mbuf comp) { CcpState const ccp = &b->ccp; Mbuf plain; LogDumpBp(LG_FRAME, comp, "[%s] %s: recv comp", Pref(&ccp->fsm), Fsm(&ccp->fsm)); /* Decompress packet */ if ((!ccp->recv) || (!ccp->recv->Decompress)) { Log(LG_ERR, ("[%s] %s: no compression for recv", Pref(&ccp->fsm), Fsm(&ccp->fsm))); mbfree(comp); return(NULL); } plain = (*ccp->recv->Decompress)(b, comp); /* Encrypted ok? */ if (plain == NULL) { Log(LG_CCP, ("[%s] %s: decompression failed", Pref(&ccp->fsm), Fsm(&ccp->fsm))); return(NULL); } LogDumpBp(LG_FRAME, plain, "[%s] %s: recv plain", Pref(&ccp->fsm), Fsm(&ccp->fsm)); return(plain); } /* * CcpBuildConfigReq() */ static u_char * CcpBuildConfigReq(Fsm fp, u_char *cp) { Bund b = (Bund)fp->arg; CcpState const ccp = &b->ccp; unsigned type; int ok; /* Put in all options that peer hasn't rejected in preferred order */ ccp->xmit = NULL; for (type = 0; type < CCP_NUM_PROTOS; type++) { CompType const ct = gCompTypes[type]; if (Enabled(&ccp->options, type) && !CCP_PEER_REJECTED(ccp, type)) { cp = (*ct->BuildConfigReq)(b, cp, &ok); if (ok && (!ccp->xmit)) ccp->xmit = ct; } } return(cp); } /* * CcpLayerUp() */ static void CcpLayerUp(Fsm fp) { Bund b = (Bund)fp->arg; CcpState const ccp = &b->ccp; struct ngm_connect cn; char buf[64]; /* If nothing was negotiated in either direction, close CCP */ if ((!ccp->recv || !(*ccp->recv->Negotiated)(b, COMP_DIR_RECV)) && (!ccp->xmit || !(*ccp->xmit->Negotiated)(b, COMP_DIR_XMIT))) { Log(LG_CCP, ("[%s] %s: No compression negotiated", Pref(fp), Fsm(fp))); FsmFailure(fp, FAIL_NEGOT_FAILURE); return; } /* Check for required encryption */ if (CcpCheckEncryption(b) < 0) { return; } /* Initialize each direction */ if (ccp->xmit != NULL && ccp->xmit->Init != NULL && (*ccp->xmit->Init)(b, COMP_DIR_XMIT) < 0) { Log(LG_CCP, ("[%s] %s: compression init failed", Pref(fp), Fsm(fp))); FsmFailure(fp, FAIL_NEGOT_FAILURE); /* XXX */ return; } if (ccp->recv != NULL && ccp->recv->Init != NULL && (*ccp->recv->Init)(b, COMP_DIR_RECV) < 0) { Log(LG_CCP, ("[%s] %s: decompression init failed", Pref(fp), Fsm(fp))); FsmFailure(fp, FAIL_NEGOT_FAILURE); /* XXX */ return; } if (ccp->xmit != NULL && ccp->xmit->Compress != NULL) { /* Connect a hook from the ppp node to our socket node */ snprintf(cn.path, sizeof(cn.path), "[%x]:", b->nodeID); snprintf(cn.ourhook, sizeof(cn.ourhook), "c%d", b->id); strcpy(cn.peerhook, NG_PPP_HOOK_COMPRESS); if (NgSendMsg(gCcpCsock, ".:", NGM_GENERIC_COOKIE, NGM_CONNECT, &cn, sizeof(cn)) < 0) { Perror("[%s] can't connect \"%s\"->\"%s\" and \"%s\"->\"%s\"", b->name, ".:", cn.ourhook, cn.path, cn.peerhook); } } if (ccp->recv != NULL && ccp->recv->Decompress != NULL) { /* Connect a hook from the ppp node to our socket node */ snprintf(cn.path, sizeof(cn.path), "[%x]:", b->nodeID); snprintf(cn.ourhook, sizeof(cn.ourhook), "d%d", b->id); strcpy(cn.peerhook, NG_PPP_HOOK_DECOMPRESS); if (NgSendMsg(gCcpCsock, ".:", NGM_GENERIC_COOKIE, NGM_CONNECT, &cn, sizeof(cn)) < 0) { Perror("[%s] can't connect \"%s\"->\"%s\" and \"%s\"->\"%s\"", b->name, ".:", cn.ourhook, cn.path, cn.peerhook); } } /* Report what we're doing */ Log(LG_CCP, ("[%s] CCP: Compress using: %s (%s)", b->name, !ccp->xmit ? "none" : ccp->xmit->name, (ccp->xmit && ccp->xmit->Describe) ? (*ccp->xmit->Describe)(b, COMP_DIR_XMIT, buf, sizeof(buf)) : "")); Log(LG_CCP, ("[%s] CCP: Decompress using: %s (%s)", b->name, !ccp->recv ? "none" : ccp->recv->name, (ccp->recv && ccp->recv->Describe) ? (*ccp->recv->Describe)(b, COMP_DIR_RECV, buf, sizeof(buf)) : "")); /* Update PPP node config */ b->pppConfig.bund.enableCompression = (ccp->xmit != NULL)?ccp->xmit->mode:0; b->pppConfig.bund.enableDecompression = (ccp->recv != NULL)?ccp->recv->mode:0; NgFuncSetConfig(b); /* Update interface MTU */ BundUpdateParams(b); } /* * CcpLayerDown() */ static void CcpLayerDown(Fsm fp) { Bund b = (Bund)fp->arg; CcpState const ccp = &b->ccp; /* Update PPP node config */ b->pppConfig.bund.enableCompression = 0; b->pppConfig.bund.enableDecompression = 0; NgFuncSetConfig(b); /* Update interface MTU */ BundUpdateParams(b); if (ccp->xmit != NULL && ccp->xmit->Compress != NULL) { char hook[NG_HOOKSIZ]; /* Disconnect hook. */ snprintf(hook, sizeof(hook), "c%d", b->id); NgFuncDisconnect(gCcpCsock, b->name, ".:", hook); } if (ccp->recv != NULL && ccp->recv->Decompress != NULL) { char hook[NG_HOOKSIZ]; /* Disconnect hook. */ snprintf(hook, sizeof(hook), "d%d", b->id); NgFuncDisconnect(gCcpCsock, b->name, ".:", hook); } if (ccp->recv && ccp->recv->Cleanup) (*ccp->recv->Cleanup)(b, COMP_DIR_RECV); if (ccp->xmit && ccp->xmit->Cleanup) (*ccp->xmit->Cleanup)(b, COMP_DIR_XMIT); ccp->xmit_resets = 0; ccp->recv_resets = 0; } /* * CcpDecodeConfig() */ static void CcpDecodeConfig(Fsm fp, FsmOption list, int num, int mode) { Bund b = (Bund)fp->arg; CcpState const ccp = &b->ccp; u_int ackSizeSave, rejSizeSave; int k, rej; /* Forget our previous choice on new request */ if (mode == MODE_REQ) ccp->recv = NULL; /* Decode each config option */ for (k = 0; k < num; k++) { FsmOption const opt = &list[k]; int index; CompType ct; char buf[32]; Log(LG_CCP, ("[%s] %s", b->name, CcpTypeName(opt->type, buf, sizeof(buf)))); if ((ct = CcpFindComp(opt->type, &index)) == NULL) { if (mode == MODE_REQ) { Log(LG_CCP, ("[%s] Not supported", b->name)); FsmRej(fp, opt); } continue; } switch (mode) { case MODE_REQ: ackSizeSave = gAckSize; rejSizeSave = gRejSize; rej = (!Acceptable(&ccp->options, index) || CCP_SELF_REJECTED(ccp, index) || (ccp->recv && ccp->recv != ct)); if (rej) { (*ct->DecodeConfig)(fp, opt, MODE_NOP); FsmRej(fp, opt); break; } (*ct->DecodeConfig)(fp, opt, mode); if (gRejSize != rejSizeSave) { /* we rejected it */ CCP_SELF_REJ(ccp, index); break; } if (gAckSize != ackSizeSave) /* we accepted it */ ccp->recv = ct; break; case MODE_REJ: (*ct->DecodeConfig)(fp, opt, mode); CCP_PEER_REJ(ccp, index); break; case MODE_NAK: case MODE_NOP: (*ct->DecodeConfig)(fp, opt, mode); break; } } } /* * CcpSubtractBloat() * * Given that "size" is our MTU, return the maximum length frame * we can compress without the result being longer than "size". */ int CcpSubtractBloat(Bund b, int size) { CcpState const ccp = &b->ccp; /* Account for transmit compression overhead */ if (OPEN_STATE(ccp->fsm.state) && ccp->xmit && ccp->xmit->SubtractBloat) size = (*ccp->xmit->SubtractBloat)(b, size); /* Account for CCP's protocol number overhead */ if (OPEN_STATE(ccp->fsm.state)) size -= CCP_OVERHEAD; /* Done */ return(size); } /* * CcpCheckEncryption() * * Because MPPE is negotiated as an option to MPPC compression, * we have to check for encryption required when CCP comes up. */ static int CcpCheckEncryption(Bund b) { #if 0 CcpState const ccp = &b->ccp; /* Already checked? */ if (ccp->crypt_check) return(0); ccp->crypt_check = 1; /* Is encryption required? */ if (Enabled(&ccp->options, gMppePolicy)) { if (b->params.msoft.policy != MPPE_POLICY_REQUIRED) return(0); } else { if (!Enabled(&b->conf.options, BUND_CONF_CRYPT_REQD)) return(0); } /* Was MPPE encryption enabled? If not, ignore requirement */ if (!Enabled(&ccp->options, gMppe40) && !Enabled(&ccp->options, gMppe56) && !Enabled(&ccp->options, gMppe128) && !Enabled(&ccp->options, gMppePolicy)) return(0); /* Make sure MPPE was negotiated in both directions */ if (!OPEN_STATE(ccp->fsm.state) || !ccp->xmit || ccp->xmit->type != CCP_TY_MPPC || !ccp->recv || ccp->recv->type != CCP_TY_MPPC #ifdef CCP_MPPC || !(ccp->mppc.recv_bits & MPPE_BITS) || !(ccp->mppc.xmit_bits & MPPE_BITS) #endif ) goto fail; /* Looks OK */ return(0); fail: Log(LG_ERR, ("[%s] %s: encryption required, but MPPE was not" " negotiated in both directions", Pref(&ccp->fsm), Fsm(&ccp->fsm))); FsmFailure(&ccp->fsm, FAIL_CANT_ENCRYPT); FsmFailure(&b->ipcp.fsm, FAIL_CANT_ENCRYPT); FsmFailure(&b->ipv6cp.fsm, FAIL_CANT_ENCRYPT); return(-1); #else (void)b; #endif return (0); } /* * CcpSetCommand() */ static int CcpSetCommand(Context ctx, int ac, const char *const av[], const void *arg) { CcpState const ccp = &ctx->bund->ccp; if (ac == 0) return(-1); switch ((intptr_t)arg) { case SET_ACCEPT: AcceptCommand(ac, av, &ccp->options, gConfList); break; case SET_DENY: DenyCommand(ac, av, &ccp->options, gConfList); break; case SET_ENABLE: EnableCommand(ac, av, &ccp->options, gConfList); break; case SET_DISABLE: DisableCommand(ac, av, &ccp->options, gConfList); break; case SET_YES: YesCommand(ac, av, &ccp->options, gConfList); break; case SET_NO: NoCommand(ac, av, &ccp->options, gConfList); break; default: assert(0); } return(0); } /* * CcpFindComp() */ static CompType CcpFindComp(int type, int *indexp) { unsigned k; for (k = 0; k < CCP_NUM_PROTOS; k++) { if (gCompTypes[k]->type == type) { if (indexp) *indexp = k; return(gCompTypes[k]); } } return(NULL); } /* * CcpTypeName() */ static const char * CcpTypeName(int type, char *buf, size_t len) { const struct ccpname *p; for (p = gCcpTypeNames; p->name; p++) { if (p->type == type) { strlcpy(buf, p->name, len); return (buf); } } snprintf(buf, sizeof(buf), "UNKNOWN[%d]", type); return(buf); }