/* * mlcp.cc - Self explanitory * Copyright (C) 1997-2000 SpellCaster Telecommunications Inc. * $Id: mlcp.cc,v 1.2 2004/04/07 15:15:51 bcrl Exp $ * Released under the GNU Public License. See LICENSE file for details. */ #include "debug.h" #include "mlcp.h" #include "iface.h" static unsigned char fatalCodes[] = { CONFREQ, CONFACK, CONFNAK, CONFREJ, TERMREQ, TERMACK, CODEREJ }; CMlcpProtocol::CMlcpProtocol() { DebugEnter("CMlcpProtocol::CMlcpProtocol()"); m_protocolName = "MLCP"; m_parent = NULL; our_mrru = 1500; their_mrru = 1500; acked_mrru = 0; our_ssn = 0; their_ssn = 0; acked_ssn = 0; DebugExit(); } CMlcpProtocol::~CMlcpProtocol() { DebugEnter("CMlcpProtocol::~CMlcpProtocol()"); DebugExit(); } void CMlcpProtocol::Input(CPppPacket *pkt) { u8 code = pkt->Pull8(); u8 ident = pkt->Pull8(); /*u16 len =*/ pkt->Pull16(); DebugEnter("CMlcpProtocol::Input()"); switch(code) { case CONFREQ: Log(LF_DEBUG|LF_PROTO, "Mlcp CONFREQ"); m_lastRcvdIdent = ident; RcvConfReq(pkt); break; case CONFACK: Log(LF_DEBUG|LF_PROTO, "Mlcp CONFACK"); if (ident != m_lastSentIdent) { // Drop Packet Log(LF_DEBUG|LF_PROTO, "MLCP ConfAck, Bad Ident...dropping"); DebugVoidReturn; } RcvConfAck(pkt); break; case CONFNAK: Log(LF_DEBUG|LF_PROTO, "Mlcp CONFNAK"); if (ident != m_lastSentIdent) { // Drop Packet Log(LF_INFO|LF_PROTO, "MLCP ConfNak, Bad Ident...dropping"); DebugVoidReturn; } RcvConfNak(pkt); break; case CONFREJ: Log(LF_DEBUG|LF_PROTO, "Mlcp CONFREJ"); if (ident != m_lastSentIdent) { // Drop Packet Log(LF_DEBUG|LF_PROTO, "MLCP ConfRej, Bad Ident...dropping"); DebugVoidReturn; } RcvConfRej(pkt); break; case TERMREQ: Log(LF_DEBUG|LF_PROTO, "Mlcp TERMREQ"); m_lastRcvdIdent = ident; RcvTermReq(); break; case TERMACK: Log(LF_DEBUG|LF_PROTO, "Mlcp TERMACK"); if (ident != m_lastSentIdent) { // Drop Packet Log(LF_DEBUG|LF_INFO, "ICPC TermAck, Bad Ident...dropping"); DebugVoidReturn; } RcvTermAck(); break; case CODEREJ: Log(LF_DEBUG|LF_PROTO, "Mlcp CODEREJ"); RcvCodeReject(pkt); break; default: Log(LF_DEBUG|LF_PROTO, "Mlcp Unknown"); SndCodeReject(pkt); RcvUnknownCode(); break; } DebugVoidReturn; } void CMlcpProtocol::RcvConfReq(CPppPacket *pkt) { OptionSet_t ao; OptionSet_t *ack_opts = &ao; u16 mrru = 0; u8 rdata[256]; DebugEnter("CMlcpProtocol::RcvConfReq()"); Log(LF_DEBUG|LF_PROTO, "MLCP Rcvd ConfReq -->"); ack_opts->Set(m_acked); m_ackPkt.Clear(); m_rejPkt.Clear(); m_nakPkt.Clear(); m_ackPkt.Reserve(PPP_HEADER_LENGTH + MLCP_HEADER_LENGTH); m_rejPkt.Reserve(PPP_HEADER_LENGTH + MLCP_HEADER_LENGTH); m_nakPkt.Reserve(PPP_HEADER_LENGTH + MLCP_HEADER_LENGTH); while(pkt->GetLength()) { u8 type = pkt->Pull8(); u8 len = pkt->Pull8(); Log(LF_DEBUG|LF_PROTO, "Checking type: %x, len: %d", type, len); switch(type) { case MLCP_OPT_MRRU: Log(LF_DEBUG|LF_PROTO, "Checking MLCP MRRU length"); if (len != MLCP_OPT_MRRU_LEN) { // Wrong size, reject option Log(LF_DEBUG|LF_PROTO, "Wrong size - rejecting"); pkt->Pull(rdata, len > 2 ? len - 2 : 0); m_rejPkt.Put8(type); m_rejPkt.Put8(len); m_rejPkt.Put(rdata, len > 2 ? len - 2 : 0); Log(LF_DEBUG|LF_PROTO, "MLCP ConfReq: [MRRU], Invalid size (%d)", len); break; } mrru = pkt->Pull16(); if (mrru >= MLCP_OPT_MRRU_MIN) { m_ackPkt.Put8(type); m_ackPkt.Put8(len); m_ackPkt.Put16(mrru); their_mrru = mrru; } else { m_nakPkt.Put8(type); m_nakPkt.Put8(len); m_nakPkt.Put16(MLCP_OPT_MRRU_MIN); Log(LF_INFO|LF_PROTO, "%s: MLCP ConfReq: MRRU(%d) -> MRRU(%d)", m_parent->m_channel->device_name, mrru, MLCP_OPT_MRRU_MIN); } break; case MLCP_OPT_SSN: if (len != MLCP_OPT_SSN_LEN) { m_rejPkt.Put8(type); m_rejPkt.Put8(len); if (len > 2) pkt->Pull(&m_rejPkt, len - 2); fprintf(stderr, "mlcp:RcvConfReq: rej ssn\n"); break; } m_ackPkt.Put8(type); m_ackPkt.Put8(len); their_ssn = 1; fprintf(stderr, "mlcp:RcvConfReq: their_ssn = 1\n"); break; default: Log(LF_DEBUG|LF_PROTO, "Unknown MLCP option 0x%02x len %d", type, len); // Reject this unknown option m_rejPkt.Put8(type); m_rejPkt.Put8(len); if (len > 2) { pkt->Pull(rdata, len - 2); m_rejPkt.Put(rdata, len - 2); } break; } } // If we are rejecting, send it if (m_rejPkt.GetLength()) { m_rejPkt.Push16(m_rejPkt.GetLength() + MLCP_HEADER_LENGTH); m_rejPkt.Push8(m_lastRcvdIdent); m_rejPkt.Push8(CONFREJ); m_rejPkt.Push16(PPP_PROTO_MLCP); RcvBadConfReq(); goto out; } // If we are naking, send it if (m_nakPkt.GetLength()) { m_nakPkt.Push16(m_nakPkt.GetLength() + MLCP_HEADER_LENGTH); m_nakPkt.Push8(m_lastRcvdIdent); m_nakPkt.Push8(CONFNAK); m_nakPkt.Push16(PPP_PROTO_MLCP); RcvBadConfReq(); goto out; } m_acked.Set(ack_opts); m_ackPkt.Push16(m_ackPkt.GetLength() + MLCP_HEADER_LENGTH); m_ackPkt.Push8(m_lastRcvdIdent); m_ackPkt.Push8(CONFACK); m_ackPkt.Push16(PPP_PROTO_MLCP); RcvGoodConfReq(); out: DebugVoidReturn; } void CMlcpProtocol::RcvConfNak(CPppPacket *packet) { DebugEnter("CMlcpProtocol::RcvConfNak()"); Log(LF_INFO|LF_PROTO, "MLCP Rcvd ConfNak -->"); while (packet->GetLength()) { u8 type = packet->Pull8(); u8 len = packet->Pull8(); switch (type) { case MLCP_OPT_MRRU: /*mrru =*/ packet->Pull16(); break; case MLCP_OPT_SSN: our_ssn = 1; break; default: if (len > 2) packet->Trim(len - 2); break; } } CFsmProtocol::RcvConfNak(); DebugVoidReturn; } void CMlcpProtocol::RcvConfRej(CPppPacket *pkt) { DebugEnter("CMlcpProtocol::RcvConfRej()"); Log(LF_INFO|LF_PROTO, "MLCP Rcvd ConfRej -->"); while (pkt->GetLength()) { u8 type = pkt->Pull8(); u8 len = pkt->Pull8(); switch (type) { case MLCP_OPT_MRRU: our_mrru = 0; break; case MLCP_OPT_SSN: our_ssn = 0; break; default: if (len > 2) pkt->Trim(len - 2); break; } } // Same state transition so use Nak CFsmProtocol::RcvConfNak(); DebugVoidReturn; } void CMlcpProtocol::RcvConfAck(CPppPacket *pkt) { DebugEnter("CMlcpProtocol::RcvConfAck()"); Log(LF_INFO|LF_PROTO, "MLCP Rcvd ConfAck -->"); while (pkt->GetLength()) { u8 type = pkt->Pull8(); u8 len = pkt->Pull8(); switch (type) { case MLCP_OPT_MRRU: acked_mrru = pkt->Pull16(); break; case MLCP_OPT_SSN: acked_ssn = 1; break; default: if (len > 2) pkt->Trim(len - 2); break; } } CFsmProtocol::RcvConfAck(); DebugVoidReturn; } void CMlcpProtocol::SndConfAck() { DebugEnter("CMlcpProtocol::SndConfAck()"); m_parent->Output(&m_ackPkt); DebugVoidReturn; } void CMlcpProtocol::SndConfNak() { DebugEnter("CMlcpProtocol::SndConfNak()"); if (m_rejPkt.GetLength()) { Log(LF_DEBUG|LF_PROTO, "LCP Send ConfRej: len=%d, ident=%d", m_rejPkt.GetLength(), m_lastRcvdIdent); m_parent->Output(&m_rejPkt); } else { Log(LF_DEBUG|LF_PROTO, "LCP Send ConfNak: len=%d, ident=%d", m_nakPkt.GetLength(), m_lastRcvdIdent); m_parent->Output(&m_nakPkt); } DebugVoidReturn; } void CMlcpProtocol::SndTermReq() { CPppPacket reqPkt; DebugEnter("CMlcpProtocol::SndTermReq()"); if(!cntTerminate) { Log(LF_DEBUG|LF_PROTO, "Attempted to terminate %d times", maxTerminate); m_parent->Close(-2, "MLCP: Peer not responding to Terminate Request"); DebugVoidReturn; } reqPkt.Reserve(PPP_HEADER_LENGTH + MLCP_HEADER_LENGTH); reqPkt.Push16(4); reqPkt.Push8(m_lastSentIdent++); reqPkt.Push8(TERMREQ); reqPkt.Push16(PPP_PROTO_MLCP); if (cntTerminate) cntTerminate--; m_parent->Output(&reqPkt); DebugVoidReturn; } void CMlcpProtocol::SndTermAck() { CPppPacket ackPkt; DebugEnter("CMlcpProtocol::SndTermAck()"); ackPkt.Reserve(PPP_HEADER_LENGTH + MLCP_HEADER_LENGTH); ackPkt.Push16(4); ackPkt.Push8(m_lastRcvdIdent); ackPkt.Push8(TERMACK); ackPkt.Push16(PPP_PROTO_MLCP); m_parent->Output(&ackPkt); DebugVoidReturn; } void CMlcpProtocol::SndConfReq() { CPppPacket c; DebugEnter("CMlcpProtocol::SndConfReq()"); if (!cntConfig) { Log(LF_DEBUG|LF_PROTO, "Config attempts failed: Closing"); m_parent->Close(-2, "MLCP: Max Config attempts reached: Check MLCP options"); DebugVoidReturn; } if (!our_mrru && m_parent->ifOptions.pol && m_parent->ifOptions.pol->Get(P_MRRU)) our_mrru = m_parent->ifOptions.pol->Get(P_MRRU); if (m_parent->ifOptions.pol && m_parent->ifOptions.pol->Get(P_MLCP_SSN)) our_ssn = m_parent->ifOptions.pol->Get(P_MLCP_SSN); c.Reserve(PPP_HEADER_LENGTH + MLCP_HEADER_LENGTH); c.Put8(MLCP_OPT_MRRU); c.Put8(MLCP_OPT_MRRU_LEN); c.Put16(our_mrru); if (our_ssn) { c.Put8(LCP_OPT_SSN); c.Put8(LCP_OPT_SSN_LEN); } c.Push16(c.GetLength() + MLCP_HEADER_LENGTH); c.Push8(++m_lastSentIdent); c.Push8(CONFREQ); c.Push16(PPP_PROTO_MLCP); if (cntConfig) { Log(LF_DEBUG|LF_PROTO, "Decrementing cntConfig(%d)", cntConfig); cntConfig--; } Log(LF_DEBUG|LF_PROTO, "cntConfig: %d", cntConfig); Log(LF_DEBUG|LF_PROTO, "Sending Mlcp ConfReq:"); LogPacket(LF_DEBUG|LF_PROTO, c.m_start, c.GetLength()); m_parent->Output(&c); DebugVoidReturn; } void CMlcpProtocol::SndCodeReject(CPppPacket *packet) { CPppPacket cr; DebugEnter("CMlcpProtocol::SndCodeReject()"); cr.Reserve(PPP_HEADER_LENGTH + LCP_HEADER_LENGTH); // Truncate the packet if necessary if(packet->GetLength() + LCP_HEADER_LENGTH > 1500) { packet->Chop(packet->GetLength() + LCP_HEADER_LENGTH - 1500); } cr.Put(packet); cr.Push16(packet->GetLength() + MLCP_HEADER_LENGTH + PPP_HEADER_LENGTH); cr.Push8(m_lastRcvdIdent); cr.Push8(CODEREJ); cr.Push16(PPP_PROTO_MLCP); m_parent->Output(&cr); DebugVoidReturn; } void CMlcpProtocol::RcvCodeReject(CPppPacket *pkt) { int i = 0; u8 code = *((u8 *)pkt); DebugEnter("CMlcpProtocol::RcvCodeReject()"); // Determine if the code is fatal or not while(fatalCodes[i] != '\0') { if(fatalCodes[i] == code) { // Fatal, close the link RcvFatalReject(); } i++; } // The rejected code is not fatal, reconfigure to stop // sending the code CFsmProtocol::RcvCodeReject(); DebugVoidReturn; } void CMlcpProtocol::ThisLayerFinished(const char *why) { DebugEnter("CMlcpProtocol::ThisLayerFinished()"); m_parent->Close(-2, why); DebugVoidReturn; } void CMlcpProtocol::ThisLayerUp() { fprintf(stderr, "CMlcpProtocol::ThisLayerUp() their(%u) our(%u) acked(%u)\n", their_mrru, our_mrru, acked_mrru); DebugEnter("CMlcpProtocol::ThisLayerUp()"); if (acked_mrru) { m_parent->m_lpolicy.Set(P_MRRU, acked_mrru); m_parent->m_rpolicy.Set(P_MRRU, acked_mrru); } if (acked_ssn) { m_parent->m_lpolicy.Set(P_MLCP_SSN, 1); m_parent->m_rpolicy.Set(P_MLCP_SSN, 1); m_parent->m_lpolicy.Set(P_SSN, 1); m_parent->m_rpolicy.Set(P_SSN, 1); } m_parent->Ready(); DebugVoidReturn; }