Main Page | Class Hierarchy | Class List | File List | Class Members | Related Pages

Framework API for Multithreading Task and Protocol State Machine

Alpha Version

Author:
Yoshihiro Ohba
Date:
Created: December 1, 2003

Updated: January 10, 2004

Introduction

This framework API provides a general way to implement communication protocols in multithreading environments. The API contains three sub-APIs, i.e., job sub-API, task sub API and state machine sub-API.

The job sub-API defines a generic processing element that can be used for any purpose.

The task sub-API provides the functionality to execute jobs in a multithreading environment.

The state machine sub-API provides the functionality to define and execute state machines of any types.

Any protocol implementation in Open Diameter should be written by using this framework API in order to reduce development time and improve manageability of the entire source tree.

Job Sub-API

A job is an object that has an operation to execute. The operation can be executed by any entity including a task (Task Sub-API) or some other job). AAA_Job is the base class for any job and provides the following basic methods:

Queue Job

A queue job is a job that has a queue to store entries of any type. The type of entries stored in the queue is specified as template parameter. The framework API defines two queue entry types as built-in types, job and event, that are used by AAA_GroupedJob and AAA_StateMachineWithTask classes. The queue job class supports FIFO, WFQ (Weighted Fair Queueing) and priority queueing disciplines by using the two parameters "priority" and "weight". An entry with higher priority is always dequeued earlier than an entry with lower priority. Among the entries with the same priority, an entry with higher weight has a greater chance to be dequeued thatn an entry with lower weight. The AAA_QueueJob class has the following methods in addition to the methods derived from AAA_Job:

Grouped Job

A grouped job is a queue job that has a queue to store other jobs and has a parent job for which it requests scheduling on behalf of other jobs. The grouped job is used for constucting a job hierarchy. AAA_SchedulingPolicy parameter is used for specifying the job scheduling discipline. The grouped job is used for job serialization and job scheduling.

Job Handle

A job handle provides a safe way to deallocate memory for job objects, where memory deallocation can occur even in the constuctor of an object of a class that creats a job. When the handle is deleted, the handle calls AAA_JobDeleter() function which performs a delete operation with treating the job as a shared object (i.e., if the job is not deleted until the job is not owned by any objects.).

Queue Job

Task Sub-API

A task is an active object that generates threads for executing job operations. It consists of one thread (referred to as the timer thread) that handles timer events and one or more thread (each referred to as a job serving thread) that execute job operations. A task has a root job for which Serve() method is called by the job serving threads. AAA_Task provides the following basic methods:

task-1.gif

Figure 1: AAA_Task

State Machine Sub-API

A state machine is a general component to implement arbitrary communication protocols. A state machine consists of a state transition table and a set of state variables.

State Table

A state transition table (or simply a state table in this document) defines the behavior of the target protocol. Each entry of a state table is defined as AAA_StateTableEntry and has the following attributes:

class EapAction : public AAA_Action<EapSession>
{
 public:
  virtual void operator()(EapSession&) {}
 protected:
  EapAction() {}
  virtual ~EapAction() {}
};

In addition, one of the Current State values in the state table is chosen as the Initial State where the state machine starts its operation.

The base class for state table is AAA_StateTable.

State machines objects of the same class should share the same state table objects to save the memory resources. For this purpose, it is better to define state table classes as singletons.

Figure 2 shows an example state table. An event "*" indicates an wildcard event which matches any event. An action "null action" indicates an action that performs no operation.

statemachine-1.gif

Figure 2: State Table

AAA_StateTable class provides the following methods for table manipuration:

State Machine Sub-API

A state machine has the following basic methods:

The base state machine class is defined as AAA_StateMachineBase.

Other state machine classes are derived from the base class and defined as template class in which the action argument type is used as the template parameter. The base class of templated state machine is AAA_StateMachine<ARG>.

State Machine With Timer Handling

A state machine with timer is a state machine that is capable of generating and deleting timer events. Timer events generated by the a state machine with timer will be bound to an ACE_Reactor, which is typically a reactor created by the timer thread in a task (Task Sub-API).

Timer events are categorized into timer types (the default timer type is zero(0)). The state machine stores at most one timer event for each timer type. A unique timer type is allocated by using timer type allocator (defined in AAA_TimerTypeAllocator_S and AAA_TimerTypeAllocator classes).

The AAA_StateMachineWithTimer<ARG> is the base class for state machine with timer and provides the following methods:

A sample program that uses the framework API is shown below.

/* BEGIN_COPYRIGHT                                                        */
/*                                                                        */
/* Open Diameter: Open-source software for the Diameter and               */
/*                Diameter related protocols                              */
/*                                                                        */
/* Copyright (C) 2002-2004 Open Diameter Project                          */
/*                                                                        */
/* This library is free software; you can redistribute it and/or modify   */
/* it under the terms of the GNU Lesser General Public License as         */
/* published by the Free Software Foundation; either version 2.1 of the   */
/* License, or (at your option) any later version.                        */
/*                                                                        */
/* This library is distributed in the hope that it will be useful,        */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of         */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU      */
/* Lesser General Public License for more details.                        */
/*                                                                        */
/* You should have received a copy of the GNU Lesser General Public       */
/* License along with this library; if not, write to the Free Software    */
/* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307    */
/* USA.                                                                   */
/*                                                                        */
/* In addition, when you copy and redistribute some or the entire part of */
/* the source code of this software with or without modification, you     */
/* MUST include this copyright notice in each copy.                       */
/*                                                                        */
/* If you make any changes that are appeared to be useful, please send    */
/* sources that include the changed part to                               */
/* diameter-developers@lists.sourceforge.net so that we can reflect your  */
/* changes to one unified version of this software.                       */
/*                                                                        */
/* END_COPYRIGHT                                                          */
// $Id: diameter_eap_server_fsm.cxx,v 1.27 2004/06/17 20:12:26 yohba Exp $

// diameter_eap_server_fsm.cxx:  EAP session handling
// Written by Yoshihiro Ohba

#include <ace/Singleton.h>
#include <ace/Atomic_Op_T.h>
#include "diameter_eap_server_session.hxx"
#include "diameter_eap_server_fsm.hxx"
#include "diameter_eap_parser.hxx"

class DiameterEapServerAction 
  : public AAA_Action<DiameterEapServerStateMachine>
{
  virtual void operator()(DiameterEapServerStateMachine&)=0;
 protected:
  DiameterEapServerAction() {}
  ~DiameterEapServerAction() {}
};

class DiameterEapServerStateTable_S 
  : public AAA_StateTable<DiameterEapServerStateMachine>
{
  friend class 
  ACE_Singleton<DiameterEapServerStateTable_S, ACE_Recursive_Thread_Mutex>;

 private:
  class AcForwardEapResponse : public DiameterEapServerAction 
  {
    void operator()(DiameterEapServerStateMachine& sm)
    {
      AAA_LOG(LM_DEBUG, 
                   "[%N] forwarding EAP Response to authenticator.\n");
      sm.ForwardEapResponse(sm.DER().EapPayload());
    }
  };

  class AcCheckDER : public DiameterEapServerAction 
  {
    void operator()(DiameterEapServerStateMachine& sm)
    {
      AAA_LOG(LM_DEBUG, "[%N] Copying DER to DEA.\n");
      if (sm.CheckDER())
        sm.Event(DiameterEapServerStateMachine::EvSgValidDER);
      else
        sm.Event(DiameterEapServerStateMachine::EvSgInvalidDER);
    }
  };

  class AcSendDEAwithContinue : public DiameterEapServerAction 
  {
    void operator()(DiameterEapServerStateMachine& sm)
    {
      DiameterEapServerSession& session = sm.Session();
      AAA_LOG(LM_DEBUG, "[%N] Sending DEA with continue result-code.\n");

      // Set Result-Code to DIAMETER_MULTI_ROUND_AUTH.
      DEA_Data& dea = sm.DEA();
      dea.ResultCode = DIAMETER_MULTI_ROUND_AUTH;

      // Set reply message if needed.
      dea.ReplyMessage.Clear();
      sm.SetReplyMessage(dea.ReplyMessage, dea.ResultCode());

      dea.EapReissuedPayload.Clear();
      sm.SetReissuedEapPayload(dea.EapReissuedPayload);

      sm.SendDEA(); 

      // Update the session state.
      session.Update(AAASession::EVENT_AUTH_CONTINUE);
    }
  };

  class AcSendDEAwithSuccess : public DiameterEapServerAction 
  {
    void operator()(DiameterEapServerStateMachine& sm)
    {
      AAA_LOG(LM_DEBUG, "[%N] Sending DEA with a success Result-Code.\n");
      DiameterEapServerSession& session = sm.Session();
      DEA_Data& dea = sm.Session().DEA();

      dea.ResultCode = DIAMETER_SUCCESS;
      sm.SendDEA(); 
      session.Update(AAASession::EVENT_AUTH_SUCCESS);
    }
  };

  class AcSendDEA_DueToAuthenticationFailure : public DiameterEapServerAction 
  {
    void operator()(DiameterEapServerStateMachine& sm)
    {
      DiameterEapServerSession& session = sm.Session();
      DEA_Data& dea = sm.Session().DEA();
      AAA_LOG(LM_DEBUG, 
                   "[%N] Sending DEA due to authentication failure.\n");

      dea.ResultCode = DIAMETER_AUTHENTICATION_REJECTED;
      sm.SendDEA(); 
      session.Update(AAASession::EVENT_AUTH_FAILED);
    }
  };

  class AcSendDEA_DueToAuthorizationFailure : public DiameterEapServerAction 
  {
    void operator()(DiameterEapServerStateMachine& sm)
    {
      DiameterEapServerSession& session = sm.Session();
      DEA_Data& dea = sm.Session().DEA();
      AAA_LOG(LM_DEBUG, 
                   "[%N] Sending DEA due to authorization failure.\n");

      // Erase EAP payload since it may contain EAP Success message
      // when authentication is successful but authorization fails.
      dea.EapPayload.Clear();

      dea.ResultCode = DIAMETER_AUTHORIZATION_REJECTED;
      sm.SendDEA(); 
      session.Update(AAASession::EVENT_AUTH_FAILED);
    }
  };

  class AcSendDEA_DueToInvalidDER : public DiameterEapServerAction 
  {
    void operator()(DiameterEapServerStateMachine& sm)
    {
      DiameterEapServerSession& session = sm.Session();
      DEA_Data& dea = sm.DEA();
      AAA_LOG(LM_DEBUG, "[%N] Sending DEA due to invalid DER.\n");

      dea.ResultCode = DIAMETER_INVALID_AVP_VALUE;
      session.Update(AAASession::EVENT_AUTH_CONTINUE);
      sm.SendDEA(); 
    }
  };

  class AcAuthorize : public DiameterEapServerAction 
  {
    void operator()(DiameterEapServerStateMachine& sm)
    {
      if (sm.AuthorizationDone() || sm.Authorize())
        sm.Event(DiameterEapServerStateMachine::EvSgAuthorizationSuccess);
      else
        sm.Event(DiameterEapServerStateMachine::EvSgAuthorizationFailure);
    }
  };

  enum state {
    StInitialize,
    StWaitDER,
    StCheckDER,
    StWaitAuthorization,
    StAccepted,
    StRejected,
    StWaitEapMsg,
    StTerminated
  };

  enum {
    EvSgSuccess,
    EvSgLimitedSuccess,
    EvSgFailure,
    EvSgContinue,
    EvSgValid,
    EvSgInvalid
  };

  AcSendDEAwithContinue acSendDEAwithContinue; 
  AcSendDEAwithSuccess acSendDEAwithSuccess; 
  AcSendDEA_DueToAuthenticationFailure acSendDEA_DueToAuthenticationFailure;
  AcSendDEA_DueToAuthorizationFailure acSendDEA_DueToAuthorizationFailure;
  AcSendDEA_DueToInvalidDER acSendDEA_DueToInvalidDER;
  AcForwardEapResponse acForwardEapResponse; 
  AcCheckDER acCheckDER; 
  AcAuthorize acAuthorize;

  // Defined as a leaf class
  DiameterEapServerStateTable_S() 
  {
    AddStateTableEntry(StInitialize, 
                       DiameterEapServerStateMachine::EvRxDER,
                       StCheckDER, acCheckDER);
    AddStateTableEntry(StInitialize, 
                       DiameterEapServerStateMachine::EvSgDisconnect, 
                       StTerminated);
    AddWildcardStateTableEntry(StInitialize, StTerminated);

    AddStateTableEntry(StCheckDER, 
                       DiameterEapServerStateMachine::EvSgValidDER, 
                       StWaitEapMsg, 
                       acForwardEapResponse);
    AddStateTableEntry(StCheckDER, 
                       DiameterEapServerStateMachine::EvSgInvalidDER, 
                       StRejected, acSendDEA_DueToInvalidDER);

    AddStateTableEntry(StWaitDER, 
                       DiameterEapServerStateMachine::EvRxDER,
                       StCheckDER, acCheckDER);
    AddStateTableEntry(StWaitDER,
                       DiameterEapServerStateMachine::EvSgDisconnect,
                       StTerminated);
    AddWildcardStateTableEntry(StWaitDER, StTerminated);

    AddStateTableEntry(StWaitEapMsg,
                       DiameterEapServerStateMachine::EvRxEapRequest,
                       StWaitDER, acSendDEAwithContinue);
    AddStateTableEntry(StWaitEapMsg,
                       DiameterEapServerStateMachine::EvRxEapSuccess,
                       StWaitAuthorization, acAuthorize);
    AddStateTableEntry(StWaitEapMsg,
                       DiameterEapServerStateMachine::EvRxEapFailure,
                       StRejected, acSendDEA_DueToAuthenticationFailure);
    AddStateTableEntry(StWaitEapMsg,
                       DiameterEapServerStateMachine::EvSgDisconnect,
                       StTerminated);
    AddWildcardStateTableEntry(StWaitEapMsg, StTerminated);

    AddStateTableEntry(StWaitAuthorization, 
                       DiameterEapServerStateMachine::EvSgAuthorizationSuccess,
                       StAccepted, acSendDEAwithSuccess);
    AddStateTableEntry(StWaitAuthorization, 
                       DiameterEapServerStateMachine::EvSgAuthorizationFailure,
                       StRejected, acSendDEA_DueToAuthorizationFailure);
    AddStateTableEntry(StWaitAuthorization,
                       DiameterEapServerStateMachine::EvSgDisconnect,
                       StTerminated);
    AddWildcardStateTableEntry(StWaitAuthorization, StTerminated);

    // Re-authentication
    AddStateTableEntry(StAccepted, 
                       DiameterEapServerStateMachine::EvRxDER,
                       StCheckDER, acCheckDER);
    AddStateTableEntry(StAccepted,
                       DiameterEapServerStateMachine::EvSgDisconnect,
                       StTerminated);
    AddWildcardStateTableEntry(StAccepted, StTerminated);

    AddWildcardStateTableEntry(StRejected, StRejected);

    AddWildcardStateTableEntry(StTerminated, StTerminated);

    InitialState(StInitialize);
  }
  ~DiameterEapServerStateTable_S() {}
};

typedef 
ACE_Singleton<DiameterEapServerStateTable_S, ACE_Recursive_Thread_Mutex> 
DiameterEapServerStateTable;

DiameterEapServerStateMachine::DiameterEapServerStateMachine
(DiameterEapServerSession& s, DiameterJobHandle &h)
  : AAA_StateMachine<DiameterEapServerStateMachine>
  (*this, *DiameterEapServerStateTable::instance(), "DIAMETER_EAP_CLIENT"),
    session(s),
    handle(h),
    authorizationDone(false)
{}

void 
DiameterEapServerStateMachine::SendDEA()
{
  AAAMessage msg;

  deaData.AuthApplicationId = EapApplicationId;

  DEA_Parser parser;
  parser.setAppData(&session.deaData);
  parser.setRawData(&msg);

  try {
    parser.parseAppToRaw();
  }
  catch (DiameterParserError) {
    AAA_LOG(LM_ERROR, "[%N] Parsing error.\n");
    return;
  }

  AAAMessageControl msgControl(session.Self());
  if (msgControl.Send(msg) != AAA_ERR_SUCCESS) {
    AAA_LOG(LM_ERROR, "Failed sending message.\n");
  }
  else {
    AAA_LOG(LM_DEBUG, "Sent DEA Message.\n");
  }
}

bool
DiameterEapServerStateMachine::CheckDER()
{
  DER_Data& der = derData;
  DEA_Data& dea = deaData;

  // Auth-Request-Type check.
  if (der.AuthRequestType() != AUTH_REQUEST_TYPE_AUTHENTICATION_ONLY &&
      der.AuthRequestType() != AUTH_REQUEST_TYPE_AUTHORIZE_AUTHENTICATE)
    {
      AAA_LOG(LM_ERROR, "[%N] Invalid auth request type.\n");
      return false;
    }
  dea.AuthRequestType = der.AuthRequestType;

  AuthorizeMultiRoundTimeOut(dea.MultiRoundTimeOut);

  if (!dea.AuthRequestType.IsSet())
    {
      AAA_LOG(LM_ERROR, "[%N] Failed to set auth request type.\n");
      return false;
    }

  // Class check.
  for (unsigned i=0; i<dea.Class.size(); i++)
    {
      bool found = false;
      for (unsigned j=0; j<der.Class.size(); j++)
        {
          if (dea.Class[i] == der.Class[j])
            found = true;
        }
      if (!found)
        {
          AAA_LOG(LM_ERROR, "[%N] Invalid class\n.");
          return false;
        }
    }

  // UserName check
  if (der.UserName.IsSet())
    {
      if (!ValidateUserName(der.UserName()))
        {
          AAA_LOG(LM_DEBUG, "[%N] Failed to validate username.\n");
          return false;
        }
      dea.UserName = der.UserName;
    }

  // State check.
  if (dea.State.IsSet()) // Non-initial state.
    {
      if (!der.State.IsSet() || !ValidateState(der.State(), dea.State()))
        {
          AAA_LOG(LM_DEBUG, "[%N] Invalid State AVP.\n");
          return false;
        }
      else // Try to set initial state
        {
          SetState(dea.State);
        }
    }
  return true;
}

void 
DiameterEapServerStateMachine::SignalContinue(std::string &eapMsg)
{
  DEA_Data& dea = deaData;
  AAA_LOG(LM_ERROR, "[%N] EAP Request received from backend.\n");
  dea.EapPayload = eapMsg;
  Notify(EvRxEapRequest);
}

void
DiameterEapServerStateMachine::SignalSuccess(std::string &eapMsg)
{
  DEA_Data& dea = deaData;
  AAA_LOG(LM_ERROR, "[%N] EAP Success received from backend.\n");
  dea.EapPayload = eapMsg;
  Notify(EvRxEapSuccess);
}

void
DiameterEapServerStateMachine::SignalFailure(std::string &eapMsg)
{
  DEA_Data& dea = deaData;
  AAA_LOG(LM_ERROR, "[%N] EAP Failure received from backend.\n");
  dea.EapPayload = eapMsg;
  Notify(EvRxEapFailure);
}

bool
DiameterEapServerStateMachine::Authorize()
{
  AAA_LOG(LM_DEBUG, "[%N] Authorizing DER.\n");
  DEA_Data& dea = deaData;
  DER_Data& der = derData;
  bool r;

  // Authorization of mandatory AVPs.

  // If AuthRequestType indicates authentication only, do nothing.
  if (dea.AuthRequestType() == AUTH_REQUEST_TYPE_AUTHENTICATION_ONLY)
    {
      AAA_LOG(LM_DEBUG, "[%N] Authorization totally success.\n");
      return true;
    }
  
  if (!AuthorizeOriginHost(der.OriginHost()))
    {
      AAA_LOG(LM_DEBUG, "[%N] Failed to authorize origin host.\n");
      return false;
    }

  if (!AuthorizeOriginRealm(der.OriginRealm()))
    {
      AAA_LOG(LM_DEBUG, "[%N] Failed to authorize origin realm.\n");
      return false;
    }

  // Authorization of optional AVPs.
  if (der.Class.IsSet())
    r = AuthorizeClass(der.Class(), dea.Class);
  else
    r = AuthorizeClass(dea.Class);
  if (!r)
    {
      AAA_LOG(LM_DEBUG, "[%N] Failed to authorize Class.\n");
      return false;
    }

  if (!AuthorizeConfigurationToken(dea.ConfigurationToken))
    {
      AAA_LOG(LM_DEBUG, "[%N] Failed to authorize conf. token.\n");
      return false;
    }

  if (!AuthorizeAcctInterimInterval(dea.AcctInterimInterval))
    {
      AAA_LOG(LM_DEBUG, "[%N] Failed to authorize auth interim intvl.\n");
      return false;
    }

  if (der.ServiceType.IsSet())
    r = AuthorizeServiceType(der.ServiceType(), dea.ServiceType);
  else
    r = AuthorizeServiceType(dea.ServiceType);
  if (!r)
    {
      AAA_LOG(LM_DEBUG, "[%N] Failed to authorize service type.\n");
      return false;
    }

      
  if (!AuthorizeIdleTimeout(dea.IdleTimeout))
    {
      AAA_LOG(LM_DEBUG, "[%N] Failed to authorize idle timeout.\n");
      return false;
    }

  if (!AuthorizeAuthorizationLifetime(dea.AuthorizationLifetime))
    {
      AAA_LOG(LM_DEBUG, "[%N] Failed to authorize authz lifetime.\n");
      return false;
    }

  if (!AuthorizeAuthGracePeriod(dea.AuthGracePeriod))
    {
      AAA_LOG(LM_DEBUG, "[%N] Failed to authorize auth grace period.\n");
      return false;
    }

  if (!AuthorizeAuthSessionState(dea.AuthSessionState))
    {
      AAA_LOG(LM_DEBUG, "[%N] Failed to authorize auth session state.\n");
      return false;
    }

  if (!AuthorizeReAuthRequestType(dea.ReAuthRequestType))
    {
      AAA_LOG(LM_DEBUG, "[%N] Failed to authorize reauth req. type.\n");
      return false;
    }

  if (!AuthorizeSessionTimeout(dea.SessionTimeout))
    {
      AAA_LOG(LM_DEBUG, "[%N] Failed to authorize session timeout.\n");
      return false;
    }

  if (!AuthorizeFilterId(dea.FilterId))
    {
      AAA_LOG(LM_DEBUG, "[%N] Failed to authorize filter id.\n");
      return false;
    }

  if (der.PortLimit.IsSet())
    r = AuthorizePortLimit(der.PortLimit(), dea.PortLimit);
  else
    r = AuthorizePortLimit(dea.PortLimit);
  if (!r)
    {
      AAA_LOG(LM_DEBUG, "[%N] Failed to authorize port limit.\n");
      return false;
    }

  AuthorizeCallbackId(dea.CallbackId);

  if (der.CallbackNumber.IsSet())
    AuthorizeCallbackNumber(der.CallbackNumber(), dea.CallbackNumber);
  else
    AuthorizeCallbackNumber(dea.CallbackNumber);
  if (!r)
    {
      AAA_LOG(LM_DEBUG, "[%N] Failed to authorize callback num.\n");
      return false;
    }

  if (!AuthorizeFramedAppletalkLink(dea.FramedAppletalkLink))
    {
      AAA_LOG(LM_DEBUG, "[%N] Failed to authorize appletalk link.\n");
      return false;
    }

  if (!AuthorizeFramedAppletalkZone(dea.FramedAppletalkZone))
    {
      AAA_LOG(LM_DEBUG, "[%N] Failed to authorize appletalk zone.\n");
      return false;
    }

  if (!AuthorizeFramedAppletalkNetwork(dea.FramedAppletalkNetwork))
    {
      AAA_LOG(LM_DEBUG, "[%N] Failed to authorize appletalk network.\n");
      return false;
    }

  if (!der.FramedCompression.IsSet())
    r = AuthorizeFramedCompression
      (der.FramedCompression, dea.FramedCompression);
  else 
    r = AuthorizeFramedCompression(dea.FramedCompression);
  if (!r)
    {
      AAA_LOG(LM_DEBUG, "[%N] Failed to authorize framed compression.\n");
      return false;
    }

  if (der.FramedInterfaceId.IsSet())
    r = AuthorizeFramedInterfaceId
      (der.FramedInterfaceId(), dea.FramedInterfaceId);
  else
    r= AuthorizeFramedInterfaceId(dea.FramedInterfaceId);
  if (!r)
    {
      AAA_LOG(LM_DEBUG, "[%N] Failed to authorize framed ifid.\n");
      return false;
    }

  if (der.FramedIpAddress.IsSet())
    r = AuthorizeFramedIpAddress(der.FramedIpAddress(), dea.FramedIpAddress);
  else AuthorizeFramedIpAddress(dea.FramedIpAddress);
  if (!r)
    {
      AAA_LOG(LM_DEBUG, "[%N] Failed to authorize framed ipaddr.\n");
      return false;
    }

  if (der.FramedIpv6Prefix.IsSet())
    r = AuthorizeFramedIpv6Prefix
      (der.FramedIpv6Prefix(), dea.FramedIpv6Prefix);
  else
    r = AuthorizeFramedIpv6Prefix(dea.FramedIpv6Prefix);
  if (!r)
    {
      AAA_LOG(LM_DEBUG, "[%N] Failed to authorize framed ipv6prx.\n");
      return false;
    }
      
  if (!AuthorizeFramedIpv6Pool(dea.FramedIpv6Pool))
    {
      AAA_LOG(LM_DEBUG, "[%N] Failed to authorize framed ipv6 pool.\n");
      return false;
    }

  if (!AuthorizeFramedPool(dea.FramedPool))
    {
      AAA_LOG(LM_DEBUG, "[%N] Failed to authorize framed pool.\n");
      return false;
    }

  if (!AuthorizeFramedIpv6Route(dea.FramedIpv6Route))
    {
      AAA_LOG(LM_DEBUG, "[%N] Failed to authorize framed ipv6 route.\n");
      return false;
    }

  if (!AuthorizeFramedRoute(dea.FramedRoute))
    {
      AAA_LOG(LM_DEBUG, "[%N] Failed to authorize framed route.\n");
      return false;
    }

  if (der.FramedIpNetmask.IsSet())
    r = AuthorizeFramedIpNetmask(der.FramedIpNetmask(), dea.FramedIpNetmask);
  else
    r = AuthorizeFramedIpNetmask(dea.FramedIpNetmask);
  if (!r)
    {
      AAA_LOG(LM_DEBUG, "[%N] Failed to authorize framed ipmask.\n");
      return false;
    }

  if (!AuthorizeFramedIpxNetwork(dea.FramedIpxNetwork))
    {
      AAA_LOG(LM_DEBUG, "[%N] Failed to authorize framed ipx network.\n");
      return false;
    }

  if (der.FramedMtu.IsSet())
    r = AuthorizeFramedMtu(der.FramedMtu(), dea.FramedMtu);
  else
    r = AuthorizeFramedMtu(dea.FramedMtu);
  if (!r)
    {
      AAA_LOG(LM_DEBUG, "[%N] Failed to authorize framed mtu.\n");
      return false;
    }

  if (der.FramedProtocol.IsSet())
    r = AuthorizeFramedProtocol
      (der.FramedProtocol(), dea.FramedProtocol);
  else
    r = AuthorizeFramedProtocol(dea.FramedProtocol);
  if (!r)
    {
      AAA_LOG(LM_DEBUG, "[%N] Failed to authorize framed proto.\n");
      return false;
    }

  if (!AuthorizeFramedRouting(dea.FramedRouting))
    {
      AAA_LOG(LM_DEBUG, "[%N] Failed to authorize framed routing.\n");
      return false;
    }

  if (!AuthorizeNasFilterRule(dea.NasFilterRule))
    {
      AAA_LOG(LM_DEBUG, "[%N] Failed to authorize nas filter rule.\n");
      return false;
    }

  if (der.Tunneling.IsSet())
    r = AuthorizeTunneling(der.Tunneling(), dea.Tunneling);
  else
    r = AuthorizeTunneling(dea.Tunneling);
  if (!r)
    {
      AAA_LOG(LM_DEBUG, "[%N] Failed to authorize tunneling.\n");
      return false;
    }

  if (!AuthorizeEapMasterSessionKey(dea.EapMasterSessionKey))
    {
      AAA_LOG(LM_DEBUG, "[%N] Failed to authorize MSK.\n");
      return false;
    }

  if (!AuthorizeAccountingEapAuthMethod(dea.AccountingEapAuthMethod))
    {
      AAA_LOG(LM_DEBUG, "[%N] Failed to authorize acct eap method.\n");
      return false;
    }

  AAA_LOG(LM_DEBUG, "[%N] Authorization totally success.\n");
  authorizationDone = true;
  return true;
}
/* BEGIN_COPYRIGHT                                                        */
/*                                                                        */
/* Open Diameter: Open-source software for the Diameter and               */
/*                Diameter related protocols                              */
/*                                                                        */
/* Copyright (C) 2002-2004 Open Diameter Project                          */
/*                                                                        */
/* This library is free software; you can redistribute it and/or modify   */
/* it under the terms of the GNU Lesser General Public License as         */
/* published by the Free Software Foundation; either version 2.1 of the   */
/* License, or (at your option) any later version.                        */
/*                                                                        */
/* This library is distributed in the hope that it will be useful,        */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of         */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU      */
/* Lesser General Public License for more details.                        */
/*                                                                        */
/* You should have received a copy of the GNU Lesser General Public       */
/* License along with this library; if not, write to the Free Software    */
/* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307    */
/* USA.                                                                   */
/*                                                                        */
/* In addition, when you copy and redistribute some or the entire part of */
/* the source code of this software with or without modification, you     */
/* MUST include this copyright notice in each copy.                       */
/*                                                                        */
/* If you make any changes that are appeared to be useful, please send    */
/* sources that include the changed part to                               */
/* diameter-developers@lists.sourceforge.net so that we can reflect your  */
/* changes to one unified version of this software.                       */
/*                                                                        */
/* END_COPYRIGHT                                                          */

/* $Id: */
/* 
   server_test.cxx
   Server test program for Diameter EAP Application 
   Written by Yoshihiro Ohba
   Created December 23, 2003.
*/

/*
  ---------------------------------------------------------
  Brief explanation on what is done in this sample program.
  ---------------------------------------------------------

  This program includes the test for Diameter EAP application where
  EAP Identity method is performed either between a peer and a NAS
  (Case 1) or between the peer and a backend EAP server via the NAS
  (Case 2), and then EAP MD5-Challenge authenticator method is
  performed between the peer and the backend EAP server via the NAS.

  The peer session entity will prompt you to input a username.  Once
  the username is input, it is carried in an Response/Identity message
  and sent to the Diameter server.

  The NAS will retransmit the Request/Identity message until you input
  the username or the number of retransmission reaches its maximum
  value (the maximum value is set to 3 in this program).  For the
  latter case, the authenticator will stop its state machine and the
  peer will timeout and fail.

Case 1:

  Peer                NAS                         EAP Server
                     (= EAP PassThough          (= Diameter Server)
                        Authenticator +
                        Diameter client)         
   |                        |  Null message            |
   |                        |------------------------->|
   |                        |                          |
   |                        |  Request/Identity        |
   |  Request/Identity      |<-------------------------|
   |<-----------------------|                          |
   |                        |                          |
(input userid)              |                          |
   |                        |                          |
   |  Response/Identity     |                          |
   |----------------------->|  Response/Identity       |
   |                        |------------------------->|
   |                        |  Request/MD5-Challenge   |
   |  Request/MD5-Challenge |<-------------------------|
   |<-----------------------|                          |
   |  Response/MD5-Challenge|                          |
   |----------------------->|  Response/MD5-Challenge  |
   |                        |------------------------->|
   |                        |                          |
   |                        |          Success         |
   |       Success          |<-------------------------|
   |<-----------------------|

Case 2:

  Peer                NAS                         EAP Server
                     (= EAP PassThough          (= Diameter Server)
                        Authenticator +
                        Diameter client)         
   |  Request/Identity      |                          |
   |<-----------------------|                          |
   |                        |                          |
(input userid)              |                          |
   |                        |                          |
   |  Response/Identity     |                          |
   |----------------------->|  Response/Identity       |
   |                        |------------------------->|
   |                        |  Request/MD5-Challenge   |
   |  Request/MD5-Challenge |<-------------------------|
   |<-----------------------|                          |
   |  Response/MD5-Challenge|                          |
   |----------------------->|  Response/MD5-Challenge  |
   |                        |------------------------->|
   |                        |                          |
   |                        |          Success         |
   |       Success          |<-------------------------|
   |<-----------------------|

 */

#include <iostream>
#include <ace/Log_Msg.h>
#include <ace/OS.h>
#include "diameter_api.h"
#include "diameter_eap_server_session.hxx"
#include "eap.hxx"
#include "eap_peerfsm.hxx"
#include "eap_authfsm.hxx"
#include "eap_identity.hxx"
#include "eap_policy.hxx"
#include "eap_method_registrar.hxx"
#include "eap_fsm.hxx"
#include "eap_log.hxx"
#include "eap_md5.hxx"

typedef AAA_JobHandle<AAA_GroupedJob> MyJobHandle;

class MyBackendAuthSwitchStateMachine;

/******** Diameter EAP Client Session ********/

// Class definition for authenticator identity method for my application.
class MyEapAuthIdentityStateMachine : public EapAuthIdentityStateMachine
{
  friend class EapMethodStateMachineCreator<MyEapAuthIdentityStateMachine>;
public:
  MyEapAuthIdentityStateMachine(EapSwitchStateMachine &s)
    : EapAuthIdentityStateMachine(s) {} 

  // Reimplemented from EapAuthIdentityStateMachine.
  ProcessIdentityResult ProcessIdentity(std::string& identity) 
  {
    std::cout << "Identity received : " << identity << std::endl;
    return EapAuthIdentityStateMachine::Success;
  }
private:
  ~MyEapAuthIdentityStateMachine() {} 
};

// Class definition for authenticator MD5-Challenge method for my application.
class MyEapAuthMD5ChallengeStateMachine 
  : public EapAuthMD5ChallengeStateMachine
{
  friend class 
  EapMethodStateMachineCreator<MyEapAuthMD5ChallengeStateMachine>;
public:
  MyEapAuthMD5ChallengeStateMachine(EapSwitchStateMachine &s)
    : EapAuthMD5ChallengeStateMachine(s) {} 

  // Reimplemented from EapPeerMD5ChallengeStateMachine.
  void InputPassphrase() 
  {
    std::string &passphrase = Passphrase();
    passphrase.assign("abcd1234");
  }

private:
  ~MyEapAuthMD5ChallengeStateMachine() {} 
};

class MyBackendAuthSwitchStateMachine 
  : public EapBackendAuthSwitchStateMachine
{
 public:

  MyBackendAuthSwitchStateMachine(ACE_Reactor &r, MyJobHandle& h) 
    : EapBackendAuthSwitchStateMachine(r, h)
  {}

  void Send(AAAMessageBlock *b);

  void Success(AAAMessageBlock *b);

  void Success();

  void Failure(AAAMessageBlock *b);

  void Failure();

  void Abort();

 private:
};

class MyDiameterEapServerSession : public DiameterEapServerSession,
                                   public AAA_JobData
{
 public:
  MyDiameterEapServerSession(AAAApplicationCore& appCore, 
                             diameter_unsigned32_t appId=EapApplicationId) 
    : DiameterEapServerSession(appCore, appId),
      handle(EapJobHandle
             (AAA_GroupedJob::Create(appCore.GetTask().Job(), 
                                     this, "backend"))),
      eap(boost::shared_ptr<MyBackendAuthSwitchStateMachine>
          (new MyBackendAuthSwitchStateMachine
           (*appCore.GetTask().reactor(), handle))),
      identityMethod(EapContinuedPolicyElement(EapType(1))),
      md5Method(EapContinuedPolicyElement(EapType(4))),
      initial(true)
  {
    identityMethod.AddContinuedPolicyElement
      (&md5Method, EapContinuedPolicyElement::PolicyOnSuccess);
      
    eap->Policy().InitialPolicyElement(&identityMethod);

    eap->NeedInitialRequestToSend(false);

    this->Start();
  }

  void Start() throw(AAA_Error)
  { 
    DiameterEapServerSession::Start(); 
  }

  void Abort()
  {
    std::cout << "Diameter EAP server session aborted." << std::endl;
    DiameterEapServerSession::Stop();
    Eap().Stop();
  }

  MyBackendAuthSwitchStateMachine& Eap() { return *eap; }

  void ForwardEapResponse(std::string &eapMsg);

  bool ValidateUserName(const diameter_utf8string_t &userName)
  {
// Modified by Santiago Zapata Hernandez
// Old
#if 0
    if (userName == "ohba")
      return true;
    else
      return false;
#endif
// New
        return true;
// End New
  }

 private:
  EapJobHandle handle;
  boost::shared_ptr<MyBackendAuthSwitchStateMachine> eap;
  EapContinuedPolicyElement identityMethod;
  EapContinuedPolicyElement md5Method;
  bool initial;
};

// ----------------- Definition --------------
void MyBackendAuthSwitchStateMachine::Send(AAAMessageBlock *b)
  {
    std::cout << "EAP Request sent from backend authenticator" 
              << std::endl;
    std::string eapMsg(b->base(), b->length());
    JobData(Type2Type<MyDiameterEapServerSession>()).SignalContinue(eapMsg);
  }
void MyBackendAuthSwitchStateMachine::Success(AAAMessageBlock *b)
  {
    std::cout << "EAP Success sent from backend authenticator" 
              << std::endl;
    std::string eapMsg(b->base(), b->length());
    JobData(Type2Type<MyDiameterEapServerSession>()).SignalSuccess(eapMsg);
    Stop();
  }
void MyBackendAuthSwitchStateMachine::Success()
  {
    std::cout << "Success without an EAP Success" << std::endl;
    std::string eapMsg("");
    JobData(Type2Type<MyDiameterEapServerSession>()).SignalSuccess(eapMsg);
    Stop();
  }
void MyBackendAuthSwitchStateMachine::Failure(AAAMessageBlock *b)
  {
    std::cout << "EAP Failure sent from backend authenticator" 
              << std::endl;
    std::string eapMsg(b->base(), b->length());
    JobData(Type2Type<MyDiameterEapServerSession>()).SignalFailure(eapMsg);
    Stop();
  }
void MyBackendAuthSwitchStateMachine::Failure()
  {
    std::cout << "Failure without an EAP Failure" << std::endl;
    std::string eapMsg("");
    JobData(Type2Type<MyDiameterEapServerSession>()).SignalFailure(eapMsg);
    Stop();
  }
void MyBackendAuthSwitchStateMachine::Abort()
  {
    std::cout << "Session aborted for an error in state machine" << std::endl;
    Stop();
  }


void 
MyDiameterEapServerSession::ForwardEapResponse(std::string &eapMsg)
{
  std::cout << "EAP Response forwarded to backend authenticator" 
            << std::endl;
  AAAMessageBlock *msg;
  if (eapMsg.length() > 0)
    {
      msg = AAAMessageBlock::Acquire((ACE_UINT32)eapMsg.length());
      msg->copy((char*)eapMsg.data(), eapMsg.length());
      if (initial)
        {
          initial=false;
          Eap().Start(msg);    // The initial EAP-Response message.
        }
      else
        {
          Eap().Receive(msg);
        }
      msg->release();
    }
}

typedef AAAServerSessionClassFactory<MyDiameterEapServerSession> 
MyServerFactory;

class MyInitializer
{
 public:
  MyInitializer(AAAApplicationCore &appCore) 
    : applicationCore(appCore), 
      myAuthFactory(MyServerFactory(2000, AAA_STYPE_AUTHENTICATION))
  {
    Start();
  }

  ~MyInitializer() 
  {
    Stop();
  }

 private:

  void Start()
  {
    InitApplicationCore();
    InitEap();
    applicationCore.RegisterServerSessionFactory(&myAuthFactory);
  }

  void Stop() {}

  void InitApplicationCore()
  {
    ACE_DEBUG((LM_DEBUG, "[%N] Application starting\n"));
    if (applicationCore.Open() != AAA_ERR_SUCCESS)
      {
        ACE_ERROR((LM_ERROR, "[%N] Can't open configuraiton file."));
        exit(1);
      }
  }

  void InitEap()
  {
    ACE_DEBUG((LM_DEBUG, "[%N] EAP initialization.\n"));
    methodRegistrar.registerMethod
      (std::string("Identity"), EapType(1), 
       Authenticator, myAuthIdentityCreator);

    methodRegistrar.registerMethod
      (std::string("MD5-Challenge"), EapType(4), Authenticator, 
       myAuthMD5ChallengeCreator);
  }

  AAAApplicationCore &applicationCore;
  EapMethodRegistrar methodRegistrar;

  EapMethodStateMachineCreator<MyEapAuthMD5ChallengeStateMachine> 
  myAuthMD5ChallengeCreator;

  EapMethodStateMachineCreator<MyEapAuthIdentityStateMachine> 
  myAuthIdentityCreator;

  MyServerFactory myAuthFactory;
};

int
main(int argc, char *argv[])
{
  AAAApplicationCore applicationCore("config/server.local.xml");

  MyInitializer initializer(applicationCore);

  while (1) 
      ACE_OS::sleep(1);
  return 0;
}


Generated on Fri Jun 25 17:35:03 2004 for Framework API for Task Management and Protocol State Machines by doxygen 1.3.5