/* * ipv6cp.cc - Self explanitory * Copyright (C) 1997-2000 SpellCaster Telecommunications Inc. * 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 }; CIpv6cpProtocol::CIpv6cpProtocol() { DebugEnter("CIpv6cpProtocol::CIpv6cpProtocol()"); m_protocolName = "IPV6CP"; m_parent = NULL; m_their_token = 0; DebugExit(); } CIpv6cpProtocol::~CIpv6cpProtocol() { DebugEnter("CIpv6cpProtocol::~CIpv6cpProtocol()"); DebugExit(); } void CIpv6cpProtocol::Input(CPppPacket *pkt) { u8 code = pkt->Pull8(); u8 ident = pkt->Pull8(); /*u16 len =*/ pkt->Pull16(); DebugEnter("CIpv6cpProtocol::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, "IPV6CP 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, "IPV6CP 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, "IPV6CP 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 CIpv6cpProtocol::RcvConfReq(CPppPacket *pkt) { u64 rem_addr; u8 rdata[256]; DebugEnter("CIpv6cpProtocol::RcvConfReq()"); Log(LF_DEBUG|LF_PROTO, "IPV6CP Rcvd ConfReq -->"); m_ackPkt.Clear(); m_rejPkt.Clear(); m_nakPkt.Clear(); m_ackPkt.Reserve(PPP_HEADER_LENGTH + IPV6CP_HEADER_LENGTH); m_rejPkt.Reserve(PPP_HEADER_LENGTH + IPV6CP_HEADER_LENGTH); m_nakPkt.Reserve(PPP_HEADER_LENGTH + IPV6CP_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 IPV6CP_OPT_TOKEN: Log(LF_DEBUG|LF_PROTO, "Checking address length"); if (len != IPV6CP_OPT_TOKEN_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, "IPV6CP ConfReq: [ADDR], Invalid size (%d)", len); } else { rem_addr = pkt->Pull64(); #if 0 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 #endif { // Ack Log(LF_DEBUG|LF_PROTO, "Request looks good"); m_ackPkt.Put8(type); m_ackPkt.Put8(len); m_ackPkt.Put64(rem_addr); m_their_token = rem_addr; fprintf(stderr, "ipv6cp: acking remote address %016Lx\n", m_their_token); //ack_opts->rem_ip = rem_addr; } } break; default: Log(LF_DEBUG|LF_PROTO, "Unknown IPV6CP 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() + IPV6CP_HEADER_LENGTH); m_rejPkt.Push8(m_lastRcvdIdent); m_rejPkt.Push8(CONFREJ); m_rejPkt.Push16(PPP_PROTO_IPV6CP); RcvBadConfReq(); goto out; } // If we are naking, send it if (m_nakPkt.GetLength()) { m_nakPkt.Push16(m_nakPkt.GetLength() + IPV6CP_HEADER_LENGTH); m_nakPkt.Push8(m_lastRcvdIdent); m_nakPkt.Push8(CONFNAK); m_nakPkt.Push16(PPP_PROTO_IPV6CP); RcvBadConfReq(); goto out; } //m_acked.Set(*ack_opts); m_ackPkt.Push16(m_ackPkt.GetLength() + IPV6CP_HEADER_LENGTH); m_ackPkt.Push8(m_lastRcvdIdent); m_ackPkt.Push8(CONFACK); m_ackPkt.Push16(PPP_PROTO_IPV6CP); RcvGoodConfReq(); out: DebugVoidReturn; } void CIpv6cpProtocol::RcvConfNak(CPppPacket *packet) { DebugEnter("CIpv6cpProtocol::RcvConfNak()"); Log(LF_INFO|LF_PROTO, "IPV6CP Rcvd ConfNak -->"); while (packet->GetLength()) { u8 type = packet->Pull8(); u8 len = packet->Pull8(); switch (type) { case IPV6CP_OPT_TOKEN: #if 0 token = packet->Pull32(); if (m_acked.loc_ip == 0) { if (!ip) { m_parent->Close(-2, "IPV6CP: 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, "IPV6CP: incompatible IP addresses local=%08x vs remote=%08x", m_acked.loc_ip, ip); m_parent->Close(-2, msg); goto out; } #endif #endif break; default: if (len > 2) packet->Trim(len - 2); break; } } CFsmProtocol::RcvConfNak(); DebugVoidReturn; } void CIpv6cpProtocol::RcvConfRej(CPppPacket *) { DebugEnter("CIpv6cpProtocol::RcvConfRej()"); Log(LF_INFO|LF_PROTO, "IPV6CP Rcvd ConfRej -->"); // Same state transition so use Nak CFsmProtocol::RcvConfNak(); DebugVoidReturn; } void CIpv6cpProtocol::RcvConfAck(CPppPacket *) { DebugEnter("CIpv6cpProtocol::RcvConfAck()"); Log(LF_INFO|LF_PROTO, "IPV6CP Rcvd ConfAck -->"); //Log(LF_DEBUG|LF_PROTO, "Local IP is %#x", m_acked.loc_ip); fprintf(stderr, "Local IP: m_our_token(%016Lx)\n", m_our_token); fprintf(stderr, "m_their_token(%016Lx)\n", m_their_token); CFsmProtocol::RcvConfAck(); DebugVoidReturn; } void CIpv6cpProtocol::SndConfAck() { DebugEnter("CIpv6cpProtocol::SndConfAck()"); m_parent->OutputQ(&m_ackPkt); DebugVoidReturn; } void CIpv6cpProtocol::SndConfNak() { DebugEnter("CIpv6cpProtocol::SndConfNak()"); if (m_rejPkt.GetLength()) { Log(LF_DEBUG|LF_PROTO, "LCP Send ConfRej: len=%d, ident=%d", m_rejPkt.GetLength(), m_lastRcvdIdent); m_parent->OutputQ(&m_rejPkt); } else { Log(LF_DEBUG|LF_PROTO, "LCP Send ConfNak: len=%d, ident=%d", m_nakPkt.GetLength(), m_lastRcvdIdent); m_parent->OutputQ(&m_nakPkt); } DebugVoidReturn; } void CIpv6cpProtocol::SndTermReq() { CPppPacket reqPkt; DebugEnter("CIpv6cpProtocol::SndTermReq()"); if(!cntTerminate) { Log(LF_DEBUG|LF_PROTO, "Attempted to terminate %d times", maxTerminate); m_parent->Close(-2, "IPV6CP: Peer not responding to Terminate Request"); DebugVoidReturn; } reqPkt.Reserve(PPP_HEADER_LENGTH + IPV6CP_HEADER_LENGTH); reqPkt.Push16(4); reqPkt.Push8(m_lastSentIdent++); reqPkt.Push8(TERMREQ); reqPkt.Push16(PPP_PROTO_IPV6CP); if (cntTerminate) cntTerminate--; m_parent->OutputQ(&reqPkt); DebugVoidReturn; } void CIpv6cpProtocol::SndTermAck() { CPppPacket ackPkt; DebugEnter("CIpv6cpProtocol::SndTermAck()"); ackPkt.Reserve(PPP_HEADER_LENGTH + IPV6CP_HEADER_LENGTH); ackPkt.Push16(4); ackPkt.Push8(m_lastRcvdIdent); ackPkt.Push8(TERMACK); ackPkt.Push16(PPP_PROTO_IPV6CP); m_parent->OutputQ(&ackPkt); DebugVoidReturn; } void CIpv6cpProtocol::SndConfReq() { CPppPacket c; DebugEnter("CIpv6cpProtocol::SndConfReq()"); if (!cntConfig) { Log(LF_DEBUG|LF_PROTO, "Config attempts failed: Closing"); fprintf(stderr, "ipv6cp: SndConfReq failed\n"); m_parent->Close(-2, "IPV6CP: Max Config attempts reached: Check IPV6CP options"); DebugVoidReturn; } c.Reserve(PPP_HEADER_LENGTH + IPV6CP_HEADER_LENGTH); //Log(LF_DEBUG|LF_PROTO, "Local IP: %x", m_acked.loc_ip); c.Put8(IPV6CP_OPT_TOKEN); c.Put8(IPV6CP_OPT_TOKEN_LEN); c.Put64(m_our_token); c.Push16(c.GetLength() + IPV6CP_HEADER_LENGTH); c.Push8(++m_lastSentIdent); c.Push8(CONFREQ); c.Push16(PPP_PROTO_IPV6CP); 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 CIpv6cpProtocol::SndCodeReject(CPppPacket *packet) { CPppPacket cr; DebugEnter("CIpv6cpProtocol::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() + IPV6CP_HEADER_LENGTH + PPP_HEADER_LENGTH); cr.Push8(m_lastRcvdIdent); cr.Push8(CODEREJ); cr.Push16(PPP_PROTO_IPV6CP); m_parent->OutputQ(&cr); DebugVoidReturn; } void CIpv6cpProtocol::RcvCodeReject(CPppPacket *pkt) { int i = 0; u8 code = *((u8 *)pkt); DebugEnter("CIpv6cpProtocol::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 CIpv6cpProtocol::ThisLayerStarted(void) { get_random((u8 *)&m_our_token, sizeof(m_our_token)); /* ensure universal bit is clear*/ m_our_token &= ~(0x02ULL << 56); if (debug) { fprintf(stderr, "IPV6CP: ThisLayerStarted: m_our_token = %016Lx\n", m_our_token); } } void CIpv6cpProtocol::ThisLayerFinished(const char *why) { DebugEnter("CIpv6cpProtocol::ThisLayerFinished()"); m_parent->Close(-2, why); DebugVoidReturn; } void CIpv6cpProtocol::ThisLayerUp() { DebugEnter("CIpv6cpProtocol::ThisLayerUp()"); m_parent->ReadyIpv6(m_our_token, m_their_token); //m_parent->Ready(&m_acked); fprintf(stderr, "IPV6CP: ThisLayerUp!\n"); fprintf(stderr, "Local IP: m_our_token(%016Lx)\n", m_our_token); fprintf(stderr, "m_their_token(%016Lx)\n", m_their_token); DebugVoidReturn; }