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 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 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(); } } ...
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/openser/modules" loadmodule "sl.so" loadmodule "textops.so" loadmodule "tm.so" loadmodule "rr.so" loadmodule "maxfwd.so" loadmodule "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; }; } ...