<div dir="ltr"><div class="gmail_extra"><br><div class="gmail_quote">On Mon, Jan 12, 2015 at 10:53 AM, Daniel-Constantin Mierla <span dir="ltr"><<a href="mailto:miconda@gmail.com" target="_blank">miconda@gmail.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">Hey guys,<br>
<br>
I hope everyone here enjoyed nice time during the winter (or summer for<br>
lucky ones at this time of the year) holidays break!<br>
<br>
Being rather busy after AstriDevCon, but I also postponed starting the<br>
discussion, given that I expected low activity ahead and didn't want to<br>
get it lost in the archive ... so back to the topic ... and expect large<br>
message below ...<br></blockquote><div><br></div><div>No worries at all! I'm still digging through the list of items from AstriDevCon as well - the holidays always slows down progress on these kinds of things.<br></div><div> <br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
During AstriDevCon sessions in Las Vegas, we discussed briefly of what<br>
can be done for a better integration between Asterisk (as media server)<br>
and Kamailio (as a front end SIP singling server/proxy). I said I would<br>
follow up on mailing list, so here we are. Obviously, many are using the<br>
two together, therefore any improvement to make it simpler could make a<br>
very good impact on both sides.<br>
<br>
To be honest, I lost track of many new 'cool' things happening lately in<br>
Asterisk with the newer APIs, I will have to catch up very soon,<br>
meanwhile, if any of my ideas are already there, then even better.<br>
<br>
The typical integration of Asterisk and Kamailio was to share somehow<br>
user profiles via database -- relying on Asterisk realtime, either using<br>
Asterisk database structure with kamailio connecting to it, or making<br>
SQL views to suit the two applications. I wrote some tutorials over the<br>
time -- for reference, just in case anyone wants to get the idea better:<br>
<br>
- <a href="http://kb.asipto.com/asterisk:index" target="_blank">http://kb.asipto.com/asterisk:index</a><br>
<br>
The approach works very well when dealing in a more or less static<br>
deployment. However, there is a lot to benefit on top of elasticity of<br>
the cloud networks, where the nodes could be fired on demand, in<br>
different locations of the world. In such situations, sharing the<br>
database can become really slow, adds issues with replications, etc.<br>
<br>
To set the specifics of a deployment:<br>
- kamailio takes care of signaling 'heavy' processing (authentication,<br>
user location, filtering DoS attacks, etc.)<br>
- asterisk takes care of 'heavy' pbx features and media processing<br>
<br>
When I fire a new asterisk, I want to give it (e.g., in config file) one<br>
or many IP addresses that belong to kamailios and asterisk should<br>
consider them trusted, no other authentication should be done for<br>
traffic originating from those IPs. The rest of integration would<br>
hopefully automagically just work.<br>
<br>
One of my ideas was to be able to define subscriber (phone) profile<br>
templates and the proxy will indicate which template to use for caller<br>
and callee (when they are local subscribers). Attributes of the template<br>
can be taken from SIP message or be statically defined. I know about the<br>
config templates with [name](!), but that is still quite static,<br>
extension have to be defined and templates associated with them. It<br>
could be on same direction, but more dynamic.<br></blockquote><div><br></div><div>This gets into the notion of having a "push" configuration model instead of a "pull" configuration model that was discussed at AstriDevCon. Note that this is just one method of solving this problem - as you mention later, there are others which may be better - but it's probably worth discussing push configuration here.<br><br>A fuller explanation follows:<br><br></div><div>In Asterisk 12, the notion of a generic data access layer was introduced that wraps up the traditional configuration approaches (static conf files, static Asterisk Realtime, and dynamic Asterisk Realtime) as well as some internal data providers (memory, AstDB). This was done for quite a few reasons:<br></div><div>(1) Using the ARA had quite a few deficiencies, as evidenced by the SQL statements littering chan_sip and other portions of the code base. It was quite common for logic to diverge based on what provided the configuration, which led to all sorts of difficult bugs to test and fix. That being said, the actual ARA drivers themselves could fit behind a new API, and so they were mostly left alone. This lets older modules continue to use ARA as they previously did, and newer modules to take advantage of the existing backends.<br></div><div>(2) A new generic data access layer allowed us to take advantage of work done in Asterisk 11's configuration framework. The configuration framework in Asterisk 11 provides type conversion from configuration data to in memory objects, default value application and object construction, documentation checking, object lifetime management, as well as thread safety during reloads. All of this makes modules that use the configuration data a lot safer and more consistent.<br></div><div>(3) Because data can also be provided by an in memory cache or the AstDB, we can now provide caching for ARA backends. A user can also modify via configuration where data is stored, pushing data out to a more permanent storage if the AstDB isn't desired.<br><br></div><div>On top of it, this is a pluggable architecture, where data providers are simply dynamically loadable modules. The method of providing configuration is very 'ad hoc', wherein a user of the new data access layer does not care where the data comes from, so long as something can provide it.<br><br></div><div>In the end, Josh called this "sorcery", presumably because there is an awful lot of magic that happens under the hood in that API.<br><br></div><div>Today, all of the providers - called "wizards" - use a pull model of providing data. Taking the PJSIP stack as an example (this is a very high level view, there's a lot of details removed):<br><br></div><div>* An inbound request is received and passed up the stack into res_pjsip<br></div><div>* res_pjsip, which has a pluggable architecture of its own, looks for an endpoint identifier module<br></div><div>* res_pjip_endpoint_identifier_user picks up the request, and look at the user portion of the SIP URI in the From header<br></div><div>* res_pjsip_endpoint_identifier_user queries sorcery for an endpoint that matches the user<br></div><div>* sorcery (via configuration) maps this to some configuration provider. This could be a static file (such as pjsip.conf), a database, or something else. It converts the key/value pairs into an ast_sip_endpoint ao2 ref counted object.<br></div><div>* res_pjsip_endpoint_identifier_user gets back the ast_sip_endpoint object, passing it back to res_pjsip - which now has an endpoint to use for the request.<br><br></div><div>Now, we could just as easily *not* pull for the configuration data. Instead, we could do something like the following:<br><br></div><div>* An API call is made which pushes a JSON blob into Asterisk containing the necessary information for a PJSIP endpoint. A sorcery wizard picks this up and stores it in the in-memory sorcery wizard.<br></div><div>* An inbound request is received and passed up the stack into res_pjsip<br></div><div>... and all the rest is actually the same - as sorcery simply uses the in-memory cache of the received information from the API.<br><br></div><div>Since caching is tricky, the lifetime of the PJSIP endpoint in this Asterisk instance could just as easily be managed by the external application. A different API call could tell the Asterisk instance to purge the endpoint from memory.<br><br>From Kamailio's perspective, you could:<br><br></div><div>* Receive an inbound request<br></div><div>* Pick some Asterisk resource in your pool of available servers<br></div><div>* (Assuming this is done using a REST API) PUT the configuration of the endpoint on the specific destination server<br></div><div>* Route the call to that server<br></div><div>* Later, DELETE the configuration off the server<br> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<br>
For better exemplification, in pseudo code:<br>
<br>
[template-match]<br>
template-as-caller=${header(X-Caller-Template)<br>
template-as-callee=${header(X-Callee-Template)<br>
<br>
[template-user-x]<br>
userid-as-caller=${From-Header-URI-Username}<br>
userid-as-caller=${Request-URI-Username}<br>
codecs=g711,g729<br>
mwi=yes<br>
maiboxid=${header(X-MailBoxId)}<br>
maiboxpin=${header(X-MailBoxPin)}<br>
location =<br>
"sip:${From-Header-URI-Username}@${Source-IP}:${Source-Port};transport={Transport-Protocol}"<br>
publish-dialog-info=yes<br>
...<br>
<br>
The proxy has to set the header X-Caller-Template if the caller is local<br>
and X-Callee-Template if the callee is local, plus other headers than<br>
are necessary/used in templates.<br></blockquote><div><br></div><div>This is another way of identifying inbound requests without requiring any API interaction whatsoever. You'd certainly have the advantage of being even more performant - no push configuration necessary whatsoever - but at the cost of some of the flexibility that the push configuration affords.<br><br></div><div>As mentioned earlier, the PJSIP stack has a very flexible architecture that lets you "plug-in" how endpoints are identified. In the existing stack, there are three different endpoint identifiers:<br></div><div>* res_pjsip_endpoint_identifier_user - associate inbound requests based on the user portion of the SIP URI in the From header<br></div><div>* res_pjsip_endpoint_identifier_ip - associate inbound requests based on a static IP address<br></div><div>* res_pjsip_endpoint_identifier_anonymous - associate inbound requests with one particular endpoint. If present, all unmatched requests are matched to this endpoint.<br><br></div><div>You could just as easily have res_pjsip_endpoint_identifier_header. It could do what you said: look for a custom header (which, itself, could be configurable) and, if the header is present, match it to a single specific endpoint in the same fashion as res_pjsip_endpoint_identifier_anonymous.<br></div><div><br></div><div>The downside of this approach is that all devices must support all configuration parameters of a single configured endpoint. That is, if you have something like:<br><br></div><div>[header-identifier]<br>type=header-identifier<br></div><div>endpoint=caller-endpoint<br></div><div>X-Caller-Template=foo<br><br></div><div>[header-identifier]<br></div><div>type=header-identifier<br></div><div>endpoint=callee-endpoint<br></div><div>X-Callee-Template=bar<br></div><div><br></div><div>[endpoint-template](!)<br></div><div>type=endpoint<br></div><div>allow=!all.g722,ulaw<br></div><div><br></div><div>[caller-endpoint](endpoint-template)</div><div>context=from-kamailio-local<br></div><div>identify_by=header<br></div><div><br></div><div>[callee-endpoint](endpoint-template)<br></div><div>context=from-kamailio-remote<br></div><div>identify_by=header<br></div><div><br></div><div>Then you could have your endpoint identifier match all requests with a header of X-Caller-Template to caller-endpoint and all requests with a header of X-Callee-Template to callee-endpoint. However, all devices would have to support g722 and ulaw, and - if NAT settings were necessary - then all devices would have to share the same NAT settings. This may not be must of a concern in a well defined system front-ended by Kamailio.<br><br></div><div>Writing this module would not be terribly difficult - it would look a lot like a combination of res_pjsip_endpoint_identifier_ip and res_pjsip_endpoint_identifier_anonymous. (Not that SLOC is a good measurement of difficulty, but res_pjsip_endpoint_identifier_ip tops out at 540 lines of code, and it is by far the largest of the endpoint identifiers).<br></div><div><br></div><div>Note that this approach is not mutually exclusive with a push configuration, or with using a database. With sorcery, the configuration shown above could come from a database - it's just that Asterisk won't need to obtain it often.<br></div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<br>
Practically, Asterisk would have to build the user profile on demand,<br>
without needing to interogate any database or other resource over the<br>
network (I know APIs can be used everywhere :-), but they fail also, or<br>
in other words, SIP is an API as well). It can reuse the variable<br>
already available on the SIP channel, I used pseudo-code to make it<br>
easier to understand.<br></blockquote><div><br></div><div>Yup! And as I mentioned, the idea of having an endpoint identifier that looks at custom SIP headers is not mutually exclusive with an API. One could foresee having a small set of SIP headers that Kamailio uses to 'tag' allowed requests, particularly if there were a variety of devices that it was passing to Asterisk (different codec sets, etc.) It could have all of these pre-configured on its pool of servers; have the templates defined in a database; or it could use a push model to provide the configuration on the fly to Asterisk servers as they are spun up.<br></div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<br>
For the moment I would stop here, already long message. And might be<br>
already possible ... if not, before going on other directions, lets see<br>
the interest and maybe other proposals for a better media<br>
server/signaling server integration.<br></blockquote><div><br></div><div>I think it's very possible to add a new endpoint identifier.<br><br>The push configuration model is a bit more work, but also very doable.<br><br>I'd be happy to provide more details on either approach if someone were interested in contributing it!<br></div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<br>
Before the real end, some questions for the larger devel community that<br>
should help deciding on how to get the two projects working smoother<br>
together:<br>
- what would you like from the proxy (signaling server) to be exposed<br>
easier?<br>
- if already using the two, what parts you would like to simplify?<br>
- how would you split the workload? In other words, what services you<br>
would prefer to be done better in one or the other, given that some<br>
features overlap?<br></blockquote><div><br><div>I'm going to defer to Olle on those
questions for now - as he uniquely has a lot of experience with both
Asterisk and Kamailio - but I'll think some more on those questions this
week.<br> <br></div></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
Have a great 2015 coding Asterisk and Kamailio! Looking forward to<br>
meeting some of you around the globe this year!<br>
Daniel<br>
<span class=""></span></blockquote><div><br></div>Thanks for the e-mail and suggestions! I'm looking forward to what else we can come up with.<br><br></div><div class="gmail_quote">Matt<br></div><br></div><div class="gmail_extra">-- <br><div class="gmail_signature"><div dir="ltr"><div>Matthew Jordan<br></div><div>Digium, Inc. | Engineering Manager</div><div>445 Jan Davis Drive NW - Huntsville, AL 35806 - USA</div><div>Check us out at: <a href="http://digium.com" target="_blank">http://digium.com</a> & <a href="http://asterisk.org" target="_blank">http://asterisk.org</a></div></div></div>
</div></div>