[asterisk-dev] classifying SIP peers

Daniel Pocock daniel at pocock.com.au
Tue Feb 11 11:55:07 CST 2014

On 11/02/14 03:13, Matthew Jordan wrote:
> 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.

I'm thinking of a situation where an edge proxy is used with Asterisk
and the proxy is adding the class parameter to the request URI

In my use case, the main reason for this is simply to separate the
different types of WebRTC callers (Chrome/without DTLS, Firefox with
DTLS) and also standard SIP callers (no AVPF/maybe no icesupport)

>> 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.

That is another interesting permutation, I agree it is simpler and for
those who trust their proxy it is just as valid

>> 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.

I had a look over this, what you describe is very relevant. Is the
Asterisk 12 code at a stage where people should be considering it for
production projects though?  I don't want to be rushing Digium to
announce it is stable though.

> 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
> ast_sip_register_endpoint_identifier.
> 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
> ast_sip_identify_endpoint:
> /*!
>  * \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
> calling
> 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.
> Matt

More information about the asterisk-dev mailing list