SIP Stack Integration

Eyeball AnyFirewall™ Engine can work with any standard 3rd Party SIP stacks. The basic idea of integrating AnyFirewall™ Engine with a SIP stack is to replace sockets with AnyFirewall™ Engine channels. Following is the brief description of how it can be integrated with two of the most popular open source SIP stacks of the industry.


Integration with oSIP

In this section, we describe oSIP’s high level architecture and changes required to integrate it with AnyFirewall™ Engine. Then we will show the procedure of making a call using the integrated solution.


oSIP

The GNU oSIP library is a popular open source implementation of SIP. The library provides support for multimedia and telecom software developers with an easy and powerful interface to use SIP based sessions in their applications. The high level architecture of oSIP is as following:


Figure 6: oSIP Architecture

One way to integrate oSIP with AnyFirewall™ Engine is to use SIP factory, SIP parser and SIP transaction libraries from oSIP only. The user application uses a network controller class which communicates with the SIP stack and AnyFirewall™ Engine. The network controller maintains the call state and uses AnyFirewall™ Engine’s recv() and send() functions to receive and send messages. AnyFirewall™ Engine’s internal functions take care of DNS resolving. In short, the integration steps are as follows:

  • Network controller asks oSIP to prepare the SIP message.
  • oSIP prepares the message and sends it back to the network controller.
  • Network controller sends the message using AnyFirewall™ Engine.
  • Network controller checks if any SIP message is received.
  • When received, it sends the message to oSIP for parsing.


Figure 7: oSIP architecture integrated with AnyFirewall™ Engine

Initializing oSIP

To handle incoming events, oSIP provides some handler functions. We have to set these callback functions during the oSIP initialization. The initialization function is as follows:

void InitoSIPCallback()
{   ...
    // initial request received callback
    osip_set_message_callback(m_osip ,OSIP_IST_INVITE_RECEIVED,
    &RequestMessageCallback);
    ...
    // set other call back messages
}

In this code fragment RequestMessageCallback function is set as the handler function for the OSIP_IST_INVITE_RECEIVED event. The structure of RequestMessageCallback function will be described later.


Making a Call

Making a call starts with creating audio channels. It is similar to the process described in Creating an audio channel.

m_iAudioChannelRTP = m_pAFEngine->Create(AF_CHANNEL_RTP);
m_iAudioChannelRTCP = m_pAFEngine->Create(AF_CHANNEL_RTCP);

Then candidate parameters of the audio channels are obtained. The CreateSession API creates a session with one media stream and two audio channels. It returns a unique session ID. Then the MakeOffer API is used to initialize all the channels. This returns the SDP structure.

char sMediaStreamSpec[100];
sprintf(sMediaStreamSpec, "%s %d %d",
            AF_MEDIA_STREAM_AUDIO,
            m_iAudioChannelRTP,
            m_iAudioChannelRTCP
        );
m_iSession = m_pAFEngine->CreateSession(sMediaStreamSpec);
std::string sAudioSDPInfo = m_pAFEngine->MakeOffer(m_iSession);
...
osip_message_t*msg = BuildInviteRequest(
        sCallerUri,
        sCalleeUri,
        sAudionSDPInfo);
        SendSipMessage(msg)
    );

Then to initiate a call, a SIP INVITE message is to be sent. The BuildInviteRequest will build the message with the SDP structure and SendSipMessage will send it. This is also similar to the code given in Sending a SIP INVITE. These two functions will have oSIP functions in them. Following is the sample code of these two functions:


osip_message_t*BuildInviteRequest(
        const string& sCallerUri,
        const string& sCalleeUri,   
        const string& sAudionSDPInfo)
{
    osip_message_t* msg = NULL;
    osip_message_init(&msg);
    osip_message_set_method(msg, _strdup(METHOD_INVITE));
    ...
    // Set uri, version, via, to, from, contact, call-id,
    // cseq, max-forward etc. fields with osip functions here
    ...
    //add SDP
    osip_message_set_content_type(msg, SDP_CONTENT_TYPE);
    osip_message_set_content_length(msg,
              itoa(sAudionSDPInfo.size()).c_str());
              osip_message_set_body(msg,
              sAudionSDPInfo.c_str(),sBody.length()
        ); 
    return msg;
}
SendSipMessage(osip_message_t* message)
{
    int iLen;
    char *szMsg = NULL;
    osip_message_to_str(message, &szMsg, (size_t*)&iLen);
    if (m_pAFEngine->Send(m_iSipChannel, szMsg, (int)iLen, AF_ BLOCKING) < 0)
    {    
        //handle errors here
    }
}
To receive incoming messages to a SIP channel we need a thread.


NetworkThread(void *arg)
{
    …
    for (;;)
    {
        int iSelect = m_pAFEngine->Select(NUM_CHANNEL, aChannels,
                            aInputEvents,
                            aOutputEvents, 
                            NETWORK_WAIT_TIME
                        );
        if (iSelect>0 && aOutputEvents[sipChannelIndex]==1)
        {
            iRet = m_pAFEngine->Recv (m_iSipChannel,
            m_pNetworkBuffer.get(),BUFFER_SIZE, AF_NON_BLOCKING);
            //save the message for parsing and
            // call event handler functions
        }
    }
}

The handler functions initialization as described in Initializing oSIP, ensures that when there is an incoming event in the network, it will call the respective function.

If set, the RequestMessageCallback function will decide the response based on the event. For example response to an invite message will be 100TRYING and 180RINGING. So the RequestMessageCallback code will be as follows:

case OSIP_IST_INVITE_RECEIVED:
response = BuildInviteResponse(request, 100, "Trying");
SendSipMessage(response);
response = BuildInviteResponse(request, 180, "Ringing");
SendSipMessage(response);
When responding to a call, user will send a 200OK response.
response = BuildInviteResponse(request, 200, "OK");
SendSipMessage(response);

In this section we showed how to send SIP INVITE message and receive responses to establish a call using oSIP integrated with AnyFirewall™ Engine. Similarly other SIP messages and responses can be sent and received.


Integration with reSIProcate

In this section, we describe the high level architecture of reSIProcate and changes required to integrate it with AnyFirewall™ Engine. Then we show the procedure of initializing reSIProcate and making a call using the integrated solution.


reSIProcate

reSIProcate is another popular open source SIP implementation. The reSIProcate SIP stack is now used in many commercial and open source products. It is a high performance, object oriented C++ SIP stack. It supports variety of operating systems (Windows, UNIX) with TCP, UDP and TLS transports. Following is the high level architecture of reSIProcate



Figure 8: reSIProcate architecture
After integrating reSIProcate with AnyFirewall™ Engine it will have following structure:



Figure 9: reSIProcate architecture after integration


InitializingDUM

At first the DUM object needs to be initialized with handlers.

reSIProcate has InviteSessionHandler, ClientRegistrationHandler and OutOfDialogHandler.

We design a common handler (AFEUa) for these three handlers that uses AnyFirewall™ Engine API.


Figure 10: DUM handlers


The initialization is as following:

DialogUsageManager* dumUa = new DialogUsageManager(m_SipStack);
// here AFEUa is a common handler for DUM which inherits the reSIProcate
// handler classes
AFEUa ua = new AFEUa();
//init handlers
dumUa->setInviteSessionHandler(&ua);
dumUa->setClientRegistrationHandler(&ua);
dumUa->addOutOfDialogHandler(OPTIONS, &ua);
...

Making a call

To make a call, audio channels need to be created as shown in Creating an audio channel.

m_iAudioChannelRTP  = m_pAFEngine->Create(AF_CHANNEL_RTP);
m_iAudioChannelRTCP = m_pAFEngine->Create(AF_CHANNEL_RTCP);

Then, candidate parameters are obtained as shown in Obtaining candidate parameters of the audio channels. AnyFirewall™ Engine APIs CreateSession and MakeOffer are also used here.


char sMediaStreamSpec[100];
sprintf(sMediaStreamSpec, "%s %d %d",
        AF_MEDIA_STREAM_AUDIO,
        m_iAudioChannelRTP,
        m_iAudioChannelRTCP
  );
m_iSession = m_pAFEngine->CreateSession(sMediaStreamSpec);
std::string sAudioSDPInfo = m_pAFEngine->MakeOffer(m_iSession);

To send an INVITE message, the SDP message is added to the DUM handler. Then makeInviteSession will create the message. Send function will put the message in queue to transport.

ua.txt = (new Data(sAudioSDPInfo));
ua.hfv = new HeaderFieldValue(ua.txt->data(), ua.txt->size());
// Mime is a resiprocate object for assigning types. Mime type("application", "sdp");
ua.sdp = new SdpContents(ua.hfv, type);
inviteMessage = dumUa->makeInviteSession(uacAor, ua.sdp,
new AFEAppDialogSet(*dumUa, "UA(INVITE)"));
dumUa->send(inviteMessage);

The SIP message will be sent through AnyFirewall™ Engine function.

CAFEConnection::write( const char *buf, const int count )
{
  ...
  CAFETransport::pAFEngine->Send(getChannel(),
  buf,count,AF_NON_BLOCKING);
  ...
}

To receive messages reSIProcate can have single or multiple threads for SIP stack or DUM. Here in this example, a single thread is shown:

while (1)
{
    // updates timer queue and transaction classes m_SipStack.process();    
    // process goes on until Transaction User’s(SIP stack) FIFO is empty    
    while(dumUa->process());     
    //take decision as the uas status
}


Similar AnyFirewall™ Engine function is used for reading SIP messages:

int CAFEConnection::read( char* buf, int count )
{
  ...
  CAFETransport::pAFEngine->Recv(getChannel(),buf, count,AF_NON_BLOCKING);
  ...
}

Also the AFEUa handler classes have to be defined with their functions. For example: when an INVITE message is received and processed, it generates onNewSession and onOffer event. Receiving these events we call respective AnyFirewall™ Engine functions from the onNewSession and onOffer functions. To send 180 Ringing and 200OK, following codes could be used for the handler:


virtual void onNewSession()
{
    ...
    serverInviteSession->provisional(180);
}
virtual void onOffer()
{
  offered = true;
  ...
  m_iSession = m_pAfe->CreateSession(mediaDescription);
  const std::string callerTag = m_pSipUtil->GetTagOfFromLine(msg);
  string sAudioSDPInfo = m_pAfe->MakeAnswer(m_iSession, remoteSDP, callerTag);
  txt = (new Data(sAudioSDPInfo));
  hfv = new HeaderFieldValue(txt->data(), txt->size());
  Mime type("application", "sdp");
  Sdp1 = new SdpContents(hfv, type);
  ...
}

In this section we showed how to implement the common DUM handler that uses AnyFirewall™ Engine methods. Then we showed how to send SIP INVITE message and receive responses using reSIProcate integrated with AnyFirewall™ Engine. Using the same procedure other messages can be sent and received.