Chinaunix首页 | 论坛 | 博客
  • 博客访问: 193556
  • 博文数量: 31
  • 博客积分: 2595
  • 博客等级: 少校
  • 技术积分: 334
  • 用 户 组: 普通用户
  • 注册时间: 2008-07-28 16:03
个人简介

知行合一

文章分类

全部博文(31)

文章存档

2015年(1)

2014年(1)

2010年(9)

2009年(20)

我的朋友

分类: 系统运维

2009-08-10 23:19:25

注:以下文章如需转载,请注明所属作者,转载地址,谢谢!

第七章:与PSTN的连通(Connectivity to the PSTN

在前两章中,我们已经使用认证(authentication)和数据库为OpenSER处理通话做好了准备。SerMyAdmin用来处理数据记录。然而,你仍然不能打给普通电话,因为你没有连上PSTN。现在的挑战是如何将通话由PSTN路由进来和如何将通话路由到PSTNPublic Switched Telephone Network


为了能将通话路由到PSTN,你需要一个叫做SIP PSTN网关(SIP PSTN Gateway)的设备。在市面上,有很多生产这种设备的厂家,诸如CiscoAudioCodesNortelQuintum等等。你也可以使用Asterisk PBX完成这个工作。Asterisk是一个你绝对能够承担的起的网关,而且与上面提到的各个厂商的设备兼容。它完全是开源的,也是按照GPL许可的。

这一章的结束,你将能够:

l         OpenSERSIP网关连上

l         将认证应用到带内通话

l         使ACLs防止PSTN网关不被没有经过认证的用户使用

l         使用LCRLeast Cost Route)模块路由你的通话

l         使用SerMyAdmin来管理授信主机(Trusted Hosts),网关(Gateways)和路由器(Routes

在这一章中,你将学会如何将通话打到PSTN。我们将介绍三个新的模块(LCRPERMISSIONS,还有GROUP),他们将帮助你路由这些通话并保证他们的安全。你可以在互联网上轻易的找到关于regexps的指南。如果你对regular expressionsregexps不熟悉,这条廉洁可以作为参考。

我们在哪儿?(Where Are We?)

VoIP服务提供商的方案中有很多的部件。为了避免迷失,我们将在每一章中展示下面的这张图片。在这一章中,我们将利用SIP代理部件和PSTN网关一起工作。

本章之后,我们的VoIP提供者将能够使用SIP网关将通话打给PSTN

发往网关的请求(Requests Sent to the Gateway

在标明给网关的请求中,我们必须验证该用户属于哪一组(group),以查看其是否被允许使用PSTN


要达到这个目的,我们要使用到‘group’模块。这个暴露了函数is_user_in(“credentials”, “group”)用来检查用户是否属于指定组。在上面的例子中,我们已经创建了3个组:local代表本地通话,ld代表长途,int代表国际长途。脚本中,我们使用正则表达式(regular expressions)来检查通话是属于上面介绍的三种中的哪一种。

你必须将这些组插进叫做groupMySQL表中才能使用它。你可以很容易的插入,删除,显示组成员(group membership):

  • Openserctl acl show []
  • Openserctl acl grant
  • Openserctl acl revoke []

使用SerMyAdmin来管理你的表也是可能的。要添加和删除组,你可以浏览“User Groups”部分,在那里,你可以添加,删除组(groups)。

 

要改变用户的组成员,你可以在下面显示的用户菜单菜单中进行编辑:

 

使用检查栏选择用户属于的这儿显示的指点的组。

来自网关的请求(Requests Coming From the Gateway

现在,我们要使用PERMISSIONS模块来对来自没有摘要认证过程的PSTN网关的通话进行授权。

 

我们将使用的allow_trusted()函数有PERMISSIONS模块曝露出来。许可模块可以被用来授权(authorizeREGISTERREFER,和INVITE请求。我们可以用permissions.allowpermissions.denyregister.allow,和register.deny文件来对其进行管理和调节。然而这个模块中使用的allow_trusted()函数要将请求的源IP地址同我们数据库中的授信表中的数据进行比较检查。

当通话到来时,函数allow_trusted要试着去找到一条符合该请求的规则。该规则包含下面的域

如果下面的规则存在一条,则接受该请求:

l        Ip地址和请求的源IP地址相同

l        传输层协议是“any”或是同请求的传输层协议相符

l        正则表达式为空或符合请求

对于网关来说,不注册到SIP代理上是很正常的事。因此,来自网关的请求不应该接收“407 Proxy Authentication Required”响应。在我们目前的脚本中,所有来自我们域的INVITE请求都要求他们带有凭据。然而,如果像从网关发出的请求,没有凭据发出,从而通话将会失败。所有,为了修正这一点,我们使用allow_trusted()函数检查源IP地址的方式,而不是去检查凭据。

           不要忘了将授信的IP地址插入我们MySQL数据库的授信表中,      

           这样我们的脚本才能工作。                                                              

你可以使用SerMyAdmin来查看,更新授信主机列表(trusted hosts list)。使用如下显示的授信主机菜单:


要添加新的主机,只需简单的点击“New Trusted Host”菜单选项。


使用函数rewritehostport()前转通话到PSTN网关。


这个脚本的名字为openser.pstn。可以在找到。一份拷贝展示如下。之前的脚本修改处使用高亮显示。

# ------------------ module loading ----------------------------------

 #set module path

 mpath="//lib/openser/modules/"

 loadmodule "mysql.so"

 loadmodule "sl.so"

 loadmodule "tm.so"

 loadmodule "rr.so"

 loadmodule "maxfwd.so"

 loadmodule "usrloc.so"

 loadmodule "registrar.so"

 loadmodule "textops.so"

 loadmodule "uri.so"

 loadmodule "uri_db.so"

 loadmodule "domain.so"

 loadmodule "permissions.so"

 loadmodule "group.so"

 loadmodule "mi_fifo.so"

 # Uncomment this if you want digest authentication

 # mysql.so must be loaded !

 loadmodule "auth.so"

 loadmodule "auth_db.so"

 # ----------------- setting module-specific parameters ---------------

 modparam("mi_fifo", "fifo_name", "/tmp/openser_fifo")

 modparam("usrloc", "db_mode", 2)

 modparam("auth_db", "calculate_ha1", yes)

 modparam("auth_db", "password_column", "password")

 modparam("rr", "enable_full_lr", 1)

 modparam("auth_db|permissions|uri_db|usrloc","db_url",

          "mysql:// openser:openserrw@localhost/openser")

 modparam("permissions", "db_mode", 1)

 modparam("permissions", "trusted_table", "trusted")

 # -------------------------  request routing logic -------------------

 # main routing logic

 route{

         #

         # -- 1 -- Request Validation

         #

         if (!mf_process_maxfwd_header("10")) {

                       sl_send_reply("483","Too Many Hops");

                       exit;

         };

         if (msg:len >= 2048 ) {

                       sl_send_reply("513", "Message too big");

                       exit;

         };

          #

          # -- 2 -- Routing Preprocessing

          #

          ## Record-route all except Register

          if (!method=="REGISTER") record_route();

         ##Loose_route packets

         if (loose_route()) {

                       # mark routing logic in request

                       append_hf("P-hint: rr-enforced\r\n");

                       route(1);

         };

 #CANCEL processing

     if (is_method("CANCEL")) {

         if (t_check_trans()) t_relay();

         exit;

     };

     t_check_trans();

         #

         # -- 3 -- Determine Request Target

         #

         if (method=="REGISTER") {

                       route(2);

         } else {

                       route(3);

         };

 }

 route[1] {

         #

    # -- 4 -- Forward request to target

         #

         ## Forward statefully

         if (!t_relay()) {

                        sl_reply_error();

         };

         exit;

 }

 route[2] {

         ## Register request handler

         if (is_uri_host_local()) {

                        if (!www_authorize("", "subscriber")) {

                                     www_challenge("", "1");

                                     exit;

                        };

                        if (!check_to()) {

                                     sl_send_reply("403", "Forbidden");

                                     exit;

                        };

                        save("location");

                 exit;

         } else if {

                        sl_send_reply("403", "Forbidden");

         };

 }

 

route[3] {

         ## INVITE request handler

         if (is_from_local()){

             # From an internal domain -> check the credentials

              and the FROM

               if(!allow_trusted()){

                     if (!proxy_authorize("","subscriber")) {

                           proxy_challenge("","1");

                           exit;

                     } else if (!check_from()) {

                           sl_send_reply("403", "Forbidden, use From=ID");

                           exit;

                     };

               } else {

                     log("Request bypassed the auth.using allow_trusted");

               };

             

               consume_credentials();

               #Verify aliases, if found replace R-URI.

               lookup("aliases");

               if (is_uri_host_local()) {

                   # -- Inbound to Inbound

                   route(10);

                } else {

                   # -- Inbound to outbound

                   route(11);

                };

         } else {

                        #From an external domain ->do not check credentials

                        #Verify aliases, if found replace R-URI.

                        lookup("aliases");

                        if (is_uri_host_local()) {

                       #-- Outbound to inbound

                       route(12);

                        } else {

                       # -- Outbound to outbound

                       route(13);

                        };

         };

 }

 route[4] {

       # routing to the public network

       rewritehostport("10.1.30.45");

       route(1);

 }

 route[10] {

       #from an internal domain -> inbound

       #Native SIP destinations are handled using the location table

       #Gateway destinations are handled by regular expressions

       append_hf("P-hint: inbound->inbound \r\n");

       if (uri=~"^sip:[2-9][0-9]{6}@") {

            if (is_user_in("credentials","local")) {

                 route(4);

                 exit;

            } else {

                 sl_send_reply("403", "No permissions for local calls");

                 exit;

            };

       };

       if (uri=~"^sip:1[2-9][1-9]{9}@") {

           if (is_user_in("credentials","ld")) {

                 route(4);

                 exit;

           } else {

              sl_send_reply("403", "No permissions for long distance");

                 exit;

           };

       };

       if (uri=~"^sip:011[0-9]*@") {

           if (is_user_in("credentials","int")) {

                route(4);

                exit;

           } else {

                sl_send_reply("403", "No permissions for

                                   international calls");

           };

       };

       if (!lookup("location")) {

           sl_send_reply("404", "Not Found");

           exit;

       };

       route(1);

 }

 route[11] {

      # from an internal domain -> outbound

      # Simply route the call outbound using DNS search

      append_hf("P-hint: inbound->outbound \r\n");

      route(1);

 }

 

route[12] {

      # From an external domain -> inbound

      # Verify aliases, if found replace R-URI.

      lookup("aliases");

      if (!lookup("location")) {

           sl_send_reply("404", "Not Found");

           exit;

      };

      route(1);

 }

 route[13] {

   #From an external domain outbound

   #we are not accepting these calls

   append_hf("P-hint: outbound->inbound \r\n");

   sl_send_reply("403", "Forbidden");

   exit;

 }

 

openser.cfg检查(openser.cfg Inspection

PERMISSIONS模块曝露了一些重要的函数以对到我们SIP代理的访问进行控制。其中之一是allow_trusted(),这个函数允许我们控制网关使用IP地址访问代理,而不是使用认证凭据。授信表(trusted table)是授信地址的存储库。你应该将每个网关的IP地址和传输层协议插入这个数据库。这就使得来自网关的请求避免了标准摘要认证的进行。

PERMISSIONS模块还有些标准的许可和拒绝文件。我们这次不使用这些特性。为了不要日志中的这些信息,请将PERMISSIONS模块的config文件夹下的文件拷贝到/etc/openser文件夹下。

cp /usr/src/openser-1.2.2/modules/permissions/config/* /etc/openser

在许可文件中,基于正则表达式来过滤请求是可能的,这可以改进环境的安全性。检查用例文件是不是符合正确的句法。

group.so模块用来检查用户的组成员资格。这个叫做ACLAccess Control List)。你可以使用openserctl工具(如下)来添加,删除或是显示用户ACLs

loadmodule “permissions.so”

loadmodule “group.so”

下面的第一行告诉模块到哪去寻找传递需要凭据的数据库。第二行提示模块使用数据库上的缓存(cache)访问以增加性能。

modparam("auth_db|permissions|uri_db|usrloc","db_url",

 "mysql://openser:openserrw@localhost/openser")

 modparam("permissions", "db_mode", 1)

 

当的代理服务器收到INVITE请求后,通常的行为是向UAC请求凭据。然而,PSTN网关通常并不对认证进行响应。因此,你需要采取特殊的处理过程。函数allow_trusted()将INVITE请求的源IP地址同数据库中授信表进行对比检查。如果符合,则接受之。如果不符合,则再请求凭据。

if(!allow_trusted()){

     if (!proxy_authorize("","subscriber")) {

             proxy_challenge("","0");

             exit;

     } else if (!check_from()) {`

           sl_send_reply("403","Forbidden, use FROM=ID");

             exit;

     };

  };

 

 

                         网关的IP地址插入数据库是很重要的                          

你可以使用诸如SerMyAdmin或是phpMyAdmin的工具来维护数据库。这样可比手动在MySQLCLICommand line interface)操作容易的多。

在授信表中,插入的是网关的IP地址,传输层协议(udptcptlsany)和正则表达式。

下面是我们按照正则表达式进行通话的路由:

if (uri=~"^sip:[2-9][0-9]{6}@") {

    if (is_user_in("credentials","local")) {

        route(4);

        exit;

    } else {

        sl_send_reply("403", "No permissions for local calls");

        exit;

    };

 };

 

if (uri=~"^sip:1[2-9][0-9]{9}@") {

    if (is_user_in("credentials","ld")) {

       route(4);

       exit;

    } else {

       sl_send_reply("403", "No permissions for long distance");

       exit;

    };

 };

 

if (uri=~"^sip:011[0-9]*@") {

    if (is_user_in("credentials","int")) {

       route(4);

       exit;

    } else {

       sl_send_reply("403", "No permissions for

                     internat. calls");

       exit;

    };

 };

 

本地通话(local call)使用数字7分辨并且以29之间的数开头(“^sip:[2-9][0-9]{6}@”)。长途号码符合这条正则 “^sip:1[2-9][0-9]{9}@”,号码以1开头,后面紧接这29之间的数,加起来一共9个数字,这样的号码被认为是长途号码。最后,国际长途号码以011+国家编码+地区编码+电话号码作为前缀。所有的情况下,脚本都会被抛到路由4route 4

           ACL数据插入数据库,对于脚本的正常工作很重要        

你可以使用openserctl工具,SerMyAdmin或是phpMyAdmin来完成。

最后,我们让路由块4routing block 4)来处理PSTN的目的地。函数rewritehostport()用来改变URI的主机部分,也就是说当你使用t_relay()中继请求时,此请求将被发往网关。

route[4] {

         ##--

         ## PSTN gateway handling

         ##--

         rewritehostport("10.1.30.45");

         route(1);

 }

阅读(3318) | 评论(2) | 转发(0) |
给主人留下些什么吧!~~

zhaiqi1632009-08-12 22:27:13

先把这本书翻译完吧,一件一件事情来,之后会写一些原创的关于opensips和asterisk方面的文章

jusl2009-08-12 11:40:56

能否拿出一片文章对opensips.cfg 这个配置文件进行详细的说明. 现在我在搭建opensips的环境,目前只能实现电话拨打,注册,认证.但是其他更多的就不知道怎么弄了!比如opensips-cp的配合使用等!如果是有个配置例子再加上你的翻译会更好些!呵呵!主要是对新人帮助很大! 目前的想法是把opensips的所有服务都跑通!但是,发现好多配置不知道怎么弄!