We talked a little about the Transaction module and using it for Transaction Stateful SIP Proxy, but it’s worth knowing a bit more about the Transaction Module and the powerful functions it offers.

So today I’ll cover some cool functionality TM offers!

Different Reply Routes

By calling the t_on_reply(); we can specify the reply route to be used for replies in this transaction.

route[RELAY]{

        #Use reply route "OurReplyRoute" for responses for this transaction
        t_on_reply("OurReplyRoute");

        #Relay (aka Forward) the request
        t_relay_to_udp("192.168.3.118", "5060");

}

onreply_route[OurReplyRoute] {
        #On replies from route[RELAY]

        #Check our AVP we set in the initial request
        xlog("for $rs response the value of AVP \"state_test_var\" is $avp(state_test_var) ");

        #Append a header so we can see this was proxied in the SIP capture
        append_hf("X-Proxied: For the reply\r\n");

}

Any responses from the route[RELAY] routing block will go to onreply_route[OurReplyRoute], the beauty of this is it allows you to have multiple reply routes each with their own logic. For example for a call leg to a carrier you may want to preserve CLI, but for a call leg to a customer you may wish to restrict it if that’s the option the user has selected, and you can make these changes / modifications in the reply messages.

Failure Routes

Failure routes allow the transaction module to know to try again if a call fails, for example if no response is received from the destination, send it to a different destination, like a backup.

route[RELAY]{

        #Use reply route "OurReplyRoute" for responses for this transaction
        t_on_reply("OurReplyRoute");
        t_on_failure("OurFailureRoute");
        #Relay (aka Forward) the request
        t_relay_to_udp("192.168.1.118", "5060");

}

failure_route[OurFailureRoute]{
        xlog("At failure route");
        t_reply("500", "Remote end never got back to us");
        exit;
}

We can build upon this, and try a different destination if the first one fails:

request_route {

        #Enable record_routing so we see the BYE / Re-INVITE etc
        record_route();

        #Handle Registrations in a dumb way so they don't messy our tests
        if(is_method("REGISTER")){
                sl_reply("200", "ok");
                exit;
        }



                 #Append a header so we can see this was proxied in the SIP capture
                append_hf("X-Proxied: You betcha\r\n");

        if(is_method("INVITE")){
                        #Createa  new AVP called "state_test_var" and set the value to "I remember"
                        $avp(state_test_var) = "I remember";
        }
                #Let syslog know we've set the value and check it
                xlog("for $rm the value of AVP \"state_test_var\" is $avp(state_test_var) ");

                #Send to route[RELAY] routing block
                rewritehostport("nonexistentdomain.com");
                route(RELAY);

}

route[RELAY]{

        #Use reply route "OurReplyRoute" for responses for this transaction
        t_on_reply("OurReplyRoute");
        t_on_failure("OurFailureRoute");
        #Relay (aka Forward) the request
        t_relay();
}

failure_route[OurFailureRoute]{
        xlog("At failure route");
        #t_reply("500", "Remote end never got back to us");

                rewritehostport("192.168.3.118");
                append_branch();
                t_relay();

}

onreply_route[OurReplyRoute] {
        #On replies from route[RELAY]

        #Check our AVP we set in the initial request
        xlog("for $rs response the value of AVP \"state_test_var\" is $avp(state_test_var) ");

        #Append a header so we can see this was proxied in the SIP capture
        append_hf("X-Proxied: For the reply\r\n");

}

One thing to keep in mind is that there’s lots of definitions of failure, for example if you are sending a call to a carrier and get a 404 response back, you probably want to relay that through to the end user, because that destination isn’t there.

But if you get back a 5xx series response you may consider that to be a failure and select the next carrier for example.

Different conditions / requirements have different definitions of “failures” and so there’s a lot to think about when implementing this, along with timeouts for no replies, TCP session management, etc.

Parallel Forking the Call to Multiple Destinations

Parallel Forking is a fancy way of saying ring multiple destinations at the same time.

/* Main SIP request routing logic
 * - processing of any incoming SIP request starts with this route
 * - note: this is the same as route { ... } */
request_route {

        #Enable record_routing so we see the BYE / Re-INVITE etc
        record_route();

        #Handle Registrations in a dumb way so they don't messy our tests
        if(is_method("REGISTER")){
                sl_reply("200", "ok");
                exit;
        }



                 #Append a header so we can see this was proxied in the SIP capture
                append_hf("X-Proxied: You betcha\r\n");

        if(is_method("INVITE")){
                        #Createa  new AVP called "state_test_var" and set the value to "I remember"
                        $avp(state_test_var) = "I remember";
        }
                #Let syslog know we've set the value and check it
                xlog("for $rm the value of AVP \"state_test_var\" is $avp(state_test_var) ");

                #Send to route[RELAY] routing block
                route(RELAY);

}

route[RELAY]{

        #Use reply route "OurReplyRoute" for responses for this transaction
        t_on_reply("OurReplyRoute");

        #Append branches for each destination we want to forward to
        append_branch("sip:[email protected]");
        append_branch("sip:[email protected]");
        append_branch("sip:[email protected]");

        t_on_failure("OurFailureRoute");
        #Relay (aka Forward) the request
        t_relay();
}

failure_route[OurFailureRoute]{
        xlog("At failure route");
        t_reply("500", "All those destinations failed us");

}

onreply_route[OurReplyRoute] {
        #On replies from route[RELAY]

        #Check our AVP we set in the initial request
        xlog("for $rs response the value of AVP \"state_test_var\" is $avp(state_test_var) ");

        #Append a header so we can see this was proxied in the SIP capture
        append_hf("X-Proxied: For the reply\r\n");

}

Bit of a mess but here we see the initial INVITE being branched to the 3 destinations at the same time (Parallel forking)

Serial Forking / Sequential Forking the calls to Multiple Destinations one after the Other

This could be used to try a series of weighted destinations and only try the next if the preceding one fails:

/* Main SIP request routing logic
 * - processing of any incoming SIP request starts with this route
 * - note: this is the same as route { ... } */
request_route {

        #Enable record_routing so we see the BYE / Re-INVITE etc
        record_route();


        #Send to route[RELAY] routing block
        route(RELAY);

}

route[RELAY]{

        #Use reply route "OurReplyRoute" for responses for this transaction
        t_on_reply("OurReplyRoute");

        append_branch("sip:[email protected]", "0.3");
        append_branch("sip:[email protected]", "0.2");
        append_branch("sip:[email protected]", "0.1");

        t_load_contacts();

        t_next_contacts();

        t_on_failure("OurFailureRoute");
        #Relay (aka Forward) the request
        t_relay();
        break;
}

failure_route[OurFailureRoute]{

        xlog("At failure route - Trying next destination");
        t_on_failure("OurFailureRoute");
        t_relay();


}

onreply_route[OurReplyRoute] {
        #On replies from route[RELAY]
        #Append a header so we can see this was proxied in the SIP capture
        append_hf("X-Proxied: For the reply\r\n");

}

Again this will try each destination, but one after the other based on the weight we added to each destination in the append_branch()

Here we see each destination being tried sequentially

2 thought on “Kamailio Bytes – Transaction Module”
  1. Hello to you I have been following this tutorial series for a long time on the kamailio SIP server, I would like you to deepen this part on the stateful server and the means of implementing the one with the billing of calls and also an example practical with serial forking

    Thanks a lot to you !

  2. Dear Nick,
    I followed the way to use serial and parallel fork.

    I am using RTPENGINE and the audio doesn’t routed.
    Any highlight where the route NATMANAGE needs to be applied ?

Leave a Reply

Your email address will not be published. Required fields are marked *

Want more telecom goodness?

I have a good old fashioned RSS feed you can subscribe to.

You can get the latest posts dropped into your inbox by subscribing to our mailing list

I cross post some of this content to LinkedIn and Twitter.