Italic Text
Ramona-Elena Modroiu <ramona (at) rosdev.ro> Bogdan-Andrei Iancu Daniel-Constantin Mierla
Abstract
Tutorial for AVPOPS module of Kamailio (OpenSER) SIP Server.
AVPops (AVP-operations) module implements a set of script functions which allow access and manipulation of user AVPs (preferences). AVP means attribute-value-pair and it could be seen as a variable which can have integer or string ID and integer or string value. In Kamailio (OpenSER), an AVP is linked to SIP request or SIP transaction, if Kamailio (OpenSER) is in stateful mode. The AVP is automatically deallocated when the processing of the SIP request ends.
AVPs introduces in Kamailio (OpenSER) many new opportunities for implementing services and preferences per user or domain. With AVPops module they are usable directly from configuration script, allowing the administrators to customize the VoIP service in a more flexible way. Offering capabilities to load dynamically AVPs from databases, many situations that required to restart Kamailio (OpenSER) due to configuration changes can be avoided.
The AVPops module exports functions for interfacing DB resources (loading/storing/removing AVPs from database), functions for swapping information between AVPs and SIP messages, functions for testing/checking the value of an AVP.
The following modules must be loaded before this module:
The following libraries or applications must be installed before running Kamailio (OpenSER) with this module loaded:
The AVP core (provided by Kamailio core) defines two types of AVPs: with numerical ID and with string names. Each of them has its own advantage: the ID AVPs are faster to manipulate internally (no string operation involved); the name AVPs are easier to use (by nature, humans remember easier names than numbers). AVPOPS allows the usage of both types of AVPS, using the following grammar:
avp_name = string | ('I'|'i')':'ID | ('S'|'s')':'string
Examples:
To combine the benefits from both types of AVPS, AVPOPS module allows you to define aliases for AVP names. So you can use an ID avp - which is faster, but refer it by alias - as easy to remember string. The substitution of aliases with real AVP names is done only once, at configuration script compilation (at startup).
alias_definition = alias'='avp_name
alias = no_space_string
When used, the aliases are invoked with '$' sign in front (to avoid overlapping with predefined strings or AVP's name).
AVPs can be associated to a user or a domain. So, when performing DB operations like load/store/delete, the AVPs can be identified as follows:
The table format used by AVPOPS module in DB operations is:
Of a huge interest, is the posibility of accessing (loading) into AVPs more general data form all kind of sources (tables), sources which does not follow the standart AVPOPS database description. To do so, the AVPOPS module defines the DB schemes. Basically, a DB scheme describes how the AVPOPS should access and extract general data from a non-standart DB table - this description includes table name, column names, column to be used for extracting the value and pre-definition of value type.
In order to be used via DB schemes, the minimum requirement for a DB table is to contain at least an equivalent to UUID column or the equivalent to USERNAME and DOMAIN columns. This is required for making possible the AVP identification - by UUID or by USERNAME and DOMAIN.
A DB scheme contains:
For enabling DB operations, you must specify a DB URL pointing to the database containing the table(s) containing the AVPs. If required, a user name and password can be set for allowing the module to connect to the database server.
This parameter is optional, its default value being NULL.
Example 1. Set avp_url parameter
... modparam("avpops","avp_url","mysql://user:passwd@host/database") ...
Indicates the name of the default table that store the AVPS. This table must be locate into the database specified by “avp_url” parameter.
This parameter is required only if avp_url was set. Note that the default value is NULL, so , if you want to use it, you must explicitly set it.
Example 2. Set avp_table parameter
... modparam("avpops","avp_table","avptable") ...
Contains a multiple definition of aliases for AVP names.
Syntax:
This parameter is optional.
Example 3. Set avp_aliases parameter
... modparam("avpops","avp_aliases","uuid=I:660;email=s:email_addr;fwd=i:753") ...
If the domain part of the URI should be used for identifying an AVP in DB operations.
Default value is “0 (no)”.
Example 4. Set use_domain parameter
... modparam("avpops","use_domain","1") ...
Name of column containing the uuid (unique user id).
Default value is “uuid”.
Example 5. Set uuid_column parameter
... modparam("avpops","uuid_column","uuid") ...
Name of column containing the username.
Default value is “username”.
Example 6. Set username_column parameter
... modparam("avpops","username_column","username") ...
Name of column containing the domain name.
Default value is “domain”.
Example 7. Set domain_column parameter
... modparam("avpops","domain_column","domain") ...
Name of column containing the attribute name (AVP name).
Default value is “attribute”.
Example 8. Set attribute_column parameter
... modparam("avpops","attribute_column","attribute") ...
Name of column containing the AVP value.
Default value is “value”.
Example 9. Set value_column parameter
... modparam("avpops","value_column","value") ...
Name of column containing the AVP type.
Default value is “type”.
Example 10. Set type_column parameter
... modparam("avpops","type_column","type") ...
Definition of a DB scheme. Scheme syntax is:
Default value is “NULL (none)”.
If some elements of a DB scheme are missing, the default values at module level will be used for columns and table names. For “value_type”, default is “string”.
Example 11. Set db_scheme parameter
... modparam("avpops","db_scheme", "scheme1:uuid_col=uid;value_col=job;value_type=string;table=emp") - from table "emp", using "uid" column as uuid, get as string value the "job" column modparam("avpops","db_scheme", "scheme2:username_col=user;domain_col=domain;value_col=email;table=users") - from table "users", using "user" column as username and "domain" as domain column, get as string value the "email" column ...
Loads from DB into memory the AVPs corresponding to the given source (from user, to user, ruri user, uuid). Will be loaded all AVPs with name or, if empty, all AVPs. Normally, the function looks into the default table, but there is also the possibility to specify a different table to be used.
If a DB scheme is used, the data specified by the scheme will be loaded into AVP(s) name. In this case, full AVP name is required.
Function returns true only if there was at least one AVP loaded successfully.
Meaning of the parameters is as follows:
The following parameters are available:
Parameter syntax:
source = (sip_uri|avp_alias|str_value) ['/'('username'|'domain'|'uri'|'uuid')] sip_uri = '$from' | '$to' | '$ruri'
Parameter syntax is:
name = (''|'s:'|'i:'|avp_name|avp_alias)['/'db_spec] db_spec = table_name | '$'db_scheme_name
Example 12. avp_db_load usage
... avp_db_load("$from","i:678"); - loads all AVPs with ID 678 for username@domain from FROM header avp_db_load("$ruri/domain","i:/domain_preferences"); - loads all AVPs with ID from 'domain_preferences' table for domain from RURI avp_db_load("$uuid","s:404fwd/fwd_table"); - loads from table 'fwd_table' all AVPs with NAME '404fwd' and UUID given by the value of AVP defined by alias 'uuid' avp_db_load("$to","$email"); - loads all AVPs defined by alias 'email' for username@domain from TO header avp_db_load("keys","s:my_keys"); - loads all AVPs with NAME 'my_keys' using as UUID the string 'keys' modparam("avpops","db_scheme", "scheme1:uuid_col=uid;value_col=job;value_type=string;table=emp") avp_db_load("$uuid","s:jobs/$scheme1"); - load from table 'emp', using 'uid' column as uuid and 'job' column as string value into AVP name 'jobs' avp_db_load("sip:test@openser.org/uri","i:678"); - loads all AVPs with ID 678 for user 'test@openser.org' ...
Stores to DB the AVPs corresponding to the given source (from user, to user, ruri user, uuid). Will be stored all AVPs with name or, if empty, all AVPs. Normally, the function uses the default table, but there is also the possibility to specify a different table to be used.
The AVPs which were loaded from DB or which were already stored in a previous operation will not be stored into DB!!
Function returns true only if there was at least one AVP stored successfully.
The meaning and usage of the parameters are identical as for avp_db_load(source,name) function. Please refer to its description.
NOTE: function does not support DB schemes.
Example 13. avp_db_store usage
... avp_db_store("$to","i:678"); - all AVPs with ID 678 will be stored with username@domain from TO header avp_db_store("$ruri/username","$email"); - all AVPs defined by alias 'email' will be stored with username from RURI avp_db_store("$uuid","s:/fwd_table"); - all AVPs with NAME will be stored into table 'fwd_table' with the UUID given by the value of AVP defined by alias 'uuid' avp_db_store("keys","s:my_keys"); - stores all AVPs with NAME 'my_keys' using as uuid the string 'keys' ...
Deletes from DB the AVPs corresponding to the given source (from user, to user, ruri user, uuid). Will be deleted all AVPs with name or, if empty, all AVPs. Normally, the function uses the default table, but there is also the possibility to specify a different table to be used.
This function has no effect on the AVPs from memory!!
Function returns true if the delete operation was successfully.
The meaning and usage of the parameters are identical as for avp_db_load(source,name) function. Please refer to its description.
NOTE: function does not support DB schemes.
Example 14. avp_db_delete usage
... avp_db_delete("$to","i:678"); - deletes all AVPs with ID 678 using username@domain from TO header avp_db_delete("$ruri/username","$email"); - deletes all AVPs defined by alias 'email' using username from RURI avp_db_delete("$uuid","s:404fwd/fwd_table"); - deletes from table 'fwd_table' all AVPs with NAME '404fwd' using the UUID given by the value of AVP defined by alias 'uuid' avp_db_delete("keys","s:my_keys"); - deletes all AVPs with NAME 'my_keys' using as UUID the string 'keys' ...
The function writes some value (given) or some information from the SIP message into a new AVP. The information is specified by source and the AVP name by name (it cannot be empty or generic).
Function returns true if the value was written into AVP successfully. Meaning of the parameters is as follows:
If a SIP URI is used, other than '$duri', the following parameters are available:
Parameter syntax:
value = (variable) | (fix_value) variable = '$src_ip' | '$duri' | '$hdr[hdr_name]' | (sip_uri)['/'('username'|'domain')] sip_uri = '$from' | '$to' | '$ruri' fix_value = 'i:'integer | 's:'string | string
Example 15. avp_write usage
... avp_write("$to","i:678"); - writes the whole TO uri as AVP ID 678 avp_write("$ruri/username","$email"); - writes the RURI username into AVP defined by alias 'email' avp_write("foobar","s:test"); - writes the 'foobar' string into AVP NAME 'test' avp_write("i:333","i:6"); - writes the integer 333 into AVP ID 6 avp_write("$src_ip","ip"); - writes the source IP into AVP name 'ip' avp_write("$hdr[My-header]","i:11"); - writes the body of the first header named 'My-header' into AVP id '11' ...
Deletes from memory the AVP(s) with name or, if empty, all AVPs.
Function returns true if at least one AVP was deleted successfully.
Meaning of the parameters is as follows:
Parameter syntax is:
name = (''|'s:'|'i:'|avp_name|avp_alias)['/'flag] flag = 'g' | 'G'
Without g global flag, if a full AVP name is given, only the first matching AVP will be deleted. The global flag can force the deletion of all AVP matching the given name.
Example 16. avp_delete usage
... avp_delete("i:678"); - deletes the first AVPs with ID 678 avp_delete("$email/g"); - deletes all AVPs defined by alias 'email' avp_delete("i:"); - deletes all AVPs with ID avp_write(""); - deletes all AVPs ...
Pushes the value of the (all) AVP(s) with name into the SIP message as RURI or header (destination).
Function returns true if at least one AVP was pushed successfully into the SIP message.
Meaning of the parameters is as follows:
By default the whole RURI will be replaced.
By default the header is consider belonging to request. Parameter syntax:
destination = ruri_dst | '$duri' | hdr_dst ruri_dst = '$ruri'['/'('username'|'domain')] hdr_dst = '$hdr_name'['/'('request'|'reply')]
Parameter syntax is:
name = ( avp_name | avp_alias )['/'flags] flags = 'g'
Example 17. avp_pushto usage
... avp_pushto("$ruri","i:678"); - first AVP with ID 678 will be pushed to RURI avp_pushto("$ruri/domain","s:backup_domains/g"); - all AVPs with NAME 'backup_domains' will be pushed into domain RURI (request will fork) avp_pushto("$Email/reply","s:email"); - first AVP with NAME 'email' will be pushed as header into reply avp_pushto("$Foo","$bar/g"); - all AVPs defined by alias 'bar' will be pushed as header into request avp_pushto("$duri","i:670"); - first AVP with ID 670 will be pushed to dst_uri ...
Checks the value of the (all) AVP(s) with name against the operator and value (op_value).
If the two involved values have different types (string/integer), the check will be considered failed and skipped.
Function returns true if at least one AVP satisfies successfully the check.
Meaning of the parameters is as follows:
Check operators can be:
The value can be:
Integer values can be given in hexadecimal using notation: 'i:0xhex_number' (e.g.,: 'i:0xabcd'); Possible flags are:
Parameter syntax is:
* op_value = operator '/' value ['/'flags] * operator = 'eq' | 'ne' | 'lt' | 'le' | 'gt' | 'ge' | 're' | 'fm' | 'and' | 'or' | 'xor' * value = variable | fix_value * variable = '$src_ip'|'$from'|'$ruri'|'$from'|avp_alias * fix_value = 'i:'integer | 's:'string | string * flags = 'g' | 'G' | 'i' | 'I'
NOTE: for re operator, the value can be only a string fixed value which defines the regular expression. NOTE: for fm operator, the value can be only a string fixed value which defines the regular expression or an AVP ALIAS which containes the regular expression.
Example 18. avp_check usage
... avp_check("i:678", "lt/i:345/g"); - check values of all AVP with ID 678 if less than integer 345. avp_check("s:person","eq/$from/I"); - check case insensitive if AVP NAME 'person' equals to FROM URI avp_check("s:foo","gt/$bar/g"); - checks if one of the values of the AVP NAME "foo" is greater than the one of values of AVP defined by alias 'bar' avp_check("s:friends","re/sip:.*@bar.com/g"); - checks if one of the values of the AVP NAME "friends" matches the regular expression "sip:.*@bar.com" avp_write("*@siphub.net","$fm_avp"); avp_write("$from","$value"); avp_check("$value","fm/$fm_avp/g"); - checks if one of the values of the AVP NAME "value" matches the regular expression "*@siphub.net" (checks if the FROM URI belongs to "siphub.net" domain) NOTE: the regular expresion can be also loaded from DB avp_check("i:678", "lt/i:345/g"); - check values of all AVP with ID 678 if less than integer 345 avp_check("i:678", "and/i:0x02"); - check if the value of AVP i:678 has the second bit set ...
Copies / moves (all) AVP(s) with old_name under a different name (new_name), keeping the original value.
Both names can be AVP aliases and there is no restriction about their types (AVP ID or AVP NAME); any combination is allowed. Function returns true if at least one AVP was successfully copied or moved. Meaning of the parameters is as follows:
name = ( avp_name | avp_alias ) ['/'flags] flags = 'g' | 'G' | 'd' | 'D' | 'n' | 'N' | 's' | 'S'
Meaning of the flags:
Example 19. avp_copy usage
... avp_copy("i:678", "s:test/g"); - copy all AVPs with ID 678 as AVPs with NAME "test" (duplication). avp_copy("$old", "$new/gd"); - copy all AVPs defined by alias "old" as AVPs defined by alias "new" and delete the original AVPs (rename). ...
The function executes integer operations with AVPs.
Meaning of the parameters is as follows:
Parameter syntax is:
name = ( source_avp[/destination_avp] ) source_avp = ( avp_name | avp_alias ) destination_avp = ( avp_name | avp_alias )
Parameter syntax is:
op_value = operator '/' value ['/'flags] operator = 'add' | 'sub' | 'mul' | 'div' | 'mod' | 'and' | 'or' | 'xor' | 'not'
Meaning of the operators:
add - arithmetic addition (in C: '+') sub - arithmetic substitution (in C: '-') mul - arithmetic multiplication (in C: '*') div - arithmetic division (in C: '/') mod - arithmetic modulo (in C: '%') and - bitwise AND (in C: '&') or - bitwise OR (in C: '|') xor - bitwise XOR (in C: '^') not - bitwise negation (in C: '~') value = variable | fix_value variable = avp_alias fix_value = 'i:'integer flags = 'g' | 'G' | 'd' | 'D'
Meaning of the flags:
Integer values can be given in hexadecimal using notation 'i:0xhex_number' (e.g.,: 'i:0xabcd');
Example 20. avp_op usage
... modparam("avpops", "avp_aliases", "number=i:30;number2=i:40") ... avp_write("i:20","$number"); avp_write("i:5", "$number2"); avp_op("i:30", "add/i:5/g"); avp_op("$number","sub/$number2/d"); avp_op("$number2","or/i:0x02/d"); ...
Prints the formatted string 'format' in the AVP 'dest'. The 'format' parameter can include any pseudo-variable defined in Kamailio (OpenSER). The list with all pseudo-variables in Kamailio (OpenSER) can be found at: http://kamailio.org/docs/pseudo-variables.html.
The function can be used for string concatenation between AVPs, pseudo-variables and constants. Functionalities like prefix or suffix are covered, as well.
Meaning of the parameters is as follows:
Example 21. avp_printf usage
... avp_printf("i:20", "This is a $rm request with the call-id $hdr(call-id)"); ...
Perl/sed-like substitutions applied to AVPs having string value. Meaning of the parameters is as follows:
Meaning of the flags: 'g' | 'G' - apply the operations to all 'source_avp' 'd' | 'D' - delete the 'source_avp' after operation.
Parameter syntax is:
* subst = "/regexp/replacement/flags" * regexp - regular expression * replacement - replacement string, can include pseudo-variables and \1, ..., \9 for matching tokens, \0 for whole matching text * flags = 'g' | 'G' | 'i' | 'i'
Meaning of the flags:
Example 22. avp_subst usage
... # if avp i:678 has a string value in e-mail format, replace the # domain part with the value of domain part from R-URI avp_subst("i:678", "/(.*)@(.*)/\1@$rd/"); # if any avp i:678 has a string value in e-mail format, replace the # domain part with the value of domain part from R-URI # and place the result in avp i:679 avp_subst("i:678/i:679/g", "/(.*)@(.*)/\1@$rd/"); ...
IMPORTANT NOTE: if the replacement string includes src_avp or dst_avp you will get something that you may not expect. In case you have many src_avp and you make the substitution to be applied to all of them, after the first src_avp is processed, it will be added in avp list and next processing will use it.
Check if any AVP identified by name is set. Flags can be used to test if the AVP has number or string value, as well. Meaning of the parameters is as follows:
Meaning of the flags:
If no flag is specified, the function returns true if any AVP with name 'name' exists.
Example 23. is_avp_set usage
... if(is_avp_set("i:678/n")) log("AVP with integer id 678 and having integer value exists\n"); if(is_avp_set("$email")) log("AVP having the alias $email exists\n"); ...
Prints the list with all the AVPs from memory. This is only a helper/debug function.
Example 24. avp_print usage
... avp_print(); ...
The AVPOPS module requires one more column in the usr_preferences table than how it is created now by ser_mysql script. The missing column is “type”. Till the discrepancy is fixed, please add by hand the column “type” integer, not null, default 0.
For MySQL databases, if you want to create the table from scratch, you can use the next SQL script with a MySQL client.
Example 25. usr_preferences - table structure
... DROP TABLE IF EXISTS usr_preferences; CREATE TABLE usr_preferences ( uuid varchar(64) NOT NULL default '', username varchar(100) NOT NULL default '', domain varchar(128) NOT NULL default '', attribute varchar(32) NOT NULL default '', value varchar(128) NOT NULL default '', type integer NOT NULL default '0', modified timestamp(14) NOT NULL, PRIMARY KEY (username, domain, attribute, value) ) TYPE=MyISAM; ...
To add the “type” column, you can use the next SQL script.
Example 26. usr_preferences - adding 'type' column
... ALTER TABLE usr_preferences ADD COLUMN type integer NOT NULL default 0; ...
A few examples of how to use the avpops module are listed below.
Trusting different source IPs for each local domain (in multi or single domain scenarios).
In all Kamailio (OpenSER) based platforms, there are some external components which are considered trusted and no authentication is required for them - as Gateways, Media Servers, B2Bua, etc. In a multi-domain setup, for each domain will be a different set of components (IPs) which must be trusted.
Even in a single-domain configuration, hardwiring these IPs in config file will require restarting Kamailio (OpenSER) at each modification (adding/removing IPs). To implement this, you can define as AVPs belonging to a domain, all the IPs considered as trusted.
Let's consider AVP name 't_ips' of type 0 (string name and string value), stored in 'ips' table:
... uuid username domain attribute value type "" "" "domain1" "t_ips" "10.10.0.5" 0 "" "" "domain1" "t_ips" "10.10.0.7" 0 "" "" "domain2" "t_ips" "10.10.0.5" 0 ...
So, domain 'domain1' will trust IPs 10.10.0.5 and 10.10.0.7, but domain 'domain2' will trust only 10.10.0.5.
The script will look like:
Example 27. Trusted IPs example
... # if the request pretends to belong to a local domain if (is_from_local()) { # authenticate only INVITE and MESSAGES if (method=="INVITE" || method=="MESSAGE") { # is it a trusted IP address? - first load the trusted IPs (avp # NAME 't_ips" from DB table "ips") for the target domain (domain part # of RURI); then check if at least one value of 't_ips' AVPs equals # the source IP of the request if (!(avp_db_load("$ruri/domain","s:t_ips/ips") && avp_check("s:t_ips", "eq/$src_ip/gi"))) { #do proxy authentication ........... } } } ...
A user can own one or more conference rooms. He can allow access to each of these rooms only to some desired people (depending from room to room). For each conference room, there will be defined by AVPs the users who can access it.
The AVPs will belong to the conference room and the value will be the allowed users. Let's say, for '001' conference room, on 'domain1' domain, we define the AVPs ID 123, type 2 (ID name, string value - ID AVPs are faster):
... uuid username domain attribute value type "" "001" "domain1" "123" "user1@domain1" 2 "" "001" "domain1" "123" "user2@domain1" 2 "" "001" "domain1" "123" "userx@domain2" 2 ...
This scenario works in single-domain, but also in multi-domain scenarios, by using domain along with username.
The script will look like:
Example 28. Restricting access example
... #define 'conf_allowed' alias for AVP ID 123 modparam("avpops","avp_aliases","conf_alowed=i:123") modparam("avpops","use_domain",1) # make sens for multi-domain setups ..... ..... # is it for conference service? (prefix 444) if (uri=~"sip:444.*@") { strip(3); # we have in username only the conference room number - load all users # that are allowed to access (if any) from "conf" db table and check # if the caller (from user) is among these users if ( avp_db_load("$ruri", "$conf_allowed/conf") && avp_check("$conf_allowed", "eq/$from/gi") ) { # user allowed to access conf room .......... break; } else { sl_send_reply("403", "Forbidden - no access to conf"); break; } } ...
The canonical format of a URI (username@domain) is obtained after all the possible transformations where applied to the received RURI: enum query, aliases, speed-dial, prefixes, etc. As all the checks and service access rely on this canonical URI, it's imperative to have it saved until the request processing is over - a request can be processed in multiple steps, if a forward on failure mechanism is used.
Obviously, any other useful information can be stored to be used later, in a next processing step. Also, the usage of canonical URI is very useful in accounting, since no other transformation is needed when accounting data are processed.
Let's store the canonical URI into memory as AVP ID 34.
Example 29. Canonical URI example
... modparam("avpops","avp_aliases","can_uri=i:34") ..... route { ..... #apply all transformations for RURIs targeting your domain ..... #save the canonical URI avp_write("$ruri","$can_uri"); .... t_on_failure("x"); .... } failure_route[x] { # set back the canonical URI avp_pushto("$ruri", "$can_uri"); # resume processing ..... } ...
The greatest obstacle in implementing serial forking was storing the next addresses (as many as they are) for later usage and retrieving one by one in the next cycles (forward/failure cycles).
By storing all the next addresses as AVPs, it will be possible to use them in failure routes as next forward target. There are no limitations; the AVP list (with all addresses to be used in serial forking) can be defined before starting the forking process and also can be modified (by removing or adding new AVP) during the forking process.
Let's use the AVP ID 665 for keeping all addresses for serial forking. The script will look like:
Example 30. Serial forking example
... modparam("avpops","avp_aliases","serial_fork=i:665") ..... route { ..... # build the initial set of addresses for serial forking # either loading them from DB (let's say table fork) avp_db_load("$ruri", "$serial_fork/fork") # either by writing them avp_write("sip:addr1@domain1", "$serial_fork"); avp_write("sip:addr2@domain1", "$serial_fork"); # either by calling some hypothetical module function which generates the # AVP list set_fork_list(); ...... # intercept failure replies t_on_failure("x"); t_relay(); } failure_route[x] { # use the first element of the list (if any) and delete it from list if (avp_pushto("$ruri", "$serial_fork")) { append_branch(); avp_delete("$serial_fork"); t_on_failure("x"); t_relay(); } } ...
Note: since internally the AVP list is kept backwards (a new AVP is inserted at the begin), the serial forking list should be built in opposite order.
This is a trivial example that shows how the capabilities of AVPOPS module can be used for a wide range of checking using elements from AVPs and parts of SIP messages - from URI, To uri, Request URI, source IP, etc.
The example shows how to check if a request is to be sent to the same address as the one it was received from:
Example 31. Origin and destination checks example
... # get to the final RURI if (!lookup("location")) { sl_send_reply("404","Not found"); break; } # get the host part of the final uri (IP part) and store it in AVP ID 13 avp_write("$ruri/domain", "i:13"); if (avp_check("i:13","eq/$src_ip/i")) { # source IP is the same as destination IP ........... } avp_delete("i:13/g"); ...
This example shows how to use avpops module to store bitmap ACL (access control list) per user to control the calls to different destinations. The big advantage for this approach is the speed of processing (only one DB query and bitwise operations).
This approach is a replacement of 'group' usage which requires a DB query for each group membership checking.
Example 32. ACL per user as bitmap example
... mpath="/usr/local/lib/kamailio/modules" loadmodule "sl.so" loadmodule "textops.so" loadmodule "tm.so" loadmodule "rr.so" loadmodule "maxfwd.so" loadmodule "db_mysql.so" loadmodule "avpops.so" # ----------------- setting module-specific parameters --------------- --- avpops params --- modparam("avpops", "avp_url","mysql://openser:openserrw@localhost/openser") modparam("avpops","db_scheme", "scheme0:username_col=username;domain_col=domain;value_col=acl;value_type=integer;table=subscriber") modparam("avpops","avp_aliases","acl=i:800") # ------------------------- request routing logic ------------------- # main routing logic route{ # filter too old messages if (!mf_process_maxfwd_header("10")) { log("LOG: Too many hops\n"); sl_send_reply("483","Too Many Hops"); return; }; if (len_gt( max_len )) { sl_send_reply("513", "Wow -- Message too large"); return; }; if (loose_route()) { t_relay(); return; }; if (method=="INVITE") { record_route(); } else { t_relay(); return; }; if(!uri=~"sip:[0-9]+@") { sl_send_reply("403", "Forbidden - Bad dialed number"); return; }; if(!avp_db_load("$from","$acl/$scheme0")) { sl_send_reply("403", "Forbidden - No ACL"); return; }; # ACL bitmap # # 1 - free destination # 2 - local call # 3 - long distance call # 4 - international call # if(uri=~"sip:0900[0-9]+") /* free destinations */ { if (!avp_check("$acl", "and/i:0x01") { sl_send_reply("403","Forbidden - Free Destinations Not Allowed"); return; }; } else if(uri=~"sip:0[1-9][0-9]+") { if (!avp_check("$acl", "and/i:0x04") { sl_send_reply("403","Forbidden - Long Distance Calls Not Allowed"); return; }; } else if(uri=~"sip:00[1-9][0-9]+") { if (!avp_check("$acl", "and/i:0x08") { sl_send_reply("403","Forbidden - International Calls Not Allowed"); return; }; } else { if (!avp_check("$acl", "and/i:0x02") { sl_send_reply("403","Forbidden - Local Calls Not Allowed"); return; }; }; # authorized # rewritehostport("gateway_ip:gateway_port"); rewritehostport("10.10.10.10:5060"); if (!t_relay()) { sl_reply_error(); return; }; } ...