[asterisk-dev] classifying SIP peers

Matthew Jordan mjordan at digium.com
Mon Feb 10 20:13:04 CST 2014

On Mon, Feb 10, 2014 at 1:25 PM, Olle E. Johansson <oej at edvina.net> wrote:
> On 10 Feb 2014, at 20:16, Daniel Pocock <daniel at pocock.com.au> wrote:
>> I'm looking at the way chan_sip.c matches peers (by name or by IP/port)
>> It may be possible to add an extra discriminator, for example, it could
>> be called "class"
>> [my-proxy-a]
>> host=
>> class=group-a
>> dtlsenable=no
>> [my-proxy-b]
>> host=
>> class=group-b
>> dtlsenable=yes
>> The proxy then signals the desired class in the request URI, e.g.
>>   INVITE sip:1234 at;x-asterisk-class=group-b
>> If no tag is supplied or it doesn't match, it would fall back to a peer
>> with class=<nothing> (ensuring existing behavior is consistent)
>> It appears that this logic can be inserted in most of the places where
>> callbackexten is used as a discriminator for peer selection.
>> Has anybody else already started on anything like this on a branch or in
>> any third-party patch?
> I am not sure it is the right way to add it to a URI. I have thought of creating a
> branch where we control the peer choice with a header. It implies *A LOT* of trust,
> but in some systems it would be useful.
> No, I have no similar code. No, I don't like complicating the peer configuration
> with a new setting called "class". A simple header that tells you the peer name
> is propably enough. The header should be configured in the "[general]" section
> and there has to be ACLs on the peers.
> Maybe one way could be to copy the ideas I had in the match-beyond-proxy
> code that I actually have in a branch. In that you first hit one peer definition
> and that one leads somewhere else. Like:
> [my-proxy]
> host=
> peerselectheader=Peername
> [my-proxy-nodtls]
> host=
> I would force the user of the same peer name as a prefix so that we don't
> open ALL peers for a single peer. In the case above the my-proxy-nodtls
> would be selected by adding the header "Peername: nodtls" to the
> SIP request.
> Just thinking loud...

I agree with Olle. Complicating peer matching in chan_sip isn't
something I would ever encourage. Each request type in chan_sip is
handled just slightly differently in how it looks up a peer (or, for
that matter, a "user") - so to fully deploy this logic, you would have
a ripple effect across all of the request handlers. Further
complicating it is whether or not users.conf is in use, or realtime,
or... you get the idea. Given the structure of chan_sip, this is not
easy to get right in a universal fashion.

However, if you're interested in adding this kind of functionality,
I'd highly recommend looking at the new PJSIP-based stack in Asterisk
12. It supports the notion of custom endpoint identifiers, which are
implemented entirely in separate modules.

To do this, you would write a new module that would implement the
ast_sip_endpoint_identifier virtual table (defined in res_pjsip.h):

 * \brief An entity responsible for identifying the source of a SIP message
struct ast_sip_endpoint_identifier {
     * \brief Callback used to identify the source of a message.
     * See ast_sip_identify_endpoint for more details
    struct ast_sip_endpoint *(*identify_endpoint)(pjsip_rx_data *rdata);

And you would register your new module with the stack using

When any request is received, the stack will ask its registered
endpoint identifiers to identify an endpoint that matches the request.
When one does so, it will associate the request with that endpoint and
process it accordingly. For reference, the stack kicks this off with

 * \brief Determine the endpoint that has sent a SIP message
 * This will call into each of the registered endpoint identifiers'
 * identify_endpoint() callbacks until one returns a non-NULL endpoint.
 * This will return an ao2 object. Its reference count will need to be
 * decremented when completed using the endpoint.
 * \param rdata The inbound SIP message to use when identifying the endpoint.
 * \retval NULL No matching endpoint
 * \retval non-NULL The matching endpoint
struct ast_sip_endpoint *ast_sip_identify_endpoint(pjsip_rx_data *rdata);

There's a number of modules already written that you could use as a
basis for this:

 * res_pjsip_endpoint_identifier_user - identify an endpoint by the username
 * res_pjsip_endpoint_identifier_ip - identify an endpoint by a static
IP address
 * res_pjsip_endpoint_identifier_anonymous - if loaded, will associate
unidentified requests with an 'anonymous' endpoint for anonymous

The benefit here is that you can write all of this in a separate
module. This has several benefits:
(1) It can be loaded only as an optional addition by setting
defaultenabled to no in its menuselect parameters. This lets end users
choose if they want the functionality or not, avoiding some of Olle's
concerns where the logic in identifying the endpoint for the request
only makes sense on certain systems, such as ones where you can be
absolutely positive of the nature of the inbound traffic.
(2) If you choose not to push the module upstream, it is far easier to
maintain the code in a separate module then when it is a patch to a
large monolithic module.
(3) It makes testing the code far easier, as you can easily isolate
the behaviour of the module by removing the other endpoint identifiers
(4) It makes verifying the correctness of the code much easier, as it
is less tightly coupled with other code

If you decide to go down that road, feel free to talk with the other
developers in #asterisk-dev or on the mailing list. There's lots of
people who can answer questions about how to write a custom endpoint
identifier module.


Matthew Jordan
Digium, Inc. | Engineering Manager
445 Jan Davis Drive NW - Huntsville, AL 35806 - USA
Check us out at: http://digium.com & http://asterisk.org

More information about the asterisk-dev mailing list