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

Diameter NASREQ Application API

Alpha Version

Author:
Yoshihiro Ohba
Date:
Created: June 18, 2004

Updated: June 23, 2004

Introduction

The libdiameternasreq library provides a C++ API to Diameter NASREQ Application. The library implements the specification defined in draft-ietf-aaa-diameter-nasreq-16.txt. This document describes the API.

Basic Features

The API has the following features.

Sample Program

Server Sample Program

/* 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 NASREQ Application 
   Written by Yoshihiro Ohba
   Created June 2, 2004.
*/

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

  This program includes a test for Diameter NASREQ application where
  PAP or CHAP is used for authentication between a client host and a
  NASREQ server via a NAS.  Half of the clients are authenticated with
  PAP and the other half are authenticated with CHAP.

  The client session entity will prompt you to input a username.  Once
  the username is input, it is passed to the NAS.  The NAS generates a
  NASREQ AA-Request and sents to the NASREQ server.

  When PAP is used, it will be the following sequence:

  Peer                NAS                         EAP Server
                     (= NASREQ client)          (= NASREQ Server)
   |    null request        |                          |
   |<-----------------------|                          |
   |                        |                          |
(input username)            |                          |
   |                        |                          |
   |  Username/Password     |                          |
   |----------------------->|  AA_Request/User-Name    |
   |                        |    User-Password         |
   |                        |------------------------->|
   |                        |  AA-Answer/Result-Code=  |
   |                        |    DIAMETER_SUCCESS      |
   |  Success indication    |<-------------------------|
   |<-----------------------|                          |
   |                        |                          |

  When CHAP is used, it will be the following sequence:

  Peer                NAS                         EAP Server
                     (= NASREQ client)          (= NASREQ Server)
   |  Challenge request     |                          |
   |<-----------------------|                          |
   |                        |                          |
(input username)            |                          |
   |                        |                          |
   |  Challenge response/   |                          |
   |    Username            |                          |
   |----------------------->|  AA_Request/User-Name    |
   |                        |    CHAP-Auth,            |
   |                        |    CHAP-Challenge        |
   |                        |------------------------->|
   |                        |  AA-Answer/Result-Code=  |
   |                        |    DIAMETER_SUCCESS      |
   |  Success indication    |<-------------------------|
   |<-----------------------|                          |
   |                        |                          |

 */

#include <iostream>
#include <ace/Log_Msg.h>
#include <ace/OS.h>
#include "diameter_api.h"
#include "diameter_nasreq_parser.hxx"
#include "diameter_nasreq_server_session.hxx"
#include "diameter_nasreq_authinfo.hxx"

typedef AAA_JobHandle<AAA_GroupedJob> MyJobHandle;

/******** Diameter NASREQ Server Session ********/

class BackendSession : AAA_Job
{
 public:

  BackendSession(MyJobHandle &handle) : handle(handle) {}

  void Send(DiameterNasreqAuthenticationInfo &authInfo);

  void Receive(DiameterNasreqAuthenticationInfo &authInfo)
  {
    bool authSuccess;
    AAA_LOG(LM_DEBUG, "Username = %s.\n", authInfo.UserName().c_str());
    diameter_utf8string_t password("abcdef12345678");
    if (authInfo.AuthenticationType() == NASREQ_AUTHENTICATION_TYPE_PAP)
      authSuccess = ((PAP_Info&)authInfo).Validate(password);
    else if (authInfo.AuthenticationType() == NASREQ_AUTHENTICATION_TYPE_CHAP)
      authSuccess = ((CHAP_Info&)authInfo).Validate(password);
    else
      authSuccess = false;
    if (authSuccess)
      Success();
    else
      Failure();
  }

  int Serve() { return 0; }
  
  int Schedule(AAA_Job*, size_t) { return 0; }

  void Success();

  void Failure();

 private:
  std::string username;
  std::string password;
  MyJobHandle& handle;
};

class MyDiameterNasreqServerSession : public DiameterNasreqServerSession,
                                      public AAA_JobData
{
 public:
  MyDiameterNasreqServerSession(AAAApplicationCore& appCore,
                                diameter_unsigned32_t appId=NasreqApplicationId) 
    : DiameterNasreqServerSession(appCore, appId),
      handle(MyJobHandle
             (AAA_GroupedJob::Create(appCore.GetTask().Job(), 
                                     this, "backend"))),
      session(boost::shared_ptr<BackendSession>(new BackendSession(handle)))
  {
    this->Start();
  }

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

  void Abort()
  {
    std::cout << "Diameter NASREQ server session aborted." << std::endl;
    DiameterNasreqServerSession::Stop();
  }

  BackendSession& Session() { return *session; }

  void ForwardAuthenticationInfo(DiameterNasreqAuthenticationInfo &authInfo);

 private:
  MyJobHandle handle;
  boost::shared_ptr<BackendSession> session;
};

// ----------------- Definition --------------
void BackendSession::Send(DiameterNasreqAuthenticationInfo &authInfo)
  {
    std::cout << "A request sent from backend" << std::endl;
    handle.Job().Data(Type2Type<MyDiameterNasreqServerSession>())->
      SignalContinue(authInfo);
  }
void BackendSession::Success()
  {
    std::cout << "Success sent from backend" << std::endl;
    handle.Job().Data(Type2Type<MyDiameterNasreqServerSession>())->
      SignalSuccess();
  }
void BackendSession::Failure()
  {
    std::cout << "Failure" << std::endl;
    handle.Job().Data(Type2Type<MyDiameterNasreqServerSession>())->
      SignalFailure();
  }

void 
MyDiameterNasreqServerSession::ForwardAuthenticationInfo
(DiameterNasreqAuthenticationInfo &authInfo)
{
  std::cout << "Auth-Info forwarded to backend" << std::endl;
  Session().Receive(authInfo);
}

typedef AAAServerSessionClassFactory<MyDiameterNasreqServerSession> 
MyServerFactory;

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

  ~MyInitializer() 
  {
    Stop();
  }

 private:

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

  void Stop() {}

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

  AAAApplicationCore &applicationCore;
  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;
}

Client Sample Program

/* 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: */
/* 
   client_test.cxx
   Client test program for Diameter NASREQ Application 
   Written by Yoshihiro Ohba
   Created May 4, 2004.
*/

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

  This program includes a test for Diameter NASREQ application where
  PAP or CHAP is used for authentication between a client host and a
  NASREQ server via a NAS.  Half of the clients are authenticated with
  PAP and the other half are authenticated with CHAP.

  The client session entity will prompt you to input a username.  Once
  the username is input, it is passed to the NAS.  The NAS generates a
  NASREQ AA-Request and sents to the NASREQ server.

  When PAP is used, it will be the following sequence:

  Peer                NAS                         EAP Server
                     (= NASREQ client)          (= NASREQ Server)
   |    null request        |                          |
   |<-----------------------|                          |
   |                        |                          |
(input username)            |                          |
   |                        |                          |
   |  Username/Password     |                          |
   |----------------------->|  AA_Request/User-Name    |
   |                        |    User-Password         |
   |                        |------------------------->|
   |                        |  AA-Answer/Result-Code=  |
   |                        |    DIAMETER_SUCCESS      |
   |  Success indication    |<-------------------------|
   |<-----------------------|                          |
   |                        |                          |

  When CHAP is used, it will be the following sequence:

  Peer                NAS                         EAP Server
                     (= NASREQ client)          (= NASREQ Server)
   |  Challenge request     |                          |
   |<-----------------------|                          |
   |                        |                          |
(input username)            |                          |
   |                        |                          |
   |  Challenge response/   |                          |
   |    Username            |                          |
   |----------------------->|  AA_Request/User-Name    |
   |                        |    CHAP-Auth,            |
   |                        |    CHAP-Challenge        |
   |                        |------------------------->|
   |                        |  AA-Answer/Result-Code=  |
   |                        |    DIAMETER_SUCCESS      |
   |  Success indication    |<-------------------------|
   |<-----------------------|                          |
   |                        |                          |

 */

#include <iostream>
#include <ace/Log_Msg.h>
#include <ace/OS.h>
#include <ace/Atomic_Op_T.h>
#include "diameter_api.h"
#include "diameter_nasreq_client_session.hxx"

ACE_Atomic_Op<ACE_Thread_Mutex, int> TotalSuccess;

typedef AAA_JobHandle<AAA_GroupedJob> MyJobHandle;

class ClientData;
class NAS_Application;

class NasreqTask : public AAA_Task
{
 public:
  NasreqTask() : AAA_Task(AAA_SCHED_FIFO, "NASREQ") 
  {}

  ~NasreqTask() {}
};

class Channel 
{
 public:
  Channel() {}
  virtual void Transmit(DiameterNasreqAuthenticationInfo &authInfo)=0;
};


/******** Diameter NASREQ Client Session ********/

class MyDiameterNasreqClientSession : public DiameterNasreqClientSession
{
 public:
  MyDiameterNasreqClientSession(AAAApplicationCore& appCore, MyJobHandle h) 
    : DiameterNasreqClientSession(appCore, h)
  {}

  void Abort();

  void SignalContinue(DiameterNasreqAuthenticationInfo&);

  void SignalSuccess();

  void SignalFailure();

  void SignalReauthentication();

  void SignalDisconnect() {}

  void SetDestinationRealm
  (AAA_ScholarAttribute<diameter_utf8string_t> &realm);

  void SetAuthInfo(DiameterNasreqAuthenticationInfo &authInfo);
};

class ClientSession : AAA_Job
{
 public:

  ClientSession(MyJobHandle &handle) : handle(handle) {}

  int Serve() { return 0; }
  
  int Schedule(AAA_Job*, size_t) { return 0; }

  void Receive(DiameterNasreqAuthenticationInfo &authInfo)
  {

    if (authInfo.AuthenticationType() == 
        NASREQ_AUTHENTICATION_TYPE_PAP)
      // PAP
      {
        PAP_Info& papInfo = (PAP_Info&)authInfo;
        papInfo.UserName() = username;
        papInfo.UserPassword() = password;
        Send(papInfo);
      }
    else if (authInfo.AuthenticationType() == 
             NASREQ_AUTHENTICATION_TYPE_CHAP)
      // CHAP
      {
        CHAP_Info& chapInfo = (CHAP_Info&)authInfo;
        
        chapInfo.UserName() = username;
        // Initialize the result.
        std::string md5Result(MD5_DIGEST_LENGTH, '\0');

        // Do MD5.
        std::string rawResponse(chapInfo.ChapAuth().ChapIdent());
        rawResponse.append(password);
        rawResponse.append((std::string&)chapInfo.ChapChallenge());
        MD5((const unsigned char*)rawResponse.data(), 
            (unsigned)rawResponse.size(), (unsigned char*)md5Result.data());
        chapInfo.ChapAuth().ChapResponse = md5Result;
        Send(chapInfo);
      }
    else
      {
        AAA_LOG(LM_ERROR, "invalid authenticaiton type.\n");
        Send(authInfo);
      }
  }

  void Send(DiameterNasreqAuthenticationInfo &authInfo);

  void Success();

  void Failure();

  void SetCredentials(std::string uname, std::string pword)
  {
    username = uname;
    password = pword;
  }

 private:

  std::string username;
  std::string password;
  MyJobHandle& handle;
};

class ClientChannel : public Channel
{
 public:
  ClientChannel(ClientSession &s) : session(s) {}
  void Transmit(DiameterNasreqAuthenticationInfo &authInfo) 
  { session.Receive(authInfo); }
  ClientSession &session;
};

class NAS_Channel : public Channel
{
 public:
  NAS_Channel(MyDiameterNasreqClientSession& s) : session(s) {}

  void Transmit(DiameterNasreqAuthenticationInfo &authInfo) 
  { session.ForwardAuthenticationInfo(authInfo); }

  MyDiameterNasreqClientSession &session;
};

class ClientApplication : public AAA_JobData
{
 public:
  ClientApplication(NasreqTask &task, ACE_Semaphore &sem) : 
    handle(MyJobHandle
           (AAA_GroupedJob::Create(task.Job(), this, "client"))),
    session(boost::shared_ptr<ClientSession>(new ClientSession(handle))),
    rxChannel(ClientChannel(*session)),
    txChannel(0)
  {
    session->SetCredentials("opendiameter@research.org", "abcdef12345678");
  }
  ~ClientApplication() 
  {}
  void Start(Channel *c)
  { 
    txChannel = c;
  }

  ClientChannel* RxChannel() { return &rxChannel; }

  Channel& TxChannel() { return *txChannel; }

 private:
  MyJobHandle handle;
  boost::shared_ptr<ClientSession> session;
  ClientChannel rxChannel;
  Channel  *txChannel;
};

// My application session (not used in this test program).
class NAS_Application : public AAA_JobData
{

 public:
  NAS_Application(NasreqTask &task, AAAApplicationCore& appCore,
                  ACE_Semaphore &sem)
    : handle(MyJobHandle
             (AAA_GroupedJob::Create(appCore.GetTask().Job(), this, "NAS"))),
      diameter(boost::shared_ptr<MyDiameterNasreqClientSession>
               (new MyDiameterNasreqClientSession(appCore, handle))),
      semaphore(sem),
      rxChannel(NAS_Channel(*diameter)),
      txChannel(0)
  {
    semaphore.acquire();
  }
  ~NAS_Application() {}

  void Start(Channel *c, int algorithm)
  { 
    txChannel = c;
    diameter->Start(); 
    if (algorithm == 0)
      {
        std::cout << "Start PAP." << std::endl;
        PAP_Info papInfo;
        c->Transmit(papInfo);
      }
    else
      {
        std::cout << "Start CHAP." << std::endl;
        CHAP_Info chapInfo;
        chapInfo.ChapAuth().ChapAlgorithm = CHAP_ALGORITHM_MD5;
        chapInfo.ChapAuth().ChapIdent = std::string(1, '1'-'0');
        chapInfo.ChapChallenge() = std::string("dkjf;akjkljrk;le");
        c->Transmit(chapInfo);
      }
  }

  Channel* RxChannel() { return &rxChannel; }

  Channel& TxChannel() { return *txChannel; }

  MyDiameterNasreqClientSession &Diameter() { return *diameter; }

  ACE_Semaphore& Semaphore() { return semaphore; }

 private:
  MyJobHandle handle;
  boost::shared_ptr<MyDiameterNasreqClientSession> diameter;
  ACE_Semaphore &semaphore;
  NAS_Channel rxChannel;
  Channel *txChannel;
  std::string username;
};

// ----------------- Definition --------------
void 
MyDiameterNasreqClientSession::Abort()
  {
    std::cout << "Diameter NASREQ client session aborted." << std::endl;
    DiameterNasreqClientSession::Stop();
    JobData(Type2Type<NAS_Application>()).Semaphore().release();
  }

void 
MyDiameterNasreqClientSession::SignalContinue
(DiameterNasreqAuthenticationInfo& authInfo)
{
  JobData(Type2Type<NAS_Application>()).TxChannel().Transmit(authInfo);
}

void 
MyDiameterNasreqClientSession::SignalSuccess()
  {
    AAA_LOG(LM_DEBUG, "Client authentication successful.\n");
    TotalSuccess++;
    Stop();
    JobData(Type2Type<NAS_Application>()).Semaphore().release();
  }

void 
MyDiameterNasreqClientSession::SignalFailure()
  {
    AAA_LOG(LM_DEBUG, "Client authentication failed.\n");
    Abort();
  }

void 
MyDiameterNasreqClientSession::SignalReauthentication()
  {
    AAA_LOG(LM_DEBUG, "Client Re-authentication triggerred (to be implemented).\n");
    Abort();
  }

void
MyDiameterNasreqClientSession::SetDestinationRealm
(AAA_ScholarAttribute<diameter_utf8string_t> &realm)
{
  std::string& userName = AuthenticationInfo().UserName();

  size_t pos;

  if ((pos = userName.find('@')) != std::string::npos)
    realm.Set(std::string(userName, ++pos, userName.length() - pos));
  else
    realm.Set(std::string("research.org"));
}

void
ClientSession::Send(DiameterNasreqAuthenticationInfo &authInfo)
{
  handle.Job().Data(Type2Type<ClientApplication>())->TxChannel()
    .Transmit(authInfo);
}

class MyInitializer
{
 public:
  MyInitializer(NasreqTask &t, AAAApplicationCore &appCore) 
    : task(t), applicationCore(appCore)
  {
    Start();
  }

  ~MyInitializer() 
  {
    Stop();
  }

 private:

  void Start()
  {
    InitApplicationCore();
  }

  void Stop()
  {
    task.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);
      }
  }

  NasreqTask &task;

  AAAApplicationCore &applicationCore;
};

int
main(int argc, char *argv[])
{

  NasreqTask task;
  AAAApplicationCore applicationCore("config/client.local.xml");
  MyInitializer initializer(task, applicationCore);

#if defined(WIN32)
  #define num 100
  char c;
  std::cout << "Input any characer and return after peer connection has established: ";
  std::cin >> c;
#else
  int num;
  std::cout << "Input number of sessions and return after peer connection has established: ";
  std::cin >> num;
#endif

  ACE_Semaphore semaphore(num);

  TotalSuccess=0;

  std::auto_ptr<ClientApplication> clientApp[num];
  std::auto_ptr<NAS_Application> nasApp[num];
  for (int i=0; i<num; i++)
    {
      clientApp[i] 
        = std::auto_ptr<ClientApplication>
        (new ClientApplication(task, semaphore));
      nasApp[i] 
        = std::auto_ptr<NAS_Application>
        (new NAS_Application(task, applicationCore, semaphore));
      clientApp[i]->Start(nasApp[i]->RxChannel());
      nasApp[i]->Start(clientApp[i]->RxChannel(), i % 2);
    }

  // Block until the NAS conversation completes.
  for (int i=0; i<num; i++)
    semaphore.acquire();

  task.Stop();
  std::cout << "Total number of sessions : " << num << std::endl;
  std::cout << "Total number of success : " << TotalSuccess.value() << std::endl;
  return 0;
}


Generated on Fri Jun 25 19:15:15 2004 for Diameter NASREQ Application by doxygen 1.3.5