コード例 #1
0
ファイル: ClntMsg.cpp プロジェクト: arachnist/dibbler
/**
 * 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;
}