/** * method is used to process received REPLY message (as an answer for REQUEST or * as an answer for SOLICIT with RAPID COMMIT option) * * @param reply */ void TClntMsg::answer(SPtr<TClntMsg> reply) { SPtr<TOptDUID> ptrDUID; ptrDUID = (Ptr*) reply->getOption(OPTION_SERVERID); if (!ptrDUID) { Log(Warning) << "Received REPLY message without SERVER ID option. Message ignored." << LogEnd; return; } SPtr<TDUID> duid = ptrDUID->getDUID(); SPtr<TClntIfaceIface> iface = (Ptr*)ClntIfaceMgr().getIfaceByID(getIface()); if (!iface) { Log(Error) << "Unable to find physical interface with ifindex=" << getIface() << LogEnd; return; } SPtr<TClntCfgIface> cfgIface = ClntCfgMgr().getIface( getIface() ); if (!cfgIface) { Log(Error) << "Unable to find configuration interface with ifindex=" << getIface() << LogEnd; } // analyse all options received SPtr<TOpt> option; // Check authentication first. If the checks fail, we need to drop the whole message // without using its contents. if (!reply->checkReceivedAuthOption()) { Log(Warning) << "AUTH: AUTH option verification failed. Ignoring message." << LogEnd; return; } // find ORO in received options SPtr<TClntOptOptionRequest> optORO = (Ptr*) this->getOption(OPTION_ORO); reply->firstOption(); while (option = reply->getOption() ) { if (optORO) optORO->delOption(option->getOptType()); // delete received option from ORO switch (option->getOptType()) { case OPTION_IA_NA: { SPtr<TClntOptIA_NA> clntOpt = (Ptr*)option; if (clntOpt->getStatusCode()!=STATUSCODE_SUCCESS) { Log(Warning) << "Received IA (IAID=" << clntOpt->getIAID() << ") with non-success status:" << clntOpt->getStatusCode() << ", IA ignored." << LogEnd; break; } // configure received IA clntOpt->setContext(duid, 0/* srvAddr used is unicast */, this->Iface); clntOpt->doDuties(); // delete that IA from request list for (TOptList::iterator requestOpt = Options.begin(); requestOpt!=Options.end(); ++requestOpt) { if ( (*requestOpt)->getOptType()!=OPTION_IA_NA) continue; SPtr<TClntOptIA_NA> ptrIA = (Ptr*) (*requestOpt); if ( ptrIA->getIAID() == clntOpt->getIAID() ) { requestOpt = Options.erase(requestOpt); break; } } // delete request for IA, if it was mentioned in Option Request if ( optORO && optORO->isOption(OPTION_IA_NA) ) optORO->delOption(OPTION_IA_NA); break; } case OPTION_IA_TA: { SPtr<TClntOptTA> ta = (Ptr*) option; if (ta->getStatusCode()!=STATUSCODE_SUCCESS) { Log(Warning) << "Received TA (IAID=" << ta->getIAID() << ") with non-success status:" << ta->getStatusCode() << ", TA ignored." << LogEnd; break; } SPtr<TOpt> requestOpt; // delete that TA from request list for (TOptList::iterator requestOpt = Options.begin(); requestOpt!=Options.end(); ++requestOpt) { if ( (*requestOpt)->getOptType()!=OPTION_IA_TA) continue; SPtr<TClntOptTA> ptrTA = (Ptr*) (*requestOpt); if ( ta->getIAID() == ptrTA->getIAID() ) { requestOpt = Options.erase(requestOpt); break; } } ta->setIface(Iface); ta->doDuties(); break; } case OPTION_IA_PD: { SPtr<TClntOptIA_PD> pd = (Ptr*) option; if (pd->getStatusCode()!=STATUSCODE_SUCCESS) { Log(Warning) << "Received PD (PDAID=" << pd->getIAID() << ") with non-success status:" << pd->getStatusCode() << ", PD ignored." << LogEnd; break; } if (!pd->getOption(OPTION_IAPREFIX)) { Log(Notice) << "Received IA_PD without prefixes, ignoring." << LogEnd; break; } bool pdOk = true; int prefixCount = pd->countPrefixes(); pd->firstPrefix(); SPtr<TClntOptIAPrefix> ppref; while (ppref = pd->getPrefix()) { if (!ppref->isValid()) { Log(Warning) << "Option IA_PREFIX from IA_PD " << pd->getIAID() << " is not valid." << LogEnd; // RFC 3633, section 10: // A requesting router discards any prefixes for which the // preferred lifetime is greater than the valid lifetime. pd->deletePrefix(ppref); prefixCount--; if (!prefixCount) { // ia_pd hasn't got any valid prefixes. if (ClntCfgMgr().insistMode()) { // if insist-mode is enabled and one of received // pd's has no valid prefixes, answer is rejected. pdOk = false; } break; } } } if (!pdOk) { break; } // configure received PD pd->setContext(duid, 0/* srvAddr used in unicast */, this); pd->doDuties(); // delete that PD from request list for (TOptList::iterator requestOpt = Options.begin(); requestOpt!=Options.end(); ++requestOpt) { if ( (*requestOpt)->getOptType()!=OPTION_IA_PD) continue; SPtr<TClntOptIA_PD> reqPD = (Ptr*) (*requestOpt); if ( pd->getIAID() == reqPD->getIAID() ) { requestOpt = Options.erase(requestOpt); break; } } // delete request for PD, if it was mentioned in Option Request if ( optORO && optORO->isOption(OPTION_IA_PD) ) optORO->delOption(OPTION_IA_PD); break; } case OPTION_DNS_SERVERS: { SPtr<TOptAddrLst> dnsservers = (Ptr*) option; cfgIface->setDNSServerState(STATE_CONFIGURED); iface->setDNSServerLst(duid, reply->getRemoteAddr(), dnsservers->getAddrLst()); break; } case OPTION_NIS_SERVERS: { SPtr<TOptAddrLst> nisservers = (Ptr*) option; cfgIface->setNISServerState(STATE_CONFIGURED); iface->setNISServerLst(duid, reply->getRemoteAddr(), nisservers->getAddrLst()); break; } case OPTION_NISP_SERVERS: { SPtr<TOptAddrLst> nispservers = (Ptr*) option; cfgIface->setNISPServerState(STATE_CONFIGURED); iface->setNISPServerLst(duid, reply->getRemoteAddr(), nispservers->getAddrLst()); break; } case OPTION_SNTP_SERVERS: { SPtr<TOptAddrLst> ntpservers = (Ptr*) option; cfgIface->setNTPServerState(STATE_CONFIGURED); iface->setNTPServerLst(duid, reply->getRemoteAddr(), ntpservers->getAddrLst()); break; } case OPTION_SIP_SERVER_A: { SPtr<TOptAddrLst> sipservers = (Ptr*) option; cfgIface->setSIPServerState(STATE_CONFIGURED); iface->setSIPServerLst(duid, reply->getRemoteAddr(), sipservers->getAddrLst()); break; } case OPTION_DOMAIN_LIST: { SPtr<TOptDomainLst> domains = (Ptr*) option; cfgIface->setDomainState(STATE_CONFIGURED); iface->setDomainLst(duid, reply->getRemoteAddr(), domains->getDomainLst() ); break; } case OPTION_SIP_SERVER_D: { SPtr<TOptDomainLst> sipdomains = (Ptr*) option; cfgIface->setSIPDomainState(STATE_CONFIGURED); iface->setSIPDomainLst(duid, reply->getRemoteAddr(), sipdomains->getDomainLst() ); break; } case OPTION_NIS_DOMAIN_NAME: { SPtr<TOptDomainLst> nisdomain = (Ptr*) option; List(string) domains = nisdomain->getDomainLst(); if (domains.count() == 1) { cfgIface->setNISDomainState(STATE_CONFIGURED); iface->setNISDomain(duid, reply->getRemoteAddr(), nisdomain->getDomain()); } else { Log(Warning) << "Malformed NIS Domain option received. " << domains.count() << " domain(s) received, expected exactly 1." << LogEnd; cfgIface->setNISDomainState(STATE_FAILED); } break; } case OPTION_NISP_DOMAIN_NAME: { SPtr<TOptDomainLst> nispdomain = (Ptr*) option; List(string) domains = nispdomain->getDomainLst(); if (domains.count() == 1) { cfgIface->setNISPDomainState(STATE_CONFIGURED); iface->setNISPDomain(duid, reply->getRemoteAddr(), nispdomain->getDomain()); } else { Log(Warning) << "Malformed NIS+ Domain option received. " << domains.count() << " domain(s) received, expected exactly 1." << LogEnd; cfgIface->setNISDomainState(STATE_FAILED); } break; } #ifdef MOD_REMOTE_AUTOCONF case OPTION_NEIGHBORS: { SPtr<TOptAddrLst> neighbors = (Ptr*) option; ClntTransMgr().updateNeighbors(reply->getIface(), neighbors); break; } #endif case OPTION_IAADDR: Log(Warning) << "Option OPTION_IAADDR misplaced." << LogEnd; break; default: { SPtr<TOpt> requestOpt; if ( optORO && (optORO->isOption(option->getOptType())) ) optORO->delOption(option->getOptType()); //Log(Debug) << "Setting up option " << option->getOptType() << "." << LogEnd; if (!option->doDuties()) { Log(Warning) << "Setting option " << option->getOptType() << " failed." << LogEnd; // do nothing about it } // find options specified in this message firstOption(); while ( requestOpt = getOption() ) { if ( requestOpt->getOptType() == option->getOptType() ) { delOption(requestOpt->getOptType()); }//if }//while } } // switch } //Options and IAs serviced by server are removed from requestOptions list SPtr<TOpt> requestOpt; firstOption(); bool iaLeft = false; bool taLeft = false; bool pdLeft = false; while ( requestOpt = getOption() ) { if (requestOpt->getOptType() == OPTION_IA_NA) iaLeft = true; if (requestOpt->getOptType() == OPTION_IA_TA) taLeft = true; if (requestOpt->getOptType() == OPTION_IA_PD) pdLeft = true; } // taLeft = false; if (iaLeft || taLeft || pdLeft) { // send new Request to another server Log(Notice) << "There are still " << (iaLeft?"some IA(s)":"") << (taLeft?"TA":"") << (pdLeft?"some PD(s)":"") << " to configure." << LogEnd; ClntTransMgr().sendRequest(this->Options, this->Iface); } else { if (optORO) optORO->delOption(OPTION_ADDRPARAMS); // don't insist on getting ADDR-PARAMS if ( optORO && (optORO->count()) ) { Log(Warning) << "All IA(s), TA and PD(s) has been configured, but some options ("; for (int i=0; i< optORO->count(); i++) Log(Cont) << optORO->getReqOpt(i) << " "; Log(Cont) << ") were not assigned." << LogEnd; if (ClntCfgMgr().insistMode()) { Log(Notice) << "Insist-mode enabled, sending INF-REQUEST." << LogEnd; ClntTransMgr().sendInfRequest(this->Options, this->Iface); } else { Log(Notice) << "Insist-mode disabled, giving up (not sending INF-REQUEST)." << LogEnd; /// @todo: set proper options to FAILED state } } } IsDone = true; return; }