Hi,
I'd like to know how you guys on one hand accomplish the task of dispatching requests to trusted UACs grouped to destination classes (e.g. PSTN GWs, Media servers), and to check if an incoming request is from such a trusted UAC on the other hand.
Currently the following possibilities seem to be available:
- dispatcher-module: dispatching of outgoing requests based on destination classes using ds_select_dst/ds_next_dst, but no check for trusted incoming requests
- lcr-module: dispatching of outgoing requests using load_gws/next_gw and check for incoming requests using from_gw, but no support for destination classes
- permission-module: check for trusted incoming requests, but no outgoing dispatching
- a combination of dispatcher-module for outgoing requests and manual src-ip matching or permissions-module or lcr-module for checking trusted incoming requests
The problem with this is that maintaining these UACs gets unnecessarily complicated because there always has to be maintained two module config files or DB tables.
So I'm curious which approaches exist to accomplish this or if one module should be extended to combine these two tasks (e.g. adding destination class support for lcr or incoming src-ip check for dispatcher comes to mind)...
Thanks, Andy
andreas,
i have implemented two new functions from_peer and to_peer that based on two db tables check if request comes or goes from/to a (trusted) peer, but they may be too specific for my environment.
-- juha
Juha Heinanen writes:
i have implemented two new functions from_peer and to_peer that based on two db tables check if request comes or goes from/to a (trusted) peer, but they may be too specific for my environment.
also, there is currently no classification of peers, e.g., other proxies of the same organization, proxies of other operators, etc.
-- juha
Juha Heinanen wrote:
also, there is currently no classification of peers, e.g., other proxies of the same organization, proxies of other operators, etc.
Wouldn't it be sufficient for supporting classification to let the user optionally just pass the grp_id to load_gws() and from_gw() and extend the db query and the src-addr check to take that id into account?
Andy
Andreas Granig writes:
Wouldn't it be sufficient for supporting classification to let the user optionally just pass the grp_id to load_gws() and from_gw() and extend the db query and the src-addr check to take that id into account?
db_query cannot be made more complex than what it already is. someone reported that even the current query takes too much mysql time and therefore caching was developed.
like i did, i would prefer to leave gw selection as it is and write introduce new peer functions. those could very well take a class as argument.
-- juha
andy,
i'll send you the diffs privately first and if you think they are ok for general consumption (perhaps after adding peer class into the db tables), i can commit them to cvs.
-- juha
Juha Heinanen wrote:
i'll send you the diffs privately first and if you think they are ok for general consumption (perhaps after adding peer class into the db tables), i can commit them to cvs.
As far as I can see, it's basically the same concept as for load_gws()/next_gw() and from_gw() with some additional checks for incoming requests, isn't it? The thing I'm missing here for my usage is the fail-over handling (serial forking) to another outgoing peer. In many situations it's also not handy to split up incoming and outgoing peers into separate tables (administration overhead). But as you have pointed out, it's made to fit your own needs.
Some thoughts on the performance issue of the DB query regarding my patch which extends the functionality of load_gws(), from_gw() and to_gw():
- there's no performance impact in from_gw() and to_gw() - if no group id is passed to load_gws() , the query stays the same, thus also no performance impact - grp_id is filtered first in the query if passed to load_gws(), so the join of lcr and gw tables should speed up (I'm not a DB specialist though, just an assumption, correct me if I'm wrong) - if one has performance issues with non-cached load_gws(), he maybe should consider using caching anyway.
Andy
Andreas Granig writes:
As far as I can see, it's basically the same concept as for load_gws()/next_gw() and from_gw() with some additional checks for incoming requests, isn't it?
no, as you point out, there is no fail-over capability.
In many situations it's also not handy to split up incoming and outgoing peers into separate tables (administration overhead). But as you have pointed out, it's made to fit your own needs.
a peer may have more than one proxy, one for incoming and abother for outgoing traffic.
- if one has performance issues with non-cached load_gws(), he maybe
should consider using caching anyway.
search archives where someone pointed out that in mysql LIKE test is extremely slow, which prompted development of caching version of lcr module.
so i would suggest that if you want to include your functionality into lcr module, please do it using caching, because the idea is to deprecate non-cached version of lcr implementation once we have had enough practical experience with caching so that we are confident that there are no bugs in it.
-- juha
Juha Heinanen wrote:
so i would suggest that if you want to include your functionality into lcr module, please do it using caching, because the idea is to deprecate non-cached version of lcr implementation once we have had enough practical experience with caching so that we are confident that there are no bugs in it.
The patch already includes support for both the non-caching and the caching mode (I've attached it again because last time I sent it off-list).
Andy
Index: modules/lcr/lcr_mod.c =================================================================== RCS file: /cvsroot/openser/sip-server/modules/lcr/lcr_mod.c,v retrieving revision 1.25 diff -u -r1.25 lcr_mod.c --- modules/lcr/lcr_mod.c 21 Mar 2006 12:53:10 -0000 1.25 +++ modules/lcr/lcr_mod.c 27 Apr 2006 13:04:47 -0000 @@ -73,6 +73,7 @@ static void destroy(void); /* Module destroy function */ static int child_init(int rank); /* Per-child initialization function */ static int mod_init(void); /* Module initialization function */ +static int fixstring2int(void **param, int param_count); /* string to int fixup */
int reload_gws ( void );
@@ -221,9 +222,12 @@ * Module functions that are defined later */ int load_gws(struct sip_msg* _m, char* _s1, char* _s2); +int load_gws_grp(struct sip_msg* _m, char* _s1, char* _s2); int next_gw(struct sip_msg* _m, char* _s1, char* _s2); int from_gw(struct sip_msg* _m, char* _s1, char* _s2); +int from_gw_grp(struct sip_msg* _m, char* _s1, char* _s2); int to_gw(struct sip_msg* _m, char* _s1, char* _s2); +int to_gw_grp(struct sip_msg* _m, char* _s1, char* _s2); int load_contacts (struct sip_msg*, char*, char*); int next_contacts (struct sip_msg*, char*, char*);
@@ -232,9 +236,12 @@ */ static cmd_export_t cmds[] = { {"load_gws", load_gws, 0, 0, REQUEST_ROUTE | FAILURE_ROUTE}, + {"load_gws", load_gws_grp, 1, fixstring2int, REQUEST_ROUTE | FAILURE_ROUTE}, {"next_gw", next_gw, 0, 0, REQUEST_ROUTE | FAILURE_ROUTE}, {"from_gw", from_gw, 0, 0, REQUEST_ROUTE | FAILURE_ROUTE | ONREPLY_ROUTE}, + {"from_gw", from_gw_grp, 1, fixstring2int, REQUEST_ROUTE | FAILURE_ROUTE | ONREPLY_ROUTE}, {"to_gw", to_gw, 0, 0, REQUEST_ROUTE | FAILURE_ROUTE}, + {"to_gw", to_gw_grp, 1, fixstring2int, REQUEST_ROUTE | FAILURE_ROUTE}, {"load_contacts", load_contacts, 0, 0, REQUEST_ROUTE}, {"next_contacts", next_contacts, 0, 0, REQUEST_ROUTE | FAILURE_ROUTE}, {0, 0, 0, 0, 0} @@ -959,11 +966,10 @@ } }
- /* * Load info of matching GWs from database to gw_uri AVPs */ -int load_gws(struct sip_msg* _m, char* _s1, char* _s2) +static int do_load_gws(struct sip_msg* _m, int grp_id) { db_res_t* res; db_row_t *row, *r; @@ -1034,22 +1040,42 @@ }
if (db_mode == 0) { - q_len = snprintf(query, MAX_QUERY_SIZE, "SELECT %.*s.%.*s, %.*s.%.*s, %.*s.%.*s, %.*s.%.*s, %.*s.%.*s, %.*s.%.*s FROM %.*s, %.*s WHERE '%.*s' LIKE %.*s.%.*s AND '%.*s' LIKE CONCAT(%.*s.%.*s, '%%') AND %.*s.%.*s = %.*s.%.*s ORDER BY CHAR_LENGTH(%.*s.%.*s), %.*s.%.*s DESC, RAND()", - gw_table.len, gw_table.s, ip_addr_col.len, ip_addr_col.s, - gw_table.len, gw_table.s, port_col.len, port_col.s, - gw_table.len, gw_table.s, uri_scheme_col.len, uri_scheme_col.s, - gw_table.len, gw_table.s, transport_col.len, transport_col.s, - gw_table.len, gw_table.s, strip_col.len, strip_col.s, - gw_table.len, gw_table.s, prefix_col.len, prefix_col.s, - gw_table.len, gw_table.s, lcr_table.len, lcr_table.s, - from_uri.len, from_uri.s, - lcr_table.len, lcr_table.s, from_uri_col.len, from_uri_col.s, - ruri_user.len, ruri_user.s, - lcr_table.len, lcr_table.s, prefix_col.len, prefix_col.s, - lcr_table.len, lcr_table.s, grp_id_col.len, grp_id_col.s, - gw_table.len, gw_table.s, grp_id_col.len, grp_id_col.s, - lcr_table.len, lcr_table.s, prefix_col.len, prefix_col.s, - lcr_table.len, lcr_table.s, priority_col.len, priority_col.s); + if(grp_id >= 0) { + q_len = snprintf(query, MAX_QUERY_SIZE, "SELECT %.*s.%.*s, %.*s.%.*s, %.*s.%.*s, %.*s.%.*s, %.*s.%.*s, %.*s.%.*s FROM %.*s, %.*s WHERE %.*s.%.*s = %d AND '%.*s' LIKE %.*s.%.*s AND '%.*s' LIKE CONCAT(%.*s.%.*s, '%%') AND %.*s.%.*s = %.*s.%.*s ORDER BY CHAR_LENGTH(%.*s.%.*s), %.*s.%.*s DESC, RAND()", + gw_table.len, gw_table.s, ip_addr_col.len, ip_addr_col.s, + gw_table.len, gw_table.s, port_col.len, port_col.s, + gw_table.len, gw_table.s, uri_scheme_col.len, uri_scheme_col.s, + gw_table.len, gw_table.s, transport_col.len, transport_col.s, + gw_table.len, gw_table.s, strip_col.len, strip_col.s, + gw_table.len, gw_table.s, prefix_col.len, prefix_col.s, + gw_table.len, gw_table.s, lcr_table.len, lcr_table.s, + lcr_table.len, lcr_table.s, grp_id_col.len, grp_id_col.s, grp_id, + from_uri.len, from_uri.s, + lcr_table.len, lcr_table.s, from_uri_col.len, from_uri_col.s, + ruri_user.len, ruri_user.s, + lcr_table.len, lcr_table.s, prefix_col.len, prefix_col.s, + lcr_table.len, lcr_table.s, grp_id_col.len, grp_id_col.s, + gw_table.len, gw_table.s, grp_id_col.len, grp_id_col.s, + lcr_table.len, lcr_table.s, prefix_col.len, prefix_col.s, + lcr_table.len, lcr_table.s, priority_col.len, priority_col.s); + } else { + q_len = snprintf(query, MAX_QUERY_SIZE, "SELECT %.*s.%.*s, %.*s.%.*s, %.*s.%.*s, %.*s.%.*s, %.*s.%.*s, %.*s.%.*s FROM %.*s, %.*s WHERE '%.*s' LIKE %.*s.%.*s AND '%.*s' LIKE CONCAT(%.*s.%.*s, '%%') AND %.*s.%.*s = %.*s.%.*s ORDER BY CHAR_LENGTH(%.*s.%.*s), %.*s.%.*s DESC, RAND()", + gw_table.len, gw_table.s, ip_addr_col.len, ip_addr_col.s, + gw_table.len, gw_table.s, port_col.len, port_col.s, + gw_table.len, gw_table.s, uri_scheme_col.len, uri_scheme_col.s, + gw_table.len, gw_table.s, transport_col.len, transport_col.s, + gw_table.len, gw_table.s, strip_col.len, strip_col.s, + gw_table.len, gw_table.s, prefix_col.len, prefix_col.s, + gw_table.len, gw_table.s, lcr_table.len, lcr_table.s, + from_uri.len, from_uri.s, + lcr_table.len, lcr_table.s, from_uri_col.len, from_uri_col.s, + ruri_user.len, ruri_user.s, + lcr_table.len, lcr_table.s, prefix_col.len, prefix_col.s, + lcr_table.len, lcr_table.s, grp_id_col.len, grp_id_col.s, + gw_table.len, gw_table.s, grp_id_col.len, grp_id_col.s, + lcr_table.len, lcr_table.s, prefix_col.len, prefix_col.s, + lcr_table.len, lcr_table.s, priority_col.len, priority_col.s); + } if (q_len >= MAX_QUERY_SIZE) { LOG(L_ERR, "load_gws(): Too long database query\n"); return -1; @@ -1199,7 +1225,7 @@ if ((*gws)[j].ip_addr == 0) { break; } - if (lcr_rec.grp_id == (*gws)[j].grp_id) { + if (lcr_rec.grp_id == (*gws)[j].grp_id && (grp_id < 0 || (*gws)[j].grp_id == grp_id)) { /* 3. grp_id matching is done */ for (k = 0; k < gw_index; k++) { if ((*gws)[j].ip_addr == @@ -1392,6 +1418,28 @@ } }
+/* + * Load info of matching GWs from database to gw_uri AVPs + * taking into account the given group id. + */ +int load_gws_grp(struct sip_msg* _m, char* _s1, char* _s2) +{ + int grp_id; + + grp_id = (int)(long)_s1; + return do_load_gws(_m, grp_id); +} + +/* + * Load info of matching GWs from database to gw_uri AVPs + * ignoring the group id. + */ +int load_gws(struct sip_msg* _m, char* _s1, char* _s2) +{ + return do_load_gws(_m, -1); +} + +
/* * If called from request route block, rewrites scheme, host, port, and @@ -1532,7 +1580,7 @@ /* * Checks if request comes from a gateway */ -int from_gw(struct sip_msg* _m, char* _s1, char* _s2) +static int do_from_gw(struct sip_msg* _m, int grp_id) { int i; unsigned int src_addr; @@ -1543,7 +1591,8 @@ if ((*gws)[i].ip_addr == 0) { return -1; } - if ((*gws)[i].ip_addr == src_addr) { + if ((*gws)[i].ip_addr == src_addr && + (grp_id < 0 || (*gws)[i].grp_id == grp_id)) { return 1; } } @@ -1553,9 +1602,31 @@
/* + * Checks if request comes from a gateway, taking + * into account the group id. + */ +int from_gw_grp(struct sip_msg* _m, char* _s1, char* _s2) +{ + int grp_id; + + grp_id = (int)(long)_s1; + return do_from_gw(_m, grp_id); +} + +/* + * Checks if request comes from a gateway, ignoring + * the group id. + */ +int from_gw(struct sip_msg* _m, char* _s1, char* _s2) +{ + return do_from_gw(_m, -1); +} + + +/* * Checks if in-dialog request goes to gateway */ -int to_gw(struct sip_msg* _m, char* _s1, char* _s2) +static int do_to_gw(struct sip_msg* _m, int grp_id) { char host[16]; struct in_addr addr; @@ -1580,7 +1651,8 @@ if ((*gws)[i].ip_addr == 0) { return -1; } - if ((*gws)[i].ip_addr == addr.s_addr) { + if ((*gws)[i].ip_addr == addr.s_addr && + (grp_id < 0 || (*gws)[i].grp_id == grp_id)) { return 1; } } @@ -1589,6 +1661,29 @@ }
+/* + * Checks if in-dialog request goes to gateway, taking + * into account the group id. + */ +int to_gw_grp(struct sip_msg* _m, char* _s1, char* _s2) +{ + int grp_id; + + grp_id = (int)(long)_s1; + return do_to_gw(_m, grp_id); +} + + +/* + * Checks if in-dialog request goes to gateway, ignoring + * the group id. + */ +int to_gw(struct sip_msg* _m, char* _s1, char* _s2) +{ + return do_to_gw(_m, -1); +} + + /* * Frees contact list used by load_contacts function */ @@ -1825,3 +1920,28 @@
return 1; } + +/* + * Convert string parameter to integer for functions that expect an integer. + * Taken from mediaproxy module. + */ +static int fixstring2int(void **param, int param_count) +{ + unsigned long number; + int err; + + if (param_count == 1) { + number = str2s(*param, strlen(*param), &err); + if (err == 0) { + pkg_free(*param); + *param = (void*)number; + return 0; + } else { + LOG(L_ERR, "lcr/fixstring2int(): ERROR: bad number `%s'\n", + (char*)(*param)); + return E_CFG; + } + } + return 0; +} + Index: modules/lcr/doc/lcr_user.sgml =================================================================== RCS file: /cvsroot/openser/sip-server/modules/lcr/doc/lcr_user.sgml,v retrieving revision 1.9 diff -u -r1.9 lcr_user.sgml --- modules/lcr/doc/lcr_user.sgml 11 Jan 2006 11:38:55 -0000 1.9 +++ modules/lcr/doc/lcr_user.sgml 27 Apr 2006 13:04:48 -0000 @@ -67,8 +67,7 @@ </para> <para> In addition to gw and lcr tables there is third table gw_grp that is - used for administrative purposes only to associate names with gateway - group ids. + used to associate names with gateway group ids. </para> </section>
@@ -594,6 +593,30 @@ </section> <section> <title> + <function moreinfo="none">load_gws(int)</function> + </title> + <para> + Loads URI schemes, addresses, ports, and transports of gateways + that match user part of Request-URI and the given group id to + gw_uri_avp AVPs. Returns 1 or -1 depending on success. + </para> + <para> + This function can be used from REQUEST_ROUTE. + </para> + <example> + <title><function>load_gws</function> usage</title> + <programlisting format="linespecific"> +... +if (!load_gws("1")) { + sl_send_reply("500", "Server Internal Error - Cannot load gateways of group 1"); + break; +}; +... +</programlisting> + </example> + </section> + <section> + <title> <function moreinfo="none">next_gw()</function> </title> <para> @@ -666,6 +689,30 @@ </programlisting> </example> </section> + <section> + <title> + <function moreinfo="none">from_gw(int)</function> + </title> + <para> + Checks if request came from IP address of a gateway belonging + to the group with the given id. + </para> + <para> + This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, + ONREPLY_ROUTE. + </para> + <example> + <title><function>from_gw</function> usage</title> + <programlisting format="linespecific"> +... +if (from_gw("1")) { + ... + break; +}; +... +</programlisting> + </example> + </section> <section> <title> <function moreinfo="none">to_gw()</function> @@ -688,6 +735,29 @@ </programlisting> </example> </section> + <section> + <title> + <function moreinfo="none">to_gw(int)</function> + </title> + <para> + Checks if in-dialog request goes to a gateway belonging to + the group with the given id. + </para> + <para> + This function can be used from REQUEST_ROUTE, FAILURE_ROUTE. + </para> + <example> + <title><function>to_gw</function> usage</title> + <programlisting format = "linespecific"> +... +if (to_gw("1")) { + ... + break; +}; +... +</programlisting> + </example> + </section> <section> <title> <function moreinfo="none">load_contacts()</function>
Andreas Granig writes:
Loads URI schemes, addresses, ports, and transports of gateways
that match user part of Request-URI and the given group id to
gw_uri_avp AVPs. Returns 1 or -1 depending on success.
andreas,
this gives impression that group id need always be given. if i understood correctly, group id is optional. if so this should be clearly stated (also for other functions).
otherwise ok with me,
-- juha
Juha Heinanen wrote:
Andreas Granig writes:
Loads URI schemes, addresses, ports, and transports of gateways
that match user part of Request-URI and the given group id to
gw_uri_avp AVPs. Returns 1 or -1 depending on success.
this gives impression that group id need always be given. if i understood correctly, group id is optional. if so this should be clearly stated (also for other functions).
Group id is optional, yes.
I introduced new sections for each of the new functions, thus there will be a separate documentation section for load_gws(), load_gws(int), from_gw(), from_gw(int) and so on.
But I'm going to combine the sections as for example found in http://openser.org/docs/modules/1.1.x/nathelper.html#AEN229 and send you the patch.
Andy