/* * ipcp.cc - Self explanitory * Copyright (C) 1997-2000 SpellCaster Telecommunications Inc. * $Id: ipcp.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 "ipcp.h" #include "iface.h" static unsigned char fatalCodes[] = { CONFREQ, CONFACK, CONFNAK, CONFREJ, TERMREQ, TERMACK, CODEREJ }; CIpcpProtocol::CIpcpProtocol() { DebugEnter("CIpcpProtocol::CIpcpProtocol()"); m_protocolName = "IPCP"; m_parent = NULL; DebugExit(); } CIpcpProtocol::~CIpcpProtocol() { DebugEnter("CIpcpProtocol::~CIpcpProtocol()"); DebugExit(); } void CIpcpProtocol::Input(CPppPacket *pkt) { u8 code = pkt->Pull8(); u8 ident = pkt->Pull8(); /*u16 len =*/ pkt->Pull16(); DebugEnter("CIpcpProtocol::Input()"); switch(code) { case CONFREQ: Log(LF_DEBUG|LF_PROTO, "Ipcp CONFREQ"); m_lastRcvdIdent = ident; RcvConfReq(pkt); break; case CONFACK: Log(LF_DEBUG|LF_PROTO, "Ipcp CONFACK"); if (ident != m_lastSentIdent) { // Drop Packet Log(LF_DEBUG|LF_PROTO, "IPCP ConfAck, Bad Ident...dropping"); DebugVoidReturn; } RcvConfAck(pkt); break; case CONFNAK: Log(LF_DEBUG|LF_PROTO, "Ipcp CONFNAK"); if (ident != m_lastSentIdent) { // Drop Packet Log(LF_INFO|LF_PROTO, "IPCP ConfNak, Bad Ident...dropping"); DebugVoidReturn; } RcvConfNak(pkt); break; case CONFREJ: Log(LF_DEBUG|LF_PROTO, "Ipcp CONFREJ"); if (ident != m_lastSentIdent) { // Drop Packet Log(LF_DEBUG|LF_PROTO, "IPCP ConfRej, Bad Ident...dropping"); DebugVoidReturn; } RcvConfRej(pkt); break; case TERMREQ: Log(LF_DEBUG|LF_PROTO, "Ipcp TERMREQ"); m_lastRcvdIdent = ident; RcvTermReq(); break; case TERMACK: Log(LF_DEBUG|LF_PROTO, "Ipcp 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, "Ipcp CODEREJ"); RcvCodeReject(pkt); break; default: Log(LF_DEBUG|LF_PROTO, "Ipcp Unknown"); SndCodeReject(pkt); RcvUnknownCode(); break; } DebugVoidReturn; } void CIpcpProtocol::RcvConfReq(CPppPacket *pkt) { OptionSet_t ao; OptionSet_t *ack_opts = &ao; u32 rem_addr; // Address of the remote?? u32 dns_prime; u32 dns_second; u8 rdata[256]; DebugEnter("CIpcpProtocol::RcvConfReq()"); Log(LF_DEBUG|LF_PROTO, "IPCP Rcvd ConfReq -->"); ack_opts->Set(m_acked); m_ackPkt.Clear(); m_rejPkt.Clear(); m_nakPkt.Clear(); m_ackPkt.Reserve(PPP_HEADER_LENGTH + IPCP_HEADER_LENGTH); m_rejPkt.Reserve(PPP_HEADER_LENGTH + IPCP_HEADER_LENGTH); m_nakPkt.Reserve(PPP_HEADER_LENGTH + IPCP_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 IPCP_OPT_ADDR: Log(LF_DEBUG|LF_PROTO, "Checking address length"); if (len != IPCP_OPT_ADDR_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, "IPCP ConfReq: [ADDR], Invalid size (%d)", len); } else { rem_addr = pkt->Pull32(); if ((!rem_addr) && (!m_will.rem_ip)) { // They want an address and we don't have one // to give them. We're screwed. Just send it back // to them and hope they can do better. Log(LF_DEBUG|LF_PROTO, "No address to supply"); m_nakPkt.Put8(type); m_nakPkt.Put8(len); m_nakPkt.Put32(rem_addr); } else if (m_will.rem_ip && (m_will.rem_ip != rem_addr)) { // We already have an address we want them to // use. Nak and suggest ours Log(LF_DEBUG|LF_PROTO, "Invalid request for address"); m_nakPkt.Put8(type); m_nakPkt.Put8(len); m_nakPkt.Put32(m_will.rem_ip); } else { // Ack Log(LF_DEBUG|LF_PROTO, "Request looks good"); m_ackPkt.Put8(type); m_ackPkt.Put8(len); m_ackPkt.Put32(rem_addr); ack_opts->rem_ip = rem_addr; } } break; case IPCP_OPT_PRIMARY_DNS: Log(LF_DEBUG|LF_PROTO, "Checking Primary DNS"); if (len != IPCP_OPT_ADDR_LEN) { // Wrong size, reject option 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, "IPCP ConfReq: [P-DNS], Invalid size (%d)", len); } else { dns_prime = pkt->Pull32(); if (!dns_prime && !m_will.dns_primary) { // They want an address and we don't have one // to give them. m_rejPkt.Put8(type); m_rejPkt.Put8(len); m_rejPkt.Put32(dns_prime); } else if (!dns_prime && m_will.dns_primary) { // They want an address and we have one // to give them. m_nakPkt.Put8(type); m_nakPkt.Put8(len); m_nakPkt.Put32(m_will.dns_primary); } else if ((dns_prime == m_will.dns_primary) || (dns_prime && !m_will.dns_primary)) { // Ack m_will.dns_primary = dns_prime; m_ackPkt.Put8(type); m_ackPkt.Put8(len); m_ackPkt.Put32(dns_prime); ack_opts->dns_primary = dns_prime; } } break; case IPCP_OPT_SECOND_DNS: Log(LF_DEBUG|LF_PROTO, "Checking Secondary DNS"); if (len != IPCP_OPT_ADDR_LEN) { // Wrong size, reject option 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, "IPCP ConfReq: [S-DNS], Invalid size (%d)", len); } else { dns_second = pkt->Pull32(); if (!dns_second && !m_will.dns_second) { // They want an address and we don't have one // to give them. m_rejPkt.Put8(type); m_rejPkt.Put8(len); m_rejPkt.Put32(dns_second); } else if (!dns_second && m_will.dns_second) { // They want an address and we have one // to give them. m_nakPkt.Put8(type); m_nakPkt.Put8(len); m_nakPkt.Put32(m_will.dns_second); } else if (dns_second == m_will.dns_second) { // Ack m_ackPkt.Put8(type); m_ackPkt.Put8(len); m_ackPkt.Put32(dns_second); ack_opts->dns_second = dns_second; } } break; default: Log(LF_DEBUG|LF_PROTO, "Unknown IPCP 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() + IPCP_HEADER_LENGTH); m_rejPkt.Push8(m_lastRcvdIdent); m_rejPkt.Push8(CONFREJ); m_rejPkt.Push16(PPP_PROTO_IPCP); RcvBadConfReq(); goto out; } // If we are naking, send it if (m_nakPkt.GetLength()) { m_nakPkt.Push16(m_nakPkt.GetLength() + IPCP_HEADER_LENGTH); m_nakPkt.Push8(m_lastRcvdIdent); m_nakPkt.Push8(CONFNAK); m_nakPkt.Push16(PPP_PROTO_IPCP); RcvBadConfReq(); goto out; } m_acked.Set(*ack_opts); m_ackPkt.Push16(m_ackPkt.GetLength() + IPCP_HEADER_LENGTH); m_ackPkt.Push8(m_lastRcvdIdent); m_ackPkt.Push8(CONFACK); m_ackPkt.Push16(PPP_PROTO_IPCP); RcvGoodConfReq(); out: DebugVoidReturn; } void CIpcpProtocol::RcvConfNak(CPppPacket *packet) { u32 ip; DebugEnter("CIpcpProtocol::RcvConfNak()"); Log(LF_INFO|LF_PROTO, "IPCP Rcvd ConfNak -->"); while (packet->GetLength()) { u8 type = packet->Pull8(); u8 len = packet->Pull8(); switch (type) { case IPCP_OPT_ADDR: ip = packet->Pull32(); if (m_acked.loc_ip == 0) { if (!ip) { m_parent->Close(-2, "IPCP: Peer did not provide an IP address"); goto out; } m_acked.loc_ip = ip; Log(LF_DEBUG|LF_PROTO, "Local IP set to %#x", ip); } #if 0 else if (ip && ip != m_acked.loc_ip) { char msg[256]; sprintf(msg, "IPCP: incompatible IP addresses local=%08x vs remote=%08x", m_acked.loc_ip, ip); m_parent->Close(-2, msg); goto out; } #endif break; default: if (len > 2) packet->Trim(len - 2); break; } } CFsmProtocol::RcvConfNak(); out: DebugVoidReturn; } void CIpcpProtocol::RcvConfRej(CPppPacket *) { DebugEnter("CIpcpProtocol::RcvConfRej()"); Log(LF_INFO|LF_PROTO, "IPCP Rcvd ConfRej -->"); // Same state transition so use Nak CFsmProtocol::RcvConfNak(); DebugVoidReturn; } void CIpcpProtocol::RcvConfAck(CPppPacket *) { DebugEnter("CIpcpProtocol::RcvConfAck()"); Log(LF_INFO|LF_PROTO, "IPCP Rcvd ConfAck -->"); Log(LF_DEBUG|LF_PROTO, "Local IP is %#x", m_acked.loc_ip); CFsmProtocol::RcvConfAck(); DebugVoidReturn; } void CIpcpProtocol::SndConfAck() { DebugEnter("CIpcpProtocol::SndConfAck()"); m_parent->OutputQ(&m_ackPkt); DebugVoidReturn; } void CIpcpProtocol::SndConfNak() { DebugEnter("CIpcpProtocol::SndConfNak()"); if (m_rejPkt.GetLength()) { Log(LF_DEBUG|LF_PROTO, "IPCP Send ConfRej: len=%d, ident=%d", m_rejPkt.GetLength(), m_lastRcvdIdent); m_parent->OutputQ(&m_rejPkt); } else { Log(LF_DEBUG|LF_PROTO, "IPCP Send ConfNak: len=%d, ident=%d", m_nakPkt.GetLength(), m_lastRcvdIdent); m_parent->OutputQ(&m_nakPkt); } DebugVoidReturn; } void CIpcpProtocol::SndTermReq() { CPppPacket reqPkt; DebugEnter("CIpcpProtocol::SndTermReq()"); if(!cntTerminate) { Log(LF_DEBUG|LF_PROTO, "Attempted to terminate %d times", maxTerminate); m_parent->Close(-2, "IPCP: Peer not responding to Terminate Request"); DebugVoidReturn; } reqPkt.Reserve(PPP_HEADER_LENGTH + IPCP_HEADER_LENGTH); reqPkt.Push16(4); reqPkt.Push8(m_lastSentIdent++); reqPkt.Push8(TERMREQ); reqPkt.Push16(PPP_PROTO_IPCP); if (cntTerminate) cntTerminate--; m_parent->OutputQ(&reqPkt); DebugVoidReturn; } void CIpcpProtocol::SndTermAck() { CPppPacket ackPkt; DebugEnter("CIpcpProtocol::SndTermAck()"); ackPkt.Reserve(PPP_HEADER_LENGTH + IPCP_HEADER_LENGTH); ackPkt.Push16(4); ackPkt.Push8(m_lastRcvdIdent); ackPkt.Push8(TERMACK); ackPkt.Push16(PPP_PROTO_IPCP); m_parent->OutputQ(&ackPkt); DebugVoidReturn; } void CIpcpProtocol::SndConfReq() { CPppPacket c; DebugEnter("CIpcpProtocol::SndConfReq()"); if (!cntConfig) { Log(LF_DEBUG|LF_PROTO, "Config attempts failed: Closing"); m_parent->Close(-2, "IPCP: Max Config attempts reached: Check IPCP options"); DebugVoidReturn; } c.Reserve(PPP_HEADER_LENGTH + IPCP_HEADER_LENGTH); Log(LF_DEBUG|LF_PROTO, "Local IP: %x", m_acked.loc_ip); c.Put8(IPCP_OPT_ADDR); c.Put8(IPCP_OPT_ADDR_LEN); c.Put32(m_acked.loc_ip); c.Push16(c.GetLength() + IPCP_HEADER_LENGTH); c.Push8(++m_lastSentIdent); c.Push8(CONFREQ); c.Push16(PPP_PROTO_IPCP); 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 Ipcp ConfReq:"); LogPacket(LF_DEBUG|LF_PROTO, c.m_start, c.GetLength()); m_parent->OutputQ(&c); DebugVoidReturn; } void CIpcpProtocol::SndCodeReject(CPppPacket *packet) { CPppPacket cr; DebugEnter("CIpcpProtocol::SndCodeReject()"); cr.Reserve(PPP_HEADER_LENGTH + IPCP_HEADER_LENGTH); // Truncate the packet if necessary if(packet->GetLength() + IPCP_HEADER_LENGTH > 1500) { packet->Chop(packet->GetLength() + IPCP_HEADER_LENGTH - 1500); } cr.Put(packet); cr.Push16(packet->GetLength() + IPCP_HEADER_LENGTH + PPP_HEADER_LENGTH); cr.Push8(m_lastRcvdIdent); cr.Push8(CODEREJ); cr.Push16(PPP_PROTO_IPCP); m_parent->OutputQ(&cr); DebugVoidReturn; } void CIpcpProtocol::RcvCodeReject(CPppPacket *pkt) { int i = 0; u8 code = *((u8 *)pkt); DebugEnter("CIpcpProtocol::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 CIpcpProtocol::ThisLayerFinished(const char *why) { DebugEnter("CIpcpProtocol::ThisLayerFinished()"); m_parent->Close(-2, why); DebugVoidReturn; } void CIpcpProtocol::ThisLayerUp() { DebugEnter("CIpcpProtocol::ThisLayerUp()"); m_parent->Ready(&m_acked); DebugVoidReturn; }