/* * lcp.cc - Self explanitory * Copyright (C) 1997-2000 SpellCaster Telecommunications Inc. * $Id: lcp.cc,v 1.11 2004/08/22 20:57:30 bcrl Exp $ * Released under the GNU Public License. See LICENSE file for details. */ #include "kernel.h" #include "debug.h" #include "lcp.h" #include "link.h" // These protocol codes must not be rejected by the other side // or the result is terminal. Any other codes can be rejected unsigned char fatalCodes[] = { LCP_CODE_CONFREQ, LCP_CODE_CONFACK, LCP_CODE_CONFREJ, LCP_CODE_CONFNAK, LCP_CODE_TERMREQ, LCP_CODE_TERMACK, LCP_CODE_DISCREQ, LCP_CODE_ECHOREQ, LCP_CODE_ECHORPLY, LCP_CODE_CODEREJ, LCP_CODE_PROTOREJ, '\0' }; // These protocol IDs must not be rejected by peer or the result // is terminal. Other protocols can be rejected. unsigned short fatalProtos[] = { PPP_PROTO_LCP, 0x0000 }; class LcpEchoTimer : public CTimer { protected: int timeout; CQueue q; public: LcpEchoTimer(int default_timeout = 10) { timeout = default_timeout; Start(timeout); } void Add(CLcpProtocol *lcp) { q.Append(lcp); if (!IsActive()) Start(timeout); } void Remove(CLcpProtocol *lcp) { q.Remove(lcp); } void TimerExpired (void) { CQueueItem *qi = q.Pull(); if (qi) { CLcpProtocol *lcp = (CLcpProtocol *)qi; lcp->SndEchoReq(); } if (!q.IsEmpty()) Start(timeout); } }; static LcpEchoTimer echoTimer(10); static LcpEchoTimer slowEchoTimer(10000); /* 100s */ #if 0 case LongLcpEchoTimer : public BaseLcpEchoTimer { void TimerExpired (void) {} { CQueueItem *qi; while ((qi = q.Pull()) { CLcpProtocol *lcp = (CLcpProtocol *)qi; lcp->EchoTimeout(); } // Always restart the timer, as this one is slow Start(timeout); } } static LcpEchoTimer longEchoTimer(Mode_Long, 100*60); #endif void CLcpProtocol::EchoTimeout() { m_echoTimer.Stop(); if (m_echoSent >= 3) { m_parent->Close(-2, "LCP: timeout sending Echo Requests."); return; } slowEchoTimer.Add(this); } void CLcpProtocol::SndEchoReq() { CPppPacket pkt; pkt.Reserve(PPP_HEADER_LENGTH + LCP_HEADER_LENGTH + sizeof(u32)); pkt.Push32(m_parent->m_lpolicy.Get(P_MAGIC)); pkt.Push16(1 + LCP_HEADER_LENGTH + sizeof(u32)); pkt.Push8(++m_lastSentIdent); pkt.Push8(LCP_CODE_ECHOREQ); pkt.Push16(PPP_PROTO_LCP); pkt.Put8(m_lastSentIdent); m_lastEchoIdent = m_lastSentIdent; m_echoSent++; m_parent->Output(&pkt); m_echoTimer.Start(m_echoTime); } static void echoTimeout(void *data) { CLcpProtocol *lcp = (CLcpProtocol *)data; lcp->EchoTimeout(); } CLcpProtocol::CLcpProtocol() : CFsmProtocol() { m_restartTime = 3; m_protocolName = "LCP"; m_parent = NULL; m_echoTimer.SetFunction(echoTimeout, this); m_lastEchoIdent = 0; m_echoSent = 0; m_rxPackets = 0; } CLcpProtocol::~CLcpProtocol() { } void CLcpProtocol::Input(CPppPacket *packet) { DebugEnter("CLcpProtocol::Input()"); u8 code = packet->Pull8(); u8 ident = packet->Pull8(); u16 len = packet->Pull16(); if (len < 4 || (int)packet->GetLength() < (len - 4)) return; /* bad packet */ m_rxPackets++; // Process the packet switch(code) { case LCP_CODE_CONFREQ: m_lastRcvdIdent = ident; RcvConfReq(packet); break; case LCP_CODE_CONFACK: if(ident != m_lastSentIdent) { // Drop packet Log(LF_INFO|LF_PROTO, "LCP ConfAck, Bad ident...dropping"); break; } RcvConfAck(packet); break; case LCP_CODE_CONFNAK: if(ident != m_lastSentIdent) { // Drop packet Log(LF_INFO|LF_PROTO, "LCP ConfNak, Bad ident...dropping"); break; } RcvConfNak(packet); break; case LCP_CODE_CONFREJ: if(ident != m_lastSentIdent) { // Drop packet Log(LF_INFO|LF_PROTO, "LCP ConfRej, Bad ident...dropping"); break; } RcvConfRej(packet); break; case LCP_CODE_TERMREQ: m_lastRcvdIdent = ident; RcvTermReq(); break; case LCP_CODE_TERMACK: if(ident != m_lastSentIdent) { // Drop packet Log(LF_INFO|LF_PROTO, "LCP TermAck, Bad ident...dropping"); break; } RcvTermAck(); break; case LCP_CODE_CODEREJ: RcvCodeReject(packet); break; case LCP_CODE_PROTOREJ: break; case LCP_CODE_ECHOREQ: m_lastRcvdIdent = ident; RcvEchoReq(packet); break; case LCP_CODE_ECHORPLY: if (m_lastEchoIdent == ident) m_echoSent = 0; else Log(LF_ERROR|LF_PROTO, "%s: LCP: received echo reply ident=%02x expected %02x", m_parent->m_channel->device_name, ident, m_lastEchoIdent); break; case LCP_CODE_DISCREQ: // Discard Log(LF_INFO|LF_PROTO, "LCP DiscReq, Discarding packet"); break; default: Log(LF_INFO|LF_PROTO, "LCP Rcvd-Unknown-Code %d, Rejecting", code); SndCodeReject(packet); RcvUnknownCode(packet); break; } DebugVoidReturn; } // // Receive a Configuration-Request from the peer. Simultaniously build // ack, nak and reject responses while we go, sending the appropriate // response and discarding the others. Also, build a set of acked options // and copy them to the link configuration if we send an ack // void CLcpProtocol::RcvConfReq(CPppPacket *packet) { u8 addr[LCP_OPT_EPD_ADDR_LEN + 256]; char *port = m_parent->m_channel->device_name; policy_t theirs; policy_t &want = m_parent->m_policy; DebugEnter("CLcpProtocol::RcvConfReq()"); // // Reserve header space in the responses // m_ackPkt.Clear(); m_nakPkt.Clear(); m_rejPkt.Clear(); m_ackPkt.Reserve(PPP_HEADER_LENGTH + LCP_HEADER_LENGTH); m_nakPkt.Reserve(PPP_HEADER_LENGTH + LCP_HEADER_LENGTH); m_rejPkt.Reserve(PPP_HEADER_LENGTH + LCP_HEADER_LENGTH); theirs.Clear(); while(packet->GetLength()) { u8 type = packet->Pull8(); u8 len = packet->Pull8(); switch(type) { #if 0 case 0: if (len > 2) packet->Trim(len - 2); break; #endif case LCP_OPT_AUTH: { u16 auth_proto = packet->Pull16(); if (auth_proto == PPP_PROTO_CHAP) { if (len != LCP_OPT_CHAP_LEN) { // Wrong size, reject option m_rejPkt.Put8(type); m_rejPkt.Put8(len > 2 ? len : 2); if (len > 2) { packet->Pull(&m_rejPkt, len - 2); } Log(LF_INFO|LF_PROTO, "%s: LCP ConfReq: [CHAP], Invalid size (%d)", port, len); } else { u8 digest = packet->Pull8(); if (digest != LCP_OPT_CHAP_MD5) { // We do MD5 only, suggest it m_nakPkt.Put8(type); m_nakPkt.Put8(LCP_OPT_PAP_LEN); m_nakPkt.Put16(PPP_PROTO_PAP); Log(LF_INFO|LF_PROTO, "%s: LCP ConfReq: [CHAP->PAP]", port); } else { m_ackPkt.Put8(type); m_ackPkt.Put8(len); m_ackPkt.Put16(PPP_PROTO_CHAP); m_ackPkt.Put8(LCP_OPT_CHAP_MD5); theirs.Set(P_AUTH, PPP_PROTO_CHAP); Log(LF_INFO|LF_PROTO, "%s: LCP ConfReq: [CHAP]", port); } } } else if (auth_proto == PPP_PROTO_PAP) { if (len != LCP_OPT_PAP_LEN) { // Wrong size, reject option m_rejPkt.Put8(type); m_rejPkt.Put8(len > 2 ? len : 2); if (len > 2) packet->Pull(&m_rejPkt, len - 2); Log(LF_INFO|LF_PROTO, "%s: LCP ConfReq: [PAP], Invalid size (%d)", port, len); } else { m_ackPkt.Put8(type); m_ackPkt.Put8(len); m_ackPkt.Put16(PPP_PROTO_PAP); theirs.Set(P_AUTH, PPP_PROTO_PAP); Log(LF_INFO|LF_PROTO, "%s: LCP ConfReq: [PAP]", port); } } else { // Unknown authentication protocol, NAK and suggest PAP as a minimum m_nakPkt.Put8(type); m_nakPkt.Put8(LCP_OPT_PAP_LEN); if (len > 2) { packet->Trim(len - 2); m_nakPkt.Put16(PPP_PROTO_PAP); } Log(LF_INFO|LF_PROTO, "%s: LCP ConfReq: [AUTH] Unknown protocol (%#x)", port, auth_proto); } break; } case LCP_OPT_MRU: { // Maximum-Receive-Unit if (len != LCP_OPT_MRU_LEN) { // Wrong size, reject option m_rejPkt.Put8(type); m_rejPkt.Put8(len > 2 ? len : 2); if (len > 2) packet->Pull(&m_rejPkt, len - 2); m_rejPkt.Print(); Log(LF_INFO|LF_PROTO, "%s: LCP ConfReq: [MRU], Invalid size (%d)", port, len); break; } u16 mru = packet->Pull16(); CPppPacket *wpkt; if (mru > m_parent->m_hard_mru || (mru < LCP_OPT_MRU_MIN && mru > m_parent->m_hard_mru)) { Log(LF_INFO|LF_PROTO, "%s: LCP ConfReq: [MRU(%d)]->[MRU(%d)]", port, mru, LCP_OPT_MRU_DEFAULT); wpkt = &m_nakPkt; mru = m_parent->m_hard_mru; } else { Log(LF_INFO|LF_PROTO, "%s: LCP ConfReq: [MRU(%d)]", port, mru); wpkt = &m_ackPkt; } theirs.Set(P_MRU, mru); wpkt->Put8(type); wpkt->Put8(len); wpkt->Put16(mru); break; } case LCP_OPT_MAGIC: { if(len != LCP_OPT_MAGIC_LEN) { // Wrong size, reject option m_rejPkt.Put8(type); m_rejPkt.Put8(len > 2 ? len : 2); if (len > 2) packet->Pull(&m_rejPkt, len - 2); Log(LF_INFO|LF_PROTO, "%s: LCP ConfReq: [Magic], Invalid size (%d)", port, len); break; } u32 magic = packet->Pull32(); if (magic == want.Get(P_MAGIC)) { Log(LF_WARN|LF_PROTO, "%s: Possible Loopback detected.", port); m_parent->m_loop_count++; if(m_parent->m_loop_count > MAX_LOOP_COUNT) { Log(LF_ERROR|LF_PROTO, "%s: Loopback confirmed, closing connection", port); m_parent->Close(-2, "LCP: Connection looped back"); goto rcr_cleanup; } // Nak the magic number option in case he is just using the same // number as us, give him another suggestion m_nakPkt.Put8(type); m_nakPkt.Put8(len); m_nakPkt.Put32(magic); Log(LF_INFO|LF_PROTO, "%s: LCP ConfReq: [Magic(%#x)]", port, magic); } else { m_ackPkt.Put8(type); m_ackPkt.Put8(len); m_ackPkt.Put32(magic); theirs.Set(P_MAGIC, magic); Log(LF_INFO|LF_PROTO, "%s: LCP ConfReq: [Magic(%#x)]", port, magic); } break; } case LCP_OPT_PFC: if(len != LCP_OPT_PFC_LEN) { // Wrong size, reject option m_rejPkt.Put8(type); m_rejPkt.Put8(len); if (len > 2) packet->Pull(&m_rejPkt, len - 2); Log(LF_INFO|LF_PROTO, "%s: LCP ConfReq: [PFC], Invalid size (%d)", port, len); break; } // We can do PFC m_ackPkt.Put8(type); m_ackPkt.Put8(len); theirs.Set(P_PFC, 1); Log(LF_INFO|LF_PROTO, "%s: LCP ConfReq: [PFC]", port); break; case LCP_OPT_ACFC: if(len != LCP_OPT_ACFC_LEN) { // Wrong size, reject option m_rejPkt.Put8(type); m_rejPkt.Put8(len); if (len > 2) packet->Pull(&m_rejPkt, len - 2); Log(LF_INFO|LF_PROTO, "%s: LCP ConfReq: [ACFC], Invalid size (%d)", port, len); break; } // We can do ACFC m_ackPkt.Put8(type); m_ackPkt.Put8(len); theirs.Set(P_ACFC, 1); Log(LF_INFO|LF_PROTO, "%s: LCP ConfReq: [ACFC]", port); break; case LCP_OPT_ACCM: { if(len != LCP_OPT_ACCM_LEN) { // Wrong size, reject option m_rejPkt.Put8(type); m_rejPkt.Put8(len); if (len > 2) packet->Pull(&m_rejPkt, len - 2); Log(LF_INFO|LF_PROTO, "%s: LCP ConfReq: [ACCM], Invalid size (%d)", port, len); break; } u32 accm = packet->Pull32(); m_ackPkt.Put8(type); m_ackPkt.Put8(len); m_ackPkt.Put32(accm); theirs.Set(P_ACCM, accm); Log(LF_INFO|LF_PROTO, "%s: LCP ConfReq: [ACCM(%#x)]", port, accm); break; } case LCP_OPT_MRRU: { if(len != LCP_OPT_MRRU_LEN) { // Wrong size, reject option m_rejPkt.Put8(type); m_rejPkt.Put8(len); if (len > 2) packet->Pull(&m_rejPkt, len - 2); Log(LF_INFO|LF_PROTO, "%s: LCP ConfReq: [MRRU], Invalid size (%d)", port, len); break; } u16 mrru = packet->Pull16(); if (mrru >= LCP_OPT_MRU_MIN) { m_ackPkt.Put8(type); m_ackPkt.Put8(len); m_ackPkt.Put16(mrru); theirs.Set(P_MRRU, mrru); Log(LF_INFO|LF_PROTO, "%s: LCP ConfReq: [MRRU(%d)]", port, mrru); } else { m_nakPkt.Put8(type); m_nakPkt.Put8(len); m_nakPkt.Put16(LCP_OPT_MRU_DEFAULT); Log(LF_INFO|LF_PROTO, "%s: LCP ConfReq: [MRRU(%d)]->[MRRU(%d)]", port, mrru, LCP_OPT_MRU_DEFAULT); } break; } case LCP_OPT_SSN: if(len != LCP_OPT_SSN_LEN) { // Wrong size, reject option m_rejPkt.Put8(type); m_rejPkt.Put8(len); if (len > 2) packet->Pull(&m_rejPkt, len - 2); m_rejPkt.Print(); Log(LF_INFO|LF_PROTO, "%s: LCP ConfReq: [SSNHF], Invalid size (%d)", port, len); break; } // We'll do SSNHF m_ackPkt.Put8(type); m_ackPkt.Put8(len); theirs.Set(P_SSN, 1); Log(LF_INFO|LF_PROTO, "%s: LCP ConfReq: [SSNHF]", port); break; case LCP_OPT_EPD: { if (len < LCP_OPT_EPD_LEN) { // Wrong size, reject option m_rejPkt.Put8(type); m_rejPkt.Put8(len); if (len > 2) packet->Pull(&m_rejPkt, len - 2); Log(LF_INFO|LF_PROTO, "%s: LCP ConfReq: [EPD], Invalid size (%d)", port, len); break; } u8 aClass = packet->Pull8(); packet->Pull(addr, len - 3); if (aClass) { m_ackPkt.Put8(type); m_ackPkt.Put8(len); m_ackPkt.Put8(aClass); m_ackPkt.Put(addr, len - 3); theirs.Set(P_EPD_CLASS, aClass); memcpy(theirs.epd_addr, addr, len - 3); theirs.Set(P_EPD_LENGTH, len - 3); } else { m_ackPkt.Put8(type); m_ackPkt.Put8(len); m_ackPkt.Put8(aClass); m_ackPkt.Put(addr, len - 3); } Log(LF_INFO|LF_PROTO, "%s: LCP ConfReq: [EPD(%d)]", port, aClass); break; } case LCP_OPT_LDISC: { if (len != LCP_OPT_LDISC_LEN) { // Wrong size, reject option m_rejPkt.Put8(type); m_rejPkt.Put8(len); if (len > 2) packet->Pull(&m_rejPkt, len - 2); m_rejPkt.Print(); Log(LF_INFO|LF_PROTO, "%s: LCP ConfReq: [LDISC], Invalid size (%d)", port, len); break; } u16 ldisc = packet->Pull16(); m_ackPkt.Put8(type); m_ackPkt.Put8(len); m_ackPkt.Put16(ldisc); theirs.Set(P_LDISC, ldisc); Log(LF_INFO|LF_PROTO, "%s: LCP ConfReq: [LDISC(%d)]", port, ldisc); break; } default: // Reject options we don't know m_rejPkt.Put8(type); m_rejPkt.Put8(len); if (len > 2) packet->Pull(&m_rejPkt, len - 2); Log(LF_INFO|LF_PROTO, "%s: LCP ConfReq: (%d), Unknown option", port, type); break; } } if (!theirs.Valid(P_MRU) && want.Valid(P_MRU) && want.Get(P_MRU) != LCP_OPT_MRU_DEFAULT) { theirs.Set(P_MRU, want.Get(P_MRU)); m_nakPkt.Put8(LCP_OPT_MRU); m_nakPkt.Put8(LCP_OPT_MRU_LEN); m_nakPkt.Put16(want.Get(P_MRU)); } if(m_rejPkt.GetLength()) { // Send the reject packet m_rejPkt.Push16(m_rejPkt.GetLength() + LCP_HEADER_LENGTH); m_rejPkt.Push8(m_lastRcvdIdent); m_rejPkt.Push8(LCP_CODE_CONFREJ); m_rejPkt.Push16(PPP_PROTO_LCP); CFsmProtocol::RcvBadConfReq(); } else if(m_nakPkt.GetLength()) { // Send the nak packet m_nakPkt.Push16(m_nakPkt.GetLength() + LCP_HEADER_LENGTH); m_nakPkt.Push8(m_lastRcvdIdent); m_nakPkt.Push8(LCP_CODE_CONFNAK); m_nakPkt.Push16(PPP_PROTO_LCP); CFsmProtocol::RcvBadConfReq(); } else { // Send the ack packet m_ackPkt.Push16(m_ackPkt.GetLength() + LCP_HEADER_LENGTH); m_ackPkt.Push8(m_lastRcvdIdent); m_ackPkt.Push8(LCP_CODE_CONFACK); m_ackPkt.Push16(PPP_PROTO_LCP); m_parent->m_rpolicy.Set(theirs); CFsmProtocol::RcvGoodConfReq(); } rcr_cleanup: DebugVoidReturn; } void CLcpProtocol::SndConfAck() { DebugEnter("CLcpProtocol::SndConfAck"); Log(LF_INFO|LF_PROTO|LF_TX, "%s: LCP Send ConfAck: len = %d, ident = %d", m_parent->m_channel->device_name, m_ackPkt.GetLength(), m_lastRcvdIdent); m_parent->Output(&m_ackPkt); DebugVoidReturn; } void CLcpProtocol::SndConfNak() { DebugEnter("CLcpProtocol::SndConfNak"); if (m_rejPkt.GetLength()) { Log(LF_INFO|LF_PROTO|LF_TX, "%s: LCP Send ConfRej: len = %d, ident = %d", m_parent->m_channel->device_name, m_rejPkt.GetLength(), m_lastRcvdIdent); m_parent->Output(&m_rejPkt); } else { Log(LF_INFO|LF_PROTO|LF_TX, "%s: LCP Send ConfNak: len = %d, ident = %d", m_parent->m_channel->device_name, m_nakPkt.GetLength(), m_lastRcvdIdent); m_parent->Output(&m_nakPkt); } DebugVoidReturn; } // // RcvConfNak() - Receive a Configure-Nak from the peer // Adjust config options we ask for and reject any options // that the peer has tossed in that we dont understand. Call // the base class function to update the state machine. // void CLcpProtocol::RcvConfNak(CPppPacket *packet) { u8 digest; u8 addr[LCP_OPT_EPD_ADDR_LEN]; char *port = m_parent->m_channel->device_name; policy_t &want = m_parent->m_npolicy; policy_t new_opts; // Options we want as modified here DebugEnter("CLcpProtocol::RcvConfNak()"); new_opts.Set(m_parent->m_npolicy); while (packet->GetLength()) { u8 type = packet->Pull8(); u8 len = packet->Pull8(); switch(type) { case LCP_OPT_AUTH: { u16 auth_proto = packet->Pull16(); if (auth_proto == PPP_PROTO_CHAP) { if (len != LCP_OPT_CHAP_LEN) { // Wrong size, drop packet Log(LF_INFO|LF_PROTO, "%s: LCP ConfNak: [CHAP], Invalid size (%d)", port, len); goto out; } digest = packet->Pull8(); if (digest != LCP_OPT_CHAP_MD5) { // We do MD5 only, so drop down to PAP new_opts.Set(P_AUTH, PPP_PROTO_PAP); Log(LF_INFO|LF_PROTO, "%s: LCP ConfNak: Unknown digest(%#x) [CHAP->PAP]", port, digest); } else { new_opts.Set(P_AUTH, PPP_PROTO_CHAP); Log(LF_INFO|LF_PROTO, "%s: LCP ConfReq: [CHAP]", port); } } else if (auth_proto == PPP_PROTO_PAP) { if (len != LCP_OPT_PAP_LEN) { // Wrong size, drop packet Log(LF_INFO|LF_PROTO, "%s: LCP ConfNak: [PAP], Invalid size (%d)", port, len); goto out; } new_opts.Set(P_AUTH, PPP_PROTO_PAP); Log(LF_INFO|LF_PROTO, "%s: LCP ConfNak: [PAP]", port); } else { // Unknown authentication protocol, NAK and suggest PAP as a minimum if (len > 2) packet->Trim(len - 2); new_opts.Set(P_AUTH, PPP_PROTO_PAP); Log(LF_INFO|LF_PROTO, "%s: LCP ConfNak: [AUTH] Unknown protocol (%#x)", port, auth_proto); } break; } case LCP_OPT_MRU: { if (len != LCP_OPT_MRU_LEN) { // Drop packet Log(LF_INFO|LF_PROTO, "<7>%s: LCP ConfNak: [MRU], Invalid Length (%d)", port, len); goto out; } u16 sugg_mru = packet->Pull16(); // The MRU we asked for is unacceptable, just use the default u16 mru = m_parent->m_hard_mru < LCP_OPT_MRU_DEFAULT ? m_parent->m_hard_mru : LCP_OPT_MRU_DEFAULT; if (sugg_mru < mru && sugg_mru >= 576) mru = sugg_mru; new_opts.Set(P_MRU, mru); Log(LF_INFO|LF_PROTO, "%s: LCP ConfNak: [MRU], using default (%u)", port, mru); break; } case LCP_OPT_MAGIC: { if (len != LCP_OPT_MAGIC_LEN) { // Drop packet Log(LF_INFO|LF_PROTO, "%s: LCP ConfNak: [Magic], Invalid Length (%d)", port, len); goto out; } u32 magic = packet->Pull32(); // See if we're looped back if (m_parent->m_policy.Get(P_MAGIC) == magic) { Log(LF_INFO|LF_RX, "%s: LCP ConfNak: Possible Loopback", port); m_parent->m_loop_count++; if (m_parent->m_loop_count > MAX_LOOP_COUNT) { Log(LF_ERROR|LF_PROTO, "%s: Loopback confirmed, closing connection", port); m_parent->Close(-2, "Connection looped back"); goto out; } } break; } case LCP_OPT_PFC: if(len != LCP_OPT_PFC_LEN) { // Drop packet Log(LF_INFO|LF_PROTO, "%s: LCP ConfNak: [PFC], Invalid Length", port); goto out; } // This should really be a rej, so it must be a request if (want.Get(P_PFC)) { // This should be a reject! Log(LF_INFO|LF_PROTO, "%s: LCP ConfNak: [PFC]", port); new_opts.Set(P_PFC, 0); } else { // They want it? Log(LF_INFO|LF_PROTO, "%s: LCP ConfNak: [PFC] will try", port); new_opts.Set(P_PFC, 1); } break; case LCP_OPT_ACFC: if (len != LCP_OPT_ACFC_LEN) { // Drop packet Log(LF_INFO|LF_PROTO, "%s: LCP ConfNak: [ACFC], Invalid Length", port); goto out; } if (want.Get(P_ACFC)) { // Should be a reject Log(LF_INFO|LF_PROTO, "%s: LCP ConfNak: [ACFC]", port); new_opts.Set(P_ACFC, 0); } else { // They want it? Log(LF_INFO|LF_PROTO, "%s: LCP ConfNak: [ACFC] will try", port); new_opts.Set(P_ACFC, 1); } break; case LCP_OPT_ACCM: { if (len != LCP_OPT_ACCM_LEN) { // Drop packet Log(LF_INFO|LF_PROTO, "%s: LCP ConfNak: [ACCM], Invalid Length", port); goto out; } u32 accm = packet->Pull32(); // And what they want to our ACCM accm |= new_opts.Get(P_ACCM); new_opts.Set(P_ACCM, accm); Log(LF_INFO|LF_PROTO, "%s: LCP ConfNak: [ACCM], now %#x", port, accm); break; } case LCP_OPT_MRRU: if(len != LCP_OPT_MRRU_LEN) { // Drop packet Log(LF_INFO|LF_PROTO, "%s: LCP ConfNak: [MRRU], Invalid Length", port); goto out; } new_opts.Set(P_MRRU, packet->Pull16()); break; case LCP_OPT_SSN: if(len != LCP_OPT_SSN_LEN) { // Drop packet Log(LF_INFO|LF_PROTO, "%s: LCP ConfNak: [SSNHF], Invalid Length", port); goto out; } if (want.Get(P_SSN)) { // Should be REJ! Log(LF_INFO|LF_PROTO, "%s: LCP ConfNak: [SSNHF]", port); new_opts.Set(P_SSN, 0); } else { // They want it Log(LF_INFO|LF_PROTO, "%s: LCP ConfNak: [SSNHF] will try", port); new_opts.Set(P_SSN, 1); } break; case LCP_OPT_EPD: { if (len < LCP_OPT_EPD_LEN) { // Drop packet Log(LF_INFO|LF_PROTO, "%s: LCP ConfNak: [EPD], Invalid Length", port); goto out; } u8 aClass = packet->Pull8(); packet->Pull(addr, len - 3); // Our class is no good but this shouldn't happen?? Log(LF_INFO|LF_PROTO, "%s: LCP ConfNak: [EPD(%d)(%s)]", port, aClass, addr); want.Set(P_EPD_CLASS, 0); break; } case LCP_OPT_LDISC: { if (len != LCP_OPT_LDISC_LEN) { // Drop packet Log(LF_INFO|LF_PROTO, "%s: LCP ConfNak: [LDISC], Invalid Length", port); goto out; } packet->Pull16(); /* discard ldisc */ // The LDISC we asked for is unacceptable, no BoD Log(LF_INFO|LF_PROTO, "%s: LCP ConfNak: [LDISC], disabling", port); new_opts.Set(P_LDISC, 0); break; } default: // Must ignore everything else! if (len > 2) packet->Trim(len - 2); break; } } // If we get here the packet must good and we can set the new options m_parent->m_npolicy.Set(new_opts); // Tell the state machine CFsmProtocol::RcvConfNak(); out: DebugVoidReturn; } // // // void CLcpProtocol::RcvConfRej(CPppPacket *packet) { policy_t new_opts; char *port = m_parent->m_channel->device_name; u8 addr[LCP_OPT_EPD_ADDR_LEN]; DebugEnter("CLcpProtocol::RcvConfRej()"); Log(LF_INFO|LF_PROTO|LF_RX, "%s: LCP Rcvd ConfRej -->", port); new_opts.Set(m_parent->m_npolicy); while(packet->GetLength()) { u8 type = packet->Pull8(); u8 len = packet->Pull8(); if (len < 2) { Log(LF_INFO|LF_PROTO, "%s: LCP ConfRej: Dropping packet -- Invalid Length", port); goto out; } switch(type) { case LCP_OPT_PFC: if (len != LCP_OPT_PFC_LEN) { // Drop packet Log(LF_INFO|LF_PROTO, "%s: LCP ConfRej: [PFC], Invalid Length", port); goto out; } new_opts.Set(P_PFC, 0); Log(LF_INFO|LF_PROTO, "%s: LCP ConfRej: [PFC]", port); break; case LCP_OPT_ACFC: if (len != LCP_OPT_ACFC_LEN) { // Drop packet Log(LF_INFO|LF_PROTO, "%s: LCP ConfRej: [ACFC], Invalid Length", port); goto out; } new_opts.Set(P_ACFC, 0); Log(LF_INFO|LF_PROTO, "%s: LCP ConfRej: [ACFC]", port); break; case LCP_OPT_MRRU: if (len != LCP_OPT_MRRU_LEN) { // Drop packet Log(LF_INFO|LF_PROTO, "%s: LCP ConfRej: [MRRU], Invalid Length", port); goto out; } packet->Pull16(); // Don't do MP new_opts.Set(P_MRRU, 0); Log(LF_INFO|LF_PROTO, "%s: LCP ConfRej: [MRRU], Disabling MP", port); break; case LCP_OPT_SSN: if (len != LCP_OPT_SSN_LEN) { // Drop packet Log(LF_INFO|LF_PROTO, "%s: LCP ConfRej: [SSNHF], Invalid Length", port); goto out; } new_opts.Set(P_SSN, 0); break; case LCP_OPT_EPD: if (len < LCP_OPT_EPD_LEN) { // Drop packet Log(LF_INFO|LF_PROTO, "%s: LCP ConfRej: [EPD], Invalid Length", port); goto out; } packet->Pull8(); packet->Pull(addr, len - 3); new_opts.Set(P_EPD_CLASS, 0); Log(LF_INFO|LF_PROTO, "%s: LCP ConfRej: [EPD]", port); break; case LCP_OPT_LDISC: if(len != LCP_OPT_LDISC_LEN) { // Drop packet Log(LF_INFO|LF_PROTO, "%s: LCP ConfRej: [LDISC], Invalid Length", port); goto out; } packet->Pull16(); // Use the default new_opts.Set(P_LDISC, 0); Log(LF_INFO|LF_PROTO, "%s: LCP ConfRej: [LDISC], Disabling BACP", port); break; case LCP_OPT_MRU: if(len != LCP_OPT_MRU_LEN) { // Drop packet Log(LF_INFO|LF_PROTO, "%s: LCP ConfNak: [MRU], Invalid Length (%d)", port, len); goto out; } packet->Pull16(); // Pull off the mru // The MRU we asked for is unacceptable, just use the default new_opts.Set(P_MRU, m_parent->m_hard_mru < LCP_OPT_MRU_DEFAULT ? m_parent->m_hard_mru : LCP_OPT_MRU_DEFAULT); Log(LF_INFO|LF_PROTO, "%s: LCP ConfRej: [MRU], using default (%d)", port, LCP_OPT_MRU_DEFAULT); break; default: // Fatal option rejected, close the link Log(LF_DEBUG|LF_PROTO, "Option? %d len %d", type, len); m_parent->Close(-2, "LCP: Fatal option rejected"); goto out; } } m_parent->m_npolicy.Set(new_opts); // Same state transition as Nak CFsmProtocol::RcvConfNak(); out: DebugVoidReturn; } // // // void CLcpProtocol::RcvConfAck(CPppPacket *) { DebugEnter("CLcpProtocol::RcvConfAck()"); memcpy(&m_parent->m_lpolicy, &m_parent->m_npolicy, sizeof(policy_t)); CFsmProtocol::RcvConfAck(); DebugVoidReturn; } // // // void CLcpProtocol::SndTermAck() { CPppPacket ackPkt; DebugEnter("CLcpProtocol::SndTermAck()"); ackPkt.Reserve(PPP_HEADER_LENGTH + LCP_HEADER_LENGTH); ackPkt.Push16(4); ackPkt.Push8(m_lastRcvdIdent); ackPkt.Push8(LCP_CODE_TERMACK); ackPkt.Push16(PPP_PROTO_LCP); m_parent->Output(&ackPkt); DebugVoidReturn; } void CLcpProtocol::SndConfReq() { CPppPacket c; policy_t &want = m_parent->m_npolicy; char *port = m_parent->m_channel->device_name; DebugEnter("CLcpProtocol::SndConfReq()"); if (!cntConfig) { Log(LF_DEBUG|LF_PROTO, "Config attempts failed: Closing"); m_parent->Close(-2, "LCP:Max config attempts reached: Check LCP options"); DebugVoidReturn; } // Reserving header space in packet c.Reserve(PPP_HEADER_LENGTH + LCP_HEADER_LENGTH); Log(LF_INFO|LF_PROTO, "%s: m_hard_mru = %d", port, m_parent->m_hard_mru); if (want.Get(P_MRU) && (want.Get(P_MRU) != LCP_OPT_MRU_DEFAULT)) { Log(LF_INFO|LF_PROTO, "%s: Adding option MRU %d", port, want.Get(P_MRU)); c.Put8(LCP_OPT_MRU); c.Put8(LCP_OPT_MRU_LEN); c.Put16(want.Get(P_MRU)); } if (want.Get(P_AUTH) == PPP_PROTO_CHAP) { Log(LF_INFO|LF_PROTO, "%s: Adding option CHAP", port); c.Put8(LCP_OPT_AUTH_CHAP); c.Put8(LCP_OPT_CHAP_LEN); c.Put16(PPP_PROTO_CHAP); c.Put8(LCP_OPT_CHAP_MD5); } else if (want.Get(P_AUTH) == PPP_PROTO_PAP) { Log(LF_INFO|LF_PROTO, "%s: Adding option PAP", port); c.Put8(LCP_OPT_AUTH_PAP); c.Put8(LCP_OPT_PAP_LEN); c.Put16(PPP_PROTO_PAP); } if (want.Get(P_MAGIC)) { Log(LF_INFO|LF_PROTO, "%s :Adding option MAGIC %#x", port, want.Get(P_MAGIC)); c.Put8(LCP_OPT_MAGIC); c.Put8(LCP_OPT_MAGIC_LEN); c.Put32(want.Get(P_MAGIC)); } if (want.Get(P_PFC)) { Log(LF_INFO|LF_PROTO, "%s: Adding option PFC", port); c.Put8(LCP_OPT_PFC); c.Put8(LCP_OPT_PFC_LEN); } if (want.Get(P_ACFC)) { Log(LF_INFO|LF_PROTO, "%s: Adding option ACFC", port); c.Put8(LCP_OPT_ACFC); c.Put8(LCP_OPT_ACFC_LEN); } if (want.Get(P_ACCM)) { Log(LF_INFO|LF_PROTO, "%s: Adding option ACCM %#x", port, want.Get(P_ACCM)); c.Put8(LCP_OPT_ACCM); c.Put8(LCP_OPT_ACCM_LEN); c.Put32(want.Get(P_ACCM)); } if (want.Get(P_MRRU)) { Log(LF_INFO|LF_PROTO, "%s: Adding option MRRU %d", port, want.Get(P_MRRU)); c.Put8(LCP_OPT_MRRU); c.Put8(LCP_OPT_MRRU_LEN); c.Put16(want.Get(P_MRRU)); if (want.Get(P_SSN)) { Log(LF_INFO|LF_PROTO, "%s: Adding option SSN", port); c.Put8(LCP_OPT_SSN); c.Put8(LCP_OPT_SSN_LEN); } u8 epd_class = want.Get(P_EPD_CLASS); u8 epd_length = want.Get(P_EPD_LENGTH); if (epd_class) { Log(LF_INFO|LF_PROTO, "%s: Adding option EPD class %d", port, epd_class); c.Put8(LCP_OPT_EPD); c.Put8(LCP_OPT_EPD_LEN + epd_length); c.Put8(epd_class); c.Put(want.epd_addr, epd_length); } } if (want.Get(P_LDISC)) { Log(LF_INFO|LF_PROTO, "%s: Adding option LDISC %#x", port, want.Get(P_LDISC)); c.Put8(LCP_OPT_LDISC); c.Put8(LCP_OPT_LDISC_LEN); c.Put16(want.Get(P_LDISC)); } // Decrement the config restart count if (cntConfig) { Log(LF_DEBUG|LF_PROTO, "Decrementing cntConfig(%d)", cntConfig); cntConfig--; } Log(LF_DEBUG|LF_PROTO, "cntConfig: %d", cntConfig); c.Push16(c.GetLength() + LCP_HEADER_LENGTH); c.Push8(++m_lastSentIdent); c.Push8(CONFREQ); c.Push16(PPP_PROTO_LCP); m_parent->Output(&c); DebugVoidReturn; } void CLcpProtocol::SndTermReq() { CPppPacket tPkt; DebugEnter("CLcpProtocol::SndTermReq()"); if (!cntTerminate) { Log(LF_DEBUG|LF_PROTO, "Attempted %d times to terminate connection", maxTerminate); m_parent->Close(-2, "LCP: Peer not responding to Terminate Request"); DebugVoidReturn; } // Decrement the terminate request count if (cntTerminate) cntTerminate--; // Send the request tPkt.Reserve(PPP_HEADER_LENGTH + LCP_HEADER_LENGTH); tPkt.Push16(LCP_HEADER_LENGTH); tPkt.Push8(++m_lastSentIdent); tPkt.Push8(LCP_CODE_TERMREQ); tPkt.Push16(PPP_PROTO_LCP); m_parent->Output(&tPkt); DebugVoidReturn; } void CLcpProtocol::ThisLayerFinished(const char *why) { char tmp[1024]; strcpy(tmp, "LCP: "); strncat(tmp, why, sizeof(tmp)-32); if (!m_rxPackets) strcat(tmp, " -- no packets received"); DebugEnter("CLcpProtocol::ThisLayerFinished()"); //printk("CLcpProtocol::ThisLayerFinished() -- Hangup channel %s\n", m_parent->m_channel->device_name); m_parent->Close(-2, tmp); m_parent->Hangup(); DebugVoidReturn; } void CLcpProtocol::ThisLayerUp() { DebugEnter("CLcpProtocol::ThisLayerUp()"); /* start doing our echo probes */ m_echoSent = 0; m_echoTime = 100 * 60; // (long)m_parent->m_lpolicy.Get(P_ECHO); if (m_echoTime) echoTimer.Add(this); // Proceed to the authentication phase if there is authentication to do // otherwise go straight to the network phase Log(LF_DEBUG|LF_PROTO, "Local Auth %#x, Remote Auth %#x", m_parent->m_lpolicy.Get(P_AUTH), m_parent->m_rpolicy.Get(P_AUTH)); if (m_parent->m_lpolicy.Get(P_AUTH) || m_parent->m_rpolicy.Get(P_AUTH)) { // Authentication is required, bring up the authentication protocols m_parent->m_phase = PHASE_AUTHENTICATE; m_parent->m_papProto.Up(); m_parent->m_chapProto.Up(); } else { m_parent->Ready(); } DebugVoidReturn; } void CLcpProtocol::ThisLayerDown() { DebugEnter("CLcpProtocol::ThisLayerDown()"); echoTimer.Remove(this); m_echoTimer.Stop(); m_lastEchoIdent = 0xffff; m_rxPackets = 0; m_parent->m_phase = PHASE_TERMINATE; /* close the link */ //m_parent->Close(); DebugVoidReturn; } void CLcpProtocol::SndCodeReject(CPppPacket *packet) { CPppPacket cr; DebugEnter("CLcpProtocol::SndCodeReject()"); cr.Reserve(PPP_HEADER_LENGTH + LCP_HEADER_LENGTH); // Truncate the packet if necessary if(packet->GetLength() + LCP_HEADER_LENGTH > m_parent->m_rpolicy.Get(P_MRU)) { packet->Chop(packet->GetLength() + LCP_HEADER_LENGTH - m_parent->m_rpolicy.Get(P_MRU)); } cr.Put(packet); cr.Push16(packet->GetLength() + LCP_HEADER_LENGTH + PPP_HEADER_LENGTH); cr.Push8(m_lastRcvdIdent); cr.Push8(LCP_CODE_CODEREJ); cr.Push16(PPP_PROTO_LCP); m_parent->Output(&cr); DebugVoidReturn; } void CLcpProtocol::SndEchoReply(CPppPacket *packet) { u32 magic = packet->Pull32(); CPppPacket rpl_pkt; DebugEnter("CLcpProtocol::SndEchoReply()"); if (magic != 0 && magic == m_parent->m_lpolicy.Get(P_MAGIC)) { // Loopback m_parent->m_loop_count++; if(m_parent->m_loop_count > MAX_LOOP_COUNT) { Log(LF_ERROR|LF_PROTO, "%s: Loopback confirmed, closing connection", m_parent->m_channel->device_name); m_parent->Close(-2, "LCP: Connection looped back"); } DebugVoidReturn; } rpl_pkt.Reserve(PPP_HEADER_LENGTH + LCP_HEADER_LENGTH + sizeof(u32)); rpl_pkt.Push32(m_parent->m_lpolicy.Get(P_MAGIC)); rpl_pkt.Push16(packet->GetLength() + LCP_HEADER_LENGTH + sizeof(u32)); rpl_pkt.Push8(m_lastRcvdIdent); rpl_pkt.Push8(LCP_CODE_ECHORPLY); rpl_pkt.Push16(PPP_PROTO_LCP); rpl_pkt.Put(packet); m_parent->Output(&rpl_pkt); DebugVoidReturn; } // // We received an LCP code-reject packet // void CLcpProtocol::RcvCodeReject(CPppPacket *pkt) { int i = 0; u8 code; DebugEnter("CLcpProtocol::RcvCodeReject()"); code = *((const u8 *)pkt); // Determine if the code is fatal or not while(fatalCodes[i] != '\0') { if(fatalCodes[i] == code) { // Fatal, close the link Log(LF_INFO|LF_PROTO, "%s: Rcv'd fatal LCP code reject code=%d", m_parent->m_channel->device_name, code); m_parent->Close(-2, "LCP: Received fatal code reject"); DebugVoidReturn; } i++; } // The rejected code is not fatal, reconfigure to stop // sending the code? CFsmProtocol::RcvCodeReject(); DebugVoidReturn; } // // We received an LCP protocol-reject packet // void CLcpProtocol::RcvProtoReject(CPppPacket *pkt) { int i = 0; u16 id = pkt->Pull16(); DebugEnter("CLcpProtocol::RcvProtoReject()"); // Determine if the protocol is fatal or not while (fatalProtos[i] != '\0') { if(fatalProtos[i] == id) { // Fatal, close the link m_parent->Close(-2, "LCP: Fatal protocol rejected"); DebugVoidReturn; } i++; } // The rejected protocol is not fatal, reconfigure to stop // sending the protocol CFsmProtocol::RcvCodeReject(); DebugVoidReturn; } void CLcpProtocol::RejectProtocol(CPppPacket *packet) { CPppPacket rejPkt; u16 proto = packet->Pull16(); u16 len = packet->GetLength(); DebugEnter("CLcpProtocol::RejectProtocol()"); u16 mru = m_parent->m_rpolicy.Get(P_MRU); if (!mru) len = len > 1492 ? 1492 : len; else if (len + 8 > mru) len = mru > 8 ? mru - 8 : 0; packet->Pull(&rejPkt, len); rejPkt.Push16(proto); rejPkt.Push16(rejPkt.GetLength() + 4); rejPkt.Push8(m_lastSentIdent++); rejPkt.Push8(LCP_CODE_PROTOREJ); rejPkt.Push16(PPP_PROTO_LCP); m_parent->Output(&rejPkt); DebugVoidReturn; }