SIP Trunks


With the dawn of IP telephony service and cloud communication platforms in recent years, the SIP has caught the attention of many application developers. while SIP is essentially a session management multimedia signalling protocol its generic stack can be used for various use cases from IoT camera streaming sessions to call centres even auto calling for purpose of sharing OTP(one-time password) etc. In this I will highlight the usecase of large calltraffic and the use of SIP trunks.

SIP based trunking can provide significant cost savings and business process improvements by supporting the native SIP protocol that controls the VoIP systems used in call centres and business communication platforms.

  • (+) unified communication
  • (+) lower telco network
  • (+) streamline operations for multicountry/ geography

Traditional trunk call

In the past, telephone systems used trunk lines to connect different parts of the network. Trunk lines were long-distance communication lines that connected telephone exchanges in different locations. Trunk calls were calls made over these trunk lines. They were typically used for long-distance communication, as they allowed calls to be made between exchanges that were geographically far apart. Trunk calls were generally more expensive than local calls, as they involved the use of long-distance communication lines.

Traditional trunk calls operated like a circuit with local loops , trunk lines and switching offices. The telco acted as carriers that sell of lease communication lines to facilitate communication over long distances using local exchanges and interexchange carriers.

In the early days of telephone systems, trunk lines were typically made of copper wires or cables. Later, trunk lines were replaced with satellite links and fiber optic cables, which provided higher capacity and faster transmission speeds. Today, with the widespread adoption of VoIP (Voice over Internet Protocol) technology, many telephone systems no longer use trunk lines in the traditional sense. Instead, they use virtual connections, such as SIP trunks (Session Initiation Protocol trunks), which allow organizations to make and receive phone calls over the internet. SIP trunks are generally more flexible and cost-effective than traditional trunk lines, and do not require the installation of additional hardware.

Voice trunk Lines in SS7 based Next Generations IN networks used media gate ways and MGCP, H323 protocols

Image credits : Unknown

SIP trunk (older) systems

SIP is a protocol that is commonly used in VoIP (Voice over Internet Protocol) systems to set up, modify, and terminate sessions that involve the exchange of audio, video, and other media. SIP Trunks are virtual voice channels (or paths) which deliver media (voice, video, IM) over an IP network to a designated endpoint. SIP Trunks can be thought of as a virtual line or concurrent call path. SIP Trunks are delivered over an IP connection like Tier One Carrier or Voice Optimized Recommended or UDP. SIP Trunk may be over-subscribed ie can have more numbers than trunks for example G.711 – 17 calls over T1 or G.729a – 45 calls over T1. SIP Trunking can be provided as one-way or two-way lines. Direct Inward Dialing (DIDs) can be used for toll-free number service.

Centralized SIP Trunk Model

Centralized SIP Trunk Model is designed to aggregate all calls from all sites and funnels them into a single entry point. Each site has its own SIP trunk termination of the appropriate capacity for calls to and from that site.

Such SIP trunks models offer benefits in three significant areas:

  1. Cost savings, arising from many factors including reduced telecommunications network charges and streamlined operations.
  2. Unified communications, where voice, video, email, text and other messaging technologies are combined to provide greater flexibility for users by enabling new ways to transfer information and manage connectivity. Many SIP trunk providers offer advanced features such as call forwarding, call waiting, and voicemail, which can improve the overall communication experience for employees.
  3. Business Continuity and Disaster Recovery, where the right physical configuration in conjunction with intelligence in the network can be leveraged to provide uninterrupted communications and alternative means to stay connected for employees in the event of system bottlenecks or failures.

SIP trunking is an IP-based alternative to ISDN trunking services

SIP Trunking is a low-cost IP-based alternative to ISDN offering for medium to large businesses needing upwards of several tens of channels in a trunk, often across multiple sites, with IP VPN access. 

  • (+) Optimal utilization of bandwidth by delivering both data and voice in the same bandwidth

A telephony company such as a telecom service provider may expose SIP trunks as a means of connecting inbound or outbound calls through its telecom network. For the integrator ( or the service provider managing the other enedpoint of the call leg ) it can be no different that a traditional phone call.The SIP signalling however is useful for enabling better session understaning using standard SIP requests and responses as compared to SS7 or PRI lines.

Planning to set up SIP trunk

•Cost analysis
•Assess traffic volumes and patterns
•Assess network design implications
•Emergency call policy
•Define production user community phases
•Define user community to pilot
•Evaluate future new services
•Assess security precautions

The steps to set up a SIP trunk connection may vary depending on the specific provider and the equipment being used. However, here are some general steps that are often involved in the process:

  1. Choose a SIP trunk provider: Research and compare different SIP trunk providers to find one that meets your organization’s needs and budget.
  2. Sign up for a SIP trunk account: Follow the provider’s instructions to sign up for a SIP trunk account. This may involve completing an online form, providing contact information and payment details, and selecting the desired features and services.
  3. Configure your VoIP phone system: Consult your VoIP phone system’s documentation to learn how to configure it to work with a SIP trunk. This may involve specifying the SIP trunk’s IP address and port number, as well as any authentication credentials that are required.
  4. Test the connection: Once the SIP trunk is set up, it is a good idea to test the connection to ensure that it is working properly. Make a few test calls to verify that the connection is functioning as expected.
  5. Use the SIP trunk: Once the SIP trunk is set up and tested, it can be used to make and receive calls using your VoIP phone system.

SIP Trunking platform has to integrate with multiple networks seamlessly. Components for setting up a SIP trunking system requires atleast these

  • Compliance with standrad signalling protol, like SIP.
  • SBC( Session Border Controller ) facing the private PBX
  • Gateway for specific endpoints such as PSTN gateway , public internet gateway etc
  • L3/L4 Layer switches
  • Telco operator lines
  • Codec support

Kamailio is an open-source SIP (Session Initiation Protocol) server that can be used to create a SIP trunk. Kamailio can be PBX used to connect different locations within an organization, enabling employees to communicate with each other using their VoIP phones. Kamailio can also be used to set up a SIP trunk in a number of ways. For example, it can be used to connect an organization’s VoIP phone system to the public telephone network, allowing employees to make and receive calls from outside the organization.

https://telecom.altanai.com/2016/08/02/session-border-controller-for-webrtc/

Kamailio is a highly flexible and customizable SIP server that can be configured to meet the specific needs of an organization. It offers a range of features and functionality, including call routing, load balancing, and security. Kamailio is a popular choice for organizations that want to set up a SIP trunk because it is open-source and can be customized to meet their specific needs.

Features of SIP trunking

SIP trunk with VoIP phone systems are often preferred over traditional phone systems because they are generally more flexible and cost-effective. They allow employees to make and receive calls from any device with an internet connection, including desk phones, smartphones, and laptops. They can be easily scaled up or down to meet changing communication needs and do not require the installation of additional physical hardware. Some factors to consider when evaluating SIP trunks include:

  1. Cost: It is important to compare the costs of different SIP trunk providers and consider factors such as monthly fees, per-minute charges, and any additional fees for features or services.
  2. Coverage: Make sure that the SIP trunk provider has coverage in the areas where your organization needs to make and receive calls.
  3. Quality: The quality of a SIP trunk can vary greatly depending on the provider and the connection. Be sure to research the provider’s reputation for call quality and reliability.
  4. Features: Different SIP trunk providers may offer different features, such as call forwarding, call waiting, and voicemail. Consider which features are important to your organization and make sure that the SIP trunk provider offers them.
  5. Customer support: It is important to choose a SIP trunk provider that offers reliable customer support in case you experience any issues with your service.

Other features that are good to have is integration to existing backend for OSS/BSS stack. Some of the feature set for a carrier grade SIP trunking solution are listed here

  • Inbound and outbound trunks
  • Number Import/Export
  • Security
    • Dynamic registeration of users
    • Authentication and Authorization
    • Security (SRTP)
  • Cost Savings
    • Low cost for large traffic volumes instead of charges of call per second
    • CDR for tracing and monitoring call failures
  • Clear media stream ( no robotic or choopy audio). Good MOS score
  • realtime traffic monitoring to rule out bad players.
  • Inbound and Outbound call – Call Establishment, Rejection, Termination
  • DDI: Direct Dialling-In ranges can be provided on the SIP Trunk
  • CLIP(Calling Line Identification Presentation )/CLIR Calling Line Identification Presentation Restriction) for Inbound and Outbound
  • Call Management
    • AUTH Code Screening
    • Combined Screening
    • Data Call Screening
    • Local Screening
    • Anonymous Call Rejection: Anonymous Call Rejection
    • Incoming Call Barring: bar receiving of calls to certain extensions
    • Outgoing Call Barring: Restrict calls to certain numbers
    • Incoming Call Diversion – unconditional, busy, and unreachable
    • Call Admission Control: Call Admission Control (CAC) is a mechanism to restrict the number of simultaneous sessions (calls) 
    • Incoming Call Diversion (DestNo not reachable, CAC exceeded, unconditional)
  • Geographic and Non-Geographic Number Support
  • Multiple Codec Support
  • Emergency Calling: Emergency Calls are routed on a priority basis irrespective of the customer’s available channel

Trunking inbound services voice can be used to support contact centres, conferencing, number translation services etc. Regulatory requirements for the operation of the customer in the PSTN of respective countries must be met with Country Specific Emergency Calling support Enhanced feature set for SIP trunking should include the features of the SIP Trunking with Multicountry support

  • Enhanced CAC(Call Admission Control) – Directional & Network
  • Global Dial Plan Support
  • Proactive MCID (Malicious CallerId) Identification and tracing
  • Call Distribution(CD)
  • Intelligent Routing involving machine learning and constant feedback
    • Origin Based Routing
    • Menu Routing
    • Origin Dependent Routing (ODR)
    • PIN Routing
    • Dynamic Route Select
    • Time-Dependent Routing (TDR)
    • Uniform Load Distribution(ULD)
    • International Routing
    • Mobile Routing
    • Payphone Routing
  • Product Association

Ultimately, the most useful SIP trunk for your organization will depend on your specific needs and budget. It is a good idea to research and compare different SIP trunk providers to find the one that best meets your organization’s needs.

Future of SIP trunks

SIP trunking systems are likely to continue to be an important part of the telecommunications landscape in the future. As more and more organizations adopt WebRTC or SRT based VoIP (Voice over Internet Protocol) technology for their phone systems, the demand for SIP trunks is likely to continue to grow. One trend that is expected to shape the future of SIP trunking is the increasing adoption of cloud-based communication systems. As more organizations move their communication systems to the cloud, they are likely to turn to SIP trunks as a way to connect their phone systems to the public telephone network and enable remote communication. Another trend that is expected to impact the future of SIP trunking is the increasing adoption of 5G technology. 5G networks offer faster speeds and lower latency, which may make it possible to use SIP trunks for real-time communication applications such as interactive and/or immersive video conferencing.


Kamailio DNS and NAT

  • DNS sub-system in Kamailio
    • DNS failover
    • DNS load balancing
    • NAT ( Network Address Translation)
  • NAT ( Network Address Translation)
  • Why is NAT is important in SIP?
  • Types of NAT solutions
  • NAT behaviours
  • RTP NAT
  • Fixing NAT
  • NAT Traversal Module
    • Why use keepalive when Registrations are already there for NATing ?
    • How keepalives work for NATing ?
    • function nat_keepalive()
    • Params
    • Functions
    • client_nat_test()
      • fix_contact()
      • nat_keepalive()
    • Pseudo Variables
    • Statistics
  • NATHelper Module
    • NAT pinging types
      • UDP packet
      • SIP request
    • params
    • functions
    • Pseudo Variables
    • RPC Commands

In this article, we discuss Nating in a SIP Server like Kamailio. Types of NAT pings, their behaviour and types. Also some implementation of some of the Kamailios modules like

  • NAT Traversal module
  • NAT helper module
  • STUN module

A repository for extensive application of kamailio modules for various usecases is https://github.com/altanai/kamailioexamples

DNS sub-system in Kamailio

To resolve hostname into IPs Kamailio can do either of below

  • use libresolv and a combination of the locally configured DNS server /etc/hosts and the local Network Information Service (NIS/YP a.s.o) or
  • cache the query results and first look into internal cache

DNS failover – if destination resolves to multiple addresses tm can try all of them until it finds one to which it can successfully send the packet or it exhausts all of them, with internal DNS cache. Also used when the destination host doesn’t send any reply to a forwarded invite within the SIP timeout interval (tm fr_timer parameter).

DNS load balancing – SRV based load balancing with weight value in the DNS SRV record.

  • (-) Only the locally configured DNS server (usually in /etc/resolv.conf) is
    used for the requests (/etc/hosts and the local Network Information Service are ignored).
    • optional: disable the DNS cache (use_dns_cache=off or compile without -DUSE_DNS_CACHE).
  • (-) DNS cache uses extra memory
    • optional: disable the DNS cache.
  • (-) DNS failover introduces a very small performance penalty
    • optional: disable the DNS failover (use_dns_failover=off).
  • (-) DNS failover increases the memory usage (the internal structures used to represent the transaction are bigger when the DNS failover support is compiled).
    • optional: compile without DNS failover support (DUSE_DNS_FAILOVER).Turning it off from the config file is not enough in this case (the extra memory will still be used).

NAT ( Network Address Translation)

Network address translation replaces the IP address within packets with a different IP address which internet endpoints can relate with. This enables multiple hosts in a private subnet with their pwn private address ( 10.x.x.x or 192.x.x.x etc ) to share single public IP address interface, to access the Internet.

NAT is bidirectional- If the private ip:port got translated to public ip:port on the inside interface while entering outside internet, on arriving from outside interface it will get translated from public ip:port to private ip:port.

For a SBC ( Session border controller ) or where the kamailio server is directly customer facing, where you dont have a private line or VPN to clients, then it is often encountered with NATed endpoints. Read more about NAT traversal using STUN and TURN here

Why is NAT is important in SIP?

These characteristics of SIP design and operation flows demonstrate why NAT solutions are so important

  • RFC 3261 for SIP presumed end-to-end reachability and does not specify much around ANT issues .
  • No NLRI (Network Layer Reachability Information) translation layer exists, such as DNS or ARP
  • SIP is designed to used RTP which uses dynamically allocated ports to stream media.
    It is comparable to FTP which creates ephemeral connections on unpredictable dynamic ports to send multiplexed data and “metadata”, instead of protocol like HTTP where all data is sent on same connection.
  • UDP (default transport for SIP) is connection less and session tracking requires these be mapped onto a statelful flow, rigorous keepalives and other such techniques like using TCP instead have their own tradeoffs
  • since sip packets put network and transport information right on sip header they are limited by the rateability and awareness of their network interface thereby prevent other endpoint from reaching its ip or port

Types of NAT solutions

  • Client-side NAT traversal – clients are responsible for identifying their WAN NLRI and adding ip and port to navigate them in outside world
  • Server-side NAT traversal – SIP server should discover the client’s WAN addressing while clients continue to work transparently behind NAT. Requires that DIP server look at the source and destination ip and port of actual packets instead of relying on the encapsulated sip headers and SDP body.
  • ALG (Application Layer Gateways) – mostly applied at router itself. wodk by susbtitung public IP/port information inplace of provate and vice versa for return packets. Limitataions – they dont provide a fullproof fix example they may fix Via but not the Contact address or SDP body or RTP ports
WebRTC signalling and media flow on Open public network
Open network leading to smooth p2p media stream

Far-end NAT traversal solution ( TURN server)

WebRTC media flow when peers are behind NAT . Uses TURN relay mechanism
public private ip mapping , firewalls and private network obstruct p2p media flow. TURN is useful to relay media pckets

NAT behaviours

Cone NATSymmetric NAT
Local client performs an outbound connection to a remote UA and a dynamic rule is created for the destination IP tuple, allowing the remote machine to connect back.Local client allows inbound connections from a specific source IP address and port, also NAT assigns a new random source port for each destination IP tuple
Further subdivied into:
1. Full Cone NAT
2. Restricted Cone NAT – all requests from the same internal IP address and port are mapped to the same external IP address and port.
-3. Port-Restricted Cone NAT

RTP NAT

NAT not only applies to sip signalling packets but also to RTP. Even RTP packets are not able to transverse accross private -public network interfaces to the right place across a NAT’d connection.

To solve two-way media RTP performs RTP latching, where client listens for at least one RTP frame arriving at the destination port it advertised, and harvests the source IP and port from that packet and uses that for the return RTP path. RTP latching works out of the box for public RTP endpoints but not for ones behind NAT.

It is thus recommended to use an intermediate RTP relay such as RTPengine on kamailio. It is controlled via a UDP control socket by kamailio as an external process. More on installation and descrition of RTP engine on kamailio is covered here. When RTPengine control module receives RTP offer /answer from Kamailio, it opens a pair of RTP/RTCP ports to receive traffic and substitues in SDP. Doing so for both ends makes RTP engine come in the path of media stream packets for both directions.

A first INVITE has no no corresponding on NAT ( as no port has been allocated yet ) so the c= contact and m = media lines would look like

c=IN IP4 192.0.2.13.
m=audio 23767 RTP/AVP 0 101.

We can force RTP to go through a proxy by changing c line and m line to

c=IN IP4 address-of-proxy  
m=audio port-on-proxy RTP/AVP 0 101

A separate daemon called an RTP proxy (with a public IP address) that both user agents can send their audio to, can be setup by calling force_rtp_proxy().

Fixing NAT

When the client is behind NAT, following needs to be taken careof to provide smooth operation

  1. Ensuring Tranactional replies are sent to correct source address ( maybe using ;rport param and forcerport() method ) instead of just relying on via header transport protocol and port.
    example:
if (client_nat_test("3")){
    //CALL RE-INVITE/UPDATE Nat DETECTED $ci\n");
    force_rport();
    fix_contact();
    ...
}

also Change Media ip address to public IP

if(nat_uac_test("8") && search("Content-type: application/sdp")) {
        // RE-INVITE/UPDATE CALL fix SDP- NAT
        fix_nated_sdp("2");
}

Any far-end NAT traversal solution ( TURN server) if employed should stay in path of entire Dialog not just for initial INVITE transaction which many times results in ACK being dropped. This can be achived by adding Record-Route header of rr module to the initial INVITE request itself

Set the advertised address of the public-facing inetrface to the Public NAT IP using “listen” parameter

Ensure contact URI is NAT processed by using NATHelper modules which rewrites the domain portion of the Contact URI to contain the source IP and port of the request or reply. add_contact_alias([ip_addr, port, proto]) in NAThelper module which adds “;alias=ip~port~transport” parameter to the contact URI containing either received ip, port, and transport protocol or those given as parameters

Implement RTP proxy which performs NAT for streams such as rtpengine module

NAT Traversal Module

Provides far-end NAT traversal to kamailio’s SIP signalling. Its role is

  • detect user agents behind NAT
  • manipulate SIP headers so that user agents can continue working behind NAT transparently
  • keepalives to UA behind NAT to preserve their visibility in network

Some pros and cons of NATTravsersal module

  • (+) detect even UAs behind multiple cascaded NAT boxes, complex distributed env with multiple proxies
  • (+) handle env where incoming and outgoing paths are diff for SIP messages
  • (+) handle cases when routing path may even change between consecutive dialogs
  • (+) can work for other than registered UA’s also
  • (-) built for IPv4 NAT handling not adapted to support IPv6 session keepalives.

Why use keepalive when Registrations are already there for NATing ?

  1. NAT binding works for registered users who want incoming calls. However for cases like outgoing calls or for presence subscription notifications, failings registration implies inability to receive further in-dialog messages after the NAT binding expires. This artificial binding for registrations makes system unreliable and volatile as it doesnot guarantee the delivery of in-dialog messages for outgoing calls without registration renewal. Therefore keepalive are adopted which also works for unregistered users.
  2. Minimizes the traffic as only border proxies send keepalives which send keepalives statelessly, instead of having to relay messages generated by the registrars.
  3. Also for situations when DNS resolves diff proxies for outgoing or incoming path traditional register based keepalives fail to associate or dissociate correct routes.

How keepalives work for NATing ?

This mechanism works by sending a SIP request to a user agent behind NAT to make that user agent send back a reply. The purpose is to have packets sent from inside the NAT to the proxy often enough to prevent the NAT box from timing out the connection.

Module sends Keeplaives to preserve their visibility only in :

  • Registration – for user agent that have registered to for incoming calls, triggering keepalive for a REGISTER request.
  • Subscription – for presence agents that have subscribed to some events for receiving back notifications with SUBSCRIBE request.
  • Dialogs – for user agents that have initiated an outgoing call for receiving further in-dialog messages.
    When all the conditions to keepalive a NAT endpoint will disappear, that endpoint will be removed from the list with the NAT endpoints that need to be kept alive.

function nat_keepalive()

  • the function needs to be called on proxy directly interacting with UA behind NAT.
  • call only once for the requests (REGISTER, SUBSCRIBE or outgoing INVITEs) that triggers the need for network visibility.
  • call before the request gets either a stateless reply or it is relayed with t_relay()
  • for outgoing INVITE , it triggers dialog tracing for that dialog and will use the dialog callbacks to detect changes in the dialog state.

Dependencies – sl , tm and dialog module

Params

  • keepalive_interval – time interval between sending a keepalive message to all the endpoints that need being kept alive. A negative value or zero will disable the keepalive functionality.
modparam("nat_traversal", "keepalive_interval", 30) // 30 seconds keeplaive inetrval
  • keepalive_method – SIP method to use to send keepalive messages.usual ones are NOTIFY and OPTIONS. Default value is “NOTIFY”.
modparam("nat_traversal", "keepalive_method", "OPTIONS")
  • keepalive_from – SIP URI to use in the From header of the keepalive requests. default sip:keepalive@proxy_ip,with IP address of the outgoing interface
modparam("nat_traversal", "keepalive_from", "sip:keepalive@altanai.com")
  • keepalive_extra_headers – extra headers that should be added to the keepalive messages. Header must also include the CRLF (\r\n) line separator. Multiple headers can be specified by concatenating with \r\n separator.
modparam("nat_traversal", "keepalive_extra_headers", "User-Agent: Kamailio\r\nX-MyHeader: some_value\r\n")
  • keepalive_state_file – filename where information about the NAT endpoints and the conditions for which they are being kept alive is saved . It is used when Kamailio starts to restore its internal state and continue to send keepalive messages to the NAT endpoints that have not expired in the meantime. Also used at kamailio restart as it avoids losing keepalive state information about the NAT endpoints.
modparam("nat_traversal", "keepalive_state_file", "/var/run/kamailio/keepalive_state")

Functions

  • client_nat_test ()– Check if the client is behind NAT. Tests to be performed gievn by int can be :
    1 – tests if client has a private IP address or one from shared address space in the Contact field of the SIP message.
    2 – tests if client has contacted Kamailio from an address that is different from the one in the Via field.
    4 – tests if client has a private IP address or one from shared address space in the top Via field of the SIP message.

For example calling client_nat_test(“3”) will perform test 1 and test 2 and return true if at least one succeeds, otherwise false.

  • fix_contact() – replace the IP and port in the Contact header with the IP and port the SIP message was received from. Usually called after a succesfull call to client_nat_test(type)
if (client_nat_test("3")) {
    fix_contact();
}
  • nat_keepalive() – Triggers keepalive functionality for the source address of the request. When called it only sets some internal flags, which will trigger later the addition of the endpoint to the keepalive list if a positive reply is generated/received (for REGISTER and SUBSCRIBE) or when the dialog is started/replied (for INVITEs). For this reason, it can be called early or late in the script. The only condition is to call it before replying to the request or before sending it to another proxy. If the request needs to be sent to another proxy, t_relay() must be used to be able to intercept replies via TM or dialog callbacks.

If stateless forwarding is used, the keepalive functionality will not work. Also for outgoing INVITEs, record_route() should also be used to make sure the proxy that keeps the caller endpoint alive stays in the path.

if ((method=="REGISTER" || method=="SUBSCRIBE" ||
    (method=="INVITE" && !has_totag())) && client_nat_test("3"))
{
    nat_keepalive();
}

Pseudo Variables

  • $keepalive.socket(nat_endpoint)
  • $source_uri

Statistics

  • keepalive_endpoints – total number of NAT endpoints that are being kept alive.
  • registered_endpoints – NAT endpoints kept alive for registrations
  • subscribed_endpoints – NAT endpoints kept alive for subscriptions.
  • dialog_endpoints – Indicates how many of the NAT endpoints are kept alive for taking part in an INVITE dialog.

NATHelper Module

NAT traversal and reuse of TCP connections.
Helps symmetric UAs who are not able to determine their public address.

NAT pinging types

UDP packet
4 bytes (zero filled) UDP packets are sent to the contact address.
SIP request
a stateless SIP request is sent to the UDP contact address.
(+) low bandwitdh traffic,
(+) easy to generate by Kamailio;
(+) bidirectional traffic through NAT
(+) since each PING request from Kamailio (inbound traffic) will force the SIP client to generate a SIP reply (outbound traffic) – the NAT bind will be surely kept open.
(-) unidirectional traffic through NAT (inbound – from outside to inside);
if many NATs do update the bind timeout only on outbound traffic, the bind may expire and closed.
(-) higher bandwitdh traffic
(-) more expensive (as time) to generate by Kamailio;

Dependencies – usrloc

Params

  • force_socket – Socket to be used when sending NAT pings for UDP communication.
modparam("nathelper", "force_socket", "127.0.0.1:5060")
  • natping_interval
  • ping_nated_only
  • natping_processes – How many timer processes should be created by the module for the exclusive task of sending the NAT pings.
  • natping_socket
  • received_avp – AVP used to store the URI containing the received IP, port, and protocol by fix_nated_register
  • sipping_bflag
  • sipping_from
  • sipping_method
  • natping_disable_bflag
  • nortpproxy_str
  • keepalive_timeout
  • udpping_from_path
  • append_sdp_oldmediaip
  • filter_server_id

Functions

  • fix_nated_contact() – rewrites the “Contact” header field with request’s source address:port pair
  • fix_nated_sdp() – adds the active direction indication to SDP and updates ource ip address information too
  • add_rcv_param() – add a received parameter to the “Contact” header fields or the Contact URI.
  • fix_nated_register() exports the request’s source address:port into an AVP to be used during save()
  • nat_uac_test()- check if client’s request originated behind a nat
  • is_rfc1918()
  • add_contact_alias() – Adds an “;alias=ip~port~transport” parameter to the contact URI
  • handle_ruri_alias() – Checks if the Request URI has an “alias” parameter and if so, removes it and sets the “$du” based on its value.
  • set_contact_alias()

Pseudo Variables

  • $rr_count – Number of Record Routes in received SIP request or reply.
  • $rr_top_count – If topmost Record Route in received SIP request or reply is a double Record Route, value of $rr_top_count is 2.

RPC Commands

nathelper.enable_ping 

References :

Kamailio WebRTC SIP Server


The purpose of this article is to demo the process of using Kamailio + RTP Engine to enable SIP-based WebRTC call to a traditional SIP UA like Xlite. Kamailio Will thus provide not only call routing but also NATing, TLS and WebSocket support for webrtc endpoints. For this bridging of SRTP from WebRTC endpoint like JSSIP to RTP for SIP UA like Xlite, we will use the RTP engine.

Kamailio

Kamailio’s modules on WebSocket, TLS, NATHelper help it to support WebSocket based SIP which the default Kamailio configuration doesn’t. Snippets from Kamailio config to support webrtc endpoints is below. A full kamailio config is present here https://github.com/altanai/kamailioexamples/blob/master/webrtc_to_webrtc_ws/websocket_tls_webrtc_kamailio.cfg

loadmodule "tm.so"
loadmodule "sl.so"
loadmodule "rr.so"
loadmodule "pv.so"
loadmodule "maxfwd.so"
loadmodule "usrloc.so"
loadmodule "textops.so"
loadmodule "siputils.so"
loadmodule "xlog.so"
loadmodule "sanity.so"
loadmodule "ctl.so"
loadmodule "kex.so"
loadmodule "corex.so"
loadmodule "tls.so"
loadmodule "xhttp.so"
loadmodule "websocket.so"
loadmodule "nathelper.so" 

Configuration of the important modules

TLS module

To provide the SSL support which webrtc endpoints require Certificate Authority CA and provate certs signed by it

creating file paths

mkdir certs
mkdir certs/private
mkdir certs/newcerts
touch certs/index.txt
echo 01 >certs/serial
echo 01 >certs/crlnumber

list the files

/home/ubuntu/certs# ls
crlnumber  index.txt  newcerts  private  serial

Create CA private key

openssl genrsa -out certs/private/cakey.pem 2048
chmod 600 certs/private/cakey.pem

Create CA self signed certificate

openssl req -out certs/cacert.pem -x509 -new -key certs/private/cakey.pem

Create server / client certificate, a private key (by name privkey.pem)

openssl req -out kamailio1_cert_req.pem -new -nodes
openssl ca -in kamailio1_cert_req.pem -out kamailio1_cert.pem

Output should be like

Certificate is to be certified until Jun 25 11:02:41 2020 GMT (365 days)
Sign the certificate? [y/n]:y
1 out of 1 certificate requests certified, commit? [y/n]y
Write out database with 1 new entries
Data Base Updated

and files genrated shoudl look like

/home/ubuntu# ls 
 certs  kamailio1_cert.pem kamailio1_cert_req.pem privkey.pem

Copy the newly created certs to their respective paths

mkdir /etc/pki/CA/
cp kamailio1_cert.pem /etc/pki/CA/
cp privkey.pem /etc/pki/CA/

Make list of ca certs by finidng all cacerts accross root firectory and appending them to a catlist pem

find / -name cacert.pem
cat /usr/share/doc/libssl-doc/demos/cms/cacert.pem >> /home/ubuntu/catlist.pem
cat /usr/share/doc/libssl-doc/demos/smime/cacert.pem >> /home/ubuntu/catlist.pem
cat /home/ubuntu/kamailio_source_code/misc/tls-ca/rootCA/cacert.pem >> /home/ubuntu/catlist.pem
...
cp /home/ubuntu/catlist.pem /etc/pki/CA/

update kamailio.cfg

#!ifdef WITH_TLS
enable_tls=1
#!endif
...
modparam("tls", "tls_method", "SSLv23")
modparam("tls", "certificate", "/etc/pki/CA/kamailio1_cert.pem")
modparam("tls", "private_key", "/etc/pki/CA/privkey.pem")
modparam("tls", "ca_list", "/etc/pki/CA/calist.pem")

Websocket module

Websocket is considered a transport option just as TCP or UDP in kamailio config , hence just as one defines IP addr and ports for TCP, UDP protocol , we need to define the same for WS or WSS

#!substdef "!MY_WS_ADDR!tcp:MY_IP_ADDR:MY_WS_PORT!g"
#!substdef "!MY_WSS_ADDR!tls:MY_IP_ADDR:MY_WSS_PORT!g"
...
listen=MY_IP_ADDR
#!ifdef WITH_WEBSOCKETS
listen=MY_WS_ADDR
#!ifdef WITH_TLS
listen=MY_WSS_ADDR
#!endif
#!endif

check if port in R-URI meant for ws or wss, did not receive websocket or secure websocket

if (($Rp == MY_WS_PORT || $Rp == MY_WSS_PORT) && !(proto == WS || proto == WSS)) {
    xlog("L_WARN", "SIP request received on $Rp\n");
    sl_send_reply("403", "Forbidden");
    exit;
}

request_route for websocket , included checking is client is behind NAT using nat_uac_test methods from NAThelper. If it is then for REGISTER methods do fix_nated_register and for other add_contact_alias

#!ifdef WITH_WEBSOCKETS
if (nat_uac_test(64)) {
	# NAT traversal  WebSocket
	force_rport();
	if (is_method("REGISTER")) {
		fix_nated_register();
	} else {
		if (!add_contact_alias()) {
		xlog("L_ERR", "Error aliasing contact <$ct>\n");
		sl_send_reply("400", "Bad Request");
		exit;
		}
	}
}
#!endif

RTPengine

RTP relay and NAT helps with RTP packets

For detailed steps goto https://telecom.altanai.com/2018/04/03/rtp-engine-on-kamailio-sip-server/

Dependencies

apt-get remove rtpproxy
sudo apt install debhelper iptables-dev libcurl4-openssl-dev libglib2.0-dev libxmlrpc-core-c3-dev libhiredis-dev markdown build-essential:native

Source

git clone https://github.com/sipwise/rtpengine.git
cd rtpengine
 ./debian/flavors/no_ngcp

Run

rtpengine --interface=54.86.35.95 --listen-ng=25061 --listen-cli=25062 --foreground --log-stderr --listen-udp=2222 --listen-tcp=25060

Integrate with kamailio using

loadmodule "rtpengine.so"
...
modparam("rtpengine", "rtpengine_sock", "udp:localhost:7722")
modparam("nathelper", "received_avp", "$avp(s:rcv)")
...
tbd

JSSIP

Like most other WebRTC libraries , JSSIP is event driven and provides provide core WEBRTC API like getUserMedia and RTP PeerConnection providing STUN,ICE,DTLS, SRTP features. It also integrated with rtcninja to provide cross browser accessibility. The differentiators with JSSIP lies in the fact that it supports SIP stack over websockets

JSSIP WebRTC client for kamailio

For tghe JSSIP default cllient UI and library one can use CDN based https://cdnjs.cloudflare.com/ajax/libs/jssip/3.1.2/jssip.min.js or can take a pull from JSSIP repo and build urself using gulp https://github.com/versatica/JsSIP.

Instantiate JSSIP websocket interface with kamailio IP

var socket = new JsSIP.WebSocketInterface('wss://<kamailio_ip>:443');

Add configuration for registeration . Note if not using kamailio as proxy to SBC, it is recommended to add regiseteration features to provide user reachability for incoming calls and NAT pings

 var configuration = {
   sockets  : socket,
   uri      : 'sip:username@example.com',
   password : 'password'
 };

create UA and start

var ua = new JsSIP.UA(configuration);
ua.start();

SIP over WEBSOCKET messages and kamailio processing

REGISTER sip JSSIP UA with user altanai, domina voiptelcom.com

REGISTER sip:voiptelco.com SIP/2.0
Via: SIP/2.0/WSS 830p2l39g8bg.invalid;branch=z9hG4bK242397
Max-Forwards: 69
To: 
From: ;tag=3jaad0q8l8
Call-ID: fvn2cd1b9gqh6kd7nqdpj5
CSeq: 1 REGISTER
Contact: ;+sip.ice;reg-id=1;+sip.instance="";expires=600
Expires: 600
Allow: INVITE,ACK,CANCEL,BYE,UPDATE,MESSAGE,OPTIONS,REFER,INFO
Supported: path,gruu,outbound
User-Agent: JsSIP 3.1.2
Content-Length: 0

Processed by kamailio using REGISTRAR route block

route(REGISTRAR);
..
route[REGISTRAR] {
	if (is_method("REGISTER")) {
		if (!save("location")) {
			sl_reply_error();
		}
		exit;
	}
}
SIP/2.0 200 OK
Via: SIP/2.0/WSS 830p2l39g8bg.invalid;branch=z9hG4bK242397;rport=19035;received=x.x.x.x
To: ;tag=4dad943d40a0a309c33d64467664aa30.f6d3
From: ;tag=3jaad0q8l8
Call-ID: fvn2cd1b9gqh6kd7nqdpj5
CSeq: 1 REGISTER
Contact: ;expires=600;received="sip:x.x.x.x:19035;transport=ws";pub-gruu="sip:altanai@voiptelco.com;gr=urn:uuid:80fa65e7-1cd7-4e40-bbee-c07f7a1ae9a5";temp-gruu="sip:uloc-5d260578-4b58-c-eeac6bd6@voiptelco.com;gr";+sip.instance="";reg-id=1
Server: kamailio (5.2.3 (x86_64/linux))
Content-Length: 0

INVITE from user1 altanai to john, notice that “To” header doesnt have tag. This will be handy for recognizing whether it is first message of dialog offer and in-dialog message such as ACK , RE-INVITE , BYE etc

INVITE sip:john@voiptelco.com SIP/2.0
Via: SIP/2.0/WSS ipoct61ao12v.invalid;branch=z9hG4bK4220209
Max-Forwards: 69
To: sip:john@voiptelco.com
From: sip:altanai@voiptelco.com;tag=2q0lecmbsn
Call-ID: s8bnv5869fp68d1ju8c1
CSeq: 1799 INVITE
Contact: 
Content-Type: application/sdp
Session-Expires: 90
Allow: INVITE,ACK,CANCEL,BYE,UPDATE,MESSAGE,OPTIONS,REFER,INFO
Supported: timer,gruu,ice,replaces,outbound
User-Agent: JsSIP 3.1.2
Content-Length: 1823

with SDP containing codecs and ICE details. Supporting audio over UDP/ TLS/RTL /SAVPF . Codecs beings

  • 111 OPUS
  • 103 ISAC/16000
  • 104 ISAC/32000
  • 9 G722
  • 0 PCMU / G.711u narrowband
  • 8 PCMA / G.711
  • 106 , 105 , 13 – CN / comfort noise
  • 110 , 112 , 113 , 126 – telephone-event / DTMF
v=0
o=- 4779000713447952953 2 IN IP4 127.0.0.1
s=-
t=0 0
a=group:BUNDLE 0
a=msid-semantic: WMS SFIXFrpsOUskJ5JQhp1mIARlDk6S3hVFTOBb
m=audio 55839 UDP/TLS/RTP/SAVPF 111 103 104 9 0 8 106 105 13 110 112 113 126
c=IN IP4 192.168.0.3
a=rtcp:9 IN IP4 0.0.0.0
a=candidate:3802297132 1 udp 2122260223 192.168.0.3 55839 typ host generation 0 network-id 1 network-cost 10
a=candidate:2887880668 1 tcp 1518280447 192.168.0.3 9 typ host tcptype active generation 0 network-id 1 network-cost 10
a=ice-ufrag:0PIq
a=ice-pwd:i7ccvGPXLDO5JqMwbCUqMcyN
a=ice-options:trickle
a=fingerprint:sha-256 AB:50:70:E3:57:E3:0C:7B:61:3B:03:5B:0F:54:14:14:9C:49:50:16:07:DC:E7:09:3E:4D:B5:A0:2B:EC:84:A1
a=setup:actpass
a=mid:0
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
a=extmap:2 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
a=extmap:3 urn:ietf:params:rtp-hdrext:sdes:mid
a=extmap:4 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id
a=extmap:5 urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id
a=sendrecv
a=msid:SFIXFrpsOUskJ5JQhp1mIARlDk6S3hVFTOBb e803836d-249a-4b81-b73f-17e0f08dde5a
a=rtcp-mux
a=rtpmap:111 opus/48000/2
a=rtcp-fb:111 transport-cc
a=fmtp:111 minptime=10;useinbandfec=1
a=rtpmap:103 ISAC/16000
a=rtpmap:104 ISAC/32000
a=rtpmap:9 G722/8000
a=rtpmap:0 PCMU/8000
a=rtpmap:8 PCMA/8000
a=rtpmap:106 CN/32000
a=rtpmap:105 CN/16000
a=rtpmap:13 CN/8000
a=rtpmap:110 telephone-event/48000
a=rtpmap:112 telephone-event/32000
a=rtpmap:113 telephone-event/16000
a=rtpmap:126 telephone-event/8000
a=ssrc:2800821831 cname:kLWViBLDLVfCXY8x
a=ssrc:2800821831 msid:SFIXFrpsOUskJ5JQhp1mIARlDk6S3hVFTOBb e803836d-249a-4b81-b73f-17e0f08dde5a
a=ssrc:2800821831 mslabel:SFIXFrpsOUskJ5JQhp1mIARlDk6S3hVFTOBb
a=ssrc:2800821831 label:e803836d-249a-4b81-b73f-17e0f08dde5a

100 trying from callee, note the to and from headers remain same for request or responses. This is send automatically by kamailio for INVITE.

SIP/2.0 100 trying -- your call is important to us
Via: SIP/2.0/WSS ipoct61ao12v.invalid;branch=z9hG4bK4220209;rport=17502;received=x.x.x.x
To: sip:john@voiptelco.com
From: sip:altanai@voiptelco.com;tag=2q0lecmbsn
Call-ID: s8bnv5869fp68d1ju8c1
CSeq: 1799 INVITE
Server: kamailio (5.2.3 (x86_64/linux))
Content-Length: 0

180 ringing from Callee, note the addition of contact header

SIP/2.0 180 Ringing
Record-Route: 
Via: SIP/2.0/WSS ipoct61ao12v.invalid;rport=17502;received=x.x.x.x;branch=z9hG4bK4220209
To: sip:john@voiptelco.com;tag=pvm73e3t89
From: sip:altanai@voiptelco.com;tag=2q0lecmbsn
Call-ID: s8bnv5869fp68d1ju8c1
CSeq: 1799 INVITE
Contact: sip:john@voiptelco.com;alias=x.x.x.x~17510~6;gr=urn:uuid:2e560b36-3ea8-41fd-80e3-ede66babb8a7
Supported: timer,gruu,ice,replaces,outbound
Content-Length: 0

200 OK with SDP

SIP/2.0 200 OK
Record-Route: 
Via: SIP/2.0/WSS ipoct61ao12v.invalid;rport=17502;received=x.x.x.x;branch=z9hG4bK4220209
To: sip:altanai@voiptelco.com;tag=pvm73e3t89
From: sip:john@voiptelco.com ;tag=2q0lecmbsn
Call-ID: s8bnv5869fp68d1ju8c1
CSeq: 1799 INVITE
Contact: sip:john@voiptelco.com;alias=x.x.x.x~17510~6;gr=urn:uuid:2e560b36-3ea8-41fd-80e3-ede66babb8a7
Session-Expires: 90;refresher=uas
Supported: timer,gruu,ice,replaces,outbound
Content-Type: application/sdp
Content-Length: 1477
v=0
o=- 4562215268128860297 2 IN IP4 127.0.0.1
s=-
t=0 0
a=group:BUNDLE 0
a=msid-semantic: WMS oxU77z9z9RfNL4CayvM1cMJKI0r7u6ZdqLBd
m=audio 55380 UDP/TLS/RTP/SAVPF 111 103 104 9 0 8 106 105 13 110 112 113 126
c=IN IP4 192.168.0.3
a=rtcp:9 IN IP4 0.0.0.0
a=candidate:3802297132 1 udp 2122260223 192.168.0.3 55380 typ host generation 0 network-id 1 network-cost 10
a=ice-ufrag:40h+
a=ice-pwd:6zJo50N7Bb2mqnrHq+jniukk
a=ice-options:trickle
a=fingerprint:sha-256 8A:F9:BE:8D:8A:80:FF:8C:89:3D:3A:D2:A1:36:B2:EC:11:53:81:7E:F4:53:E7:40:1E:B9:1E:A2:0F:D4:EA:2E
a=setup:active
a=mid:0
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
a=extmap:2 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
a=extmap:3 urn:ietf:params:rtp-hdrext:sdes:mid
a=extmap:4 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id
a=extmap:5 urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id
a=sendrecv
a=msid:oxU77z9z9RfNL4CayvM1cMJKI0r7u6ZdqLBd cb6da6b5-d5b8-460e-88bc-1458ebc718e6
a=rtcp-mux
a=rtpmap:111 opus/48000/2
a=rtcp-fb:111 transport-cc
a=fmtp:111 minptime=10;useinbandfec=1
a=rtpmap:103 ISAC/16000
a=rtpmap:104 ISAC/32000
a=rtpmap:9 G722/8000
a=rtpmap:0 PCMU/8000
a=rtpmap:8 PCMA/8000
a=rtpmap:106 CN/32000
a=rtpmap:105 CN/16000
a=rtpmap:13 CN/8000
a=rtpmap:110 telephone-event/48000
a=rtpmap:112 telephone-event/32000
a=rtpmap:113 telephone-event/16000
a=rtpmap:126 telephone-event/8000
a=ssrc:3489110087 cname:oZ2LjwJD385qPHHH

Kamailio handling replies using reply_route

onreply_route {
     if (nat_uac_test(64)) {
         add_contact_alias();
     }
 }

Sending ACK.

ACK sip:john@voiptelco.com;alias=x.x.x.x~17510~6;gr=urn:uuid:2e560b36-3ea8-41fd-80e3-ede66babb8a7 SIP/2.0

Route: 
Via: SIP/2.0/WSS ipoct61ao12v.invalid;branch=z9hG4bK1876245
Max-Forwards: 69
To: sip:altanai@voiptelco.com ;tag=pvm73e3t89
From: sip:john@voiptelco.com;tag=2q0lecmbsn
Call-ID: s8bnv5869fp68d1ju8c1
CSeq: 1799 ACK
Allow: INVITE,ACK,CANCEL,BYE,UPDATE,MESSAGE,OPTIONS,REFER,INFO
Supported: outbound
User-Agent: JsSIP 3.1.2
Content-Length: 0

since ACK is a Within dialog message and sequential request withing a dialog should take the path determined by record-routing, we first check if it has to tag. Having a to tag validates that it is a in-dialog request .

After this validate if is loose_route() and has no destination URI $du , then try to add rui alias using handle_ruri_alias( ), if that fails, reject the request.

If it is not loose_route() and method is ACK then check if the ACK matches a transaction t_check_trans() ie is stateful. If it is then relay otherwise reject.

route(WITHINDLG);
...
route[WITHINDLG] {
	if (has_totag()) {
		if (loose_route()) {
			if ($du == "") {
				if (!handle_ruri_alias()) {
				xlog("L_ERR", "Bad alias <$ru>\n");
				sl_send_reply("400", "Bad Request");
				exit;
				}
			}
			route(RELAY);
		} else {
			if ( is_method("ACK") ) {
				if ( t_check_trans() ) {
				t_relay();
				exit;
				} else {
				exit;
				}
			}
			sl_send_reply("404", "Not Found");
		}
		exit;
	}
}

Also required to convert ICE packet fromWebRTC to non ICE for Xlite.

Resources :

Lua Scripts for kamailio Routing

Kamailio uses a native scripting laguage for its configuration file kamailio.cfg . This components of this file are :

  • global parameters
  • loading modules
  • module parameters
  • routing blocks like request_route {…}, reply_route {…}, branch_route {…} etc

These parameters including initialization event routes , are interpeted and loaded at kamailio startup.

We know that restart of sip server hinder all ongoing traffic and slows development of routing logic.
Due to limitation of customizatiosn with native language and mostly due to the fact that interpreter precompiles kamailio.cfg at startup preventing routing script to reload without restart , other programming languages were used such as Kamailio Embedded Interface (KEMI) framework.

Post v5 of kamailio , the interpreters of these languages were integrated with kamailio and feature rich SIP routing logic could be written with them for runtime execution. The routing blocks are in form of functions written in a KEMI ( Kamaialio EMbedded Interface) supported scripting language such as Lua as discussed in this article. Other components are still initialised by kamailio.cfg at starup. Apart from above mentioned advantages of programming languages , another plus point is that it enables test-cases , debug functionality without having to restart the server for code update.

KEMI supported programming languages:

  • JavaScript (app_jsdt)
  • Lua (app_lua)
  • Python (app_python)
  • Python3 (app_python3)
  • Squirrel (app_sqlang)

Kamailio has other modules that allow inline execution of scripts written in other programming languages, but don’t implement the KEMI yet. Such as
Perl (app_perl)
.Net (C#, etc.)
(app_mono),
Java (app_java)

As Kamailio becomes the interpreter for these languages , it executes logic faster at runtime . KEMI also extends the modules with extension to kamailio c functions

Lua KEMI Interpreter

Lua interpreter is linked from liblua library in app_lua module.

//loading 
loadmodule "app_lua.so"
//script path
modparam("app_lua", "load", "/path/to/script.lua")
//specifying engine
cfgengine "lua"

Predefined functions in app_lua module

ksr_request_route() : executed by Kamailio core every time a SIP request is received, equivalent of request_route {} from kamailio.cfg

ksr_reply_route() : executed by Kamailio core every time a SIP Response (reply) is received, equivalent of reply_route {} from kamailio.cfg

ksr_onsend_route() : executed when a SIP request (and optionally for a response) is sent out, equivalent of onsend_route {}

branch route callback : name of the Lua function to be executed instead of a branch route has to be provided as parameter to KSR.tm.t_on_branch(…)

onreply route callback : name of the Lua function to be executed instead of an onreply route has to be provided as parameter to KSR.tm.t_on_reply(…)

failure route callback : name of the Lua function to be executed instead of a failure route has to be provided as parameter to KSR.tm.t_on_failure(…)

branch failure route callback : name of the Lua function to be executed instead of an event route for branch failure has to be provided as parameter to KSR.tm.t_on_branch_failure(…)

event route callback : name of the Lua function to be exectued instead of module specific event_route blocks is provided via event_callback parameter of that module

KSR is the new dynamic object exporting Kamailio functions. sr is the old static object exporting Kamailio functions

route.lua for routing logic

function ksr_request_route()
    KSR.info("===== request - from kamailio lua script\n");
    if KSR.maxfwd.process_maxfwd(10) < 0 then
        KSR.sl.send_reply(483, "Too Many Hops");
        return;
    end
    //KSR.sl.sreply(200, "OK Lua");
    KSR.pv.sets("$du", "sip:127.0.0.1:5080")
    KSR.tm.t_on_branch("ksr_branch_route_one");
    KSR.tm.t_on_reply("ksr_onreply_route_one");
    KSR.tm.t_on_failure("ksr_failure_route_one");
    if KSR.tm.t_relay() < 0 then
        KSR.sl.send_reply(500, "Server error")
    end
end

function ksr_reply_route()
    KSR.info("===== response - from kamailio lua script\n");
end

function ksr_branch_route_one()
    KSR.info("===== branch route - from kamailio lua script\n");
end

function ksr_onreply_route_one()
    KSR.info("===== onreply route - from kamailio lua script\n");
end

function ksr_failure_route_one()
    KSR.info("===== failure route - from kamailio lua script\n");
end

Core Kemi functions

KSR.add_local_rport() : Set the internal flag to add rport parameter to local generated Via header.

KSR.add_tcp_alias() : Adds a tcp port alias for the current connection (if tcp). Can help in firewall or nat traversal

KSR.add_tcp_alias_via() : Adds the port from the message via as an alias to TCP connection.

void KSR.dbg(…) : debug log

KSR.dbg("debug log message from embedded interpreter\n");

void KSR.err(…) : error logging

KSR.err("error log message from embedded interpreter\n");

void KSR.info(…) : info log

KSR.info("info log message from embedded interpreter\n");

KSR.force_rport() : Add rport parameter to the top Via of the incoming request and sent the SIP response to source port.

KSR.is_method() : Return true if the value of the parameter matches the method type of the SIP message.

if(KSR.is_method("INVITE")) {
  ...
}

KSR.is_method_in() ; Return true if SIP method of the currently processed message is matching one of the corresponding characters given as parameter. Matching the method is done based on corresponding characters:
I – INVITE
A – ACK
B – BYE
C – CANCEL
R – REGISTER
M – MESSAGE
O – OPTIONS
S – SUBSCRIBE
P – PUBLISH
N – NOTIFY
U – UPDATE
K – KDMQ
G – GET
T – POST
V – PUT
D – DELETE

if KSR.is_method_in("IABC") then
  -- the method is INVITE, ACK, BYE or CANCEL
  ...
end

KSR.is_INVITE() : Return true if the method type of the SIP message is INVITE.

KSR.is_ACK() : true if the method type is ACK.

KSR.is_BYE() : true if the method type is BYE.

KSR.is_CANCEL() : true if CANCEL.

KSR.is_REGISTER() : true if REGISTER.

KSR.is_MESSAGE() : true if SIP message is MESSAGE.

KSR.is_SUBSCRIBE() : true if SIP message is SUBSCRIBE.

KSR.is_PUBLISH() : true if PUBLISH.

KSR.is_NOTIFY() : true if NOTIFY.

KSR.is_OPTIONS() : true if OPTIONS.

KSR.is_INFO() : true if INFO.

KSR.is_UPDATE() : true if UPDATE.

KSR.is_PRACK() : true if PRACK.

KSR.is_myself(…) : Return true of the URI address provided as parameter matches a local socket (IP) or local domain.

if KSR.is_myself("sip:127.0.0.1:5060") then
  ...
end

KSR.is_myself_furi() : Return true if the URI in From header matches a local socket (IP) or local domain.

KSR.is_myself_ruri() : Return true if the R-URI matches a local socket (IP) or local domain.

KSR.is_myself_turi() : Return true if the URI in To header matches a local socket (IP) or local domain.

KSR.is_myself_suri() : true if the URI built from source IP, source port and protocol matches a local socket (IP).

KSR.is_myself_suri() : true if the source IP matches a local socket (IP).

void KSR.log(…) : Write a log message specifying the level value. The level parameter can be: “dbg” , “info” , “warn” , “crit” , “err”

KSR.setflag(…) : Set the SIP message/transaction flag ( int from 0 to 31 ) at the index provided by the parameter.

KSR.resetflag(…) : Reset the SIP message/transaction flag ( int from 0 to 31 ) at the index provided by the parameter.

KSR.isflagset(…) :Return true if the message/transaction flag at the index provided by the parameter is set (the bit has value 1).

KSR.setbflag(…) : Set the branch flag(0-31) at the index provided by the parameter.

KSR.resetbflag(…) : Reset the branch flag(0-31) at the index provided by the parameter.

KSR.isbflagset(…) : Return true if the branch flag at the index provided by the parameter is set (the bit has value 1).

KSR.setbiflag(…) : Set the flag at the index provided by the first parameter to the branch number specified by the second parameter. The flag parameter has to be a number from 0 to 31. The branch parameter should be between 0 and 12 (a matter of max_branches global parameter).

KSR.resetbiflag(…) : Reset a branch flag by position and branch index.

KSR.isbiflagset(…) : Test if a branch flag is set by position and branch index.

KSR.setsflag(…) : Set a script flag.

KSR.resetsflag(…) : Reset a script flag.

KSR.issflagset(…) : Test if a script flag is set.

KSR.seturi(…) : Set the request URI (R-URI).

KSR.seturi("sip:alice@voip.com");

KSR.setuser(…)

KSR.setuser("alice");

KSR.sethost(…)

KSR.sethost("voip.com");

KSR.setdsturi(…)

KSR.setdsturi("sip:voip.com:5061;transport=tls");

KSR.resetdsturi(…) : Reset the destination URI (aka: outbound proxy address, dst_uri, $du).

KSR.isdsturiset(…) : Test if destination URI is set.

KSR.force_rport(…) : Set the flag for “rport” handling (send the reply based on source address instead of Via header).

KSR.set_drop(…) : Set the DROP flag, so at the end of KEMI script execution, the SIP request branch or the SIP response is not forwarded.

KSR.set_advertised_address() : Set the address (host or ip) to be advertised in Via header.

KSR.set_advertised_port() : Set the port (in string format) to be advertised in Via header.

KSR.set_forward_close(…) : Set the flag to close the connection after forwarding the message.

KSR.set_forward_no_connect(…) : Set the flag to not open a connection if the connection to the target does not exist when attempting to forward a message.

KSR.set_reply_close(…) : Set the flag to close the connection after sending a response.

KSR.set_reply_no_connect(…) : Set the flag to not open a connection if the connection for sending the response does not exist.

KSR.forward(…) : Forward the SIP request in stateless mode to the address set in destination URI ($du), or, if this is not set, to the address in request URI ($ru).

KSR.forward_uri(…) : Forward the SIP request in stateless mode to the address provided in the SIP URI parameter.

KSR.forward_uri("sip:127.0.0.1:5080;transport=tcp");

KSR.hdr.append(…) : Append header to current SIP message (request or reply).

KSR.hdr.append("X-My-Hdr: " + KSR.pv.getw("$si") + "\r\n");

KSR.hdr.append_after(…) : Append header to current SIP message (request or reply). It will be added after the first header matching the name hdrname.

KSR.hdr.append_after("X-My-Hdr: " + KSR.pv.getw("$si") + "\r\n", "Call-Id");

KSR.hdr.insert(…) : Insert header to current SIP message (request or reply). It will be added before the first header.

KSR.hdr.insert("X-My-Hdr: " + KSR.pv.getw("$si") + "\r\n");

KSR.hdr.insert_before(…) : Insert header to current SIP message (request or reply). It will be added before the header matching the name hdrname.

KSR.hdr.insert_before("X-My-Hdr: " + KSR.pv.getw("$si") + "\r\n", "Call-Id");

KSR.hdr.remove(…) : Remove all the headers with the name hdrval.

KSR.hdr.remove("X-My-Hdr");

KSR.hdr.is_present(…) : Return greater than 0 if a header with the name hdrval exists and less than 0 if there is no such header.

if(KSR.hdr.is_present("X-My-Hdr") > 0) {
  ...
}

KSR.hdr.append_to_reply(…) : Add a header to the SIP response to be generated by Kamailio for the current SIP request.

KSR.hdr.append_to_reply("X-My-Hdr: " + KSR.pv.getw("$si") + "\r\n");

PV And Special KEMI Functions

PV And Special KEMI Functions

KSR.pv submodule provides the functions to get, set and test the values of pseduo-variables.
The pvname that appears in the next sections in the function prototypes has to be a valid pseudo-variable name for Kamailio native configuration file (for example $ru, $var(x), $shv(z), …).

KSR.pv.get(…) : Return the value of pseudo-variable pvname. The returned value can be string or integer.

KSR.dbg("ruri is: " + KSR.pv.get("$ru") + "\n");

KSR.pv.gete(…) : Return the value of pseudo-variable pvname if it is different than $null or the empty string (“”) if the variable is having the $null value.

KSR.dbg("avp is: " + KSR.pv.gete("$avp(x)") + "\n");

KSR.pv.getvn(…) : Return the value of pseudo-variable pvname if it is different than $null or the parameter vn if the variable is having the $null value.

KSR.dbg("avp is: " + KSR.pv.getvn("$avp(x)", 0) + "\n");

KSR.pv.getvs(…) : Return the value of pseudo-variable pvname if it is different than $null or the parameter vs if the variable is having the $null value.

KSR.dbg("avp is: " + KSR.pv.getvs("$avp(x)", "foo") + "\n");

KSR.pv.getw(…) : Return the value of pseudo-variable pvname if it is different than $null or the string <> if the variable is having the $null value. This should be used instead of KSR.pv.get(…) in the scripting languages that throw and error when attempting to print a NULL (or NIL) value.

KSR.dbg("avp is: " + KSR.pv.getw("$avp(x)") + "\n");

KSR.pv.seti(…) : set the value of pseudo-variable pvname to integer value provided by parameter val.

KSR.pv.seti("$var(x)", 10);

KSR.pv.sets(…) : set the value of pseudo-variable pvname to string value provided by parameter val.

KSR.pv.sets("$var(x)", "kamailio");

KSR.pv.unset(…) : Set the value of pseudo-variable pvname to $null.

KSR.pv.unset("$avp(x)");

KSR.pv.is_null(…) : Return true if pseudo-variable pvname is $null.

if(KSR.pv.is_null("$avp(x)")) {
  ...
}

KSR.x.modf(…) : Execute a function (specified by fname) exported by a Kamailio module.

KSR.x.modf("sl_send_reply", "200", "OK");

KSR.x.exit(…) : stop the execution of the SIP routing script.

KSR.x.drop(…) : stop the execution of the SIP routing script and drop routing further the SIP request branch or response.

KEMI Module Functions

Module specific exported KEMI functions.

Acc

KSR.acc.acc_db_request()

KSR.acc.acc_log_request()

KSR.acc.acc_request()

app_lua

executing Lua scripts from config file.

KSR.app_lua.dofile()

KSR.app_lua.dostring()

KSR.app_lua.run()

KSR.app_lua.run_p1()

KSR.app_lua.run_p2()

KSR.app_lua.run_p3()

KSR.app_lua.runstring()

Async

asynchronous operations for handling SIP requests

KSR.async.route()

KSR.async.task_route()

auth

KSR.auth.auth_challenge()

KSR.auth.consume_credentials()

KSR.auth.has_credentials()

KSR.auth.pv_auth_check()

auth_db

KSR.auth_db.auth_check()

KSR.auth_db.is_subscriber()

-tbd –

Ref :

VOIP Call Metric Monitoring and MOS ( Mean Opinion Score)


Metrics for monitoring a VOIP call can be obtained from any node in media path of the call flow . Essentially used for analysis via calculation and aggregation , and sometimes used for realtime performance tracking and rectification too.

VoIP call quality metrics

RTP provides real time media stream, payload type identification, packet sequencing and timestamping headers.

sequence num : tracks incremental succession of incoming packets by sendor and tracls out of order delivery.

timestamp : used by the receiver to play back the received samples at appropriate time and interval. 

source : wikipedia RTP

Note that all Synchronization source (SSRC) identifiers fields denote the synchronization source within the RTP session such as both legs of a call session

  • leg A between Caller and RTP proxy,
  • leg B between RTPproxy and Callee

RTCP provides detailed monitoring of stream to participants in an ongoing session with statistical data and enhanced metrices for QoS ( quality of service ) and synchronisation using it SR ( senders Report ) and RR ( Receivers report) segments .

  • Packet loss rate
  • Packet discard rate
  • round trip delay
  • R factor which is voice quality carried over RTP ssession
  • mos lq for listening quality and mos cq for conversation qualityy
  • jitter buffer current delay , maximum delay
RTCP – SR
RTCP – RR

Certain aspects of RTP media and its RTCP metrics were discussed before you can read more about RTCP and RTCP / AVPF here RealTime Transport protocol (RTP) and RTP control protocol (RTCP )

Other call realted factors which are not specifically part of RTCP but provide information about call quality are

  • signal level
  • noise level
  • gap density, gap threshold
  • Burst density
  • residual echo return loss

Delays like following also play a signaificant influence in VoIP Quality

  • end system delay
  • Paketzation Delay
  • Setup delay ( auth, TLS handshake, accessing mic/camera stream ..)
  • Queing Delay
  • Serialization dleay
  • Network latency
  • End device processing delay such as CPU of the end device

It should be noted that in addition to these values which can be caluvulated algorithimically and with high precsiion , there are more subjective quality parameters which can be only evaluted manually ( ie witha person listening on both ends ) such as

  • Robot voice
  • Perceptible sound but annoying speech quality

Rating Factor (R-Factor) and Mean Opinion Score (MOS)

Rating Factor (R-Factor) and Mean Opinion Score (MOS) are two commonly-used measurements of overall VoIP call quality.

What is R-Factor ?

This is a value derived from metrics such as latency, jitter, and packet loss per ITU‑T Recommendation G.107. It assess the quality-of-experience for VoIP calls on your network. Typical scores range from 50 (bad) to 90 (excellent).

  • R factor of 90 , Mos is 4.3 ( Excellent )
  • R factor 50 , Mos is 2.6 ( Bad)

What is MOS?

MOS is derived from the R-Factor per ITU‑T Recommendation G.10 which measures VoIP call quality. PacketShaper measures MOS using a scale of 10-50. To convert to a standard MOS score (which uses a scale of 1-5), divide the PacketShaper MOS value by 10.

MOS ( Mean Opinion Score )

MOS is terminology for audio, video and audiovisual quality expressions as per ITU-T P.800.1. It refers to listening, talking or conversational quality, whether they originate from subjective or objective models.

  • Very Good: 4.3-5.0
  • Bad: 3.1-3.6
  • Not Recommenced : 2.6-3.1
  • Very Bad: 1.0-2.6

It provides provisions for identifiers regarding the audio bandwidth, the type of interface (electrical or acoustical) and the video resolution too, such as

  • MOS-AVQE for audiovisual quality
  • MOS-CQE is for estimated conversational quality
  • MOS-LQE for listening quality
  • MOS-TQE is used for talking quality
  • MOS-VQE depicts video quality

For Audio Signal Speech Quality/ AV
– N denotes audio signals upto narrow-band (300-3400 Hz)
– W is for audio signals upto wideband (50-7000 Hz)
– S for upto super-wideband (20-14000 Hz)
– F is obtained for fullband (10-20000 Hz)

For Listening quality LQO

  • electrical measurement : done at electrical interfaces only. In order to predict the listening quality as perceived by the user, assumptions for the terminals are made in terms of intermediate reference system (IRS) or corrected IRS frequency response. A sealed condition between the handset receiver and the user’s ear is assumed.
  • acoustical measurement : done at acoustical interfaces. In order to predict the listening quality as perceived by the user, this measurement includes the actual telephone set products provided by the manufacturer or vendor. In combination with the choice of the acoustical receiver in the laboratory test , there will be a more or less leaky condition between the handset’s receiver and the artificial ear.

Conversational Quality / CQ

Arithmetic mean value of subjective judgments on a 5-point ACR quality scale, is calculated.

Talking Quality / TQ

This describes the quality of a telephone call as it is perceived by the talking party only. Factors affecting TQ include echo signal , background noise , double talk etc. It is calculated based on the arithmetic mean value of judgments on a 5-point ACR quality scale.

Video Quality / VQ

To account for differentiation in perceived quality for mobile and fixed devices and to allow for proper handling of different use-cases as
– M for mobile screen such as a smartphone or tablet (approximately 25 cm or less)
– T for PC/TV monitors
It is calculated based on the arithmetic mean value of subjective judgments, typically on a 5-point quality scale

Audio Visual Quality / AVQ

Refers to quality of audio visual stream under corresponding networking conditions. It is also calculated based on the arithmetic mean value of judgments on a 5-point ACR quality scale.

Other parameters also contributing to VoIP metric Analysis

Latency

Latency is primarily is the time required for packets to travel from one end to another, in milliseconds. For example, if the sum of measured latency is 800 ms and the number of latency samples is 20, then the average latency is 40 ms. The header of the RTP packets carry timestamps which later can also be used to calculate round-trip time.

Medium of propagation

  • The Terrestrial coaxial cable or radio-relay system over FDM and digital transmission submarine coaxial cables add up to 4- 6 microseconds of delay per km.
  • Similarly even the optical fibre cable using digital transmission added around 5 microseconds per km delay which also accounts for the delay in repeaters and regenerators
  • On the other hand satellite, communication system varies the delay based on altitude ( propagation delay through space and between earth stations)
    • 400 km above earth surface adds 12 ms delay,
    • 14000 km above earth adds 110 ms
    • much higher 36000 km of altitude adds 260 ms

Devices

  • FDM modem adds upto 0.75 ms delay
  • Transmultiplexer – 1.5 ms delay
  • Exchanges ( analog , digital , transit ..) add 0.45 – 0.825 ms delay
  • Echo cancellers 0.5 ms
  • DCME (Circuit manipulation, signal compression ) – 30 ms to 200 ms

RTT (Round Trip Time )

RTT is the time in milliseconds (ms) taken for data to travel to the target destination and back. In terms of SIP calls it is the time for a transaction to complete between caller/client and callee/server. It is calculated as when the packet was sent and when the acknowledgement for it was received.

High RTT : The media stream especially audio must not suffer a delay higher than 150 ms including all the processing delays at intermediate nodes and network latency. Any value above it is of poor quality. High RTT indicates a poor network quality and would result in the audio lag issue.

RTT vs Network ping calculation: RTT can represent full path network latency experienced by the packets and can do away with frequent ICMP ping/echo requests/probes to check network health. Although it should be noted that while pings happen in lower transport layers protocol, RTT happens at the high up application layer.

RTT is used to calculate RTO ( Request transmission timeouts )in TCP transmission ie how much time the sender should wait before retrying to send an unacknowledged packet.

Factors affecting RTT can include delays in propagation delay, processing delay, queuing delay and encoding delay. Porpogation delay can correlate to the

  • physical distance ( inter country/continents or intra ) ,
  • mediaum of tramsission ( copper cables , fiber , wireless)
  • bandwidth available

Simillarly propagation delay can occur due to large num of network hops like routers / servers . It should be noted that server respose time also plays a critical role in RTT as it depends on server’s processing caapcity and nature of request.

Star based network topology like MCU , SFU or TURN servers can introduce processing delays too for activities such as mixing, encdoing , NATing etc .

Network congestion can amplify the RTT the most.Traffic level must be monitored when RTT spikes such as during DDos attacks

Overcoming large RTT can be achieved by

  • identifying the choke points of network
  • ditributing the load evenly
  • ensuring scalaibility of the server side resources
  • ensuring points of presence(PoP) into geographic regions where caller/ callee is present and routing through it rather than unreliable open public network

Note : avg RTT of the session is misleading denotaion of latency as there maybe be assymetrically RTT between the two legs of the call

Calculation of RTT

EffectiveLatency = ( AverageLatency + Jitter * 2 + 10 )

In RTPengine
int eff_rtt = ssb->rtt / 1000 + ssb->jitter * 2 + 10;

Thus for RTT = 11338 and jitter =0
eff_RTT = 11338/1000 + 0*2 +10
= 11.651 + 10 = 21.651 , which is a good score as it is way below 150ms of latency

But for RTT = 129209 and jitter =7
eff_RTT = 129209/1000 + 7*2 +10
= 153.209 , which is a bad score > 150 ms

Packet Loss

When packet does not successfully make it to the destination , it is a lost packet.

It could happen due to multiple reasons such as

  • network bandwidth unavailable or network congestion
  • overloading of the buffer such that they do not have enough space to queue the packets or high priority preferences
  • intentionally configuring ACL or firewalls to drop the packets or discarding packets above rate limit by internet service provider
  • CPU unable to cope up with high security networks encryption and decryption speed requirements
  • Low battery on device may cause cause underworking of devices and hence lead to packet loss
  • limitation on physical device like softphone , hardphone or bluetooth headsets or if the hardware is broken at router , switch or cabling
  • for bluetooth headsets distance range could also be problem for weak signals and consequently packets drops
  • network errors as shown under Simple Network Management Protocol (SNMP) issues like FCS Errors, Alignment Errors, Frame Too Longs, MAC Receive Errors, Symbol Errors, Collisions, Carrier Sense Errors, Outbound Errors, Outbound Discards, Inbound Discards, Inbound Errors, and Unknown Protocol errors.
  • radio frequency interference from high voltage systems or microwaves can also cause packet drop in wireless networks

such that the packet can either not arrive or arrive late and be dropped out by the codec . To the listener it would appear like chopped voice or complete dropout for moments .

Obtaining packet loss details

  • Packet loss percentage is performed as per RFC 3550 using RTP header sequence numbers. If packets are missing sequence the media stream monitors flags that as lost packet.
  • It can also be concluded from the difference between total packets and received packets from CDR
  • RTP-XR (RFC-3611) records report real-time drops

Jitter

The variation in the delay of received packets in a flow, measured by comparing the interval when RTP packets were sent to the interval at which they were received.
For instance, if packet #1 and packet #2 leave 30 milliseconds apart and arrive 50 milliseconds apart, then the jitter is 20 milliseconds or if packets transmitted every 15ms and reach destination at every 15ms then there is no variability and the jitter is 0.

Causes of jitter

  • Frame bigger than jitter buffer size
  • algorithms to back-of collision by introducing delays in packet transmission in half duplex interfaces
  • even small jitter can get exponentially worse on slow or congestion links
  • jitter can be introduced due to bottlenecks near router buffer, rerouting / parallel routes to the same destination, load-sharing, or route tables changing the path

Handling jitter :

Jitter below 30ms is manageable with the help of jitter buffers in codecs however above that the codec starts to drop the late arrived packets and cannot reassemble / splice up the packets for a smooth media stream effectively, hence causing media quality issues like clipped audio

Detecting jitter:

  • looking at inter packet gap in the direction of RTP stream in wireshark
  • RTP-XR (RFC-3611 & RFC-7005) for real-time jitter buffer usage and drops.
  • software based detection : Network sniffers wireshark , path analyser, Application Performance Monitoring (APM) Tools , CDR analyser , Simple Network Management Protocol (SNMP) Collector
MetricGoodAverageBad
Jitter<= 10ms10ms – 30ms>=30ms
Packet Loss< 0.5%0.5% – 0.9%>= 0.9%
Audio Level>-40dB-80dB to -40dB< -80dB
RTT< 200ms200ms – 300ms> 300ms
Range for good bad attributes for calculating mos score

Ref : ITU P.800.1 : Mean opinion score (MOS) terminology 

Methods for objective and subjective assessment of speech and video quality.

Scheduling for low bandwidth networks

The ability of the end application or the RTP proxy to deal with packet loss or delays depends on its processing techniques , particularly with encoding and buffering techniquee to deal with high pac ket loss rate.

Mapping R-value to calculate MOS

To map MOS from R value using above defined metrics , a standard formula is used. First the latency and jitter are added and defined value for computation time is also added , resulting in effective latency

effectiveLatency = latency + jitter * latencyImpact + compTime

Subtracting effective latency from defined R

R = 93 – (effectiveLatency / factorLatencyBased)

Calculate percentage of packet loss

 R = R – (lostPackets * impact)
 MOS = ( (R - 60) * (100 – R) * 0.000007R) + 0.035R + 1)

Media Stats and MOS on RTP engine Kamailio

Minimum edge Values

mos_min_pv
minimum encountered MOS value for the call.
range – 1.0 to 5.0.

mos_min_at_pv
timestamp of when the minimum MOS value was encountered during the call

mos_min_packetloss_pv
amount of packetloss in percent at the time the minimum MOS value was encountered

mos_min_roundtrip_pv
packet round-trip time in milliseconds at the time the minimum MOS value was encountered

mos_min_jitter_pv
amount of jitter in milliseconds at the time the minimum MOS value was encountered

Maximum edge Values

mos_max_pv
maximum encountered MOS value for the call.

mos_max_at_pv
timestamp of when the maximum MOS value was encountered during the cal

mos_max_packetloss_pv
amount of packetloss in percent at maximum MOS moment

mos_max_roundtrip_pv
packet round-trip time in milliseconds at maximum MOS moment

mos_max_jitter_pv
amount of jitter in milliseconds at maximum moment

Average Values

mos_average_pv : average (median) MOS value for the call. Range – 1.0 through 5.0.

mos_average_packetloss_pv : average (median) amount of packetloss in percent present throughout the call.

mos_average_jitter_pv : average (median) amount of jitter in milliseconds present throughout the call.

mos_average_roundtrip_pv

mos_average_samples_pv : number of samples used to determine the other “average” MOS data points.

Labels

mos_A_label_pv : custom label used in rtpengine signalling.
If set, all the statistics pseudovariables with the A suffix will be filled in with statistics only from the call legs that match the label given in this variable.

A label’s min
mos_min_A_pv
mos_min_at_A_pv
mos_min_packetloss_A_pv
mos_min_jitter_A_pv
mos_min_roundtrip_A_pv

A label’s max
mos_max_A_pv
mos_max_at_A_pv
mos_max_packetloss_A_pv
mos_max_jitter_A_pv
mos_max_roundtrip_A_pv

A label’s average
mos_average_A_pv
mos_average_packetloss_A_pv
mos_average_jitter_A_pv
mos_average_roundtrip_A_pv
mos_average_samples_A_pv

B labels’s min
mos_B_label_pv
mos_min_B_pv
mos_min_at_B_pv
mos_min_packetloss_B_pv
mos_min_jitter_B_pv
mos_min_roundtrip_B_pv

B label’s max
mos_max_B_pv
mos_max_at_B_pv
mos_max_packetloss_B_pv
mos_max_jitter_B_pv
mos_max_roundtrip_B_pv

B label’s average
mos_average_B_pv
mos_average_packetloss_B_pv
mos_average_jitter_B_pv
mos_average_roundtrip_B_pv
mos_average_samples_B_pv

Setting MOS collection on kamailio

set the kamailio config rtpengine params for names the variable the hold specific mos values

modparam("rtpengine", "mos_max_pv", "$avp(mos_max)")
modparam("rtpengine", "mos_average_pv", "$avp(mos_average)")
modparam("rtpengine", "mos_min_pv", "$avp(mos_min)")

modparam("rtpengine", "mos_average_packetloss_pv", "$avp(mos_average_packetloss)")
modparam("rtpengine", "mos_average_jitter_pv", "$avp(mos_average_jitter)")
modparam("rtpengine", "mos_average_roundtrip_pv", "$avp(mos_average_roundtrip)")
modparam("rtpengine", "mos_average_samples_pv", "$avp(mos_average_samples)")

modparam("rtpengine", "mos_min_pv", "$avp(mos_min)")
modparam("rtpengine", "mos_min_at_pv", "$avp(mos_min_at)")
modparam("rtpengine", "mos_min_packetloss_pv", "$avp(mos_min_packetloss)")
modparam("rtpengine", "mos_min_jitter_pv", "$avp(mos_min_jitter)")
modparam("rtpengine", "mos_min_roundtrip_pv", "$avp(mos_min_roundtrip)")

modparam("rtpengine", "mos_max_pv", "$avp(mos_max)")
modparam("rtpengine", "mos_max_at_pv", "$avp(mos_max_at)")
modparam("rtpengine", "mos_max_packetloss_pv", "$avp(mos_max_packetloss)")
modparam("rtpengine", "mos_max_jitter_pv", "$avp(mos_max_jitter)")
modparam("rtpengine", "mos_max_roundtrip_pv", "$avp(mos_max_roundtrip)")

modparam("rtpengine", "mos_A_label_pv", "$avp(mos_A_label)")
modparam("rtpengine", "mos_average_packetloss_A_pv", "$avp(mos_average_packetloss_A)")
modparam("rtpengine", "mos_average_jitter_A_pv", "$avp(mos_average_jitter_A)")
modparam("rtpengine", "mos_average_roundtrip_A_pv", "$avp(mos_average_roundtrip_A)")
modparam("rtpengine", "mos_average_A_pv", "$avp(mos_average_A)")

modparam("rtpengine", "mos_B_label_pv", "$avp(mos_B_label)")
modparam("rtpengine", "mos_average_packetloss_B_pv", "$avp(mos_average_packetloss_B)")
modparam("rtpengine", "mos_average_jitter_B_pv", "$avp(mos_average_jitter_B)")
modparam("rtpengine", "mos_average_roundtrip_B_pv", "$avp(mos_average_roundtrip_B)")
modparam("rtpengine", "mos_average_B_pv", "$avp(mos_average_B)")

For individual leg labbeling fill up the lables

KSR.pv.sets("$avp(mos_A_label)","Aleg_label")
KSR.pv.sets("$avp(mos_B_label)","Bleg_label")

Gather the mos stats from the code . Given exmaple is in Lua.
The values are filled in after invoking“rtpengine_delete”, “rtpengine_query”, or “rtpengine_manage” if the command resulted in a deletion of the call (or call branch).

KSR.log("info", " mos avg " .. KSR.pv.get("$avp(mos_average)"))
KSR.log("info", " mos max " .. KSR.pv.get("$avp(mos_max)"))
KSR.log("info", " mos min " .. KSR.pv.get("$avp(mos_min)"))

KSR.log("info", "mos_average_packetloss_pv" .. KSR.pv.get("$avp(mos_average_packetloss)"))
KSR.log("info", "mos_average_jitter_pv" .. KSR.pv.get("$avp(mos_average_jitter)"))
KSR.log("info", "mos_average_roundtrip_pv" .. KSR.pv.get("$avp(mos_average_roundtrip)"))
KSR.log("info", "mos_average_samples_pv" .. KSR.pv.get("$avp(mos_average_samples)"))

KSR.log("info", "mos_min_pv" .. KSR.pv.get("$avp(mos_min)"))
KSR.log("info", "mos_min_at_pv" .. KSR.pv.get("$avp(mos_min_at)"))
KSR.log("info", "mos_min_packetloss_pv" .. KSR.pv.get("$avp(mos_min_packetloss)"))
KSR.log("info", "mos_min_jitter_pv" .. KSR.pv.get("$avp(mos_min_jitter)"))
KSR.log("info", "mos_min_roundtrip_pv" .. KSR.pv.get("$avp(mos_min_roundtrip)"))

KSR.log("info", "mos_max_pv" .. KSR.pv.get("$avp(mos_max)"))
KSR.log("info", "mos_max_at_pv" .. KSR.pv.get("$avp(mos_max_at)"))
KSR.log("info", "mos_max_packetloss_pv" .. KSR.pv.get("$avp(mos_max_packetloss)"))
KSR.log("info", "mos_max_jitter_pv" .. KSR.pv.get("$avp(mos_max_jitter)"))
KSR.log("info", "mos_max_roundtrip_pv" .. KSR.pv.get("$avp(mos_max_roundtrip)"))

local mos_A_label = KSR.pv.get("$avp(mos_A_label)")
if not (mos_A_label == nil) then
    KSR.log("info", "mos_average_packetloss_A_pv" .. KSR.pv.get("$avp(mos_average_packetloss_A)"))
    KSR.log("info", "mos_average_jitter_A_pv" .. KSR.pv.get("$avp(mos_average_jitter_A)"))
    KSR.log("info", "mos_average_roundtrip_A_pv" .. KSR.pv.get("$avp(mos_average_roundtrip_A)"))
    KSR.log("info", "mos_average_A_pv" .. KSR.pv.get("$avp(mos_average_A)"))
end

local mos_B_label = KSR.pv.get("$avp(mos_B_label)")
if not (mos_B_label == nil) then
    KSR.log("info", "mos_average_packetloss_B_pv" .. KSR.pv.get("$avp(mos_average_packetloss_B)"))
    KSR.log("info", "mos_average_jitter_B_pv" .. KSR.pv.get("$avp(mos_average_jitter_B)"))
    KSR.log("info", "mos_average_roundtrip_B_pv" .. KSR.pv.get("$avp(mos_average_roundtrip_B)"))
    KSR.log("info", "mos_average_B_pv" .. KSR.pv.get("$avp(mos_average_B)"))
end

Sample obtained result for one leg

      "average MOS": {
        "MOS": 43,
        "round-trip time": 13430,
        "jitter": 0,
        "packet loss": 0,
        "samples": 4
      },
      "lowest MOS": {
        "MOS": 43,
        "round-trip time": 24184,
        "jitter": 0,
        "packet loss": 0,
        "reported at": 1590498085
      },
      "highest MOS": {
        "MOS": 44,
        "round-trip time": 8218,
        "jitter": 0,
        "packet loss": 0,
        "reported at": 1590498089
      },

CDR with MOS on Freeswitch

<?xmlversion="1.0"?>
					
<cdr core-uuid="[UUID]" switchname="freeswitch">
<channel_data>
	<state>
	<direction>
	<state_number>
	<flags>	
	<caps>
</channel_data>
					
<call-stats>			
//Audio Compoennts 		
// Video Component
</call-stats>

// Variables 			

<app_log>			
	<application app_name="..."app_data="...">
	<application app_name="..."app_data="...">
</app_log>
				
// Callflow 
				
</cdr>		

Audio

<audio>	
	<inbound>
		<raw_bytes>	
		<media_bytes>
		<packet_count>
		<media_packet_count>		
		<skip_packet_count>
		<jitter_packet_count>
		<dtmf_packet_count>	
		<cng_packet_count>		
		<flush_packet_count>
		<largest_jb_size>
		<jitter_min_variance>
		<jitter_max_variance>
		<jitter_loss_rate>
		<jitter_burst_rate>
		<mean_interval>
		<flaw_total>
		<quality_percentage>
		<mos>
	</inbound>				
	<outbound>
		<raw_bytes>
		<media_bytes>
		<packet_count>
		<media_packet_count>
		<skip_packet_count>
		<dtmf_packet_count>
		<cng_packet_count>
		<rtcp_packet_count>
		<rtcp_octet_count>
	</outbound>	
</audio>

Video

<video>	
	<inbound>
		<raw_bytes>
		<media_bytes>
		<packet_count>
		<media_packet_count>
		<skip_packet_count>
		<jitter_packet_count>
		<dtmf_packet_count>
		<cng_packet_count>
		<flush_packet_count>
		<largest_jb_size>
		<jitter_min_variance>
		<jitter_max_variance>
		<jitter_loss_rate>
		<jitter_burst_rate>
		<mean_interval>
		<flaw_total>
		<quality_percentage>
		<mos>
	</inbound>	
	<outbound>
		<raw_bytes>
		<media_bytes>
		<packet_count>
		<media_packet_count>
		<skip_packet_count>
		<dtmf_packet_count>
		<cng_packet_count>
		<rtcp_packet_count>
		<rtcp_octet_count>	
	</outbound>
</video>

The variables

<variables>		
<is_outbound>
<uuid><session_id><text_media_flow>
<direction>
<ep_codec_string>
<channel_name>
<secondary_recovery_module>
<verto_dvar_email><verto_dvar_avatar><jsock_uuid_str>
<verto_user><presence_id>
<verto_client_address><chat_proto>
<verto_host><event_channel_cookie>
<verto_profile_name>
<record_stereo><default_areacode><transfer_fallback_extension>
<toll_allow><accountcode><user_context><effective_caller_id_name><effective_caller_id_number>
<outbound_caller_id_name><outbound_caller_id_number><callgroup><user_name><domain_name>
<Event-Name>
<Core-UUID>
<FreeSWITCH-Hostname><FreeSWITCH-Switchname><FreeSWITCH-IPv4><FreeSWITCH-IPv6><Event-Date-Local><Event-Date-GMT><Event-Date-Timestamp>
<Event-Calling-File>
<Event-Calling-Function>
<Event-Calling-Line-Number>
<Event-Sequence>
<verto_remote_caller_id_name><verto_remote_caller_id_number>
<switch_r_sdp>

<call_uuid><open>
<rtp_secure_media>
<export_vars><conference_enter_sound>
<conference_exit_sound><video_banner_text>
<rtp_use_codec_string><remote_audio_media_flow>
<audio_media_flow>
<rtp_audio_recv_pt>
<rtp_use_codec_name> 
<rtp_use_codec_fmtp>
<rtp_use_codec_rate>
<rtp_use_codec_ptime>
<rtp_use_codec_channels>
<rtp_last_audio_codec_string>
<original_read_codec>
<original_read_rate>
<write_codec><write_rate>
<remote_audio_ip>
<remote_audio_port>
<remote_audio_rtcp_ip>
<remote_audio_rtcp_port>
<dtmf_type>
<remote_video_media_flow>
<video_media_flow>
<video_possible>
<rtp_video_pt>
<rtp_video_recv_pt>
<video_read_codec>
<video_read_rate><video_write_codec><video_write_rate><rtp_last_video_codec_string>
<rtp_use_video_codec_name>
<rtp_use_video_codec_rate>
<rtp_use_video_codec_ptime>
<remote_video_ip><remote_video_port>
<remote_video_rtcp_ip><remote_video_rtcp_port>
<local_media_ip><local_media_port>
<advertised_media_ip>
<rtp_use_timer_name><rtp_use_pt>
<rtp_use_ssrc><rtp_2833_send_payload>
<rtp_2833_recv_payload><remote_media_ip>
<remote_media_port><local_video_ip>
<local_video_port><rtp_use_video_pt><rtp_use_video_ssrc><rtp_local_sdp_str><current_application_data><current_application><send_silence_when_idle><rtp_has_crypto><endpoint_disposition><conference_name><conference_member_id><conference_moderator><conference_ghost><conference_uuid><video_width><video_height><video_fps><verto_hangup_disposition><read_codec><read_rate><hangup_cause><hangup_cause_q850>
<digits_dialed>
<start_stamp><profile_start_stamp><answer_stamp><progress_media_stamp><end_stamp>
<start_epoch><start_uepoch>
<profile_start_epoch><profile_start_uepoch>
<answer_epoch><answer_uepoch>
<bridge_epoch><bridge_uepoch>
<last_hold_epoch><last_hold_uepoch>
<hold_accum_seconds><hold_accum_usec><hold_accum_ms><resurrect_epoch><resurrect_uepoch>
<progress_epoch><progress_uepoch><progress_media_epoch><progress_media_uepoch>
<end_epoch><end_uepoch>
<last_app><last_arg><caller_id><duration><billsec><progresssec><answersec><waitsec><progress_mediasec>

<flow_billsec>
   <mduration><billmsec><progressmsec><answermsec><waitmsec><progress_mediamsec><flow_billmsec><uduration>  <billusec><progressusec><answerusec><waitusec><progress_mediausec>
<flow_billusec>

<rtp_audio_in_raw_bytes>
<rtp_audio_in_media_bytes>
<rtp_audio_in_packet_count>
<rtp_audio_in_media_packet_count>
<rtp_audio_in_skip_packet_count><rtp_audio_in_jitter_packet_count><rtp_audio_in_dtmf_packet_count>
<rtp_audio_in_cng_packet_count>
<rtp_audio_in_flush_packet_count>
<rtp_audio_in_largest_jb_size>
<rtp_audio_in_jitter_min_variance><rtp_audio_in_jitter_max_variance>
<rtp_audio_in_jitter_loss_rate>
<rtp_audio_in_jitter_burst_rate>
<rtp_audio_in_mean_interval>
<rtp_audio_in_flaw_total>
<rtp_audio_in_quality_percentage>
<rtp_audio_in_mos>
<rtp_audio_out_raw_bytes>
<rtp_audio_out_media_bytes>
<rtp_audio_out_packet_count>
<rtp_audio_out_media_packet_count><rtp_audio_out_skip_packet_count><rtp_audio_out_dtmf_packet_count>
<rtp_audio_out_cng_packet_count>
<rtp_audio_rtcp_packet_count>
<rtp_audio_rtcp_octet_count>
<rtp_video_in_raw_bytes>
<rtp_video_in_media_bytes>
<rtp_video_in_packet_count>
<rtp_video_in_media_packet_count>
<rtp_video_in_skip_packet_count><rtp_video_in_jitter_packet_count><rtp_video_in_dtmf_packet_count>
<rtp_video_in_cng_packet_count>
<rtp_video_in_flush_packet_count>
<rtp_video_in_largest_jb_size>
<rtp_video_in_jitter_min_variance><rtp_video_in_jitter_max_variance>
<rtp_video_in_jitter_loss_rate>
<rtp_video_in_jitter_burst_rate>
<rtp_video_in_mean_interval
><rtp_video_in_flaw_total>
<rtp_video_in_quality_percentage>
<rtp_video_in_mos>
<rtp_video_out_raw_bytes>
<rtp_video_out_media_bytes>
<rtp_video_out_packet_count>
<rtp_video_out_media_packet_count><rtp_video_out_skip_packet_count><rtp_video_out_dtmf_packet_count>
<rtp_video_out_cng_packet_count>
<rtp_video_rtcp_packet_count>
<rtp_video_rtcp_octet_count>

</variables>

The Callflow components

<callflow dialplan="XML" unique-id="[UUID]" profile_index="1">
	
	<extension name="myconference" number="3500">		
		<application app_name="..." app_data="...">
	</extension>	
	<caller_profile>
		<username>
		<dialplan>
		<caller_id_name>
		<caller_id_number>
		<callee_id_name>
		<callee_id_number>
		<ani>
		<aniii>
		<network_addr>
		<rdnis>
		<destination_number>
		<uuid>
		<source>
		<context>
		<chan_name>
	</caller_profile>
				
			
	<times>
		<created_time>
		<profile_created_time>
		<progress_time>	
		<progress_media_time>
		<answered_time>
		<bridged_time>
		<last_hold_time>	
		<hold_accum_time>
		<hangup_time>
		<resurrect_time>	
		<transfer_time>	
	</times>
</callflow>

Standardising bodies :-

  • ITU (The International Telecommunication Union) is the United Nations specialised agency in the field of telecommunications, information and communication technologies (ICTs).
  • ITU-T ( ITU Telecommunication Standardisation Sector) is responsible for studying technical, operating and tariff questions and issuing Recommendations on them with a view to standardising tele-communications on a worldwide basis.

As the technology for packet switching matured, the voice quality between circuit-switched and packet-switched networks is mostly indistinguishable. However, the flaws in the VoIP communication system reappear under low network conditions and bad architecture design. Especially with applications that are greedy for network bandwidth such as large scale conferencing or HD streaming, the need for monitoring and quality control is very high, which can be only met by above described QoS parameters.

References

  • CDR on freeswitch
  • ITU-T G.114 TELECOMMUNICATION STANDARDIZATION SECTOR OF ITU (05/2003) SERIES G: TRANSMISSION SYSTEMS AND MEDIA, DIGITAL SYSTEMS AND NETWORKS , International telephone connections and circuits – General Recommendations on the transmission qua
  • Kamailio RTP engine https://www.kamailio.org/docs/modules/devel/modules/rtpengine.html

RTPengine on kamailio SIP server


RTPengine is a proxy for RTP traffic and other UDP based media traffic over either IPv4 or IPv6. It can even bridge between diff IP networks and interfaces. It can do TOS/QoS field setting. It is Multi-threaded, can advertise different addresses for operation behind NAT.

This article focuses on setting up sipwise rtpegine to proxy RTP traffic from the Kamailio app server. This is an updated version of the old article on RTPEngine, since then there have many many updates on the software. I also wrote an article covering all relevant and important Kamailio modules earlier including RTPProxy and RTP engine https://telecom.altanai.com/2014/11/18/kamailio-modules/

It bears in-kernel packet forwarding for low-latency and low-CPU performance. When used with the Kamailio, the RTP engine module adds more features to media stream routing and management, especially around RTP proxy and Mos scores.

Features

  • Full SDP parsing and rewriting
  • Supports non-standard RTCP ports (RFC 3605)
  • ICE (RFC 5245):
    • Bridging between ICE-enabled and ICE-unaware user agents
    • Optionally acting only as additional ICE relay/candidate
    • Optionally forcing relay of media streams by removing other ICE candidates
  • SRTP (RFC 3711):
    • Support for SDES (RFC 4568) and DTLS-SRTP (RFC 5764)
    • AES-CM and AES-F8 ciphers, both in userspace and in kernel
    • HMAC-SHA1 packet authentication
    • Bridging between RTP and SRTP user agents
  • RTCP profile with feedback extensions (RTP/AVPF, RFC 4585 and 5124)
  • Arbitrary bridging between any of the supported RTP profiles (RTP/AVP, RTP/AVPF, RTP/SAVP, RTP/SAVPF)
  • RTP/RTCP multiplexing (RFC 5761) and demultiplexing
  • Breaking of BUNDLE’d media streams (draft-ietf-mmusic-sdp-bundle-negotiation)
  • Recording of media streams, decrypted if possible
  • Transcoding and repacketization
  • Playback of pre-recorded streams/announcements

Sipwise NGCP RTP Engine Source Code

There are 3 parts of the source structure in sipwise NGCP ( Next Generation communication Platform) rtpengine :

1.daemon

The userspace daemon and workhorse, minimum requirement for anything to work. Running make will compile the binary, which will be called rtpengine.

Required packages including their development headers are required to compile the daemon:

  • pkg-config
  • GLib including GThread and GLib-JSON version 2.x
  • zlib
  • OpenSSL
  • PCRE library
  • XMLRPC-C version 1.16.08 or higher
  • hiredis library
  • gperf
  • libcurl version 3.x or 4.x
  • libevent version 2.x
  • libpcap
  • libsystemd
  • MySQL or MariaDB client library (optional for media playback and call recording daemon)
  • libiptc library for iptables management (optional)
  • ffmpeg codec libraries for transcoding (optional) such as libavcodec, libavfilter, libswresample
  • bcg729 for full G.729 transcoding support (optional)

options for make – with_iptables_option , with_transcoding

 with_transcoding=no make 

2.iptables-extension

Required for in-kernel packet forwarding. With the iptables development headers installed, issuing make will compile the plugin for iptables and ip6tables. The file will be called libxt_RTPENGINE.so and needs to be copied into the xtables module directory. The location of this directory can be determined through pkg-config xtables –variable=xtlibdir on newer systems, and/or is usually either /lib/xtables/ or /usr/lib/x86_64-linux-gnu/xtables/.

3.kernel-module

Required for in-kernel packet forwarding. Compilation of the kernel module requires the kernel development headers to be installed in/lib/modules/$VERSION/build/, where $VERSION is the output of the command uname -r.

Successful compilation of the module will produce the file xt_RTPENGINE.ko. The module can be inserted into the running kernel manually through insmod xt_RTPENGINE.ko

It is recommended to copy the module into /lib/modules/$VERSION/updates/, followed by running depmod -a.

After this, the module can be loaded by issuing modprobe xt_RTPENGINE.

Installation

Follow instructions on https://gist.github.com/altanai/0d8cadbe6876d545fd63d6b3e79dcf73

Requirements

sudo su apt-get install debhelper iptables-dev libcurl4-openssl-dev libglib2.0-dev  libjson-glib-dev libxmlrpc-core-c3-dev libhiredis-dev build-essential:native

For Pcap

apt install ibpcap-dev

Also instal ffmpeg pakages

apt install libavcodec-dev libavfilter-dev libavformat-dev libavresample-dev  libavutil-dev

Use dpkg

libcrypt-openssl-rsa-perl libdigest-crc-perl libio-multiplex-perl libnet-interface-perl libsystemd-dev markdown

For debhelper>10

vi /etc/apt/sources.list

add line

deb http://archive.ubuntu.com/ubuntu xenial-backports main restricted universe multiverse
sudo apt update

check version

apt-cache policy debhelper dh-autoreconf
debhelper:
Installed: 9.20160115ubuntu3
Candidate: 9.20160115ubuntu3
Version table:
10.2.2ubuntu1~ubuntu16.04.1 100
100 http://us-east-1.ec2.archive.ubuntu.com/ubuntu xenial-backports/main amd64 Packages
100 http://archive.ubuntu.com/ubuntu xenial-backports/main amd64 Packages
*** 9.20160115ubuntu3 500
500 http://us-east-1.ec2.archive.ubuntu.com/ubuntu xenial/main amd64 Packages
100 /var/lib/dpkg/status
dh-autoreconf:
Installed: (none)
Candidate: 11
Version table:
12~ubuntu16.04.1 100
100 http://us-east-1.ec2.archive.ubuntu.com/ubuntu xenial-backports/main amd64 Packages
100 http://archive.ubuntu.com/ubuntu xenial-backports/main amd64 Packages
11 500
500 http://us-east-1.ec2.archive.ubuntu.com/ubuntu xenial/main amd64 Packages

Force installing the version from backports repo as it have low priority.

sudo apt install dh-autoreconf=12~ubuntu16.04.1 debhelper=10.2.2ubuntu1~ubuntu16.04.1

so now new priority will be

debhelper:

  Installed: 10.2.2ubuntu1~ubuntu16.04.1
  Candidate: 10.2.2ubuntu1~ubuntu16.04.1
  Version table:
 *** 10.2.2ubuntu1~ubuntu16.04.1 100
        100 http://us-east-1.ec2.archive.ubuntu.com/ubuntu xenial-backports/main amd64 Packages
        100 http://archive.ubuntu.com/ubuntu xenial-backports/main amd64 Packages
        100 /var/lib/dpkg/status
     9.20160115ubuntu3 500
        500 http://us-east-1.ec2.archive.ubuntu.com/ubuntu xenial/main amd64 Packages
dh-autoreconf:
  Installed: 12~ubuntu16.04.1
  Candidate: 12~ubuntu16.04.1
  Version table:
 *** 12~ubuntu16.04.1 100
        100 http://us-east-1.ec2.archive.ubuntu.com/ubuntu xenial-backports/main amd64 Packages
        100 http://archive.ubuntu.com/ubuntu xenial-backports/main amd64 Packages
        100 /var/lib/dpkg/status
     11 500
        500 http://us-east-1.ec2.archive.ubuntu.com/ubuntu xenial/main amd64 Packages

ref :https://askubuntu.com/questions/863221/need-help-building-debhelper-10-2-2-bpo8-from-source

Get sourcecode

cd /usr/local/src
git clone https://github.com/sipwise/rtpengine.git
cd rtpengine
 ./debian/flavors/no_ngcp

use dpkg-checkbuilddeps to find any missing dependices

For missing dependencies

dpkg-checkbuilddeps: error: Unmet build dependencies: libbcg729-dev
remove the encoder for G.729 which is not supported by ffmoeg by exporting varible

export DEB_BUILD_PROFILES="pkg.ngcp-rtpengine.nobcg729"

Ref : https://github.com/sipwise/rtpengine#g729-support

for defaultlibmysqlclient-dev and libiptc-dev

vi debian/control
change from default-libmysqlclient-dev to libmysqlclient-dev, change from libiptcdata-dev to libiptc-dev and install the alternatives such as

apt install libmysqlclient-dev libiptcdata-dev 

Generated deb files should be outside the rtpegine home folder

generated ngcp-rtpegine deb files
cd ..
dpkg -i ngcp-rtpengine-daemon_7.3.0.0+0~mr7.3.0.0_amd64.deb
dpkg -i ngcp-rtpengine-iptables_7.3.0.0+0~mr7.3.0.0_amd64.deb
dpkg -i ngcp-rtpengine-kernel-dkms_7.3.0.0+0~mr7.3.0.0_all.deb
dpkg -i ngcp-rtpengine-kernel-source_7.3.0.0+0~mr7.3.0.0_all.deb
dpkg -i ngcp-rtpengine-recording-daemon_7.3.0.0+0~mr7.3.0.0_amd64.deb
dpkg -i ngcp-rtpengine-utils_7.3.0.0+0~mr7.3.0.0_all.deb
dpkg -i ngcp-rtpengine_7.3.0.0+0~mr7.3.0.0_all.deb
After depackaging

Manual installation and running all test cases

cd rtpengine
make check

If you dont find a package you are looking for , some alternatives are to do apt-cache search like

apt-cache search libavfilter
libavfilter-dev - FFmpeg library containing media filters - development files
libavfilter-ffmpeg5 - FFmpeg library containing media filters - runtime files

or to search in ubuntu packages web https://packages.ubuntu.com/

Running RTPEngine

rtpegine application options

  • -v, –version Print build time and exit
  • –config-file=FILE Load config from this file
  • –config-section=STRING Config file section to use
  • –log-facility=daemon|local0|…|local7 Syslog facility to use for logging
  • -L, –log-level=INT Mask log priorities above this level
  • -E, –log-stderr Log on stderr instead of syslog
  • –no-log-timestamps Drop timestamps from log lines to stderr
  • –log-mark-prefix Prefix for sensitive log info
  • –log-mark-suffix Suffix for sensitive log info
  • -p, –pidfile=FILE Write PID to file
  • -f, –foreground Don’t fork to background
  • -t, –table=INT Kernel table to use
  • -F, –no-fallback Only start when kernel module is available
  • -i, –interface=[NAME/]IP[!IP] Local interface for RTP
  • -k, –subscribe-keyspace=INT INT … Subscription keyspace list
  • -l, –listen-tcp=[IP:]PORT TCP port to listen on
  • -u, –listen-udp=[IP46|HOSTNAME:]PORT UDP port to listen on
  • -n, –listen-ng=[IP46|HOSTNAME:]PORT UDP port to listen on, NG protocol
  • -c, –listen-cli=[IP46|HOSTNAME:]PORT UDP port to listen on, CLI
  • -g, –graphite=IP46|HOSTNAME:PORT Address of the graphite server
  • -G, –graphite-interval=INT Graphite send interval in seconds
  • –graphite-prefix=STRING Prefix for graphite line
  • -T, –tos=INT Default TOS value to set on streams
  • –control-tos=INT Default TOS value to set on control-ng
  • -o, –timeout=SECS RTP timeout
  • -s, –silent-timeout=SECS RTP timeout for muted
  • -a, –final-timeout=SECS Call timeout
  • –offer-timeout=SECS Timeout for incomplete one-sided calls
  • -m, –port-min=INT Lowest port to use for RTP
  • -M, –port-max=INT Highest port to use for RTP
  • -r, –redis=[PW@]IP:PORT/INT Connect to Redis database
  • -w, –redis-write=[PW@]IP:PORT/INT Connect to Redis write database
  • –redis-num-threads=INT Number of Redis restore threads
  • –redis-expires=INT Expire time in seconds for redis keys
  • -q, –no-redis-required Start no matter of redis connection state
  • –redis-allowed-errors=INT Number of allowed errors before redis is temporarily disabled
  • –redis-disable-time=INT Number of seconds redis communication is disabled because of errors
  • –redis-cmd-timeout=INT Sets a timeout in milliseconds for redis commands
  • –redis-connect-timeout=INT Sets a timeout in milliseconds for redis connections
  • -b, –b2b-url=STRING XMLRPC URL of B2B UA
  • –log-facility-cdr=daemon|local0|…|local7 Syslog facility to use for logging CDRs
  • –log-facility-rtcp=daemon|local0|…|local7 Syslog facility to use for logging RTCP
  • –log-facility-dtmf=daemon|local0|…|local7 Syslog facility to use for logging DTMF
  • –log-format=default|parsable Log prefix format
  • -x, –xmlrpc-format=INT XMLRPC timeout request format to use. 0: SEMS DI, 1: call-id only, 2: Kamailio
  • –num-threads=INT Number of worker threads to create
  • –media-num-threads=INT Number of worker threads for media playback
  • -d, –delete-delay=INT Delay for deleting a session from memory.
  • –sip-source Use SIP source address by default
  • –dtls-passive Always prefer DTLS passive role
  • –max-sessions=INT Limit of maximum number of sessions
  • –max-load=FLOAT Reject new sessions if load averages exceeds this value
  • –max-cpu=FLOAT Reject new sessions if CPU usage (in percent) exceeds this value
  • –max-bandwidth=INT Reject new sessions if bandwidth usage (in bytes per second) exceeds this value
  • –homer=IP46|HOSTNAME:PORT Address of Homer server for RTCP stats
  • –homer-protocol=udp|tcp Transport protocol for Homer (default udp)
  • –homer-id=INT ‘Capture ID’ to use within the HEP protocol
  • –recording-dir=FILE Directory for storing pcap and metadata files
  • –recording-method=pcap|proc Strategy for call recording
  • –recording-format=raw|eth File format for stored pcap files
  • –iptables-chain=STRING Add explicit firewall rules to this iptables chain
  • –codecs Print a list of supported codecs and exit
  • –scheduling=default|none|fifo|rr|other|batch|idle Thread scheduling policy
  • –priority=INT Thread scheduling priority
  • –idle-scheduling=default|none|fifo|rr|other|batch|idle Idle thread scheduling policy
  • –idle-priority=INT Idle thread scheduling priority
  • –log-srtp-keys Log SRTP keys to error log
  • –mysql-host=HOST|IP MySQL host for stored media files
  • –mysql-port=INT MySQL port
  • –mysql-user=USERNAME MySQL connection credentials
  • –mysql-pass=PASSWORD MySQL connection credentials
  • –mysql-query=STRING MySQL select query

Run command

rtpengine --interface="10.10.10.10" --listen-ng=25061 --listen-cli=25062 --foreground --log-stderr --listen-udp=25060 --listen-tcp=25060

In-Kernal Packet Forwarding

To avoid the overhead involved in processing each individual RTP packet in userspace-only operation, especially as RTP traffic consists of many small packets at high rates, rtpengine provides a kernel module to offload the bulk of the packet forwarding duties from user space to kernel space. This also results in increasing the number of concurrent calls as CPU usage decreases.In-kernel packet forwarding is implemented as an iptables module (x_tables) and has 2 parts – xt_RTPENGINE and plugin to the iptables and ip6tables command-line utilities

Sequence of events for a newly established media stream is then:

  1. Kamailio as SIP proxy controls rtpengine and signals it about a newly established call.
  2. Rtpengine daemon allocates local UDP ports and sets up preliminary forward rules based on the info received from the SIP proxy.
  3. An RTP packet is received on the local port.
  4. It traverses the iptables chains and gets passed to the xt_RTPENGINE module.
  5. The module doesn’t recognize it as belonging to an established stream and thus ignores it.
  6. The packet continues normal processing and eventually ends up in the daemon’s receive queue.
  7. The daemon reads it, processes it and forwards it. It also updates some internal data.
  8. This userspace-only processing and forwarding continues for a little while, during which time information about additional streams and/or endpoints may be obtained from the SIP proxy.
  9. After a few seconds, when the daemon is satisfied with what it has learned about the media endpoints, it pushes the forwarding rules to the kernel.
  10. From this moment on, the kernel module will recognize incoming packets belonging to those streams and will forward them on its own. It will stop those packets from traversing the network stacks any further, so the daemon will not see them any more on its receive queues.
  11. In-kernel forwarding is allowed to cease to work at any given time, either accidentally (e.g. by removal of the iptablesrule) or deliberatly (the daemon will do so in case of a re-invite), in which case forwarding falls back to userspace-only operation.

Kernel Module

The kernel module supports multiple forwarding tables, identified through their ID number, bydefault 0 to 63. Each running instance of the rtpengine daemon controls one such table.

To load use modprobe xt_RTPENGINE and to unload rmmod xt_RTPENGINE. With the module loaded, a new directory will appear in /proc/, namely /proc/rtpengine/, containing pseudo-files, control ( to create and delete forwarding tables) and list ( list of currently active forwarding tables)

To manually create a forwarding table with ID 33, the following command can be used:

echo 'add 43' > /proc/rtpengine/control

iptables module

In order for the kernel module to be able to actually forward packets, an iptables rule must be set up to send packets into the module. Each such rule is associated with one forwarding table. In the simplest case, for forwarding table 33, this can be done through:

iptables -I INPUT -p udp -j RTPENGINE --id 33

To restrict the rules to the UDP port range used by rtpengine, e.g. by supplying a parameter like –dport 30000:40000. If the kernel module receives a packet that it doesn’t recognize as belonging to an active media stream, it will simply ignore it and hand it back to the network stack for normal processing.

A typical start-up sequence including in-kernel forwarding might look like this:

modprobe xt_RTPENGINE
iptables -I INPUT -p udp -j RTPENGINE --id 0
ip6tables -I INPUT -p udp -j RTPENGINE --id 0

ensure that the table we want to use doesn’t exist – usually needed after a daemon restart, otherwise will error

echo 'del 0' > /proc/rtpengine/control

start daemon

/usr/sbin/rtpengine --table=0 --interface=10.64.73.31 --interface=2001:db8::4f3:3d \
--listen-ng=127.0.0.1:2223 --tos=184 --pidfile=/run/rtpengine.pid --no-fallback

Running Multiple Instances

To run multiple instances of rtpengine on the same machine run multiple instances of the daemon using different command-line options ( local addresses and listening ports), together with multiple different kernel forwarding tables.

For example, if one local network interface has address 10.64.73.31 and another has address 192.168.65.73, then the start-up sequence might look like this:

modprobe xt_RTPENGINE

iptables -I INPUT -p udp -d y.y.y.y -j RTPENGINE --id 0
iptables -I INPUT -p udp -d x.x.x.x -j RTPENGINE --id 1
echo 'del 0' > /proc/rtpengine/control
echo 'del 1' > /proc/rtpengine/control
/usr/sbin/rtpengine --table=0 --interface=<ip> \
--listen-ng=127.0.0.1:2223 --tos=184 --pidfile=/run/rtpengine-10.pid --no-fallback
/usr/sbin/rtpengine --table=1 --interface=<ip_pvy>\
--listen-ng=127.0.0.1:2224 --tos=184 --pidfile=/run/rtpengine-192.pid --no-fallback

With this setup, the SIP proxy can choose which instance of rtpengine to talk to and thus which local interface to use by sending its control messages to either port 2223 or port 2224.

Transcoding

Currently transcoding is supported for audio streams. Can be turned off with with_transcoding=no option in makeFile.

Normally rtpengine leaves codec negotiation up to the clients involved in the call and does not interfere. In this case, if the clients fail to agree on a codec, the call will fail.

Transcoding options in the ng control protocol,  transcode or ptime. If a codec is requested via the transcode option that was not originally offered, transcoding will be engaged for that call. With transcoding active for a call, all unsupported codecs will be removed from the SDP.

Transcoding happens in userspace only, so in-kernel packet forwarding will not be available for transcoded codecs. Codecs that are supported by both sides will simply be passed through transparently (unless repacketization is active). In-kernel packet forwarding will still be available for these codecs.

Codecs supported by rtpengine can be shown with –codecs options

  • rtpengine –codecs
  • PCMA: fully supported
  • PCMU: fully supported
  • G723: fully supported
  • G722: fully supported
  • QCELP: supported for decoding only
  • G729: supported for decoding only
  • speex: fully supported
  • GSM: fully supported
  • iLBC: not supported
  • opus: fully supported
  • vorbis: codec supported but lacks RTP definition
  • ac3: codec supported but lacks RTP definition
  • eac3: codec supported but lacks RTP definition
  • ATRAC3: supported for decoding only
  • ATRAC-X: supported for decoding only
  • AMR: supported for decoding only
  • AMR-WB: supported for decoding only
  • PCM-S16LE: codec supported but lacks RTP definition
  • PCM-U8: codec supported but lacks RTP definition
  • MP3: codec supported but lacks RTP definition

ng Control Protocol

Advanced control protocol to pass SDP body from the SIP proxy to the rtpengine daemon, has the body rewritten in the daemon, and then pas back to the SIP proxy to embed into the SIP message. It is  based on the bencode standard and runs over UDP transport.

Each message passed between the SIP proxy and the media proxy contains of two parts:

  1. message cookie ( to match requests to responses, and retransmission detection) and
  2. bencoded dictionary

The dictionary of each request must contain at least one key called command and corresponding value must be a string and determines the type of message. Currently the following commands are defined:

  • ping
  • offer
  • answer
  • delete
  • query
  • start recording
  • stop recording
  • block DTMF
  • unblock DTMF
  • block media
  • unblock media
  • start forwarding
  • stop forwarding
  • play media
  • stop media

The response dictionary must contain at least one key called result. The value can be either ok (optional key warning) or error( to be accompanied by error-reason). For the ping command, the additional value pong is allowed.

rtpengine.sample.conf

[rtpengine]

table = 0
no-fallback = false
for userspace forwarding only:
table = -1

// separate multiple interfaces with semicolons:
interface = internal/12.23.34.45;external/23.34.45.54

listen-ng = 127.0.0.1:2223
listen-tcp = 25060
listen-udp = 12222

timeout = 60
silent-timeout = 3600
tos = 184
control-tos = 184
delete-delay = 30
final-timeout = 10800

foreground = false
pidfile = /run/ngcp-rtpengine-daemon.pid
num-threads = 16

port-min = 30000
port-max = 40000
max-sessions = 5000

recording-dir = /var/spool/rtpengine
recording-method = proc
recording-format = raw

redis = 127.0.0.1:6379/5
redis-write = password@x.x.x.x:6379/42
redis-num-threads = 8
no-redis-required = false
redis-expires = 86400
redis-allowed-errors = -1
redis-disable-time = 10
redis-cmd-timeout = 0
redis-connect-timeout = 1000

b2b-url = http://127.0.0.1:8090/
xmlrpc-format = 0

log-level = 6
log-stderr = false
log-facility = daemon
log-facility-cdr = local0
log-facility-rtcp = local1

graphite = 127.0.0.1:9006
graphite-interval = 60
graphite-prefix = foobar.

homer = 123.234.345.456:65432
homer-protocol = udp
homer-id = 2001

sip-source = false
dtls-passive = false

ngcp-rtpengine-daemon Service

To start the ngcp-rtpengine-daemon service

/etc/init.d/ngcp-rtpengine-daemon start
[ ok ] Starting ngcp-rtpengine-daemon (via systemctl): ngcp-rtpengine-daemon.service.

Checking status ngcp-rtpengine-daemonservice

# systemctl status ngcp-rtpengine-daemon.service

● ngcp-rtpengine-daemon.service - NGCP RTP/media Proxy Daemon
   Loaded: loaded (/lib/systemd/system/ngcp-rtpengine-daemon.service; disabled; vendor preset: enabled)
   Active: active (running) since Thu 2019-04-11 10:16:20 UTC; 24s ago
  Process: 13751 ExecStopPost=/usr/sbin/ngcp-rtpengine-iptables-setup stop (code=exited, status=0/SUCCESS)
  Process: 13797 ExecStartPre=/usr/sbin/ngcp-rtpengine-iptables-setup start (code=exited, status=0/SUCCESS)
 Main PID: 13814 (rtpengine)
    Tasks: 19
   Memory: 10.5M
      CPU: 102ms
   CGroup: /system.slice/ngcp-rtpengine-daemon.service
           └─13814 /usr/sbin/rtpengine -f -E --no-log-timestamps --pidfile /run/ngcp-rtpengine-daemon.pid --config-file /etc/rtpengine/rtpengine.conf --table 0

To start recording service

/etc/init.d/ngcp-rtpengine-recording-daemon start

RTP engine receives command offer

Received command 'offer' from :53888
Dump for 'offer' from :53888: {  
"sdp":"v=0 
 o=- 1554978148897419 1 IN IP4 pvt_ip 
 s=Bria 3 release 3.5.5 stamp 71243 
 c=IN IP4 192.168.1.23 
 t=0 0 
 m=audio 50754 RTP/AVP 0 98 101 
 a=rtpmap:98 ILBC/8000 
 a=rtpmap:101 telephone-event/8000 
 a=fmtp:101 0-15 
 a=sendrecv 
 ",
    "ICE":"remove",
    "record-call":"yes",
    "direction":[  
       "internal",
       "internal"
    ],
    "flags":[  
       "no-rtcp-attribute"
    ],
    "replace":[  
       "origin",
       "session-connection"
    ],
    "transport-protocol":"RTP/AVP",
    "call-id":"732597d6-6d96-485b-b6dc-7d93703c1405",
    "received-from":[  
       "IP4",
       ""
Creating new call
Turning on call recording.
Wrote metadata file to temporary path: /var/spool/rtpengine/tmp/
...

RTP engine receives command delete

Received command 'delete' from :57304
Dump for 'delete' from :57304: { "call-id": "732597d6-6d96-485b-b6dc-7d93703c1405", "received-from": [ "IP4", "" ], "from-tag": "cb8a1e30", "command": "delete" }
Deleting call branch 'cb8a1e30' (via-branch '')
Call branch 'cb8a1e30' (via-branch '') deleted, no more branches remaining
  Deleting entire call
 INFO: [ID="732597d6-6d96-485b-b6dc-7d93703c1405"]: Final packet stats:
 --- Tag 'cb8a1e30', created 0:05 ago for branch '', in dialogue with ''
 ------ Media #1 (audio over RTP/AVP) using unknown codec
 --------- Port   :10044 <>    :50754, SSRC 0, 0 p, 0 b, 0 e, 5 ts
 freeing send_timer
 --------- Port   :10045 <>    :50755 (RTCP), SSRC 0, 0 p, 0 b, 0 e, 5 ts
 freeing send_timer
 --- Tag '', created 0:05 ago for branch '', in dialogue with 'cb8a1e30'
 ------ Media #1 (audio over RTP/AVP) using unknown codec
--------- Port   :10032 <>          (null):0    , SSRC 0, 0 p, 0 b, 0 e, 5 ts
freeing send_timer
--------- Port   :10033 <>          (null):0     (RTCP), SSRC 0, 0 p, 0 b, 0 e, 5 ts
freeing send_timer
 rtpengine: ci=732597d6-6d96-485b-b6dc-7d93703c1405, created_from=:53888, 
 last_signal=1554978149, 
 tos=0, 
 ml0_start_time=1554978149.645290, 
 ml0_end_time=1554978154.822680, 
 ml0_duration=5.177390, 
 ml0_termination=REGULAR, 
 ml0_local_tag=cb8a1e30, 
 ml0_local_tag_type=FROM_TAG, 
...



Kamailio Security

Security is Critical for a VoIP platform as it is susceptible to hacks , misuse , eavesdropping or just sheer misuse of the system by making robotic flood calls . Kamailio SIP Server provides some key features to meet these challenges which will be discussed in this blog .

Sanity checks for incoming SIP requests

Being a gateway on the VOIP system permiter is a challenging task due to the security threast it posses. It is must to configure per request initial checks for all incoming SIP request. This ideally should be the first step in routing clock before any other processing.

Exmaple programs https://github.com/altanai/kamailioexamples/blob/2039639275a33a2ba2435ae0b781e6f9dd51220e/Barebone_SIPServer/kamailio.cfg

request_route {
    route(REQINIT);
    ... 
    // proceed with routing 
}

Pointers for functionality

For replies coming from local users , usually behind NAT , do not open a new TCP connection on each reply

set_reply_no_connect();

For indialog requests also close connection after forwarding the request.

if(has_totag()) {
    set_forward_no_connect();
}

Check if any IP is flooding the server with messages and block for some time ( ANTI-FLOOD and pike decsribed later in the article ).
Ofcourse exclude self IP . sample to do blocking using hastable’s psedi variable ipban

if(src_ip!=myself) {
    if (!pike_check_req()) {
        $sht(ipban=>$si) = 1;
        exit;
    }
}

Friendly-scanners are type of botnets probing and scanning known IP ranges(5060,5061..) for SIP server, softswitches, cloud PBX. Once they detect a suitably open server they use brute force tactic to send all commonly/default username/passwords accounts. The prime purpose is to extract all vulenrable accounts for creating fradulent calls such as crating DOS attacks using high tarffic and consuming all bandwidth from good calls, free internation calls or imposter/scam calls.

Among some obvious ways to block the flood of packets by these scanner are

  • imply strict firewalls rules for allowing only known client IP’s
  • changing default SIP port from 5060 to some other non standard port in network
  • checking User agent for known attackes such as (sipcli , sipvicious , sip-scan , sipsak , sundayddr , friendly-scanner , iWar , CSipSimple , SIVuS , Gulp , sipv , smap , friendly-request , VaxIPUserAgent , VaxSIPUserAgent , siparmyknife , Test Agent)
if($ua =~ "friendly-scanner|sipcli|sipvicious|VaxSIPUserAgent") {
    exit;
}
  • track unsuccesfull quick/consecutive attempts from an IP and block its access temporatliy or permamntly. Such as failing to REGISTER / autehticate for 3 consecutive time should block it .

Track if the message is hopping too many times within the server server , intra or inter networks not reaching the destination . mf_process_maxfwd_header(maxvalue). Note maxvalue is added is no Max-Forward header is found in the message.

mf_process_maxfwd_header(10)

Sanity is a complete module by itself to perform various checks and validation such as

  • ruri sip version – (1) – checks if the SIP version in the request URI is supported, currently only 2.0
  • ruri scheme – (2) – checks if the URI scheme of the request URI is supported (sip[s]|tel[s])
  • required headers – (4) -checks if the minimum set of required headers to, from, cseq, callid and via is present in the request
  • via sip version – (8) – disabled
  • via protocol – (16) – disabled
  • Cseq method – (32) – checks if the method from the Cseq header is equal to the request method
  • Cseq value – (64) – checks if the number in the Cseq header is a valid unsigned integer
  • content length – (128) – checks if the size of the body matches with the value from the content length header
  • expires value – (256) – checks if the value of the expires header is a valid unsigned integer
  • proxy require – (512) – checks if all items of the proxy require header are present in the list of the extensions from the module parameter proxy_require.
  • parse uri’s – (1024) – checks if the specified URIs are present and parseable by the Kamailio parsers
  • digest credentials (2048) – Check all instances of digest credentials in a message
  • duplicated To/From tags (4096) – checks for the presence of duplicated tags in To/From headers.
  • authorization header (8192) – checks if the Authorization is valid if the scheme in “digest” always returns success for other schemes.
    sample for URI checks for list of parsed URIs: Request URI (1), From URI (2) and To URI (4).

Full route[REQINIT] block

route[REQINIT] {

set_reply_no_connect();
if(has_totag()) {
    set_forward_no_connect();
}

if(src_ip!=myself) {
    if (!pike_check_req()) {
        $sht(ipban=>$si) = 1;
        exit;
    }
}

if($ua =~ "friendly-scanner|sipcli|sipvicious|VaxSIPUserAgent") {
    exit;
}

if (!mf_process_maxfwd_header("10")) {
    sl_send_reply("483","Too Many Hops");
    exit;
}

if(is_method("OPTIONS") && uri==myself && $rU==$null) {
    sl_send_reply("200","Keepalive");
    exit;
}

if(!sanity_check("17895", "7")) {
    xlog("Malformed SIP request from $si:$sp\n");
    exit;
}
}

Access Control Lists and Permissions

permission module handles ACL by storing permission rules in plaintext configuration files , hosts.allow and hosts.deby by tcpd.

#!ifdef WITH_IPAUTH
loadmodule "permissions.so"
#!endif
...
# ----- permissions params -----
#!ifdef WITH_IPAUTH
modparam("permissions", "db_url", DBURL)
modparam("permissions", "db_mode", 1)
#!endif
..
#!ifdef WITH_IPAUTH
    if((!is_method("REGISTER")) && allow_source_address()) {
        # source IP allowed
        return;
    }
#!endif

sample programs to check for allowed access in LUA programing o Kamailio along when acting as registrar – https://github.com/altanai/kamailioexamples/blob/master/Lua%20-%20kamailio%20Registrar%20permission%20auth/kamailio.cfg

Functions

Call Routing

if (allow_routing("rules.allow", "rules.deny")) {
    t_relay();
};

Registration permissions

if (method=="REGISTER") {
    if (allow_register("register")) {
        save("location");
        exit;
    } else {
        sl_send_reply("403", "Forbidden");
    };
};

URI permissions

if (allow_uri("basename", "$rt")) {  // Check Refer-To URI
    t_relay();
};

Address permissions

// check if sourec ip/port is in group 1
if (!allow_address("1", "$si", "$sp")) {
    sl_send_reply("403", "Forbidden");
};

Trusted Requests

if (allow_trusted("$si", "$proto")) {
    t_relay();
};

checks protocols which could be one of the “any”, “udp, “tcp”, “tls”, “ws”, “wss” and “sctp”.

Hiding Topology Details

Stripping the SIP routing headers that show topology details involves steps such as hiding the local IP address of user agent , hiding path taken to reach the server , obscuring the core SIP server’s ip and details etc . Some headers which giave away information are

  • top most Via header
  • contact address
  • Record-Route headers
  • sometimes the Call-ID header

This goes a long way in helping to keep the inner network topology secure from malacious exploiters, expecially to protect IP of the PSTN gateways which could let to an costly mess or gensrally from attackers and reverse engineering.

Topoh module hides the network topology by removing the internal IP addresa and instead add ing them in encrypted form the same sip packet. Diff server using the same shared secret key can encode decode the encrypted addresses.

This way it doesnt not even have to store the state of the call and is transpoarent to all call routing logic

sample program for kamailio sip server to provide topology hiding – https://github.com/altanai/kamailioexamples/tree/abcc7b06c00fee12252133614187b0451757fcf2/Topology_hiding

loadmodule topoh.so

modparam("topoh", "mask_key", "somekey")
modparam("topoh", "mask_ip", "1.1.1.1")
modparam("topoh", "mask_callid", 1)

topoh module

Primarily it does these things
hide the addresses of PSTN gateways
protect your internal network topology
interconnection provider – to keep the details of connected parties secret to the other, to prevent a bypass of its service in the future

loadmodule topoh.so
modparam("topoh", "mask_key", "YouDoHaveToChangeThisKey")
modparam("topoh", "mask_ip", "10.0.0.1")
modparam("topoh", "mask_callid", 1)

Params

mask_key (str)
mask_ip (str)
mask_callid (integer)
uparam_name (str)
uparam_prefix (str)
vparam_name (str)
vparam_prefix (str)
callid_prefix (str)
sanity_checks (integer)
uri_prefix_checks (integer)
event_callback (str)

Primarily tis module uses mask key to code the trimmed via header information and insert them into pre specified param names with prefixes. Hence it can work with stageful or stateless proxy and can also work if server is restarted in between

topos module

Offers topology hiding by stripping the SIP routing headers that show topology details.

It requires 2 modules rr module since server must perform record routing to ensure in-dialog requests are encoded/decoded and database module to store the data for topology stripping and restoring.

Params :
storage (str) – could be redis or database backend

modparam("topos", "storage", "redis")

db_url (str)

modparam("topos", "db_url", "dbdriver://username:password@dbhost/dbname") 
modparam("topos", "db_url", "mysql://kamailio:kamailiorw@localhost/kamailio”

mask_callid (int) – Whether to replace or not the Call-ID with another unique id generated by Kamailio. ( present with topoh)
sanity_checks (int) – with sanity module to perform checks before encoding /decoding
branch_expire (int)
dialog_expire (int)
clean_interval (int)
event_callback (str) – callback event

modparam("topos", "event_callback", "ksr_topos_event")
..
function ksr_topos_event(evname)
 KSR.info("===== topos module triggered event: " .. evname .. "\n");
 return 1;
end

event route :
event_route[topos:msg-outgoing]

loadmodule "topos.so"
loadmodule "topos_redis.so"

//topos params 
modparam("topos", "storage", "redis")
//branch_expire is 10 min
modparam("topos", "branch_expire", 10800)
// dialog_expire is 1 day
modparam("topos", "dialog_expire", 10800)
modparam("topos", "sanity_checks", 1)

FireWall

To save from the automatic port scans that attackers carry out to hack into the system use the script below

*filter
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT DROP [0:0]
:CHECK_TCP - [0:0]
:ICMP - [0:0]
:PRIVATE - [0:0]
:PSD - [0:0]
:SERVICES - [0:0]
-A INPUT -i lo -j ACCEPT 
-A INPUT -i eth0 -p ipv6 -j ACCEPT 
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT 
-A INPUT -j SERVICES 
-A OUTPUT -o lo -j ACCEPT 
-A OUTPUT -m state --state NEW,RELATED,ESTABLISHED -j ACCEPT 
-A CHECK_TCP -p tcp -m tcp ! --tcp-flags SYN,RST,ACK SYN -m state --state NEW -j DROP 
-A CHECK_TCP -p tcp -m tcp --tcp-flags FIN,SYN,RST,PSH,ACK,URG FIN,SYN,RST,ACK -j DROP 
-A CHECK_TCP -p tcp -m tcp --tcp-flags FIN,SYN,RST,PSH,ACK,URG FIN,PSH,URG -j DROP 
-A CHECK_TCP -p tcp -m tcp --tcp-flags FIN,ACK FIN -m state --state INVALID,NEW,RELATED -j DROP 
-A CHECK_TCP -p tcp -m tcp --tcp-flags FIN,SYN,RST,PSH,ACK,URG FIN,SYN -j DROP 
-A CHECK_TCP -p tcp -m tcp --tcp-flags FIN,SYN FIN,SYN -j DROP 
-A CHECK_TCP -p tcp -m tcp --tcp-flags SYN,RST SYN,RST -j DROP 
-A CHECK_TCP -p tcp -m tcp --tcp-flags FIN,RST FIN,RST -j DROP 
-A CHECK_TCP -p tcp -m tcp --tcp-flags PSH,ACK PSH -j DROP 
-A CHECK_TCP -p tcp -m tcp --tcp-flags ACK,URG URG -j DROP 
-A CHECK_TCP -p tcp -m tcp --tcp-flags FIN,SYN,RST,PSH,ACK,URG NONE -j DROP 
-A CHECK_TCP -p tcp -m tcp --tcp-flags FIN,SYN,RST,PSH,ACK,URG FIN,SYN,RST,PSH,ACK,URG -j DROP 
-A CHECK_TCP -p tcp -m tcp --tcp-option 64 -j DROP 
-A CHECK_TCP -p tcp -m tcp --tcp-option 128 -j DROP 
-A ICMP -p icmp -m icmp --icmp-type 11/1 -m limit --limit 5/sec -m state --state NEW -j ACCEPT 
-A ICMP -p icmp -m icmp --icmp-type 11/0 -m limit --limit 5/sec -m state --state NEW -j ACCEPT 
-A ICMP -p icmp -m icmp --icmp-type 3 -m limit --limit 10/sec -m state --state NEW -j ACCEPT 
-A ICMP -p icmp -m icmp --icmp-type 8 -m limit --limit 10/sec --limit-burst 10 -m state --state NEW -j ACCEPT 
-A ICMP -p icmp -j DROP 
-A PRIVATE -d 192.168.0.0/16 -j DROP 
-A PRIVATE -d 172.16.0.0/12 -j DROP 
-A PRIVATE -d 10.0.0.0/8 -j DROP 
-A PRIVATE -j RETURN 
-A PSD -p tcp -m statistic --mode random --probability 0.050000 -j REJECT --reject-with icmp-port-unreachable 
-A PSD -p tcp -m statistic --mode random --probability 0.050000 -j TARPIT  --reset 
-A PSD -p tcp -m statistic --mode random --probability 0.500000 -j TARPIT  --tarpit 
-A PSD -p udp -m statistic --mode random --probability 0.050000 -j REJECT --reject-with icmp-port-unreachable 
-A PSD -m statistic --mode random --probability 0.050000 -j REJECT --reject-with icmp-host-unreachable  
-A SERVICES -p icmp -m state --state INVALID -j DROP 
-A SERVICES -p icmp -j ICMP 
-A SERVICES -p tcp -j CHECK_TCP 
-A SERVICES -p udp -m udp --dport 123 -m state --state NEW -j ACCEPT 
-A SERVICES -p udp -m udp --dport 53 -m state --state NEW -j ACCEPT 
-A SERVICES -p tcp -m tcp --dport 53 -m state --state NEW -j ACCEPT 
-A SERVICES -p tcp -m udp -m multiport --dports 5060 -m state --state NEW -j ACCEPT 
-A SERVICES -p tcp -m udp -m multiport --dports 5061 -m state --state NEW -j ACCEPT 
-A SERVICES -i eth0 -j PSD 
COMMIT

Update/Remove Server and User Agent Headers

Rewrite server header to save the exact version of server from hackers

server_header="Server: Simple Server"

or completely rmemove it from traces

server_signature=no

and

user_agent_header="User-Agent: My SIP Server"

Remove Server warnings from traces and log file

Warnings expose the vulnerabilities of system and it is best to remove them in production enviornment

user_agent_header="User-Agent: My SIP Server"

Anti Flood

During Auth or logging there is a fair chance of leaking credentials or the fact that users opt for weak password themselves compromising the system via bruteforcing username/password . Or attacker may be bruteforcing prefixes to understand config and routing logic
Random unnecessary flood of SIP requests can consume CPU and make it slow or unavailable for others as Denial of Service . These situations can be made less daunting via pike module

pike modules

tracks the number of SIP messages per source IP address, per period.

loadmodule "pike.so"

// pike params 
modparam("pike", "sampling_time_unit", 2)
modparam("pike", "reqs_density_per_unit", 20)
modparam("pike", "remove_latency", 4)

//routing logic inclusion
route {
  if (!pike_check_req()) {
    xlog("L_ALERT","ALERT: pike block $rm from $fu (IP:$si:$sp)\n");
    exit;
  }
  ...
}

Pike module implementation in LUa on kamailio https://github.com/altanai/kamailioexamples/tree/ead84a684108600ad930027a3dcc6ab7442f139c/Lua%20-%20kamailio%20Registrar%20permission%20auth

Fail2Ban

can syslog files for specific messages based on regular expressions and act upon matching by banning IP addresses.

Traffic Monitoring and Detection

Secfilter module

offer an additional layer of security over our communications. It can perform

  • Blacklisting user agents, IP addresses, countries, domains and users.
  • Whitelisting user agents, IP addresses, countries, domains and users.
  • Blacklist of destinations where the called number is not allowed.
  • SQL injection attacks prevention.

Digest Authetication

Digest is a cryptographic function based on symmetrical encryption.

Sample kamailio exmaple with Auth https://github.com/altanai/kamailioexamples/blob/5fb6c6d0bb7416b3698e657612f016e70145a638/simple%20relay%20with%20flags/kamailio_relay_with_auth.cfg

Read more

tbd

SIPp UAC / UAS on TLS to generate traffic to check secruity of Kamailio SIP server

sipp is a powerful traffic generate for SIP applications and is widely used to test call flow routing applications in white box envrionmenet as well as stress or load testing. Read more about sipp https://telecom.altanai.com/2018/02/01/sipp/

General syntax is

sipp -sn uas -p 5060 -t l1 -tls_key key.pem  -tls_cert cert.pem  -i 127.0.0.1

More on compiling sipp from source to include ssl behavious and self generate certificates for tls can be read from https://github.com/altanai/kamailioexamples/tree/master/sipp

Ref :

Kamailio as Inbound/Outbound proxy or Session Border Controller (SBC)


SBC ( Session Border Controller )

A typical voice core network consists of a B2BUA SIP server with media proxy and media processing units/servers along with components for billing, user profile management, shared memory/ cache, transcoders, call routing logic etc. However, a VOIP provider would not want to interface these critical servers to the outside world directly. An SBC ( Session Border Controller ) helps to abstract the core VoIP platform from public access and traffic.

The role of an SBC is to shield the core network from external entities such as user agent’s, carrier networks (topology hiding) while also providing security, auth and accounting services. In many cases, SBC also provides NAT traversal and policy control features ( such as rate-limiting, ACL etc ). In advanced cases, transcoding, topology concealment and load balancing are also achievable via an SBC such as Kamailio.

SBC interfaces

Following sections are usecases / features kamailio can extend to. Routing scripts at https://github.com/altanai/kamailioexamples

Block user based on excessive REGISTER request  till an expiry time

For instance to block DDOS attacks, kamailio can check for the number of register requests a user sends and block above a threshold number subsequently .

if($sht(auth_block_list=>$au::auth_count)==30){
    $var(block) = $Ts - 900;
    $var(expire) = $Ts - 300;

    if($sht(auth_block_list=>$au::last_block) > $var(block)){
        xlog("L_INFO", "$fU@$fd - REGISTER - $au User Already Blocked for Exceeded Register Requests.\n");
        sl_send_reply("403", "Already Blocked Forbidden");
        exit;
    } else if($sht(auth_block_list=>$au::last_auth) > $var(expire)) {
        $sht(auth_block_list=>$au::last_block) = $Ts;
        xlog("L_INFO", "$fU@$fd - REGISTER - $au User Blocked for Exceeded Register Requests.\n");
        sl_send_reply("403", "Blocked Forbidden");
        exit;
    } else {
        $sht(auth_block_list=>$au::auth_count) = 0;
    } 
}

More information on kamailio security can be found on https://telecom.altanai.com/2018/02/17/kamailio-security/. It includes Sanity checks for incoming SIP requests ,Access Control Lists and Permissions, Hiding Topology Details, Anti Flood and Traffic Monitoring and Detection.

Anti Flood with Pike Module

To be on edge of a voip pltform, a SIP server must keep track of all incoming request and their sources. Blocking the ones which exceed the limit or appear like a dos attack. Pike module reports high traffic from an IP if detected.

A sample script to detect high traffic from an IP and add it to ban list for a while . But exclude the known IP sources such as PSTN gateways etc

#!ifdef WITH_ANTIFLOOD
loadmodule "htable.so"
loadmodule "pike.so"
#!endif
...
# ----- pike params -----
modparam("pike", "sampling_time_unit", 2)
modparam("pike", "reqs_density_per_unit", 16)
modparam("pike", "remove_latency", 4)
...
route[REQINIT] {
...
if(src_ip!=myself) {
	if($sht(ipban=>$si)!=$null) {
		# ip is already blocked
		xdbg("request from blocked IP - $rm from $fu (IP:$si:$sp)\n");
		exit;
	}
	if (!pike_check_req()) {
		xlog("L_ALERT","ALERT: pike blocking $rm from $fu (IP:$si:$sp)\n");
		$sht(ipban=>$si) = 1;
		exit;
	}
}

Access Control List with Permission Module

permission module handles ACL by storing permission rules in plaintext configuration files , hosts.allow and hosts.deby by tcpd.

#!ifdef WITH_IPAUTH
loadmodule "permissions.so"
#!endif
...
# ----- permissions params -----
#!ifdef WITH_IPAUTH
modparam("permissions", "db_url", DBURL)
modparam("permissions", "db_mode", 1)
#!endif
..
#!ifdef WITH_IPAUTH
    if((!is_method("REGISTER")) && allow_source_address()) {
        # source IP allowed
        return;
    }
#!endif

Call Routing Functions

if (allow_routing("rules.allow", "rules.deny")) {
    t_relay();
};

Registration permissions

if (method=="REGISTER") {
    if (allow_register("register")) {
        save("location");
        exit;
    } else {
        sl_send_reply("403", "Forbidden");
    };
};

URI permissions

if (allow_uri("basename", "$rt")) {  // Check Refer-To URI
    t_relay();
};

Address permissions

// check if sourec ip/port is in group 1
if (!allow_address("1", "$si", "$sp")) {
    sl_send_reply("403", "Forbidden");
};

Trusted Requests

if (allow_trusted("$si", "$proto")) {
    t_relay();
};

Checks protocols which could be one of the “any”, “udp, “tcp”, “tls”, “ws”, “wss” and “sctp”.

Perform Load Balancing with Dispatcher Module

Load balancing is critical to a production ready system to provide High availability and load sharing among available servers. This could be either stateless or stateful where they use call state tracking

Dispatcher module in Kamailio lends capabilities of SIP traffic dispatcher to it. It can load routes to gateways or destination sets from any storage source such as mysql , psql database or even plain text file (modparam("dispatcher", "db_url", <datasource_name>).

It can also assign priority for routing sip traffic to it ( modparam("dispatcher", "priority_col", "dstpriority"))

To discover active of inactive gateways it uses TM module. One can choose one among many algorithms to share the load , like

  • 0 – hash over callid
  • 1 – hash over from URI
  • 2 – hash over to URI
  • 3 – hash over request-URI
  • 4 – round-robin (next destination)
  • 5 – hash over authorization-username
  • 6 – random destination (using rand())
  • 7 – hash over the content of PVs string
  • 8 – select destination sorted by priority attribute value (serial forking ordered by priority).
  • 9 – use weight based load distribution . Needs attribute ‘weight’ per each address
  • 10 – call load distribution ie route to one that has the least number of calls associated
  • 11 – relative weight based load distribution(rweight)
  • 12 – dispatch to all destination in setid at once (parallel forking).
  • x – if the algorithm is not implemented, the first entry in set is chosen.

Some attributes passed with each destination set

  • duid – identify a destination (gateway address). Practically the load within the group is associated with this value.
  • maxload – upper limit of active calls per destination
  • weight – percent of calls to be sent to that gateways
  • rweight – relative weight based load distribution.
  • socket – sending socket for the gateway including keepalives
  • ping_from – from URI in OPTIONS keepalives

Active host usage probability is, rweight/(SUM of all active host rweights in destination group). recalculation is fired as host enables or disables.

Every destination has congestion threshold(weight) and after enabling c (congestion control), rweight is also used to control congestion tolerance lowering the weight by 1 as congestion is detected.

EWMA ( exponential weighted moving average ) is speed at which the older samples are dampened.

nametypesizedefaultnullkeyextra
idunsigned int10noprimaryauto
increment
setidintnot specified0no
destinationstring192“”no
flagintnot specified“”no
priorityintnot specified0no
attrsstring1980no
descriptionstring64“”no

To insert into dispatcher

INSERT INTO "dispatcher" VALUES(1,1,'sip:192.168.0.1:5060',0,12,'rweight=50;weight=50;cc=1;','');

set ping gateway once per second

modparam("dispatcher", "ds_ping_interval", 1)

enabling congestion metrics

modparam("dispatcher", "ds_ping_latency_stats", 1)

latency estimator

modparam("dispatcher", "ds_latency_estimator_alpha", 900)
loadmodule "dispatcher.so"
...
# ----- dispatcher params ----- 
modparam("dispatcher", "db_url", DBURL) 
modparam("dispatcher", "table_name", "dispatcher") modparam("dispatcher", "flags", 2) 
modparam("dispatcher", "dst_avp", "$avp(AVP_DST)") modparam("dispatcher", "grp_avp", "$avp(AVP_GRP)") modparam("dispatcher", "cnt_avp", "$avp(AVP_CNT)") modparam("dispatcher", "sock_avp", "$avp(AVP_SOCK)")
...

request_route {
# do checks , indialog etc ...
# dispatch destinations 
route(DISPATCH); 
}

# Dispatch requests
route[DISPATCH] {
     # round robin dispatching on gateways group '1'
     if(!ds_select_dst("1", "4")) {
         send_reply("404", "No destination");
         exit;
     }
     xlog("L_DBG", "--- SCRIPT: going to <$ru> via <$du>\n");
     t_on_failure("RTF_DISPATCH");
     route(RELAY);
     exit;
 }


# Try next destinations in failure route, except if session gets cancelled 
failure_route[RTF_DISPATCH] {
     if (t_is_canceled()) {
         exit;
     }
     # next DST - only for 500 or local timeout
     if (t_check_status("500") or (t_branch_timeout() and !t_branch_replied())) {
         if(ds_next_dst()) {
             t_on_failure("RTF_DISPATCH");
             route(RELAY);
             exit;
         }
     }
 }

More on Kamailio Call routing and Control

Kamailio basic setup as proxy for FreeSWITCH

I have added a detailed description of how kamalio based SIP servers can function as proxy / SBC for SIP Application server which could be an enterprise PBX or a full fledged Telecom Application Server such as Asterix , Freeswitch , Oracle Weblogic, telestax sip server etc

Kamailio basic setup as proxy for FreeSWITCH

References

Kamailio Architecture, Core and Modules


The post has been edited after publishing with updated content and Kamailio modules.

Kamailio® (successor of former OpenSER and SER) is an Open Source SIP Server released under GPLv2+, able to handle thousands of call setups per second. Kamailio can be used to build large platforms for VoIP and realtime communications – presence, WebRTC, Instant messaging and other applications.  Moreover, it can be easily used for scaling up SIP-to-PSTN gateways, PBX systems or media servers like Asterisk™, FreeSWITCH™ or SEMS.

kamailio.org

Introduction to Kamailio and its role in telephony is covered here. This article just describes the structure of the Kamailio source code, core and its associated modules which define the behaviour of the machine.

Kamailio architecture

Kamailio follows RFC 3261 for SIP and can act as Registrar, Location server, Proxy, Redirect or event Application Server based on its configuration. The modular architecture of Kamailio enables extending the core functionality in various directions using modules.

Kamailio’s modular architecture

Kamailio Core

At the Core, Kamailio is build for SIP meesage handling. The core includes:

  1. memory manager
  2. SIP message parser
  3. locking system
  4. DNS and transport layer management (UDP, TCP, TLS, SCTP)
  5. configuration file parser and interpreter
  6. stateless forwarding
  7. pseudo-variables and transformations engines
  8. RPC control interface API
  9. timer API

The internal libraries include:

  1. components from old Kamailio cores
  2. database abstraction layers (DB API v1 and v2)
  3. management interface (MI) API
  4. statistics engine

Kamailio Modules

There are over 150 modules in Kamailio dealing with add-on features that can be categorised into various functional areas such as

  • registrar and user location management
  • accounting, authorization and authentication
  • text and regular expression operations
  • stateless replying
  • stateful processing – SIP transaction management
StatefulStateless
stateful processing is per SIP transaction

Each SIP transaction will be kept in memory so that any replies, failures, or retransmissions can be recognized
Forwarding each msg in the dialog without any context.
Application understands the transactions , for example
– recognize if a new INVITE message is a resend
– know that 200 OK reponse belongs to the initial INVITE which it will be able to handle in an onreply_route[x] block.
it doesnt
know that the call is on-going.
However it can use callId to match INVITE and BYE.
Uses : manage call state , routing , call control like forward on busy, voicemailUses : Load distribution , proxying
  • SIP dialogs tracking – active calls management
  • instant messaging and presence extensions
  • RADIUS and LDAP support
  • SQL and no-SQL database connectors
  • MI and RPC transports
  • Enum, GeoIP API and CPL interpreter
  • topology hiding and NAT traversal
  • load balancing and least cost routing
  • asynchronous SIP request processing
  • interactive configuration file debugger
  • Lua, Perl, Python and Java SIP Servlet extensions

Routing a Call

A SIP server can either

  • direct the call and let the UA contact with each other directly or
  • force itself in the path by inserting a Route header in the SIP message (e record_route())

Locking Management

Kamailio provides a custom locking system its root element is a mutex semaphore, that can be set (locked) or unset (unlocked). The locking.h lib provides the functionality in C code.

Simple Locks APILock Set API
gen_lock_t
lock_alloc(…)
lock_dealloc(…)
lock_init(…)
lock_destroy(…)
lock_get(…)
lock_try(…)
lock_release(…)
gen_lock_set_t
lock_set_alloc(…)
lock_set_dealloc(…)
lock_set_init(…)
lock_set_destroy(…)
lock_set_get(…)
lock_set_release(…)

A problem however with locks is that processes can eat a lot of CPU. Locking issues also occur when locks are set but not unset resulting in no more SIP messages being processed. Hence Kamailio uses gdb with PID and get backtraces to the lock that wasnt released .

gdb /path/to/kamailio PID
gdb> bt

Memory Manager

Multliple processes run simultenously within kamailio server, hence it bears private and shared memeories to facailitate their working. One can increase shared memory using -m and private memory using -M command line option.

Private MemoryShared Memory
Memeory is specific per process.
No synchronization is needed to access structures allocated in it, such as varaibles not needed by other process or ones for temporary operations.

Private memory manager is accessed via mem/mem.h
The data stored in shared memeory is visible in all Kamailio modules such as, user location, TM structures for stateful processing, routing rules for the dispatcher etc.

Here locks are used to prvent race conditions.

Shared memeory can be acceses via mem/shm_mem.h.
pkg_malloc(…)
pkg_free(…)
pkg_realloc(…)
shm_malloc(…)
shm_free(…)
shm_realloc(…)

Problems in memory management can occur due to :

  • (-) Out of memory by allocating memory at runtime and not freeing it afterwards , called memory leaks
  • (-) writing more than allocated for that structure, called segmentation fault.

Data Structures

str
struct sip_uri
struct sip_msg
struct msg_start
struct hdr_field
struct to_body
struct via_body

SIP Parser

kamailio has an incremental parser. Lib file for SIP message parsing is parser/msg_parser.c with the corresponding header file parser/msg_parser.h.

parse_uri(…)
parse_msg(…)
parse_headers(…)
parse_to(…)


Since kamailio has a modular architecture with core components and modules to extend the functionality, this article will be discussing few of the essential modules in Kamailio. Specific modules Discussed in this Article :

  • UserLoc
  • Registrar
  • Dialog
  • UAC
  • XHTTP
  • Websocket

Few categories of modules discoverable via apt-cache search kamailio

  • kamailio – very fast and configurable SIP proxy
  • kamailio-autheph-modules – authentication using ephemeral credentials module for Kamailio
  • kamailio-berkeley-bin – Berkeley database module for Kamailio – helper program
  • kamailio-berkeley-modules – Berkeley database module for Kamailio
  • kamailio-carrierroute-modules – carrierroute module for Kamailio
  • kamailio-cnxcc-modules – cnxcc modules for Kamailio
  • kamailio-cpl-modules – CPL module (CPL interpreter engine) for Kamailio
  • kamailio-dbg – very fast and configurable SIP proxy [debug symbols]
  • kamailio-dnssec-modules – contains the dnssec module
  • kamailio-erlang-modules – earlang modules for Kamailio
  • kamailio-extra-modules – extra modules for Kamailio
  • kamailio-geoip-modules – contains the geoip module
  • kamailio-ims-modules – IMS module for Kamailio
  • kamailio-java-modules – contains the app_java module
  • kamailio-json-modules – Json parser and jsonrpc modules for Kamailio
  • kamailio-kazoo-modules – kazoo modules for Kamailio
  • kamailio-ldap-modules – LDAP modules for Kamailio
  • kamailio-lua-modules – contains the app_lua module
  • kamailio-memcached-modules – interface to memcached server
  • kamailio-mono-modules – contains the app_mono module
  • kamailio-mysql-modules – MySQL database connectivity module for Kamailio
  • kamailio-outbound-modules – Outbound module for Kamailio
  • kamailio-perl-modules – Perl extensions and database driver for Kamailio
  • kamailio-postgres-modules – PostgreSQL database connectivity module for Kamailio
  • kamailio-presence-modules – SIMPLE presence modules for Kamailio
  • kamailio-purple-modules – Provides the purple module, a multi-protocol IM gateway
  • kamailio-python-modules – contains the app_python module
  • kamailio-radius-modules – RADIUS modules for Kamailio
  • kamailio-redis-modules – Redis database connectivity module for Kamailio
  • kamailio-sctp-modules – sctp module for Kamailio
  • kamailio-snmpstats-modules – SNMP AgentX subagent module for Kamailio
  • kamailio-sqlite-modules – SQLite database connectivity module for Kamailio
  • kamailio-tls-modules – contains the TLS kamailio transport module
  • kamailio-unixodbc-modules – unixODBC database connectivity module for Kamailio
  • kamailio-utils-modules – Provides a set utility functions for Kamailio
  • kamailio-websocket-modules – Websocket module for kamailio
  • kamailio-xml-modules – XML based extensions for Kamailio’s Management Interface
  • kamailio-xmpp-modules – XMPP gateway module for Kamailio

The first set under explanation is Usrloc and Register module which take care of user persistance in Database and handling an incoming register request with authentication and validation.

The first set under explanation is Usrloc and Register module which take care of user persistance in Database and handling an incoming register request with authentication and validation.

Usrloc Module

Keeps a user location table and provides access to the table for other modules.

Parameters:

  • nat_bflag
  • user_column
  • domain_column
  • contact_column
  • expires_column
  • q_column
  • callid_column
  • cseq_column
  • methods_column
  • flags_column
  • cflags_column
  • user_agent_column
  • received_column
  • socket_column
  • path_column
  • ruid_column
  • instance_column
  •  server_id_column
  • connection_id_column
  • keepalive_column
  • partition_column
  • use_domain
  • desc_time_order
  • timer_interval
  • db_url
  • db_mode
  • db_load
  • db_insert_update
  • matching_mode
  • cseq_delay
  •  fetch_rows
  • hash_size
  • preload
  • db_update_as_insert
  • db_check_update
  • timer_procs
  • xavp_contact
  • db_ops_ruid (int)
  • handle_lost_tcp (int)
  • close_expired_tcp (int)
  • expires_type (int)
  • db_raw_fetch_type (int)
  • db_insert_null (int)
  • skip_remote_socket (int)
  • db_timer_clean (int)
  • server_id_filter (int)

RPC Commands

ul.dump
ul.lookup table AOR
ul.rm table AOR
ul.rm_contact table AOR contact
ul.flush
ul.add
ul.db_users
ul.db_contacts
ul.db_expired_contacts

Statistics

users
contacts
expires
registered_users

Functions

  • ul_register_domain(name)
  • ul_insert_urecord(domain, aor, rec)
  • ul_delete_urecord(domain, aor)
  • ul_delete_urecord_by_ruid(domain, ruid)
  • ul_get_urecord(domain, aor)
  • ul_lock_udomain(domain)
  • ul_unlock_udomain(domain)
  • ul_release_urecord(record)
  • ul_insert_ucontact(record, contact, expires, q, callid, cseq, flags, cont, ua, sock)
  • ul_delete_ucontact (record, contact)
  • ul_get_ucontact(record, contact)
  • ul_get_all_ucontacts (buf, len, flags)
  • ul_update_ucontact(contact, expires, q, callid, cseq, set, res, ua, sock)
  • ul_bind_ursloc( api )
  • ul_register_ulcb(type ,callback, param)
  • ul_get_num_users()


Registrar Module

SIP registration processing logic can be defined here.

Path support – off , lazy , strict ( RFC 3327)

GRU ( Globally Routbale User agent URIs)  support –  public , temporary ( RFC 5627)

Dependencies :

  • usrloc – User Location Module.
  • sl – Stateless Replies.

Parameters :

  • default_expires
  • default_expires_range
  • expires_range
  • min_expires
  • max_expires
  • default_q
  • realm_prefix
  • append_branches
  • aor_avp (str)
  • case_sensitive
  • received_avp (str)
  • received_param
  • max_contacts
  • retry_after
  • sock_flag
  • sock_hdr_name
  • method_filtering
  • use_path
  • path_mode
  • path_use_received
  • path_check_local
  • reg_callid_avp
  • xavp_cfg
  • xavp_rcd
  • gruu_enabled
  • outbound_mode
  • regid_mode
  • flow_timer
  • contact_max_size

Functions :

  • save(domain, [, flags [, uri]])
  • lookup(domain [, uri])
  • lookup_branches(domain)
  • registered(domain [, uri [, match_option [, match_action]]])
  • add_sock_hdr(hdr_name)
  • unregister(domain, uri[, ruid])
  • reg_fetch_contacts(domain, uri, profile)
  • reg_free_contacts(profile)

Event Routes :

event_route[usrloc:contact-expired]

Statistics :

max_expires
max_contacts
default_expires
accepted_regs
rejected_regs

Dialog Module

We know that dialog represent the p2p relationship between 2 sip clients and contains sequence of transactions along with routing information and facilitate sequencing of more messages. Dialog module keeps track of current dialogs also provides API support. It can be loaded and used as

loadmodule "dialog.so"
..

# ---- dialog params ----
modparam("dialog", "enable_stats", 1)
modparam("dialog", "dlg_flag", 4)
modparam("dialog", "event_callback", "ksr_dialog_event")

Parameters :

  • enable_stats
  • hash_size
  • rr_param
  • dlg_flag
  • timeout_avp
  • default_timeout
  • early_timeout
  • noack_timeout
  • end_timeout
  • dlg_extra_hdrs
  • dlg_match_mode
  • detect_spirals
  • db_url
  • db_mode
  • db_update_period
  • db_fetch_rows
  • db_skip_load
  • table_name
  • call_id_column
  • from_uri_column
  • from_tag_column (string)
  • to_uri_column (string)
  • to_tag_column (string)
  • from_cseq_column (string)
  • to_cseq_column (string)
  • from_route_column (string)
  • to_route_column (string)
  • from_contact_column (string)
  • to_contact_column (string)
  • from_sock_column (string)
  • to_sock_column (string)
  • h_id_column (string)
  • h_entry_column (string)
  • state_column (string)
  • start_time_column (string)
  • timeout_column (string)
  • sflags_column (string)
  • toroute_name_column (string)
  • vars_table_name
  • vars_h_id_column
  • vars_h_entry_column
  • vars_key_column
  • vars_value_column (string)
  • profiles_with_value (string)
  • profiles_no_value (string)
  • bridge_controller (string)
  • bridge_contact (string)
  • initial_cbs_inscript (int)
  • send_bye (int)
  • wait_ack (int)
  • ka_timer (int)
  • ka_interval (int)
  • ka_failed_limit (int)
  • timeout_noreset (int)
  • timer_procs (int)
  • enable_dmq (int)
  • track_cseq_updates (int)
  • lreq_callee_headers (string)
  • event_callback (str) – name of the function in the kemi configuration file (embedded scripting language such as Lua, Python, …) to be executed instead of event_route[…] blocks.

The callback function receives a string parameter with the name of the event, the values are: ‘dialog:start’, ‘dialog:end’, ‘dialog:failed’. It is also executed if ‘$dlg_ctx(timeout_route)’ is set

function ksr_dialog_event(evname)
     KSR.info("===== dialog module triggered event: " .. evname .. "\n");
     if (evname == "dialog:end") or (evname == "dialog:failed") then
         logger.log("info", "in dialog event callback with event-name - " .. evname .. " start CDR process ")
         if not cdrProcess.post() then
             logger.log("err", "Failed")
         else
             logger.log("info", "successfully posted")
             core.exit()
         end
     end
 end
  • h_id_start (int)
  • h_id_step (int)

Functions :

  • set_dlg_profile(profile,[value])
  • unset_dlg_profile(profile,[value])
  • is_in_profile(profile,[value])
  • get_profile_size(profile,[value],size)
  • dlg_isflagset(flag)
  • dlg_setflag(flag)
  • dlg_resetflag(flag)
  • dlg_bye(side)
  • dlg_refer(side, address)
  • dlg_manage()
  • dlg_bridge(from, to, op)
  • dlg_get(callid, ftag, ttag)
  • is_known_dlg()
  • dlg_set_timeout(timeout [, h_entry, h_id])
  • dlg_set_timeout_by_profile(profile, [value], timeout)
  • dlg_set_property(attr)
  • dlg_remote_profile(cmd, profile, value, uid, expires)
  • dlg_set_ruri()

Statistics :

active_dialogs
early_dialogs
processed_dialogs
expired_dialogs
failed_dialogs

RPC Commands :

dlg.list
dlg.list_ctx
dlg.dlg_list
dlg.dlg_list_ctx
dlg.terminate_dlg
dlg.end_dlg
dlg.profile_get_size
dlg.profile_list
dlg.bridge_dlg

Exported Variables :

$DLG_count
$DLG_status
$DLG_lifetime
$dlg(…)
$dlg_ctx(…)
$dlg_var(key)

Event Routes :

event_route[dialog:start] , event_route[dialog:end] , event_route[dialog:failed]

UAC module

This set deals with RTP proxy and RTP engine which are used for proxing media streams via kamailio server.


This set deals with HTTP and Websocket adapters to handle web based ( such as webRTC) calls on kamailio.

XHTTP

Provides basic HTTP/1.0 server functionality. SIP requires a Content-Length header for TCP transport. But most HTTP clients do not set the content length for normal GET requests. Therefore, the core must be configured to allow incoming requests without content length header:

tcp_accept_no_cl=yes

Parameters :

  • url_skip : if there is a match , event route is not executed
    modparam(“xhttp”, “url_skip”, “^/RPC2”)
  • url_match : if there is no match , event route is not executed
    modparam(“xhttp”, “url_match”, “^/sip/”)
  • event_Callback : function in the kemi configuration file (embedded scripting language such as Lua, Python) to be executed instead of event_route[xhttp:request] block
 modparam("xhttp", "event_callback", "ksr_xhttp_event")
 
// and the event callback function implemented in Lua
 function ksr_xhttp_event(evname)
     KSR.info("===== xhttp module triggered event: " .. evname .. "\n");
     return 1;
 end

Function

  • xhttp_reply(code, reason, ctype, body) – Send back a reply with content-type and body.
event_route[xhttp:request] {
    xhttp_reply("200", "OK", "" , "");
    xhttp_reply("403", "Forbidden", "", "");
}

Event Routes

xhttp:request
The event route is executed when a new HTTP request is received.

event_route[xhttp:request] {
    xhttp_reply("200", "OK", "text/html", "<html><body>OK</body></html>");
}

Websocket Module

This module provides websocket ( ws and wss) support to kamailio ( RFC 6455). Handles handshaking, management (including connection keep-alive), and framing for the SIP and MSRP WebSocket sub-protocols (RFC 7118 and RFC 7977).

References :

[1] SER Getting started : https://kamailio.org/docs/ser-getting-started/SER-GettingStarted.pdf

[2] Registrar module in kamailio http://kamailio.org/docs/modules/stable/modules/registrar.html

Telephony Solutions with Kamailio

  • Features
    • RFC3261 compliant
    • Customisable and Felxible – Modular Architecture
    • Call routing and control
    • NAT traversal
    • Data store integration
    • Transport Layers supported
    • Asynchronous TCP, UDP and SCTP
    • Secure Communication ( TLS + AAA)
    • IP and DNS
    • Accounting
    • External Integration
    • Rich Communication Services ( RCS)
  • Kamailio (OpenSER) SIP Server v4.3- default configuration script
    • debug
    • authentication
    • persistent user location
    • presence
    • nat traversal
    • RTPProxy
    • PSTN gateway
    • Block 3XX redirect repliest
    • Database aliases lookup
    • speed dial
    • multi-domain support
    • XMLRPC
    • anti-flood detection
  • Routing Logic
    • WITHINDLG
    • LOCATION
    • PRESENCE
    • AUTH
    • NATDETECT
    • NATMANAGE
    • DLGURI
    • SIPOUT
    • PSTN
    • XMLRPC
    • TOVOICEMAIL
    • MANAGE_BRANCH
    • MANAGE_REPLY
    • MANAGE_FAILURE
  • kamctl
  • kamdbctl
  • Kamctlrc
  • Kamailio.cfg
  • kamcmd
  • siremis

Kamailio™ (former OpenSER) is an Open Source SIP Server released under GPL.

Kamailio primarily acts as a SIP server for VOIP and telecommunications platforms under various roles and can handle load of hight CPS ( Calls per second ) with custom call routing logic with the help of scripts.

Rich features set suiting to telephony domain that includes IMS extensions for VoLTE; ENUM; DID and least cost routing; load balancing; routing fail-over; Json and XMLRPC control interface, SNMP monitoring.

To integrate with a carrier grade telecom network as SBC / gateway / inbound/outbound proxy , it can act as IPv4-IPv6 gateway , UDP/TCP/SCTP/WS translator and even had NAT and anti DOS attack support .

Features

If Kamailio is the central to the VoIP system it can also perform accounting with rich database extensions Mysql PostgreSQL UnixODBC Berkeley DB Oracle Redis, MongoDB Cassandra etc

  • Kamailio is SIP (RFC3261) compliant

It can work as Registrar or Location server. For SIP call logic it can become a Proxy or SIP Application server. Can also act like a Redirect, Dispatcher or simply a SIP over websocket server.

  • Customisable and Felxible

It can be embedded to devices as the binary file is small size. Additional modules can be appended for more functions with the same core.

Modular architecture – core, internal libraries , module interface and ability to extend functionality with scripts such as LUA, Kamailio can be readily integrated to a VOIP ecosystem.

Kamailio Architecture , Core and Modules.

  • Call routing and control with Scripting and programming

Offers stateless and transactional stateful SIP Proxy processing ( suited for inbound gateways ) and serial and parallel forking.

Kamailio Transaction management describes branches, serial and paralle forking and TM module.

Also NAT traversal support for SIP and RTP traffic ( suited to be WebRTC server ) . Read more about kamailio DNS subsystem management , load balancing , NAT and NAThelper modules in Kamailio DNS and NAT.

Kamailio as a WebRTC SIP Server with mdules websocket , TLS , NATHelper  and JSSIP integration.

Among other features it offers load balancing with many distribution algorithms and failover support, flexible least cost routing , routing failover and replication for High Availability (HA).

Can be readily integrated with external databases, caches, notification system ( SNS , APNS , GCM ), VoIP monitors, CDR processors, API systems etc for  efficient call processing.

  • Transport Layers supported 
    • UDP, TCP, TLS and SCTP
    • IPv4 and IPv6
    • gateways via (IPv4 to IPv6, UDP to TLS, a.s.o.)
    • SCTP multi-homing and multi-streaming
    • WebSocket for WebRTC 
  • Asynchronous TCP, UDP and SCTP

Asynchronous SIP message processing and  inter-process message queues communication system

  • Secure Communication ( TLS  + AAA)
    • Digest SIP User authentication
    • Authorization via ACL or group membership
    • IP and Network authentication
    • TLS support for SIP signaling
    • transparent handling of SRTP for secure audio
    • TLS domain name extension support
    • authentication and authorization against database (MySQL, PostgreSQL, UnixODBC, BerkeleyDB, Oracle, text files), RADIUS and DIAMETER

Kamailio Security here for snaity, ACL permission , firewall , flood detection , topology hiding and digests.

  • IP and DNS
    • support for SRV and NAPTR DNS lookups
    • SRV DNS failover
    • DNSsec support
    • ENUM support
    • internal DNS caching system – avoid DNS blocking
    • IP level Blacklists
    • multi-homed and multi-domain support
    • topology hiding – hide IP addresses in SIP headers to protect your network architecture
  • Accounting

Kamailio gives event based and configurable accounting data details. Can show multi-leg call accounting ( A leg to B leg ). It can store to database, Radius or Diameter based on module used . Has a prepaid engine.

  • External Interaction

text-based management interface via FIFO file, udp, xmlrpc and unix sockets.

RPC control interface – via XMLRPC, UDP or TCP

  • Rich Communication Services (RCS)
    • SIP SIMPLE Presence Server (rich presence)
    • Presence User Agent ( SUBSCRIBE , NOTIFY and PUBLSH)
    • XCAP client capabilities and Embedded XCAP Server
    • Presence DialogInfo support – SLA/BLA
    • Instant Messaging ( IM) 
    • Embedded MSRP relay
  • Monitoring and Troubleshooting

Support for SNMP – interface to Simple Network Management Protocol.  For Debugging it has config debugger , remote control via XMLRPC and error message logging system .Provides internal statistics exported via RPC and SNMP.

  • Extensibility APIs

The supported  one are Perl  , Java SIP Servlet Application Interface  , Lua  , Managed Code (C#) , Python.

Lua Scripts for kamailio Routing – KEMI interpreter , function , routing logic, PV variables

  • Multiple Database Backends

(MySQL, PostgreSQL, SQLite, UnixODBC, BerkeleyDB, Oracle, text files) and other database types which have unixodbc drivers. ‘

It can have connections pool and different backends  be used at same time (e.g., accounting to Oracle and authorization against MySQL).

Has connectors for Memcached, Redis , MongoDB and Cassandra no-SQL backends

  • Interconnectivity

Acts as SIP to PSTN gateway and gateway to sms or xmpp and other IM services. Has Interoperability with SIP enabled devices and applications such as SIP phones (Snom, Cisco, etc.), Media Servers (Asterisk, FreeSwitch, etc). More details on Kamailio as Inbound/Outbound proxy or Session Border Controller (SBC) here

  • IMS
    • diameter support and authentication
    • I-CSCF, P-CSCF, S-CSCF
    • charging, QOS, ISC
  • Miscellaneous
    • CPL – Call Processing Language (RFC3880)
    • Internal generic caching system
    • Memcached connector
    • Redis NoSQL database connector
    • CLI – kamctl and sercmd
    • Web Management Interface: Siremis
    • SIP-T and SIP-I
    • music on hold queue
    • message body compression/decompression (gzip-deflate)

Scalability with Kamailio system

  • Kamailio can run on embedded systems, with limited resources – the performances can be up to hundreds of call setups per second
  • used as load balancer in stateless mode, Kamailio can handle over 5000 call setups per second
  • on systems with 4GB memory, Kamailio can serve a population over 300 000 online subscribers
  • system can easily scale by adding more Kamailio servers
  • Kamailio can be used in geographic distributed VoIP platforms
  • Kamailio least-cost-routing scales up to millions of routing rules
  • straightforward failover and redundancy

Start Kamalio

service kamailo start

Kamaiio Logs tailing

tail -f /var/log/kamailio

To Check if Kamailio instance is running

>ps -ax | grep “kamailio”
 57411 ?        S      0:01 /usr/sbin/kamailio -f /etc/kamailio/kamailio.cfg -P /var/run/kamailio/kamailio.pid -m 4096 -M 128 -u root -g root

Read RTP engine on kamailio SIP server which focuses on setting up sipwise rtpegine to proxy rtp traffic from kamailio app server. Also daemon and kernal modules. ,transcoding , in-kernel packet forwarding , ngcontrol protocol etc.

Installation and Configuration

Installing kamailio from git repo

clone kamailio from their github https://github.com/kamailio/kamailio

goto desired branch. The contents of clonned folder are

COPYING    ChangeLog    INSTALL    ISSUES       Makefile        README README.md       doc       etc      misc       pkg      test      utils

run ‘make cfg’ which compiles using gcc abd created ‘src’ folder with contents

make cfg

Makefile Makefile.defs Makefile.groups Makefile.modules Makefile.rules Makefile.sources Makefile.utils core main.c modules.lst Makefile.cfg Makefile.dirs Makefile.libs Makefile.radius Makefile.shared Makefile.targets config.mak lib modules

Edit modules.lst to enable db_mysql and dialplan module

vim src/modules.lst

//the list of extra modules to compile
include_modules= db_mysql

Use ‘make’ command followed by make all. Make sure to have gcc bison and flex installed

make cfg
make all

Alternativey on debian system , use readymade measures like

apt install mysql-server
apt install kamailio kamailio-mysql-modules

To validate and verify the location of kamillio use ‘which kamailio’ which returns /usr/sbin/kamailio

For Modules installation, check all avaible modules with command ‘apt search kamailio’and to install a new module such as websockt module use ‘apt install kamailio-websocket-modules’

Database access : After installaing kamailio , edit the kamailio.cfg file in /etc/kamailio to set the reachabe SIP domain, database engine, username/password etc to connect to databaseand enable the kamdbctl script to run and create users and tables, etc.

SIP_DOMAIN=kamailio.org

SIP_DOMAIN=17.3.4.5

chrooted directory

$CHROOT_DIR=”/path/to/chrooted/directory”

database type: MYSQL, PGSQL, ORACLE, DB_BERKELEY, DBTEXT, or SQLITE by default none is loaded

DBENGINE=MYSQL

Run kamdbctl to create users and database now

kamdbctl create

the database created is name kamailio and its tables are

+---------------------+
 | Tables_in_kamailio  |
 +---------------------+
 | acc                 |
 | acc_cdrs            |
 | active_watchers     |
 | address             |
 | aliases             |
 | carrier_name        |
 | carrierfailureroute |
 | carrierroute        |
 | cpl                 |
 | dbaliases           |
 | dialog              |
 | dialog_vars         |
 | dialplan            |
 | dispatcher          |
 | domain              |
 | domain_attrs        |
 | domain_name         |
 | domainpolicy        |
 | globalblacklist     |
 | grp                 |
 | htable              |
 | imc_members         |
 | imc_rooms           |
 | lcr_gw              |
 | lcr_rule            |
 | lcr_rule_target     |
 | location            |
 | location_attrs      |
 | missed_calls        |
 | mohqcalls           |
 | mohqueues           |
 | mtree               |
 | mtrees              |
 | pdt                 |
 | pl_pipes            |
 | presentity          |
 | pua                 |
 | purplemap           |
 | re_grp              |
 | rls_presentity      |
 | rls_watchers        |
 | rtpproxy            |
 | sca_subscriptions   |
 | silo                |
 | sip_trace           |
 | speed_dial          |
 | subscriber          |
 | trusted             |
 | uacreg              |
 | uid_credentials     |
 | uid_domain          |
 | uid_domain_attrs    |
 | uid_global_attrs    |
 | uid_uri             |
 | uid_uri_attrs       |
 | uid_user_attrs      |
 | uri                 |
 | userblacklist       |
 | usr_preferences     |
 | version             |
 | watchers            |
 | xcap                |
 +---------------------+

Kamctlrc

The Kamailio configuration file for the control tools. Can set variables used in the kamctl and kamdbctl setup scripts. Per default all variables here are commented out, the control tools will use their internal default values. This file lets to edit  SIP domain, the database engine, username/password/ to connect to database, etc.

## your SIP domain
 SIP_DOMAIN=1.1.1.1
## chrooted directory
# $CHROOT_DIR="/path/to/chrooted/directory"
## database type: MYSQL, PGSQL, ORACLE, DB_BERKELEY, DBTEXT, or SQLITE
# by default none is loaded

# If you want to setup a database with kamdbctl, you must at least specify this parameter.

DBENGINE=MYSQL
## database host
# DBHOST=localhost
# DBPORT=3306
## database name (for ORACLE this is TNS name)
# DBNAME=kamailio
# database path used by dbtext, db_berkeley or sqlite
# DB_PATH="/usr/local/etc/kamailio/dbtext"
database read/write user
# DBRWUSER="kamailio"
## password for database read/write user
# DBRWPW="kamailiorw"

database read only user

# DBROUSER="kamailioro"
## password for database read only user
# DBROPW="kamailioro"
## database access host (from where is kamctl used)
# DBACCESSHOST=192.168.0.1

database super user (for ORACLE this is ‘scheme-creator’ user)

# DBROOTUSER="root"
## password for database super user
## - important: this is insecure, targeting the use only for automatic testing
## - known to work for: mysql
# DBROOTPW="dbrootpw"
## database character set (used by MySQL when creating database)
#CHARSET="latin1"
## user name column
# USERCOL="username"
# SQL definitions

# If you change this definitions here, then you must change them
# in db/schema/entities.xml too.
# FIXME
# FOREVER="2030-05-28 21:32:15"
# DEFAULT_Q="1.0"
# Program to calculate a message-digest fingerprint
# MD5="md5sum"
# awk tool
# AWK="awk"
# gdb tool
# GDB="gdb"

# If you use a system with a grep and egrep that is not 100% gnu grep compatible,
# e.g. solaris, install the gnu grep (ggrep) and specify this below.

grep tool
# GREP="grep"
# egrep tool
# EGREP="egrep"
# sed tool
# SED="sed"
# tail tool
# LAST_LINE="tail -n 1"
# expr tool
# EXPR="expr"

Describe what additional tables to install. Valid values for the variables below are yes/no/ask. With ask (default) it will interactively ask the user for an answer, while yes/no allow for automated, unassisted installs.

#If to install tables for the modules in the EXTRA_MODULES variable.

# INSTALL_EXTRA_TABLES=ask
# If to install presence related tables.
# INSTALL_PRESENCE_TABLES=ask
# If to install uid modules related tables.
# INSTALL_DBUID_TABLES=ask

 Define what module tables should be installed.

If you use the postgres database and want to change the installed tables, then you must also adjust the STANDARD_TABLES or EXTRA_TABLES variable accordingly in the kamdbctl.base script.

standard modules

# STANDARD_MODULES="
standard acc lcr domain group permissions registrar usrloc msilo
alias_db uri_db speeddial avpops auth_db pdt dialog dispatcher
dialplan"

extra modules

# EXTRA_MODULES="imc cpl siptrace domainpolicy carrierroute userblacklist htable purple sca"
type of aliases used: DB - database aliases; UL - usrloc aliases
- default: none , ALIASES_TYPE="DB"
control engine: RPCFIFO
 - default RPCFIFO
CTLENGINE="RPCFIFO"

## path to FIFO file for engine RPCFIFO
# RPCFIFOPATH="/var/run/kamailio/kamailio_rpc_fifo"

## check ACL names; default on (1); off (0)
# VERIFY_ACL=1

## ACL names-if VERIFY_ACL is set,only the ACL names from below list are accepted
# ACL_GROUPS="local ld int voicemail free-pstn"

## check if user exists (used by some commands such as acl);
## - default on (1); off (0)

# VERIFY_USER=1

## verbose - debug purposes - default '0'
# VERBOSE=1

## do (1) or don't (0) store plaintext passwords
## in the subscriber table - default '1'

# STORE_PLAINTEXT_PW=0

Kamailio START Options

PID file path – default is: /var/run/kamailio/kamailio.pid

# PID_FILE=/var/run/kamailio/kamailio.pid

Extra start options – default is: not set

# example: start Kamailio with 64MB share memory: STARTOPTIONS="-m 64"
# STARTOPTIONS=

Kamailio.cfg

config files are used to customize and deploy SIP services since each and every SIP packet is route based on policies specified in conf file ( routing blocks ). Location when installed from source – /usr/local/etc/kamailio/kamailio.cfg , when installed from package – /etc/kamailio/kamailio.cfg

The features in config file :-

  • User authentication

Kamailio doesn’t have user authentication by default , so to enable it one must

#!define WITH_MYSQL
#!define WITH_AUTH

kamdbctl tool is to be used for creating and managing the database.

kamdbctl create

Kamctl is used for adding subscriber information and password.

kamctl add altanai1 123
mysql: [Warning] Using a password on the command line interface can be insecure.
MySQL password for user 'kamailio@localhost': 
mysql: [Warning] Using a password on the command line interface can be insecure.
new user 'altanai1' added

More details in Tools section below .

  • IP authorization
  • accounting
  • registrar and location servicesTo have persisant location enabled so that records are not lost once kamailio are restarted , we need to save it to database and reload when restarting
#!define WITH_USRLOCDB
  • attacks detection and blocking (anti-flood protection)
  • NAT traversal

requires RTP proxy for RTP relay. NAT traversal support can be set by

#!define WITH_NAT
  • short dialing on server
  • multiple identities (aliases) for subscribers
  • multi-domain support
  • routing to a PSTN gateway
  • routing to a voicemail server
  • TLS encryption
  • instant messaging (pager mode with MESSAGE requests)
  • presence services

Kamailio (OpenSER) SIP Server v4.3- default configuration script

Several features can be enabled using ‘#!define WITH_FEATURE’ directives:

debugger params

define WITH_DEBUG
...
#!ifdef WITH_DEBUGs
modparam("debugger", "cfgtrace", 1)
#!endif

To run in debug mode: 

#!ifdef WITH_DEBUG
 debug=4
 log_stderror=yes
#!else
 debug=2
 log_stderror=no
#!endif

memdbg=5
memlog=5
log_facility=LOG_LOCAL0
fork=yes
children=4

To enable mysql: 

define WITH_MYSQL
...
#!ifdef WITH_MYSQL
loadmodule "db_mysql.so"
#!endif

To enable authentication

enable mysql
define WITH_AUTH

To enable IP authentication execute: enable mysql ,  enable authentication ,  define WITH_IPAUTH and  add IP addresses with group id ‘1’ to ‘address’ table.

To enable persistent user location :

enable mysql
define WITH_USRLOCDB

To enable presence server :

enable mysql
define WITH_PRESENCE

To enable nat traversal :

define WITH_NAT

RTPProxy

Install RTPProxy: http://www.rtpproxy.org

start RTPProxy:

rtpproxy -l your_public_ip -s udp:localhost:7722

option for NAT SIP OPTIONS keepalives: WITH_NATSIPPING

PSTN gateway

To enable PSTN gateway routing :

define WITH_PSTN

set the value of pstn.gw_ip

check route[PSTN] for regexp routing condition.

#!ifdef WITH_PSTN
pstn.gw_ip = "" desc "PSTN GW Address"
pstn.gw_port = "" desc "PSTN GW Port"
#!endif

Database aliases lookup

To enable database aliases lookup :

enable mysql
define WITH_ALIASDB

To enable speed dial lookup execute:

enable mysql
define WITH_SPEEDDIAL

To enable multi-domain support execute:

enable mysql
define WITH_MULTIDOMAIN
...
 #!ifdef WITH_MULTIDOMAIN# - the value for 'use_domain' parameter
 #!define MULTIDOMAIN 1
 #!else
 #!define MULTIDOMAIN 0
 #!endif

To enable TLS support execute:

adjust CFGDIR/tls.cfg as needed
define WITH_TLS

To enable XMLRPC support :

define WITH_XMLRPC

adjust route[XMLRPC] for access policy

To enable anti-flood detection execute:

adjust pike and htable=>ipban settings as needed (default is block if more than 16 requests in 2 seconds and ban for 300 seconds)

define WITH_ANTIFLOOD
...
      
route[REQINIT] { 
  #!ifdef WITH_ANTIFLOOD 
  # flood detection from same IP and traffic ban      
  if(src_ip!=myself) {                
    if($sht(ipban=&gt;$si)!=$null) {             
       # ip is already blocked             
       xdbg("request from blocked IP - $rm from $fu (IP:$si:$sp)\n");             
       exit;        
     }        
     if (!pike_check_req()) {             
       xlog("L_ALERT","ALERT: pike blocking $rm from $fu (IP:$si:$sp)\n");             
       $sht(ipban=&gt;$si) = 1;             
       exit;        
     }     
  }     

  if($ua =~ "friendly-scanner") {        
    sl_send_reply("200", "OK");         
    exit;     
  } 
  #!endif 
.. 
}

To block 3XX redirect replies execute:

define WITH_BLOCK3XX

To enable VoiceMail routing :

define WITH_VOICEMAIL

set the value of voicemail.srv_ip and adjust the value of voicemail.srv_port

ifdef WITH_VOICEMAIL
# VoiceMail Routing on offline, busy or no answer
# - by default Voicemail server IP is empty to avoid misrouting
voicemail.srv_ip = "" desc "VoiceMail IP Address"
voicemail.srv_port = "5060" desc "VoiceMail Port"
#!endif

To enhance accounting execute:

enable mysql
define WITH_ACCDB

add following columns to database

define WITH_MYSQL
define WITH_AUTH
define WITH_USRLOCDB

#!ifdef ACCDB_COMMENT
ALTER TABLE acc ADD COLUMN src_user VARCHAR(64) NOT NULL DEFAULT '';
ALTER TABLE acc ADD COLUMN src_domain VARCHAR(128) NOT NULL DEFAULT '';
ALTER TABLE acc ADD COLUMN src_ip varchar(64) NOT NULL default '';
ALTER TABLE acc ADD COLUMN dst_ouser VARCHAR(64) NOT NULL DEFAULT '';
ALTER TABLE acc ADD COLUMN dst_user VARCHAR(64) NOT NULL DEFAULT '';
ALTER TABLE acc ADD COLUMN dst_domain VARCHAR(128) NOT NULL DEFAULT '';
ALTER TABLE missed_calls ADD COLUMN src_user VARCHAR(64) NOT NULL DEFAULT '';
ALTER TABLE missed_calls ADD COLUMN src_domain VARCHAR(128) NOT NULL DEFAULT '';
ALTER TABLE missed_calls ADD COLUMN src_ip varchar(64) NOT NULL default '';
ALTER TABLE missed_calls ADD COLUMN dst_ouser VARCHAR(64) NOT NULL DEFAULT '';
ALTER TABLE missed_calls ADD COLUMN dst_user VARCHAR(64) NOT NULL DEFAULT '';
ALTER TABLE missed_calls ADD COLUMN dst_domain VARCHAR(128) NOT NULL DEFAULT '';
#!endif

enhanced DB accounting

#!ifdef WITH_ACCDB
modparam("acc", "db_flag", FLT_ACC)
modparam("acc", "db_missed_flag", FLT_ACCMISSED)
modparam("acc", "db_url", DBURL)
modparam("acc", "db_extra",
"src_user=$fU;src_domain=$fd;src_ip=$si;"
"dst_ouser=$tU;dst_user=$rU;dst_domain=$rd")
#!endif

Include Local Config If Exists

import_file “kamailio-local.cfg”

Value defines – IDs used later in config #!ifdef WITH_MYSQL # – database URL – used to connect to database server by modules such # as: auth_db, acc, usrloc, a.s.o.

 #!ifndef DBURL 
 #!define DBURL "mysql://kamailio:kamailiorw@localhost/kamailio" 
 #!define DBURL "mysql://kamailio:kamailiorw@localhost/kamailio" 
 # !endif



 # FLT_ - per transaction (message) flags 
 # FLB_ - per branch flags
 #!define FLT_ACC 1
 #!define FLT_ACCMISSED 2
 #!define FLT_ACCFAILED 3
 #!define FLT_NATS 5
 #!define FLB_NATB 6
 #!define FLB_NATSIPPING 

 !substdef "!MY_IP_ADDR!!g"
 #!substdef "!MY_DOMAIN!!g" 
 #!substdef "!MY_WS_PORT!8080!g"
 #!substdef "!MY_WSS_PORT!4443!g"
 #!substdef "!MY_WS_ADDR!tcp:MY_IP_ADDR:MY_WS_PORT!g"
 #!substdef "!MY_WSS_ADDR!tls:MY_IP_ADDR:MY_WSS_PORT!g"&amp;amp;lt;/pre&amp;amp;gt;
 
 #!define WITH_WEBSOCKETS
disable TCP (default on)
#disable_tcp=yes
enable_sctp = 0

disable the auto discovery of local aliases based on reverse DNS on IPs (default on)
#auto_aliases=no

add local domain aliases
#alias=”sip.mydomain.com”

//port to listen
port=5060
#!ifdef WITH_TLS
enable_tls=yes
#!endif

Life time of TCP connection when there is no traffic – a bit higher than registration expires to cope with UA behind NAT

Modules Section

set paths to location of modules (to sources or installation folders)

#!ifdef WITH_SRCPATH
mpath="modules/"
#!else
mpath="/usr/lib/x86_64-linux-gnu/kamailio/modules/"
#!endif

Load modules

loadmodule "mi_fifo.so"
loadmodule "kex.so"
loadmodule "corex.so"
loadmodule "tm.so"
loadmodule "tmx.so"
loadmodule "sl.so"
loadmodule "rr.so"
loadmodule "pv.so"
loadmodule "maxfwd.so"
loadmodule "usrloc.so"
loadmodule "registrar.so"
loadmodule "textops.so"
loadmodule "siputils.so"
loadmodule "xlog.so"
loadmodule "sanity.so"
loadmodule "ctl.so"
loadmodule "cfg_rpc.so"
loadmodule "mi_rpc.so"
loadmodule "acc.so"

#!ifdef WITH_AUTH
loadmodule "auth.so"
loadmodule "auth_db.so"
#!ifdef WITH_IPAUTH
loadmodule "permissions.so"
#!endif
#!endif

#!ifdef WITH_ALIASDB
loadmodule "alias_db.so"
#!endif

#!ifdef WITH_SPEEDDIAL
loadmodule "speeddial.so"
#!endif

#!ifdef WITH_MULTIDOMAIN
loadmodule "domain.so"
#!endif

#!ifdef WITH_PRESENCE
loadmodule "presence.so"
loadmodule "presence_xml.so"
#!endif

#!ifdef WITH_NAT
loadmodule "nathelper.so"
loadmodule "rtpproxy.so"
#!endif

#!ifdef WITH_TLS
loadmodule "tls.so"
#!endif

#!ifdef WITH_ANTIFLOOD
loadmodule "htable.so"
loadmodule "pike.so"
#!endif

#!ifdef WITH_XMLRPC
loadmodule "xmlrpc.so"
#!endif

#!ifdef WITH_DEBUG
loadmodule "debugger.so"
#!endif

#!ifdef WITH_WEBSOCKETS
loadmodule "xhttp.so"
#loadmodule "websocket.so"
loadmodule "nathelper.so"
#!endif

modules params for mi_fifo,ctl, tm and rr

----- mi_fifo params -----
#modparam("mi_fifo", "fifo_name", "/var/run/kamailio/kamailio_fifo")

----- ctl params -----
#modparam("ctl", "binrpc", "unix:/var/run/kamailio/kamailio_ctl")

----- tm params -----
# auto-discard branches from previous serial forking leg
modparam("tm", "failure_reply_mode", 3)
# default retransmission timeout: 30sec
modparam("tm", "fr_timer", 30000)
# default invite retransmission timeout after 1xx: 120sec
modparam("tm", "fr_inv_timer", 120000)

----- rr params -----
# set next param to 1 to add value to ;lr param (helps with some UAs)
modparam("rr", "enable_full_lr", 0)
# do not append from tag to the RR (no need for this script)
modparam("rr", "append_fromtag", 0)

registrar params

modparam("registrar", "method_filtering", 1)
/* uncomment the next line to disable parallel forking via location */
# modparam("registrar", "append_branches", 0)
/* uncomment the next line not to allow more than 10 contacts per AOR */
#modparam("registrar", "max_contacts", 10)
# max value for expires of registrations
modparam("registrar", "max_expires", 3600)
# set it to 1 to enable GRUU
modparam("registrar", "gruu_enabled", 0)

usrloc params – enable DB persistency for location entries

#!ifdef WITH_USRLOCDB
modparam("usrloc", "db_url", DBURL)
modparam("usrloc", "db_mode", 2)
modparam("usrloc", "use_domain", MULTIDOMAIN)
#!endif

auth_db params

#!ifdef WITH_AUTH
modparam("auth_db", "db_url", DBURL)
modparam("auth_db", "calculate_ha1", yes)
modparam("auth_db", "password_column", "password")
modparam("auth_db", "load_credentials", "")
modparam("auth_db", "use_domain", MULTIDOMAIN)
#!endif

permissions params

#!ifdef WITH_IPAUTH
modparam("permissions", "db_url", DBURL)
modparam("permissions", "db_mode", 1)
#!endif

alias_db params

#!ifdef WITH_ALIASDB
modparam("alias_db", "db_url", DBURL)
modparam("alias_db", "use_domain", MULTIDOMAIN)
#!endif

speeddial params

#!ifdef WITH_SPEEDDIAL
modparam("speeddial", "db_url", DBURL)
modparam("speeddial", "use_domain", MULTIDOMAIN)
#!endif

domain params

#!ifdef WITH_MULTIDOMAIN
modparam("domain", "db_url", DBURL)
modparam("domain", "register_myself", 1)
#!endif

presence params

#!ifdef WITH_PRESENCE
modparam("presence", "db_url", DBURL)
!endif

presence_xml params

modparam("presence_xml", "db_url", DBURL)
modparam("presence_xml", "force_active", 1)

WITH_NAT

rtpproxy params

modparam("rtpproxy", "rtpproxy_sock", "udp:127.0.0.1:7722")

nathelper params

 modparam("nathelper", "natping_interval", 30)
 modparam("nathelper", "ping_nated_only", 1)
 modparam("nathelper", "sipping_bflag", FLB_NATSIPPING)
 modparam("nathelper", "sipping_from", "sip:pinger@kamailio.org") 

params needed for NAT traversal in other modules

modparam("nathelper|registrar", "received_avp", "$avp(RECEIVED)")
modparam("usrloc", "nat_bflag", FLB_NATB)

tls params

#!ifdef WITH_TLS
modparam("tls", "config", "/etc/kamailio/tls.cfg")
#!endif

pike params

#!ifdef WITH_ANTIFLOOD
modparam("pike", "sampling_time_unit", 2)
modparam("pike", "reqs_density_per_unit", 16)
modparam("pike", "remove_latency", 4)
#!endif

htable params

ip ban htable with autoexpire after 5 minutes

modparam("htable", "htable", "ipban=&gt;size=8;autoexpire=300;")

xmlrpc params

modparam("xmlrpc", "route", "XMLRPC");
modparam("xmlrpc", "url_match", "^/RPC")

nathelper params

#!ifdef WITH_WEBSOCKETS
modparam("nathelper|registrar", "received_avp", "$avp(RECEIVED)")
#!endif

Note: leaving NAT pings turned off here as nathelper is <em>only</em> being used for&nbsp;WebSocket connections. NAT pings are not needed as WebSockets have&nbsp;their own keep-alives.

Routing Logic

Main SIP request routing logic processing of any incoming SIP request starts with this route . Read more on Kamailio Call routing and Control

request_route { 
    # per request initial checks 
    route(REQINIT);

    #!ifdef WITH_WEBSOCKETS 
        if (nat_uac_test(64)) {     
            force_rport();     
            if (is_method("REGISTER")) {          
                fix_nated_register();     
            } else {         
                fix_nated_contact();         
                if (!add_contact_alias()) {  
                   xlog("L_ERR", "Error aliasing contact \n");             
                   sl_send_reply("400", "Bad Request");                    
                   exit;         
                }     
            } 
        } 
    #!endif

    # NAT detection 
    route(NATDETECT); 

    # CANCEL processing 
    if (is_method("CANCEL")) {     
        if (t_check_trans()) {         
            route(RELAY);     
        }     
        exit; 
    } 

    # handle requests within SIP dialogs 
    route(WITHINDLG); 

    ### only initial requests (no To tag) 
    # handle retransmissions 
    if(t_precheck_trans()) { 
        t_check_trans(); 
        exit; 
    } 
    t_check_trans(); 

    # authentication 
    route(AUTH); 

    # record routing for dialog forming requests (in case they are routed) - remove preloaded route headers 
    remove_hf("Route"); 

    if (is_method("INVITE|SUBSCRIBE")) 
        record_route(); 

    # account only INVITEs 
    if (is_method("INVITE")) {     
        setflag(FLT_ACC); # do accounting 
    }

    # dispatch requests to foreign domains 
    route(SIPOUT); 

    ### requests for my local domains 
    # handle presence related requests 
    route(PRESENCE); 

    # handle registrations 
    route(REGISTRAR); 

    if ($rU==$null) {     
        # request with no Username in RURI     
        sl_send_reply("484","Address Incomplete");     
        exit; 
    } 
    
    # dispatch destinations to PSTN 
    route(PSTN); 

    # user location service 
    route(LOCATION); 
}

Wrapper for relaying requests

enable additional event routes for forwarded requests – serial forking, RTP relaying handling, a.s.o.

route[RELAY] {    
    if (is_method("INVITE|BYE|SUBSCRIBE|UPDATE")) {         
        if(!t_is_set("branch_route")) 
            t_on_branch("MANAGE_BRANCH");    
    }   
      
    if (is_method("INVITE|SUBSCRIBE|UPDATE")) {         
        
        if(!t_is_set("onreply_route"))
            t_on_reply("MANAGE_REPLY");    
        }   

        if (is_method("INVITE")) {        
            if(!t_is_set("failure_route")) 
                t_on_failure("MANAGE_FAILURE");     
        }    
        
        if (!t_relay()) {         
            sl_reply_error();     
        } 
    exit; 
}

Per SIP request initial checks

route[REQINIT] { 

if (!mf_process_maxfwd_header("10")) {     
   sl_send_reply("483","Too Many Hops");     
   exit; 
} 

if(is_method("OPTIONS") &amp;&amp; uri==myself &amp;&amp;; $rU==$null) {     
   sl_send_reply("200","Keepalive");     
   exit; 
} 

if(!sanity_check("1511", "7")) {     
  xlog("Malformed SIP message from $si:$sp\n");     
  exit; 
} 
}

Handle requests within SIP dialogs

sequential request withing a dialog should take the path determined by record-routing

route[WITHINDLG] {
    if (!has_totag()) return;
    if (has_totag()) {
        if (loose_route()) {
            #!ifdef WITH_WEBSOCKETS
            if ($du == "") {
                if (!handle_ruri_alias()) {
                    xlog("L_ERR", "Bad alias <$ru>\n");
                    sl_send_reply("400", "Bad Request");
                    exit;
                }
            }
            #!endif
         }
     exit;
     }

    if (loose_route()) {
        route(DLGURI);
        if (is_method("BYE")) {
            setflag(FLT_ACC); # do accounting ...
            setflag(FLT_ACCFAILED); # ... even if the transaction fails
        }
        else if ( is_method("ACK") ) {
            # ACK is forwarded statelessy
            route(NATMANAGE);
        }
        else if ( is_method("NOTIFY") ) {
            # Add Record-Route for in-dialog NOTIFY as per RFC 6665.
            record_route();
        }
        route(RELAY);
        exit;
    }

    if (is_method("SUBSCRIBE") && uri == myself) {
        # in-dialog subscribe requests
        route(PRESENCE);
        exit;
    }

if ( is_method("ACK") ) {
    if ( t_check_trans() ) {
        # no loose-route, but stateful ACK;
        # must be an ACK after a 487
        # or e.g. 404 from upstream server
        route(RELAY);
        exit;
    } else {
        # ACK without matching transaction ... ignore and discard
        exit;
    }
}

sl_send_reply("404","Not here");
exit;
}

Handle SIP registrations

tbd

User location service

route[LOCATION] {

#!ifdef WITH_SPEEDDIAL
# search for short dialing - 2-digit extension
if($rU=~"^[0-9][0-9]$")
    if(sd_lookup("speed_dial"))
    route(SIPOUT);
#!endif

#!ifdef WITH_ALIASDB
# search in DB-based aliases
    if(alias_db_lookup("dbaliases"))
    route(SIPOUT);
#!endif

$avp(oexten) = $rU;
if (!lookup("location")) {
    $var(rc) = $rc;
    route(TOVOICEMAIL);
    t_newtran();
    switch ($var(rc)) {
        case -1:
        case -3:
           send_reply("404", "Not Found");
        exit;
        case -2:
           send_reply("405", "Method Not Allowed");
        exit;
    }
}

# when routing via usrloc, log the missed calls also
if (is_method("INVITE")) {
    setflag(FLT_ACCMISSED);
}

route(RELAY);
exit;
}

Presence processing

route[PRESENCE] {

if(!is_method("PUBLISH|SUBSCRIBE"))
return;

if(is_method("SUBSCRIBE") && $hdr(Event)=="message-summary") {
    route(TOVOICEMAIL);
    # returns here if no voicemail server is configured
    sl_send_reply("404", "No voicemail service");
    exit;
}

#!ifdef WITH_PRESENCE
if (!t_newtran()) {
    sl_reply_error();
    exit;
}

if(is_method("PUBLISH")) {
    handle_publish();
    t_release();
} else if(is_method("SUBSCRIBE")) {
    handle_subscribe();
    t_release();
}
exit;
#!endif

# if presence enabled, this part will not be executed
if (is_method("PUBLISH") || $rU==$null) {
    sl_send_reply("404", "Not here");
    exit;
}
return;
}

IP authorization and user authentication

route[AUTH] {
#!ifdef WITH_AUTH

#!ifdef WITH_IPAUTH
if((!is_method("REGISTER")) && allow_source_address()) {
    # source IP allowed
    return;
}
#!endif

if (is_method("REGISTER") || from_uri==myself)
{
    # authenticate requests
    if (!auth_check("$fd", "subscriber", "1")) {
        auth_challenge("$fd", "0");
        exit;
    }

    # user authenticated - remove auth header
    if(!is_method("REGISTER|PUBLISH"))
        consume_credentials();
    }

# if caller is not local subscriber, then check if it calls
# a local destination, otherwise deny, not an open relay here
    if (from_uri!=myself && uri!=myself) {
        sl_send_reply("403","Not relaying");
        exit;
    }

#!endif
return;
}

Caller NAT detection

route[NATDETECT] {
    #!ifdef WITH_NAT
    force_rport();

    if (nat_uac_test("19")) {
        if (is_method("REGISTER")) {
            fix_nated_register();
        } else {
            if(is_first_hop())
                set_contact_alias();
        }
        setflag(FLT_NATS);
    }
    #!endif
    return;
}

RTPProxy control and signaling updates for NAT traversal

route[NATMANAGE] {

#!ifdef WITH_NAT
if (is_request()) {
    if(has_totag()) {
        if(check_route_param("nat=yes")) {
            setbflag(FLB_NATB);
        }
     }
}
if (!(isflagset(FLT_NATS) || isbflagset(FLB_NATB)))
return;

rtpproxy_manage("co");

if (is_request()) {
    if (!has_totag()) {
        if(t_is_branch_route()) {
            add_rr_param(";nat=yes");
        } 
    }
}

if (is_reply()) {
    if(isbflagset(FLB_NATB)) {
        if(is_first_hop())
        set_contact_alias();
    }
}

#!endif
return;
}

URI update for dialog requests

route[DLGURI] {
    #!ifdef WITH_NAT
    if(!isdsturiset()) {
        handle_ruri_alias();
    }
    #!endif
    return;
}

Routing to foreign domains

route[SIPOUT] {
    if (uri==myself) return;
    append_hf("P-hint: outbound\r\n");
    route(RELAY);
    exit;
}

PSTN GW routing

route[PSTN] {
  #!ifdef WITH_PSTN
  # check if PSTN GW IP is defined
  if (strempty($sel(cfg_get.pstn.gw_ip))) {
    xlog("SCRIPT: PSTN routing enabled but pstn.gw_ip not defined\n");
    return;
  }

  # route to PSTN dialed numbers starting with '+' or '00'(international format)
  if(!($rU=~"^(\+|00)[1-9][0-9]{3,20}$"))
  return;

  # only local users allowed to call
  if(from_uri!=myself) {
    sl_send_reply("403", "Not Allowed");
    exit;
  }

  if (strempty($sel(cfg_get.pstn.gw_port))) {
     $ru = "sip:" + $rU + "@" + $sel(cfg_get.pstn.gw_ip);
  } else {
     $ru = "sip:" + $rU + "@" + $sel(cfg_get.pstn.gw_ip) + ":"
     + $sel(cfg_get.pstn.gw_port);
  }

  route(RELAY);
  exit;
  #!endif
  return;
}

XMLRPC routing

#!ifdef WITH_XMLRPC
route[XMLRPC] {
if ((method=="POST" || method=="GET") && (src_ip==127.0.0.1)) {
# close connection only for xmlrpclib user agents
    if ($hdr(User-Agent) =~ "xmlrpclib")
        set_reply_close();
        set_reply_no_connect();
        dispatch_rpc();
    exit;
}
send_reply("403", "Forbidden");
exit;
}
#!endif

Routing to voicemail server

route[TOVOICEMAIL] {
#!ifdef WITH_VOICEMAIL
if(!is_method("INVITE|SUBSCRIBE"))
return;

# check if VoiceMail server IP is defined
if (strempty($sel(cfg_get.voicemail.srv_ip))) {
    xlog("SCRIPT: VoiceMail routing enabled but IP not defined\n");
    return;
}
if(is_method("INVITE")) {
    if($avp(oexten)==$null)
        return;
    $ru = "sip:" + $avp(oexten) + "@" + $sel(cfg_get.voicemail.srv_ip)+ ":" + $sel(cfg_get.voicemail.srv_port);
} else {
    if($rU==$null)
        return;
    $ru = "sip:" + $rU + "@" + $sel(cfg_get.voicemail.srv_ip)
+ ":" + $sel(cfg_get.voicemail.srv_port);
}
route(RELAY);
exit;
#!endif

return;
}

Manage outgoing branches

branch_route[MANAGE_BRANCH] {
    xdbg("new branch [$T_branch_idx] to $ru\n");
    route(NATMANAGE);
}

Manage incoming replies

onreply_route[MANAGE_REPLY] {
    xdbg("incoming reply\n");
    if(status=~"[12][0-9][0-9]")
        route(NATMANAGE);
}

Manage failure routing cases

failure_route[MANAGE_FAILURE] {
    route(NATMANAGE);

    if (t_is_canceled()) {
        exit;
    }

    #!ifdef WITH_BLOCK3XX
    # block call redirect based on 3xx replies.
    if (t_check_status("3[0-9][0-9]")) {
        t_reply("404","Not found");
        exit;
    }
    #!endif

    #!ifdef WITH_VOICEMAIL
    # serial forking - route to voicemail on busy or no answer (timeout)
    if (t_check_status("486|408")) {
        $du = $null;
        route(TOVOICEMAIL);
        exit;
    }
    #!endif
}

Supports pseudo-variables to access and manage parts of the SIP messages and attributes specific to users and server. Transformations to modify existing pseudo-variables, accessing only the wanted parts of the information.

Already has over 1000 parameters, variables and functions exported to config file. Supports runtime update framework – to avoid restarting the SIP server when needing to change the config parameters.

kamctl

Manage kamailio from command line, providing lots of operations, such as adding/removing/updating SIP users, controlling the ACL for users, managing the records for LCR or load balancing, viewing registered users and internal statistics, etc. When needed to interact with Kamailio, it does it via FIFO file created by mi_fifo module.

kamdbctl

Helps to configure and database needed by kamailio . First we need to select a database engine in the kamctlrc file by DBENGINE parameter .

Valid values are: MYSQL, PGSQL, ORACLE, DB_BERKELEY, DBTEXT.

The tool can be used to create and manage the database structure needed by Kamailio, therefore it should be immediately after Kamailio installation, in case you plan to run Kamailio with a database backend.

kamcmd

send RPC commands to Kamailio from command line, requires  ctl module

siremis

web management interface for Kamailio, written in PHP , AJAX , web 2.0 using MVC architecture

  • system and database administration tools for Kamailio SIP Server
  • subscriber, database aliases and speed dial management
  • location table view
  • dispatcher (load balancer), prefix-domain translation and least cost routing (lcr) management
  • access control lists (user groups) and permissions management
  • accounting records and missed calls vies
  • manage call data records (generated from acc records)
  • hash table, dial plan table and user preferences table management
  • offline message storage, presence service and sip trace views
  • communication with Kamailio SIP Server via XMLRPC ,  JSONRPC
  • communication with FreeSWITCH via event socket
  • create and display charts from statistic data stored by Kamailio
  • user location statistics charts
  • SIP traffic load charts
  • memory usage charts
  • accounting records charts and summary table
  • SQL-based CDR generation and rating billing engine

Covered in this article

  • Internal architecture
  • Configuration language
  • least cost routing
  • load balancing
  • traffic dispatching
  • DID routing
  • prefix based routing
  • SIP trunks and peering
  • traffic shaping
  • topology hiding
  • flood detection
  • scanning attacks prevention
  • anti-fraud policies
  • SQL and noSQL connectors
  • enum and DNS based routing
  • authentication and authorization
  • secure communication (TLS)
  • registration and location services
  • accounting and call data records
  • call control – redirect, forward, baring
  • redundancy and scalability
  • high availability and failover
  • websockets and webrtc

References :

  • [1] Henning Westerholt – Kamailio project-1&1 Internet AG ( 2009 )

Proxying Media Streams via Kamailio’s RTP Proxy


Proxying Media Streams via Kamailio’s RTP Proxy

Kamailio is a SIP server which does not play any role by itself in media transmission path. this behaviour leads to media packets having to attempt to stream peer to peer between caller and callee which in turn many a times causes them to get dropped in absence of NAT management

To ensure that media stream is proxied via an RTP proxy kamailio can use RTP proxy module combined with a RTP proxy.

This setup also provides other benefits such as controlling media media , security , Load balancing between many rtp proxies ,bridge signalling between multiple network interfaces etc.

RTP Proxy module

Used to proxy the media stream .

RTP proxies that can be used along with this module are:

RTP proxies can be used for bridging network interfaces , load distribution and balancing etc.It does not support transcoding.

Parameters :

rtpproxy_sock – binds a ip and port for rtp proxy

 modparam("rtpproxy", "rtpproxy_sock", "udp:localhost:12221")

rtpproxy_disable_tout – when rtp proxy is disabled then timeout till when it doesnt connect

rtpproxy_tout – timeout to wait for reply

rtpproxy_retr – num of retries after timeout

nortpproxy_str – sets the SDP attribute used by rtpproxy to mark the message’s SDP attachment with information that it have already been changed. Default value is

“a=nortpproxy:yes\r\n”

and others like

“a=sdpmangled:yes\r\n”

timeout_socket (string)

ice_candidate_priority_avp (string)

extra_id_pv (string)

db_url (string)

table_name (string)

rtp_inst_pvar (string)

Functions

set_rtp_proxy_set(setid) – Sets the Id of the rtpproxy set to be used for the next unforce_rtp_proxy(), rtpproxy_offer(), rtpproxy_answer() or rtpproxy_manage() command

rtpproxy_offer([flags [, ip_address]]) – to make the media pass through RTP the SDP is altered. Value of flag can be
1 – append first Via branch to Call-ID when sending command to rtpproxy.
2 – append second Via branch to Call-ID when sending command to rtpproxy. See flag ‘1’ for its meaning.
3 – behave like flag 1 is set for a request and like flag 2 is set for a reply
a – flags that UA from which message is received doesn’t support symmetric RTP. (automatically sets the ‘r’ flag)
b – append branch specific variable to Call-ID when sending command to rtpproxy
l – force “lookup”, that is, only rewrite SDP when corresponding session already exists in the RTP proxy
i, e – direction of the SIP message when rtpproxy is running in bridge mode. ‘i’ is internal network (LAN), ‘e’ is external network (WAN). Values ie , ei , ee and ii
x – shortcut for using the “ie” or “ei”-flags, to do automatic bridging between IPv4 on the “internal network” and IPv6 on the “external network”. Differentiated by IP type in the SDP, e.g. a IPv4 Address will always call “ie” to the RTPProxy (IPv4(i) to IPv6(e)) and an IPv6Address will always call “ei” to the RTPProxy (IPv6(e) to IPv4(i))
f – instructs rtpproxy to ignore marks inserted by another rtpproxy in transit to indicate that the session is already gone through another proxy. Allows creating a chain of proxies
r – IP address in SDP should be trusted. Without this flag, rtpproxy ignores address in the SDP and uses source address of the SIP message as media address which is passed to the RTP proxy
o – flags that IP from the origin description (o=) should be also changed.
c – flags to change the session-level SDP connection (c=) IP if media-description also includes connection information.
w – flags that for the UA from which message is received, support symmetric RTP must be forced.
zNN – perform re-packetization of RTP traffic coming from the UA which has sent the current message to increase or decrease payload size per each RTP packet forwarded if possible. The NN is the target payload size in ms, for the most codecs its value should be in 10ms increments, however for some codecs the increment could differ (e.g. 30ms for GSM or 20ms for G.723).
ip_address denotes the address of new SDP

such as : rtpproxy_offer(“FRWOC+PS”) is
rtpengine_offer(“force trust-address symmetric replace-origin replace-session-connection ICE=force RTP/SAVPF”);

route { 
... 
if (is_method("INVITE")) 
{ 
    if (has_body("application/sdp")) 
    { 
        if (rtpproxy_offer()) t_on_reply("1"); 
    } else { 
        t_on_reply("2"); 
    } 
} 

if (is_method("ACK") && has_body("application/sdp")) rtpproxy_answer(); 
... 
} 
onreply_route[1] { 
   if (has_body("application/sdp")) rtpproxy_answer(); 
} 
onreply_route[2] { 
   if (has_body("application/sdp")) rtpproxy_offer(); 
} 

rtpproxy_answer([flags [, ip_address]])- rewrite SDP to proxy media , it can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE.

rtpproxy_destroy([flags]) – tears down RTP proxy session for current call. Flags are ,
1 – append first Via branch to Call-ID
2 – append second Via branch to Call-ID
b – append branch specific variable to Call-ID
t – do not include To tag to “delete” command to rtpproxy thus causing full call to be deleted

unforce_rtp_proxy()

rtpproxy_manage([flags [, ip_address]]) – Functionality is to use predfined logic for handling requests
If INVITE with SDP, then do rtpproxy_offer()
If INVITE with SDP, when the tm module is loaded, mark transaction with internal flag FL_SDP_BODY to know that the 1xx and 2xx are for rtpproxy_answer()
If ACK with SDP, then do rtpproxy_answer()
If BYE or CANCEL, or called within a FAILURE_ROUTE[], then call unforce_rtpproxy().
If reply to INVITE with code >= 300 do unforce_rtpproxy()
If reply with SDP to INVITE having code 1xx and 2xx, then do rtpproxy_answer() if the request had SDP or tm is not loaded, otherwise do rtpproxy_offer()
This function can be used from ANY_ROUTE.

rtpproxy_stream2uac(prompt_name, count) – stream prompt/announcement pre-encoded with the makeann command. The uac/uas suffix selects who will hear the announcement relatively to the current transaction – UAC or UAS. Also used for music on hold (MOH).
Params : prompt_name – path name of the prompt to stream
count – number of times the prompt should be repeated. When count is -1, the streaming will be in loop indefinitely until the appropriate rtpproxy_stop_stream2xxx is issued.
Example rtpproxy_stream2xxx usage

if (is_method("INVITE")) { 
rtpproxy_offer();
if (is_audio_on_hold()) {
rtpproxy_stream2uas("/var/rtpproxy/prompts/music_on_hold", "-1");
} else {
rtpproxy_stop_stream2uas();
};
};

rtpproxy_stream2uas(prompt_name, count)

rtpproxy_stop_stream2uac()- Stop streaming of announcement/prompt/MOH

rtpproxy_stop_stream2uas()

start_recording()

Exported Pseudo Variables

$rtpstat

RPC Commands

rtpproxy.enable
rtpproxy.list

Ref : https://kamailio.org/docs/modules/5.3.x/modules/rtpproxy.html

Kamailio Call routing and Control

Kamailio SIP server evolved from SER and OpenSER. Written in ANSI C , primarily it is an open source proxy SIP server. RFC 3261 compliant and has support for various Operating system to install and run on as alpine , centos , deb , fedora , freebsd , netbsd , obs , openbsd , opensuse , oracle , rhel , solaris so on .

With modular design it already has 150 + modules and can have third party addons like Databases , RTP engines etc. Anyone can contribute to extensions and modules read here. Also contains cmd line tool kamcmd , kamcli and Web management interface SIREMIS .

It has provisions for complex routing logic development through scripts and programming languages interpreter support.

Over the years kamailio as proven a key component of a “carrier-grade” SIP service delivery platform. Either as SBC interfacing internal softswitch with public internet and handling complex operation as NAT, auth , flood control, topology hiding etc or even as the core SIP Server handling RTP relay as well.

Kamailio’s call routing log can be divided into:

  • Core functions
  • Core modules needed for almost any useful configuration like transaction module , Record route module etc ( read more https://telecom.altanai.com/2014/11/18/kamailio-modules/)
  • Ancillary modules to provide specific functionality (e.g. JANSSON , PIKE)
  • Pseudo-variables , which hold read/writeable attributes and
  • Transformations and custom logic definition.

Kamailio config

kamailio confguration file should be composed of
Core configuration directives as

!ifdef WITH_MULTIDOMAIN
!define MULTIDOMAIN 1
!else
!define MULTIDOMAIN 0
!endif

Global Parameters like

memdbg=5
memlog=5
log_facility=LOG_LOCAL0
fork=yes
children=4

Loading Modules

loadmodule "corex.so"
loadmodule "tm.so"
loadmodule "tmx.so"
loadmodule "sl.so"
loadmodule "rr.so"

Module parameters
For example considering for tm auto-discard branches from previous serial forking leg as failure_reply_mode ,30 sec as default retransmission timeout with 120 sec as invite retransmission timeout after 1xx

modparam("tm", "failure_reply_mode", 3)
modparam("tm", "fr_timer", 30000)
modparam("tm", "fr_inv_timer", 120000)

Subroutines (in essence, SIP event callbacks):
Request routes (request_route)
Reply routes (onreply_route)
Failure routes (failure_route)
Branch routes

For example consider main sip routing block

request_route {
    route(REQINIT);
    route(NATDETECT);
    if (is_method("CANCEL"))
    {
        if (t_check_trans()) {
            route(RELAY);
        }
        exit;
    }
    route(WITHINDLG);
    t_check_trans();
    route(AUTH);
    if (is_method("INVITE|SUBSCRIBE"))
        record_route();
    route(SIPOUT);
    route(PRESENCE);
    route(REGISTRAR);
    ...
}

Custom event routes (callbacks/event handlers exposed by modules).
Code for programming languages and runtimes:
String transformations
Variables
Ephemeral/scratch-pad variables ($var(…))
Transaction-persistent variables ($avp(…)/$xavp(…)) , extended AVP like AVP ar attached to transactions and not messages .
Dialog-persistent variables ($dlg_var(…))

$var(rc) = $rc;
route(TOVOICEMAIL);
t_newtran();
switch ($var(rc)) {
    case -1:
    case -3:
        send_reply("404", "Not Found");
        exit;
    case -2:
        send_reply("405", "Method Not Allowed");
        exit;
}

This article describes call routing config for Kamailio under following roles

  • SIP Proxy
  • Registrar
  • Accountant
  • Session border Controller

Kamailio as Proxy Server

Simple Kamailio configuration with basic features like alias , accounting , record routing , handling SIP requests like INVITE and its replies . Also failure and NAT handling . More samples of Kamailio config and call routing are at https://github.com/altanai/kamailioexamples

#!KAMAILIO

#Defined Values
!substdef "!MY_IP_ADDR!!g"
!substdef "!MY_EXTERNAL_IP!!g"
!substdef "!MY_UDP_PORT!!g"
!substdef "!MY_TCP_PORT!!g"
!substdef "!MY_UDP_ADDR!udp:MY_IP_ADDR:MY_UDP_PORT!g"
!substdef "!MY_TCP_ADDR!tcp:MY_IP_ADDR:MY_TCP_PORT!g"
!define MULTIDOMAIN 0

; - flags
; FLT_ - per transaction (message) flags
; FLB_ - per branch flags
!define FLT_ACC 1
!define FLT_ACCMISSED 2
!define FLT_ACCFAILED 3
!define FLT_NATS 5
!define FLB_NATB 6
!define FLB_NATSIPPING 7

# Global Parameters
; LOG Levels:3 = DBG, 2 = INFO, 1 = NOTICE, 0 = WARN, -1 = ERR
debug = 2
log_stderror = no
memdbg = 5
memlog = 5
log_facility = LOG_LOCAL0
log_prefix = "{$mt $hdr(CSeq) $ci} "

/* number of SIP routing processes */
children = 2
/* uncomment the next line to disable TCP (default on) */
disable_tcp = yes
/* uncomment the next line to disable the auto discovery of local aliases based on reverse DNS on IPs (default on) */
auto_aliases = no

/* add local domain aliases */
alias = "sip.mydomain.com"

/* listen addresses */
listen = udp:127.0.0.1:5060
listen = MY_UDP_ADDR advertise MY_EXTERNAL_IP:MY_UDP_PORT
listen = MY_TCP_ADDR advertise MY_EXTERNAL_IP:MY_TCP_PORT

# Modules Section
loadmodule "jsonrpcs.so"
loadmodule "kex.so"
loadmodule "corex.so"
loadmodule "tm.so"
loadmodule "tmx.so"
loadmodule "sl.so"
loadmodule "rr.so"
loadmodule "pv.so"
loadmodule "maxfwd.so"
loadmodule "textops.so"
loadmodule "siputils.so"
loadmodule "xlog.so"
loadmodule "sanity.so"
loadmodule "ctl.so"
loadmodule "cfg_rpc.so"
loadmodule "acc.so"
loadmodule "counters.so"

----------------- setting module-specific parameters --------------

----- jsonrpcs params -----
modparam("jsonrpcs", "pretty_format", 1)
/* set the path to RPC fifo control file */
modparam("jsonrpcs", "fifo_name", "/var/run/kamailio/kamailio_rpc.fifo")
/* set the path to RPC unix socket control file */
modparam("jsonrpcs", "dgram_socket", "/var/run/kamailio/kamailio_rpc.sock")

; ----- ctl params -----
/* set the path to RPC unix socket control file */
modparam("ctl", "binrpc", "unix:/var/run/kamailio/kamailio_ctl")

; ----- tm params -----
auto-discard branches from previous serial forking leg
modparam("tm", "failure_reply_mode", 3)
default retransmission timeout:30sec
modparam("tm", "fr_timer", 30000)
default invite retransmission timeout after 1xx:120sec
modparam("tm", "fr_inv_timer", 120000)

; ----- rr params -----
# set next param to 1 to add value to;lr param (helps with some UAs)
modparam("rr", "enable_full_lr", 0)
; do not append from tag to the RR (no need for this script)
modparam("rr", "append_fromtag", 0)

----- acc params -----
; /* what special events should be accounted ? / modparam("acc", "early_media", 0) modparam("acc", "report_ack", 0) modparam("acc", "report_cancels", 0) / by default ww do
; not adjust the direct of the sequential requests.
; if you enable this parameter, be sure the enable "append_fromtag"
; in "rr" module / 
modparam("acc", "detect_direction", 0) / account triggers (flags) */
modparam("acc", "log_flag", FLT_ACC)
modparam("acc", "log_missed_flag", FLT_ACCMISSED)
modparam("acc", "log_extra",
"src_user=$fU;src_domain=$fd;src_ip=$si;"
"dst_ouser=$tU;dst_user=$rU;dst_domain=$rd")
modparam("acc", "failed_transaction_flag", FLT_ACCFAILED)

# Routing Logic

/* Main SIP request routing logic*/
request_route {
;     per request initial checks
    route(REQINIT);
    ; CANCEL processing
    if (is_method("CANCEL")) {
        if (t_check_trans()) {
            route(RELAY);
        }
        exit;
    }
    
    ; handle retransmissions
    if (!is_method("ACK")) {
        if (t_precheck_trans()) {
            t_check_trans();
            exit;
        }
        t_check_trans();
    }

    ; handle requests within SIP dialogs
    route(WITHINDLG);
    ;     only initial requests (no To tag)
    ; record routing for dialog forming requests ( in case they are routed)
    ; - remove preloaded route headers
    remove_hf("Route");
    if (is_method("INVITE|SUBSCRIBE")) {
        record_route();
    }
    
    ;     account only INVITEs
    if (is_method("INVITE")) {
        setflag(FLT_ACC); # do accounting
    }
    
    if ($rU==$null) {
        # request with no Username in RURI
        sl_send_reply("484", "Address Incomplete");
        exit;
    }

    # update $du to set the destination address for proxying
    $du = "sip:" + $rd + ":9";
    route(RELAY);
    exit;
}

# Wrapper for relaying requests
route[RELAY] {
    if (is_method("INVITE|BYE|SUBSCRIBE|UPDATE")) {     
        if (!t_is_set("branch_route")) 
            t_on_branch("MANAGE_BRANCH");
    }

    if (is_method("INVITE|SUBSCRIBE|UPDATE")) {     
        if (!t_is_set("onreply_route")) 
            t_on_reply("MANAGE_REPLY");
    }

    if (is_method("INVITE")) {
        if (!t_is_set("failure_route")) 
            t_on_failure("MANAGE_FAILURE");
    }

    if (!t_relay()) {     
        sl_reply_error(); 
    } 
    exit;
}

#P er SIP request initial checks
route[REQINIT] {
    if ($ua = ~ "friendly-scanner|sipcli|VaxSIPUserAgent") {
        # sl_send_reply("200", "OK");
        exit;
    }
    
    if (!mf_process_maxfwd_header("10")) {
        sl_send_reply("483", "Too Many Hops");
        exit;
    }

    if (is_method("OPTIONS") && uri==myself && $rU==$null) {     
        sl_send_reply("200", "Keepalive");
        exit;
    }

    if (!sanity_check("1511", "7")) {
        xlog("Malformed SIP message from $si:$sp\n");
        exit;
    }
}

# Handle requests within SIP dialogs

route[WITHINDLG] {
    if (!has_totag()) 
        return ;
    
    if (loose_route()) {
        if (is_method("BYE")) {
            setflag(FLT_ACC); # do accounting ...         
            setflag(FLT_ACCFAILED); # ... even if the transaction fails
        } else{
            if (is_method("NOTIFY")) {
                # Add Record-Route for in -dialog NOTIFY as per RFC 6665.         
                record_route();
            }
            route(RELAY);
            exit;
        }
    }   

    if (is_method("ACK")) {
        if (t_check_trans()) {         
            # no loose-route, but stateful ACK;         
            must be an ACK after a 487 or e.g. 404 from upstream server         
            route(RELAY);
            exit;
        } else {
            # ACK without matching transaction, ignore and discard         
            exit;
        }
    } 
    
    sl_send_reply("404", "Not here"); exit;

    #Manage outgoing branches
    branch_route[MANAGE_BRANCH] {
    xdbg("new branch [$T_branch_idx] to $ru\n");
}

--# Manage incoming replies
onreply_route[MANAGE_REPLY] {
    xdbg("incoming reply\n");
}

--# Manage failure routing cases
failure_route[MANAGE_FAILURE] {
    if (t_is_canceled()) exit;
}




Controlling Call Routing from command Line

To find the loaded flags while starting kamailio

kamailio -v
version: kamailio 5.1.8 (x86_64/linux) d8e930
flags: STATS: Off, USE_TCP, USE_TLS, USE_SCTP, TLS_HOOKS, USE_RAW_SOCKS, DISABLE_NAGLE, USE_MCAST, DNS_IP_HACK, SHM_MEM, SHM_MMAP, PKG_MALLOC, Q_MALLOC, F_MALLOC, TLSF_MALLOC, DBG_SR_MEMORY, USE_FUTEX, FAST_LOCK-ADAPTIVE_WAIT, USE_DNS_CACHE, USE_DNS_FAILOVER, USE_NAPTR, USE_DST_BLACKLIST, HAVE_RESOLV_RES
ADAPTIVE_WAIT_LOOPS=1024, MAX_RECV_BUFFER_SIZE 262144 MAX_URI_SIZE 1024, BUF_SIZE 65535, DEFAULT PKG_SIZE 8MB
poll method support: poll, epoll_lt, epoll_et, sigio_rt, select.
id: d8e930 
compiled on 09:47:09 May  3 2019 with gcc 4.8.4

kamdbctl

creates the database support for many kamailio modules such as auth , location , dispatcher , permission etc

make sure you load a DB engine , during kamailio installation and configuration . It can be either done though make command or though modules.lst file

 make include_modules="db_mysql" cfg
 make all
 make install

since json replaced all fifo command, ensure you do not get "json.h: No such file or directory” in server by install json either via libjson-c-dev or libjson-cpp-dev

apt-get install libjson-c-dev 

For uuid/uuid.h: No such file or directory install

apt-get install uuid-dev

For libpq-fe.h: No such file or directory install

apt-get install libpq-dev

kamdbctl command list

kamdbctl create <db name or db_path, optional> ...(creates a new database)
kamdbctl drop <db name or db_path, optional> .....(!entirely deletes tables!)
kamdbctl reinit <db name or db_path, optional> ...(!entirely deletes and than re-creates tables!)
kamdbctl backup <file> ...........................(dumps current database to file)
kamdbctl restore <file> ..........................(restores tables from a file)
kamdbctl copy <new_db> ...........................(creates a new db from an existing one)
kamdbctl presence ................................(adds the presence related tables)
kamdbctl extra ...................................(adds the extra tables)
kamdbctl dbuid ...................................(adds the uid tables)
kamdbctl dbonly ..................................(creates empty database)
kamdbctl grant ...................................(grant privileges to database)
kamdbctl revoke ..................................(revoke privileges to database)
kamdbctl add-tables <gid> ........................(creates only tables groupped in gid)

if you want to manipulate database as other database user than
root, want to change database name from default value "kamailio",
or want to use other values for users and password, edit the
"config vars" section of the command kamdbctl.

kamdbctl pframework create .......................(creates a sample provisioning framework file)

For psql: received invalid response to SSL negotiation: [
ERROR: Creating database failed!
errors . Remember for mysql the defaul port is 3306, but for psql it is 5432 . Hence make the change in /etc/kamailio/kamctlrc

database port
DBPORT=3306
DBPORT=5432  

Kamctl

If kamctl isnt accessible from the machine installed with kamailio , just goto kamctl folder and compile it yourself . For example for me , I took the git pull of kamailio source code v 5.1.0 and went to util folder

cd  /kamailio_source_code/utils/kamctl 
make && make install 

some commands

‘start|stop|restart|trap’
‘acl’ – manage access control lists (acl)
‘lcr’ – manage least cost routes (lcr)
‘cr’ – manage carrierroute tables show|reload|dump
‘rpid’ – manage Remote-Party-ID (RPID)
‘add|passwd|rm’ – manage subscribers
‘add|dump|reload|rm|show’ – manage trusted
‘add|dump|reload|rm|show’ – manage address
‘add|dump|reload|rm|show’ – manage address
‘dispatcher’ – manage dispatcher

dispatcher add 1 sip:1.2.3.1:5050 1 5 'prefix=proxycall' 'gatewaye33'

‘dialog’ – manage dialog records
‘srv’ – server management commands
‘cisco_restart’ – restart CISCO phone (NOTIFY)
‘online’ – dump online users from memory
‘monitor’ – show internal status

[cycle #: 3; if constant make sure server lives]
Kamailio Runtime Details: 
kamailio 5.1.8 (x86_64/linux) d8e930
    now:  Fri May 24 13:39:19 2019
    up_since: Fri May 24 13:31:37 2019
    uptime: 462

Transaction Statistics: 
    tmx:UAS_transactions = 0        tmx:UAC_transactions = 0        tmx:active_transactions = 0     tmx:inuse_transactions = 0

Stateless Server Statistics: 
    sl:sent_replies = 0            sl:sent_err_replies = 0

UsrLoc Statistics: 
    usrloc:location-contacts = 0        usrloc:location-expires = 0         usrloc:location-users = 0           usrloc:registered_users = 0

Core Statistics: 
    core:rcv_requests = 0        core:fwd_requests = 0       core:rcv_replies = 0        core:fwd_replies = 0

Shared Memory Statistics: 
    shmem:fragments = 1                shmem:max_used_size = 2807640       shmem:total_size = 67108864
    shmem:free_size = 64301224            shmem:real_used_size = 2807640      shmem:used_size = 2566040

‘ping’ – ping a SIP URI (OPTIONS)
‘ul|alias’ – manage user location or aliases
‘ps’ – print details about running processes
‘ps’ – print details about running processes
‘stats’ – print internal statistics

{
  "jsonrpc":  "2.0",
  "result": [
    "core:bad_URIs_rcvd = 0",
    "core:bad_msg_hdr = 0",
    "core:drop_replies = 0",
    "core:drop_requests = 0",
    "core:err_replies = 0",
    "core:err_requests = 0",
    "core:fwd_replies = 0",
    "core:fwd_requests = 0",
    "core:rcv_replies = 0",
    "core:rcv_replies_18x = 0",
    "core:rcv_replies_1xx = 0",
    "core:rcv_replies_2xx = 0",
    "core:rcv_replies_3xx = 0",
    "core:rcv_replies_401 = 0",
    "core:rcv_replies_404 = 0",
    "core:rcv_replies_407 = 0",
    "core:rcv_replies_480 = 0",
    "core:rcv_replies_486 = 0",
    "core:rcv_replies_4xx = 0",
    "core:rcv_replies_5xx = 0",
    "core:rcv_replies_6xx = 0",
    "core:rcv_requests = 0",
    "core:rcv_requests_ack = 0",
    ...
}

‘rpc’ – send raw RPC commands

Kamcmd

unix tool for interfacing with Kamailio using exported RPCs. It uses binrpc (a proprietary protocol, designed for minimal packet size and fast parsing) over a variety of transports (unix stream sockets, unix datagram sockets, udp or tcp).

 cfg.add_group_inst
 cfg.commit
 cfg.del
 cfg.del_delayed
 cfg.del_group_inst
 cfg.diff
 cfg.get
 cfg.help
 cfg.list
 cfg.reset
 cfg.rollback
 cfg.set
 cfg.set_delayed
 cfg.set_delayed_int
 cfg.set_delayed_string
 cfg.set_now_int
 cfg.set_now_string
 cfg.seti
 cfg.sets
 cnt.get
 cnt.get_raw
 cnt.get_vars
 cnt.grp_get_all
 cnt.grps_list
 cnt.help
 cnt.list_groups
 cnt.list_vars
 cnt.reset
 cnt.var_list
 core.aliases_list
 core.arg
 core.echo
 core.flags
 core.info
 core.kill
 core.modules
 core.ppdefines
 core.printi
 core.prints
 core.ps
 core.psx
 core.pwd
 core.shmmem
 core.sockets_list
 core.tcp_info
 core.tcp_list
 core.tcp_options
 core.udp4_raw_info
 core.uptime
 core.version
 corex.debug
 corex.list_aliases
 corex.list_sockets
 corex.pkg_summary
 corex.shm_status
 corex.shm_summary
 ctl.connections
 ctl.listen
 ctl.who
 dns.add_a
 dns.add_aaaa
 dns.add_srv
 dns.debug
 dns.debug_all
 dns.delete_a
 dns.delete_aaaa
 dns.delete_all
 dns.delete_all_force
 dns.delete_cname
 dns.delete_ebl
 dns.delete_naptr
 dns.delete_ptr
 dns.delete_srv
 dns.delete_txt
 dns.lookup
 dns.mem_info
 dns.view
 dst_blacklist.add
 dst_blacklist.debug
 dst_blacklist.delete_all
 dst_blacklist.mem_info
 dst_blacklist.view
 jsonrpc.echo
 mod.stats
 pkg.stats
 pv.shvGet
 pv.shvSet
 sl.stats
 stats.clear_statistics
 stats.fetch
 stats.get_statistics
 stats.reset_statistics
 system.listMethods
 system.methodHelp
 system.methodSignature
 tm.cancel
 tm.clean
 tm.hash_stats
 tm.list
 tm.reply
 tm.reply_callid
 tm.stats
 tm.t_uac_start
 tm.t_uac_wait
 ul.add
 ul.db_contacts
 ul.db_expired_contacts
 ul.db_users
 ul.dump
 ul.flush
 ul.lookup
 ul.rm
 ul.rm_contact
 alias: ps
 alias: psx
 alias: list
 alias: ls
 alias: ver
 alias: version
 alias: who
 alias: listen
 alias: dns_mem_info
 alias: dns_debug
 alias: dns_debug_all
 alias: dst_blacklist_mem_info
 alias: dst_blacklist_debug
 builtin: ?
 builtin: help
 builtin: version
 builtin: quit
 builtin: exit
 builtin: warranty
 builtin: license

some examples of the kamcmd commands

stats

> kamctl kamcmd 
tm.stats 
{     
current: 0     
waiting: 0     
total: 0     
total_local: 0     
rpl_received: 0     
rpl_generated: 0     
rpl_sent: 0     
6xx: 0     
5xx: 0     
4xx: 0     
3xx: 0     
2xx: 0     
created: 0     
freed: 0     
delayed_free: 0 
}

Get info about TLS

kamcmd> tls.info
{
    max_connections: 2048
    opened_connections: 1
    clear_text_write_queued_bytes: 0
}

Get info about open sockets

kamcmd> core.sockets_list
{
    socket: {
        proto: udp
        address: 1.2.3.4
        port: 5060
        mcast: no
        mhomed: no
    }
    socket: {
        proto: tcp
        address:  1.2.3.4
        port: 5060
        mcast: no
        mhomed: no
    }
    socket: {
        proto: tcp
        address:  1.2.3.4
        port: 80
        mcast: no
        mhomed: no
    }
    socket: {
        proto: tls
        address:  1.2.3.4
        port: 5061
        mcast: no
        mhomed: no
    }
    socket: {
        proto: tls
        address:  1.2.3.4
        port: 443
        mcast: no
        mhomed: no
    }
}

get core info

kamcmd > core.info
{
    version: kamailio 5.2.3
    id: 4a4588 
    compiler: gcc 5.4.0
    compiled: 15:54:50 Jun 27 2019
    flags: STATS: Off, USE_TCP, USE_TLS, USE_SCTP, TLS_HOOKS, USE_RAW_SOCKS, DISABLE_NAGLE, USE_MCAST, DNS_IP_HACK, SHM_MEM, SHM_MMAP, PKG_MALLOC, Q_MALLOC, F_MALLOC, TLSF_MALLOC, DBG_SR_MEMORY, USE_FUTEX, FAST_LOCK-ADAPTIVE_WAIT, USE_DNS_CACHE, USE_DNS_FAILOVER, USE_NAPTR, USE_DST_BLACKLIST, HAVE_RESOLV_RES
}

Kamailio Transaction management and Transaction Module tm

Kamailio is basically only a transaction stateful proxy, without any dialog support built in. Here the TM module enables stateful processing of SIP transactions ( by maintaining state machine). State is a requirement for many complex logic such as accounting, forking, DNS resolution. 

Although most of the Kamailio module related description is covered here, I wanted to keep a separate space to describe and explain how Kamailio handles transactions and in particular, the Transaction Module.

Note: This article has been updated many times to match v5.1 since v3.0 from when it was written, if u see and outdated content or deprecated functions, please point them out to me in the comments. If you are new to Kamailio, this post is probably not a good starting point for you, instead read more on Kamailio. It is a powerful open-source SIP server here and has a widespread application in telephony.

Kamailio can manage stateless replying as well as stateful processing – SIP transaction management. The difference between the two is below

StatefulStateless
stateful processing is per SIP transaction

Each SIP transaction will be kept in memory so that any replies, failures, or retransmissions can be recognized
Forwarding each msg in the dialog without any context.
Application understands the transactions , for example
– recognize if a new INVITE message is a resend
– know that 200 OK reponse belongs to the initial INVITE which it will be able to handle in an onreply_route[x] block.
it doesnt
know that the call is on-going.
However it can use callId to match INVITE and BYE.
Uses : manage call state , routing , call control like forward on busy, voicemailUses : Load distribution , proxying

Kamailio’s Transaction management

t_relay, t_relay_to_udp and t_relay_to_tcp are main functions to setup transaction state, absorb retransmissions from upstream, generate downstream retransmissions and correlate replies to requests in Kamailio.

Lifecycle of Transaction

Transactions lifecycle are controlled by various factors which includes reliable ( TCP) or non reliable transport, invite or non-invite transaction types etc. Transaction are terminated either by final response or when timers are fired, which control it.

ACK is considered part of INVITE trasnaction when non 2xx / negative final resposne is received , When 2xx final / positive response is recievd than ACK is not considered part of the transaction.

Memory Management in Transactions

Transaction Module copies clones of received SIP messages in shared memory. Non-TM functions operate over the received message in private memory. Therefore core operations ( like record_route) should not be called before settings the transaction state ( t_realy ) for state-fully processing a message.

An INVITE transaction will be kept in memory for maximum: max_inv_lifetime + fr_timer + wt_timer.
While A non-INVITE transaction will be kept in memory for a maximum: max_noninv_lifetime + wt_timer.

Branches

A single SIP INVITE request may be forked to multiple destinations, all of which together is called destination sets and Individual elements within the destination sets are called branches. A transaction can have more than one branch. For example, during DNA failover, each failed DNS SRV destination can introduce a new branch.

Serial, Parallel and Combined Forking 

By default Kamailio performs parallel forking sending msg to all destinations and waiting for a response, however it can also do serial ie send requests one by one and wait for response /timeout before sending next. 

By use of priorities ( q value 0 – 1.0) , Kamailio can also intermix the forking technique ie describing priority oder for serial and same level for parallel . The destination uri are loaded using unctions t_load_contacts() and t_next_contacts().

Parallel forking snippet

request_route {
  seturi("sip:a@example.com");
  append_branch("sip:b@example.com");
  append_branch("sip:c@example.com");
  append_branch("sip:d@example.com");

  t_relay();
  break;
}

Mixed forking snippet

modparam("tm", "contacts_avp", "tm_contacts");
modparam("tm", "contact_flows_avp", "tm_contact_flows");

request_route {
  seturi("sip:a@example.com"); // lowest 0 
  append_branch("sip:b@example.com", "0.5"); // shoudl be in parallel with C
  append_branch("sip:c@example.com", "0.5"); // shoudl be in parallel with B
  append_branch("sip:d@example.com", "1.0"); // highest priority , should be tried first

  t_load_contacts();   // load all branches as per q values, store them in AVP configured in modparam 
  t_next_contacts();   // takes AVP and extracts higher q value branch

  t_relay();
  break;
}

Code to terminate when no more branches are found ( -1 returned) and return the message upstream

 failure_route["serial"]
 {
   if (!t_next_contacts()) {
     exit;
   }
   t_on_failure("serial");
   t_relay();
 }

TM Module

t_relay, t_relay_to_udp and t_relay_to_tcp are main functions to setup transaction state, absorb retransmissions from upstream, generate downstream retransmissions and correlate replies to requests.

Memory

TM copies clones of received SIP messages in shared memory. non-TM functions operate over the received message in private memory. Therefore core operations ( like record_route) should ne called before settings the trasnaction state ( t_realy ) for statefully processing a message.

An INVITE transaction will be kept in memory for maximum: max_inv_lifetime + fr_timer + wt_timer.
While A non-INVITE transaction will be kept in memory for a maximum: max_noninv_lifetime + wt_timer.

Parameters

Various parameters are used to fine tune how trsnactions are handled and timedout in kamailio. Note all timers are set in miliseconds notation.

  • fr_timer (integer) – timer hit when no final reply for a request or ACK for a negative INVITE reply arrives. Default 30000 ms (30 seconds).
  • fr_inv_timer (integer) – timer hit when no final reply for an INVITE arrives after a provisional message was received on branch. Default 120000 ms (120 seconds).
  • restart_fr_on_each_reply (integer) – restart fr_inv_timer fir INVITE transaction for each provisional reply. Otherwise it will be sreatred only for fisrt and then increasing provisonal replies. Turn it off in cases when dealing with bad UAs that continuously retransmit 180s, not allowing the transaction to timeout.
  • max_inv_lifetime (integer) – Maximum time an INVITE transaction is allowed to be active in a tansaction. It starts from the time trnsaction was created and after this timer is hit , transaction is moved to either wait state or in the final response retransmission state. Default 180000 ms (180 seconds )
  • max_noninv_lifetime (integer) – Maximum time a non-INVITE transaction is allowed to be active. default 32000 ms (32 seconds )
  • wt_timer (integer) – Time for which a transaction stays in memory to absorb delayed messages after it completed.
  • delete_timer (integer) – Time after which a to-be-deleted transaction currently ref-ed by a process will be tried to be deleted again. This is now obsolte and now transaction is deleted the moment it’s not referenced anymore.

Retry transmission timers

  • retr_timer1 (integer) – Initial retransmission period
  • retr_timer2 (integer) – Maximum retransmission period started increasingly from starts with retr_timer1 and stays constant after this
  • noisy_ctimer (integer) – if set, INVITE transactions that time-out (FR INV timer) will be always replied. Otherwise they will be quitely dropped without any 408 branch timeout resposne
  • auto_inv_100 (integer) – automatically send and 100 reply to INVITEs.
  • auto_inv_100_reason (string) – Set reason text of the automatically sent 100 to an INVITE.
  • unix_tx_timeout (integer) – nix socket transmission timeout,
  • aggregate_challenges (integer) – if more than one branch received a 401 or 407 as final response, then all the WWW-Authenticate and Proxy-Authenticate headers from all the 401 and 407 replies will be aggregated in a new final response.

Blacklist

  • blst_503 (integer) – reparse_invite=1.
  • blst_503_def_timeout (integer) – blacklist interval if no “Retry-After” header is present
  • blst_503_min_timeout / blst_503_max_timeout (integer) – minimum and maximun blacklist interval respectively
  • blst_methods_add (unsigned integer) – Bitmap of method types that trigger blacklisting on transaction timeouts and by default INVITE triggers blacklisting only
  • blst_methods_lookup (unsigned integer) – Bitmap of method types that are looked-up in the blacklist before being forwarded statefully. For default only applied to BYE.

Reparse

  • reparse_invite (integer) – set if CANCEL and negative ACK requests are to be constructed from the INVITE message ( same record-set etc as INVITE ) which was sent out instead of building them from the received request.
  • reparse_on_dns_failover (integer) – SIP message after a DNS failover is constructed from the outgoing message buffer of the failed branch instead of from the received request.
  • ac_extra_hdrs (string) – Header fields prefixed by this parameter value are included in the CANCEL and negative ACK messages if they were present in the outgoing INVITE. Can be only used with reparse_invite=1.
  • on_sl_reply (string) – Sets reply route block, to which control is passed when a reply is received that has no associated transaction.
modparam("tm", "on_sl_reply", "stateless_replies")
...
onreply_route["stateless_replies"] {
    // return 0 if do not allow stateless replies to be forwarded
    return 1; // will pass to core for stateless forwading
}
  • xavp_contact (string) – name of XAVP storing the attributes per contact.
  • contacts_avp (string) – name of an XAVP that stores names of destination sets. Used by t_load_contacts() and t_next_contacts() for forking branches
  • contact_flows_avp (string) – name of an XAVP that were skipped
  • fr_timer_avp (string) – override teh value of fr_timer on per transactio basis , outdated
  • cancel_b_method (integer) – method to CANCEL an unreplied transaction branch. Params :
    • 0 will immediately stop the request (INVITE) retransmission on the branch so that unrpelied branches will be terminated
    • 1 will keep retransmitting the request on unreplied branches.
    • 2 end and retransmit CANCEL even on unreplied branches, stopping the request retransmissions.
  • unmatched_cancel (string) – sets how to forward CANCELs that do not match any transaction. Params :
    • 0 statefully
    • 1 statelessly
    • 2 dropping them
  • ruri_matching (integer) – try to match the request URI when doing SIP 1.0 transaction matching as older SIP didnt have via cookies as in RFC 3261
  • via1_matching (integer) – match the topmost “Via” header when doing SIP 1.0 transaction matching
  • callid_matching (integer) – match the callid when doing transaction matching.
  • pass_provisional_replies (integer)
  • default_code (integer) – Default response code sent by t_reply() ( 500 )
  • default_reason (string) – Default SIP reason phrase sent by t_reply() ( “Server Internal Error” )
  • disable_6xx_block (integer)- treat all the 6xx replies like normal replies. However according to RFC receiving a 6xx will cancel all the running parallel branches, will stop DNS failover and forking.
  • local_ack_mode (integer) – where locally generated ACKs for 2xx replies to local transactions are sent. Params :
    • 0 – the ACK destination is choosen according next hop in contact and the route set and then DNS resolution is used on it
    • 1 – the ACK is sent to the same address as the corresponding INVITE branch
    • 2 – the ACK is sent to the source of the 2xx reply.
  • failure_reply_mode (integer) – how branches are managed and replies are selected for failure_route handling. Params :
    • 0 – all branches are kept
    • 1 – all branches are discarded
    • 2 – only the branches of previous leg of serial forking are discarded
    • 3 – all previous branches are discarded
    • if you dont want to drop all branches then use t_drop_replies() to sleectively drop
  • faked_reply_prio (integer) – how branch selection is done.
  • local_cancel_reason (boolean) – add reason headers for CANCELs generated due to receiving a final reply.
  • e2e_cancel_reason (boolean) – add reason headers for CANCELs generated due to receiving a CANCEL
  • remap_503_500 (boolean) – conversion of 503 response code to 500. RFC requirnment.
  • failure_exec_mode (boolean) – Add local failed branches in timer to be considered for failure routing blocks.
  • dns_reuse_rcv_socket (boolean) – reuse of the receive socket for additional branches added by DNS failover.
  • event_callback (str) – function in the kemi configuration file (embedded scripting language such as Lua, Python, …) to be executed instead of event_route[tm:local-request] block. The function recives a string param with name of the event.
modparam("tm", "event_callback", "ksr_tm_event")
...
function ksr_tm_event(evname)
    KSR.info("===== TM module triggered event: " .. evname .. "\n");
    return 1;
end
  • relay_100 (str) – whether or not a SIP 100 response is proxied. not valid behavior when operating in stateful mode and only useful when in stateless mode
  • rich_redirect (int) – to add branch info in 3xx class reply. Params :
    0 – no extra info is added (default)
    1 – include branch flags as contact header parameter
    2 – include path as contact uri Route header

Functions

These functions are operational blocks and route handlers for trsnactions handling in kamailio

  • t_relay([host, port]) – Relay a message statefully.
    Exmaple to show if t_relay fails, atleast send a reply to UAC statelessly to not keep it waiting
if (!t_relay()) 
{ 
    sl_reply_error(); 
    break; 
};
  • t_relay_to_udp([ip, port]) / t_relay_to_tcp([ip, port]) – same as above, relay a message statefully but using specific protocol
if (some_conditon)
    t_relay_to_udp("1.2.3.4", "5060"); # sent to 1.2.3.4:5060 over udp
else
    t_relay_to_tcp(); # relay to msg. uri, but over tcp
  • t_relay_to_tls([ip, port])
  • t_relay_to_sctp([ip, port])
  • t_on_failure(failure_route) – on route block for failure management on a branch when a negative reply is recived to transaction. here uri is reset to value which it had on relaying.
  • t_on_branch_failure(branch_failure_route) – controls when negative response come for a transacion. here uri is reset to value which it had on relaying.
  • t_on_reply(onreply_route) – gets control when a reply from transaction is received
  • t_on_branch(branch_route) – control is passed after forking (when a new branch is created)
  • t_newtran() – Creates a new transaction
  • t_reply(code, reason_phrase) – Sends a stateful reply after a transaction has been established.
  • t_send_reply(code, reason)
  • t_lookup_request() – Checks if a transaction exists
  • t_retransmit_reply()
  • t_release() – Remove transaction from memory
  • t_forward_nonack([ip, port]) – forward a non-ACK request statefully
  • t_forward_nonack_udp(ip, port) / t_forward_nonack_tcp(ip, port)
  • t_forward_nonack_tls(ip, port)
  • t_forward_nonack_sctp(ip, port)
  • t_set_fr(fr_inv_timeout [, fr_timeout]) – Sets the fr_inv_timeout
  • t_reset_fr()
  • t_set_max_lifetime(inv_lifetime, noninv_lifetime) – Sets the maximum lifetime for the current INVITE or non-INVITE transaction, or for transactions created during the same script invocation
  • t_reset_max_lifetime()
  • t_set_retr(retr_t1_interval, retr_t2_interval) – Sets the retr_t1_interval and retr_t2_interval for the current transaction
  • t_reset_retr()
  • t_set_auto_inv_100(0|1) – switch automatically sending 100 replies to INVITEs on/off on a per transaction basis
  • t_branch_timeout() – Returns true if the failure route is executed for a branch that did timeout.
  • t_branch_replied()
  • t_any_timeout()
  • t_any_replied()
  • t_grep_status(“code”)
  • t_is_canceled()
  • t_is_expired()
  • t_relay_cancel()
  • t_lookup_cancel([1])
  • t_drop_replies([mode])
  • t_save_lumps()
  • t_load_contacts()
  • t_next_contacts()
  • t_next_contact_flow()
  • t_check_status(re)
  • t_check_trans() – check if a message belongs or is related to a transaction.
  • t_set_disable_6xx(0|1)
  • t_set_disable_failover(0|1)
  • t_set_disable_internal_reply(0|1)
  • t_replicate([params]) – Replicate the SIP request to a specific address.
  • t_relay_to(proxy, flags) – KSR.tm.t_relay()
  • t_set_no_e2e_cancel_reason(0|1)
  • t_is_set(target) – KEMI – KSR.tm.t_is_set() Return true if the attribute specified by ‘target’ is set for transaction. Target can be branch_route , failure_route and onreply_route.
if not(KSR.tm.t_is_set("branch_route")>0) then
    core.set_branch_route("ksr_branch_manage");
end

if not(KSR.tm.t_is_set("onreply_route")>0) then
    core.set_reply_route("ksr_onreply_manage");
end

if not(KSR.tm.t_is_set("failure_route")>0) and (req_method == "INVITE") then
   core.set_failure_route("ksr_failure_manage");
end
  • t_use_uac_headers()
  • t_is_retr_async_reply()
  • t_uac_send(method, ruri, nexthop, socket, headers, body)
  • t_get_status_code() – Return the status code for transaction or -1 in case of error or no status code was set.

Snippet to demo stateful handling of trsansactions

Yhis program is designed to accept all Register with 200 OK and create a new transaction. Does a check for username altanai. After the check cutom message hello is replied and any other username is printed a different rejection reply.

# ------------------ module loading ----------------------------------
loadmodule "tm.so"

route{
    # for testing purposes, simply okay all REGISTERs
    if (method=="REGISTER") {
        log("REGISTER");
        sl_send_reply("200", "ok");
        break;
    };

    # create transaction state with t_newtran(); abort if error occurred
    if (t_newtran()){
        log("New Transaction created"); 
    }
    else {
        sl_reply_error();
        break;
    };

    log(1, "New Transaction Arrived\n");

    # add a check for matching username to print a cutom message with t_reply()
    if (uri=~"altanai@") {
        if (!t_reply("409", "Well , hello altanai !")) {
            sl_reply_error();
        };
    } else {
        if (!t_reply("699", "Do not proceed with this one")) {
            sl_reply_error();
        };
    };
}

Raw RPC cmds

1. kamctl rpc tm.list

{  

   "jsonrpc":"2.0",
   "result":[  
      {  
         "cell":"0x7f0698d06488",
         "tindex":50969,
         "tlabel":163886326,
         "method":"INVITE",
         "from":"From: ;tag=dddab54e\r\n",
         "to":"To: \r\n",
         "callid":"Call-ID: NjkyYjJlNzJkNzQ1OTYyZjE2MDM2NjFlYWZkNjY4OWE\r\n",
         "cseq":"CSeq: 1",
         "uas_request":"yes",
         "tflags":65,
         "outgoings":2,
         "ref_count":1,
         "lifetime":29578635
      }
   ],
   "id":3922
}

2. kamctl rpc tm.stats

before call

{  
   "jsonrpc":"2.0",
   "result":{  
      "current":0,
      "waiting":0,
      "total":3,
      "total_local":0,
      "rpl_received":6,
      "rpl_generated":6,
      "rpl_sent":6,
      "6xx":0,
      "5xx":3,
      "4xx":0,
      "3xx":0,
      "2xx":0,
      "created":3,
      "freed":3,
      "delayed_free":0
   },
   "id":4119
}

during call

{  

   "jsonrpc":"2.0",
   "result":{  
      "current":1,
      "waiting":0,
      "total":4,
      "total_local":0,
      "rpl_received":7,
      "rpl_generated":7,
      "rpl_sent":7,
      "6xx":0,
      "5xx":3,
      "4xx":0,
      "3xx":0,
      "2xx":0,
      "created":4,
      "freed":3,
      "delayed_free":0
   },
   "id":4217
}

during call wait

{  
   "jsonrpc":"2.0",
   "result":{  
      "current":1,
      "waiting":1,
      "total":4,
      "total_local":0,
      "rpl_received":8,
      "rpl_generated":8,
      "rpl_sent":8,
      "6xx":0,
      "5xx":4,
      "4xx":0,
      "3xx":0,
      "2xx":0,
      "created":4,
      "freed":3,
      "delayed_free":0
   },
   "id":4275
}

after call is completed

{  
   "jsonrpc":"2.0",
   "result":{  
      "current":0,
      "waiting":0,
      "total":4,
      "total_local":0,
      "rpl_received":8,
      "rpl_generated":8,
      "rpl_sent":8,
      "6xx":0,
      "5xx":4,
      "4xx":0,
      "3xx":0,
      "2xx":0,
      "created":4,
      "freed":4,
      "delayed_free":0
   },
   "id":4333
}