[SR-Dev] [Serdev] AVPs

Daniel-Constantin Mierla miconda at gmail.com
Tue Dec 16 09:52:32 CET 2008

On 12/16/08 10:15, Andrei Pelinescu-Onciul wrote:
> On Dec 16, 2008 at 00:36, Daniel-Constantin Mierla <miconda at gmail.com> wrote:
>> Hello,
>> On 12/12/08 17:22, Jan Janak wrote:
>>> On 12-12 00:25, Daniel-Constantin Mierla wrote:
>>>> Hello,
>>>> I think we need to decide about AVPs. Although not deeply investigated,  
>>>> ser seems to have couple of AVPs lists. In kamailio/openser it is one 
>>>> list.
>>>> Can a ser developer describe a bit the AVP implementation as it is now  
>>>> there?
>>>   A long time ago we started using AVPs to store all kinds of configuration
>>>   information. We needed some sort of general configuration mechanism because
>>>   real-world scripts were getting too complex and at that time the AVPs were
>>>   readily available.
>>>   We started by loading attribute names and value from a database table into
>>>   AVPs. This mechanism was/is same as in k. We would always load a set of
>>>   attributes this way for a particular user (identified by username and domain
>>>   from his/her SIP URI). With this mechanism in place, we were able to load a
>>>   generic set of configuration options for a particular user of the server, I
>>>   guess you have the same.
>>>   To make the AVPs easily accessible from the configuration script, we added
>>>   support for AVP identifiers in the config script, so that you can access AVP
>>>   with name foo as $foo.
>>>   As we migrated more and more configuration options to AVPs, it became clear
>>>   that having just one set of AVPs was not enough. Although we could load all
>>>   the config options for the caller (From URI) from the database into a set of
>>>   AVPs, we could not do the same for the callee (Request-URI) at the same
>>>   time. We could not do it because they would conflict with the AVPs of the 
>>>   caller as both users could have AVPs with same names but different values.
>>>   To get around this issue we added another list of AVPs. The new AVP list
>>>   works the same way, it can contain AVPs with same names as AVPs in the
>>>   original list and they do not conflict. All functions that work with lists
>>>   of AVPs now take the list to work on as parameter.
>>>   To make both AVP lists available in the configuration script, we extended
>>>   the syntax of AVPs identifiers so the script write can choose the AVP list
>>>   to work with. SO instead of just $foo you can write either
>>>   $f.foo or $t.foo
>>>   $f refers to the original AVP list which is commonly associated with the
>>>   caller. The address/uid of the caller is taken from From header, hence the
>>>   'f' in the identifier. $t refers to the AVP list which contains
>>>   configuration settings of the callee. The address/uid of the calle can be
>>>   taken either from the Request-URI or To headers, hence the 't' in the
>>>   identifier.
>>>   The original syntax without any list identifier is still available, in other
>>>   words you can still write $foo, this is defined as a shorthand for
>>>   $f.foo. If you do not specify the AVP list to be used then you are referring
>>>   to the list containing AVPs of the caller (From).
>>>   It also turned out that in some cases we would be having too many attributes
>>>   in the database table storing AVPs. This can happen in bigger setups, having
>>>   tens or hundreds of thousands users or serving multiple domains. This can
>>>   slow the database down and makes SER load too much data. Based on my
>>>   observations it is common that a large number of users have AVPs with same
>>>   values. If ten thousands of users have attribute named 'foo' with value
>>>   'bar' then the attribute will be stored in the database ten thousand times.
>>>   As a remedy for this problem, we introduced the concept of AVP levels. The
>>>   AVPs described so far and stored in user_attrs table are called user-level
>>>   attributes and they store configuration specific to particular users. Then
>>>   we added another two AVP lists to store so called domain-level AVPs.
>>>   Domain-level AVPs are used to store configuration information that is shared
>>>   by a group of users. Domain-level AVPs are stored in a separate database
>>>   table, the name of the table is domain_attrs and its contents is cached in
>>>   memory by 'domain' SER module. This is an important difference, while
>>>   user-level AVPs need to be loaded every time a SIP request is processed,
>>>   domain-level AVPs are only loaded when the contents of domain_attrs table
>>>   has changed.
>>>   The domain-level AVPs are called 'domain' because they are tied to a
>>>   particular domain handled by the SIP server. That could be 'iptel.org',
>>>   'sip-router.org', and so on. This mainly useful for multi-domain
>>>   setups. There are two domain-level AVP lists because it is common that SER
>>>   works with two domains at the same time, the domain from From header
>>>   (caller's domain) and the domain from Request-URI or To (callee's domain).
>>>   Again, we extended the syntax of AVP identifiers in the configuration
>>>   file. So you can write:
>>>   $fu.foo -- this way you are asking for the value of the user-level foo AVP.
>>>   $fd.foo -- This will return the value of the domain-level foo AVP.
>>>   And similarly there is $tu and $td for callee's user-level and domain-level
>>>   AVPs. If you specify 'u' in the AVP identifiers then SER searches only the
>>>   list of user-level attributes. If you specify 'd' then SER searches only the
>>>   list of domain-level attributes.
>>>   This behavior changes if you do NOT specify the level to be searched. In
>>>   that case SER searches the user-level list first and if no match is found
>>>   then the domain-level list will be searched. Thus if you write:
>>>   $f.foo
>>>   then you are telling SER to search for the value of 'foo' AVP in the
>>>   user-level list and continue with the domain-level list if no match is
>>>   found. In other words, user-level AVPs have higher priority than
>>>   domain-level AVPs. With this system you can efficiently set a configuration
>>>   option for all users within a domain by creating a domain-level attribute
>>>   and yet you can override that option with user-level attributes for
>>>   particular users within that domain.
>>>   Then there are the global AVPs. Global AVPs can be used to store settings
>>>   applicable to the whole SIP server and all domains on that server. Global
>>>   AVPs are stored in global_attrs table and the contents of that table is
>>>   cached in memory by gflags module. There is only one list of global
>>>   AVPs. Global AVPs can be accessed with:
>>>   $g.foo
>>>   (note that there is no 'f' or 't' in the identifier). The list of global
>>>   AVPs is searched after the list of domain-level AVPs. So if you write:
>>>   $f.foo
>>>   Then SER searches user-level AVPs first, then domain-level and then the
>>>   global AVPs.
>>>   And finally there are also so called URI-level AVPs. They work just
>>>   like user-level AVPs, but a single user can have multiple sets of
>>>   uri-level AVPs. They are tied to SIP URIs of that user. Uri-level AVPs are
>>>   denoted by 'r' in the AVP identifier, for example:
>>>   $fr.foo
>> so, if I understood correctly, there are couple of combinations between 
>> the dot, with one or two letters, right?
>> Like:
>> $xy.name
>> where x = f|t|g and y may be missing or u|d|r
> x = f|t ("track" in ser avp terminology
> y= r|u|d|g ("class")
> g is a special case (it cannot be combined with f or t).
> Here is the parse_avp_ident() usage comment:
> /** parse an avp indentifier.
>  *
>  * Parses the following avp indentifier forms:
>  *       - "i:<number>"  - old form, deprecated  (e.g. i:42)
>  *       - "s:<string>"  - old form, deprecated  (e.g. s:foo)
>  *       - "<track>.<name>"                      (e.g.: f.bar)
>  *       - "<track>.<name>[<index>]"             (e.g.: f.bar[1])
>  *       - "<track><class>.<name>"               (e.g:  tu.bar)
>  *       - "<track><class>.<name>[<index>]"      (e.g:  fd.bar[2])
>  *       - "<string>"                            (e.g.: foo)
>  * Where:
>  *          <string> = ascii string
>  *          <id>   = ascii string w/o '[', ']', '.' and '/'
>  *          <name> = <id> | '/' regex '/'
>  *                   (Note: regex use is deprecated)
>  *          <track> = 'f' | 't'
>  *                   (from or to)
>  *          <class> = 'r' | 'u' | 'd' | 'g'
>  *                    (uri, user, domain or global)
>  *          <index> = <number> | '-' <number> | ''
>  *                    (the avp index, if missing it means AVP_INDEX_ALL, but
>  *                     it's use is deprecated)
>  * More examples:
>  *       "fr.bar[1]"  - from track, uri class, avp "bar", the value 1.
>  *       "tu./^foo/"  - to track,  user class, all avps for which the name
>  *                      starts with foo (note RE in avp names are deprecated).
>  *        "t.did"     - to track, "did" avp
>  *
>  * @param name  - avp identifier
>  * @param *attr - the result will be stored here
>  * @return 0 on success, -1 on error
>  */
>> Then we can get rid of overlapping in the namespace if we enforce usage 
>> of $f.foo for $foo . In K there are couple of PV that use dot in 
>> classname (a PV can be $classname or $classname(innername)), but they 
>> are proposed to be removed.
> For the time being in sip-router this is handled in the following way:
> $(...) -> pvar
> $foo(...) -> pvar
> $foo[...] -> avp
> $bar.foo -> avp (if it has a dot in it -> avp)
> $foo  (no dot, [] or ()) -> try first to resolve it as pvar and if it
> fails consider it an avp.
> For the current version I think this is the most compatible way.
yes, indeed, thanks!

>  I'll
> use the same approach for ser module fixups (so all ser module that use 
>  core fixup functions will be able to use also pvars).
> One could also force  kamailio or ser behaviour, by starting the
> script with #!KAMAILIO or #!SER.
> For future versions we should rethink how everything is accessed.
> For example I would use $foo for script variables and some other form
> for avps or the current pvar $foo. Also we would have to unify selects
> with the select like part of the pvars.
> (Note for ser users: pvars in kamailio are used to access script vars,
> avps, message headers and something which is kind of similar to ser
> selects.).
Perhaps select implementations can be moved in modules, as well. 
Integration has to be done at some point anyhow.


Daniel-Constantin Mierla

More information about the sr-dev mailing list