如何让PJSIP 支持IPV6

     PJSIP 是非常精致的SIP Client 协议栈,   其层次结构清晰的API设计, 良好的跨平台属性, 在业内有很高的声誉,  有不少SIP商业应用基于它开发.   可是PJSIP早期版本从未对IPV6有很好的支持.     Apple 在今年5月时发布声明,  要求6月1号以后所有的iOS应用都必须包含对IPV6-ONLY网络的支持.   这个对基于PJSIP开发的iOS应用来说可谓是当头一棒.    很多人呼吁要求PJSIP来快速支持IPV6,   PJSIP的开发小组也迅速的将IPV6支持列入计划,    在7月份发布了版本 2.5.5, 宣布了对IPV6的支持.   这个新版本的发布声明如下:

PJSIP version 2.5.5 is released with the main focus on:

    IPv6 support in PJNATH
    IPv6 support in PJLIB-UTIL DNS SRV and DNS AAAA resolution
    IPv6 support for hostname resolution in PJSIP and PJSUA-LIB

     PJSIP 的这个版本刚发布, 我们就投入了极大地兴趣对其进行研究, 这次的更新确实解决了IPV6,以及IPVT NAT的一些BUG,  从其实现逻辑来分析, 它在纯IPV6网络应该可以正常工作(这里的纯IPV6网络是指从client到server,   其所有的路由节点都是IPV6设备).   但是这个却无法满足目前的Internet网络对其需求!  

       IPV6标准虽然已经推出多年, 但是由于众所周知的原因, 目前的Internet网络的主流还是IPV4,  现在我们能看到的IPV6网络, 大多是架设在IPV4网络上的一些子网, 通过IPV6 to IPV4的NAT跟外部的因特网进行通信.  所以目前应用要支持的IPV6用户案例, 基本上会是: app运行于IPV6子网, 服务器位于IPV4的云端,   连接的路由类型是IPV4/IPV6的混合类型. 非常遗憾的是 PJSIP 是无法支持这种类型的网络的.  

      其根源在于SIP自身的复杂性!   

      简单的网络应用, 通过域名解析建立连接, 并不关心服务器的真实IP,  但是SIP协议则不然,在SIP终端与服务器建立连接时, 除了第一次是通过域名解析, 以后的通信都会利用保存下来的SIP Proxy的Public 地址,  减少域名解析的次数,来达到优化之目的. 但是在上文所述的混合网络中, Server的地址是IPV4的, 由于本地地址为IPV6, PJSIP所建立的信令连接是IPV6类型的, 这会导致:  SIP Message生成, 然后交给信令通道进行传输, 但是此Message却在到达网络前就被信令通道给丢弃了!  

     这个是PJSIP无法在混合网络上建立SIP的根本原因. 好了, 理解了这一点, 我们可以来具体分析PJSIP实现的瑕疵, 或者说Bug, 解决的思路也很简单, 因为PSJIP Client是处于IPV6网络, 外部是IPV4网络, 所以只需要将外部的IPV4地址转译成内部可以支持的IPV6地址, 问题即可迎刃而解.    转译的思路如下:

     +-------------------+--------------+----------------------------+
     | Well-Known Prefix | IPv4 address | IPv4-Embedded IPv6 address |
     +-------------------+--------------+----------------------------+
     | 64:ff9b::/96      |  192.0.2.33  | 64:ff9b::192.0.2.33        |
     +-------------------+--------------+----------------------------+

    Table 2: Text Representation of IPv4-Embedded IPv6 Addresses Using
                           the Well-Known Prefix

     

       OK,  理解了原理, 我们就可以来谈一下具体的BUG应该如何解决, PJSIP的问题一共有3处:   

   1.   Server Resolve

                 其解决思路就是:当发现自身的信令通道为IPV6类型, Server地址为IPV4是, 用上述方法将Server地址转译为IPV6 

            2.    Register 

                    这里是PJSIP的BUG,  通常的SIP 注册会有2次,  第一次是普通的注册,不带鉴权信息, 收到Server Challenge 之后会发起第二次带鉴权信息的注册, 其实这里的2次注册不仅仅是为了鉴权,  第一次注册的response上, server可以告诉SIP Client它的public address,  所以以后 client 发出的SIP message, 其contact 可以带上自己的内网和外网地址.  PJSIP在这里的实现有漏洞! 

           pjsua_acc.c

       if (status == PJ_SUCCESS) {
         /* Compare the addresses as sockaddr according to the ticket above,
	 * but only if they have the same family (ipv4 vs ipv4, or
         * ipv6 vs ipv6)
         */
         matched = (contact_addr.addr.sa_family != recv_addr.addr.sa_family) ||
          (uri->port == rport &&
              pj_sockaddr_cmp(&contact_addr, &recv_addr)==0);
     } else {
           /* Compare the addresses as string, as before */
          matched = (uri->port == rport &&
        pj_stricmp(&uri->host, via_addr)==0);
    }


    if (matched) {
       /* Address doesn't change */
        pj_pool_release(pool);
         return PJ_FALSE;
    }
            当自己的内网IP family跟外网 IP family不同, 竟然被判为matched!   从而取消了第二次注册, 导致无法注册成功!  其修改也很简单,简单用pj_sockaddr_cmp判决matched 即可.

            3     Establish Data Transport.  

                 这里的修改思路跟1类似, 因为外部是IPV4网络,所以收到的SDP的对端地址也是IPV4类型, 从而引发与多媒体通道的连接类型冲突, 导致无法建立数据连接, 只要把外部的地址改为IPV6, 就可以正常建立媒体数据通道了。 需要修改stream_info.c和vid_stream_info.c

          到此, 在IPV6网络上使用PJSIP 建立SIP CALL应该非常完美了.   

         虽然上文说述的bug改动不难, 但是由于我们对PJSIP并未了如指掌, 逐个排查, 确定问题症结, 也是绞尽脑汁。    行文至此, 精华已竭, 希望对读者诸君有所帮助。 

    

本文由@Sandfox 和本人协作完成.

Reference:

 1.  Supporting IPv6 DNS64/NAT64 Networks

  2.  RFC 6052 IPV6 Addressign of IPV4/IPV6 Translators

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 11
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值