Browsed by
Category: 通信技术

通信,让生活更美好!

计算 IV

计算 IV

在 SRTP 对负荷进行加密(或者解密)时需要先计算 IV (Initialization vector, 初始化向量),RFC3711定义了该值的计算方法:

IV = (k_s * 2^16) XOR (SSRC * 2^64) XOR (i * 2^16)

看起来似乎很普通、也很容易理解,无非就是三个参数的异或计算:(1)k_s,(2)SSRC 以及(3)i。

IV 是 16 字节的数据流;k_s 是 session salt(大小14字节),要求左移2字节;SSRC 是 RTP 中的源标识参数,要求左移8字节。

i 参数有点特殊,定义为「包索引」,它是 48 比特的值,规范中定义如下:

i = 2^16 * ROC + SEQ.

ROC 是 rollover counter, RTP 包中没有这个参数,需要根据 SEQ 的值来判断是否需要设置 ROC。这里提一下, RTP 协议非常山炮地用 2 字节定义 SEQ 值,可想而知它完全不够用。因此应用层不得不自己计算ROC值,组合 ROC 值和 SEQ 值 才能完整判断 RTP 包的真实序列号。

组合出三个 16 字节的数据流后,异或计算得出 IV 值,请参考以下图表示意:

IV 异或计算

从图中也可以看出,IV 虽然有 16 个字节,但是实际上最后两个字节根本没用,因此默认设置为 0 即可。

roc + seq 的设置可以取巧一下。虽然完整定义是 48-bits,但用32-bits也足够了(考虑到 VoIP 对话中一般 20ms 一个包,序列号要溢出32bits的值需要超过990天)。而简化成 32-bits 的最大好处就是直接调用 C 函数(htonl)进行网络字节序转换即可,只需要填充第10个字节到第13个字节。

SSRC 是 32-bits 整数,因此也需要转换成网络字节序;而 session salt 本身是 14 字节的数据流,无需转换字节序。

奇怪的 SIP 呼叫流程

奇怪的 SIP 呼叫流程

最近几天帮助泰国的朋友检查一个呼叫业务流程,其中涉及很多细节和业务流程,不过让我觉得特别意外的是他使用的 VoIP 运营商的 SIP 呼叫流程。首次遇到这样的流程,请先参考下图的概要描述:

奇怪的 SIP 呼叫流程
奇怪的 SIP 呼叫流程

这个流程的奇怪之处在于:(1)VoIP 运营商发起呼叫时,INVITE 消息的媒体地址居然是“0.0.0.0”,这很明确告诉被叫:主叫只发送媒体流、甚至根本不处理媒体流;(2)被叫应答后,VoIP 运营商再次发起 reINVITE 流程,此时才真正指示出自己的真实媒体地址(当然也包括最终的媒体编码)。

通常的 SIP 呼叫流程在发起呼叫时就明确指示自己的真实媒体信息,因此在被叫应答后,没有必要再发起 reINVITE 流程。然而这家运营商为什么要放弃传统做法、采用这么奇怪的流程呢?

这家运营商是美国的一家运营商,而且规模很大,其设备供应商也是一家世界级的大软件公司(非常、非常大),因此这个流程肯定不是随意修改的结果,必定有其特殊的目的。仔细揣摩后,我认为它可能是为了节省媒体资源才这么做。

被叫方一般会振铃几秒、十几秒、甚至几十秒后,才可能应答。另外,呼叫量特别大时,统计上只有10%左右的呼叫最终才会应答。这家运营商采用这个流程,只需要在被叫真正应答之后才开始分配媒体资源,考虑到这是一家规模很大的运营商,这种流程确实可以节省很多的媒体资源。

VoIP 网络往往是主叫播放回铃音,因此这是个非常聪明、折中的呼叫流程,确实只有在被叫侧应答之后才处理真实媒体流。然而现实网络组网是非常复杂的,这个聪明到有些投机取巧的方法有固有的缺陷,请参考下图:

183 带媒体信息
被叫放音,183 消息携带 SDP。

被叫侧如果对接了传统的 PSTN 网络,例如 VoIP 网关,很有可能直接返回 183 消息并携带被叫的媒体信息。传统的 PSTN 网络往往是被叫侧播放回铃音,并且也有一些被叫侧放音的业务(例如“彩铃”),此时这家 VoIP 运营商就有问题了。

由于它告诉给被叫的地址是“0.0.0.0”,因此被叫振铃音(或者业务音)就无法传达到主叫侧。VoIP 运营商可以向被叫发送 UPDATE 消息来传递自己的真实媒体信息,但被叫未必会支持 UPDATE 消息,这个是扩展消息,不是所有的 SIP 设备都必须支持。而且 PSTN 网络往往会要求对 18x 消息用 PRACK 流程确认,因此即便支持 UPDATE 消息,这个特殊流程实际也急剧放大了被叫侧振铃阶段流程的复杂度。

这也就是我们的泰国朋友遇到的问题。

解决方式是在 VoIP 运营商与 PSTN 网络之间架设 miniSIPServer:

(1)miniSIPServer 与 VoIP 之间建立完全应答的呼叫通道;

(2)miniSIPServer 与 PSTN 之间维持正常的呼叫流程;

(3)miniSIPServer 判断被叫侧是否自己放回铃音(或者业务音),如果(a)被叫没有放音,则 miniSIPServer 主动给 VoIP 运营商放回铃音(或者呼叫等待音),而如果(b)被叫有放音,miniSIPServer 则直接建立两边的通道,让主叫直接听被叫的放音。

NAME、CNAME 格式

NAME、CNAME 格式

总体而言, 早期 DNS 协议是个比较简单的协议。受限于 UDP 大小和数据量, 基于 UDP 的 DNS 协议限制了包大小,并且采用二进制编码方式力求减少数据量占用。

二进制编码方式相比文本方式(例如HTTP、SIP、SMTP等)最明显的缺点就是不直观,再用一些技巧减少数据量,DNS 协议在定义、实现方面总显得有些弯弯绕绕。

比如 NAME、CNAME 等结构的编码。

本文记录了这些古怪的编码方式,方便以后查找,并帮助后续的开发者不会被代码给绕晕。作为我个人来说,还是尽快忘记这些吧。


方式一:纯压缩编码方式

这种方式应用很普遍,以“0xc0”标记开始,加上一个字节的 offset,进行消息包内的寻址即可。如下图所示:

DNS offset 编码
offset 方式编码

其中, 0x0c 就是偏移量。


方式二:直接编码方式

这种方式比较直接,占用数据量也相对大一些,将域名完整信息存在数据包中。如下图所示:

直接编码
直接编码方式

需要说明的是:并不是直接将所有字符存进去就 OK 了。对于域名数据,以“.”符号分割成几个单元,每个单元数据采用 “length + value” 方式进行编码,最后以0x00结束编码。以上述数据为例:

08 6e 73 69 6e 61 69 6d 67 04 67 73 6c 62 08 73
69 6e 61 65 64 67 65 03 63 6f 6d 00

比如,第一个 0x08,说明后面的“nsinaimg”(即 6e 73 69 6e 61 69 6d 67)长度;然后是 0x04,说明后面的“gslb”(即67 73 6c 62)长度……其他单元类推。


方式三: 混合编码方式

这种方式其实就是综合了上述两种方式。以直接编码方式开始,然后以压缩(偏移)编码方式结尾。如下图所示:

混合编码方式
混合编码方式

对图中的数据码流描述如下:

03 6b 6c 6e 04 67 72 69 64 c0 38

0x03 描述了“kln”(即 6b 6c 6e)的长度,0x04 描述了“grid”(即 67 72 69 64)的长度,而域名的后续部分,通过“c0 38”编码可以知道:需要偏移0x38字节获取。

RFC4235与RFC7463

RFC4235与RFC7463

基本上两篇RFC文档是传承的关系,RFC7463场景应用比RFC4235要详细很多,一些旧的SIP设备未必支持7463。

在兼容性方面,对于SIP-SUBSCRIBE消息,两篇RFC文档采用了不同的Event,rfc4235中定义“dialog”,而rfc7463中定义“dialog;shared”。rfc4235限定在只订阅SIP呼叫对话的状态,因此在dialog-info中,要求必须填充dialog元素,其中就包含call-id,remote-tag以及local-tag等典型呼叫参数。

而rfc7463不仅仅是关注呼叫,更关注“状态呈现”,因此凡是与“状态”相关的消息,都尽量进行了定义。比如在“11.1. Registration and Subscription”章节中,就定义了终端注册的状态呈现。在注册流程中,就没有dialog的信息。

呈现信息多,对用户当然有好处,对VoIP系统也很有意义,尤其是在企业应用场景中。但是需要注意的是这也大大增加了VoIP系统的负荷,实际部署中要慎重考虑。

3cx收购了elastix

3cx收购了elastix

原始新闻链接请点击此处。从新闻内容大致可以得出以下结论:

(1)Elastix V5的内核从Asterisk迁移到3CX。考虑到3cx是商业、不开源的版本,因此elastix从V5版本开始应该就会转向闭源。

(2)以前的Elastix版本,例如使用比较广泛的V2版本,以及充满了bug的V3和V4版本,依然遵循开源协议。相信这些旧版本的用户无法免费升级到V5版本,需要付费购买产品或者购买技术支持才有可能。

(3)3CX可能看重的就是elastix目前已有的大量用户,希望能从产品技术支持、付费购买等途径获得利益。

国内一些专业QQ群里有部分用户表达了对这件事的愤怒情绪,感觉自己被Elastix背叛了。仔细搜索了相关报道和各类消息后,我有些不同的看法。对Elastix项目而言,『被收购』是个不错的商业选项,项目人员获得合理的报酬完全是可以理解的,毕竟这是个商业社会,人不能光靠理想一穷二白地活着。

相反,我对3cx的收购决定感到困惑。Elastix是个不错的项目,也有很多拥趸,但究其根本,依然只是依附在Asterisk上做了个壳而已,这也就意味着客户的迁移成本实际上并不高,客户完全可以从一个壳转另一个壳,比如FreePBX。实际上很快就发现FreePBX在twitter等社交媒体上发帖欢迎Elastix用户进行迁移。

由于这些壳都是使用Asterisk作为核心,因此配置规范(接口)、管理接口实际都遵循Asterisk定义。从Elastix转换到FreePBX,相信比转换到3cx要简单、容易得多。从这点看,我很怀疑Elastix用户会如3cx所愿地迁移到3cx的平台。如果不能顺利完成这种转换,这次收购的价值就会大打折扣。

目前市面上有各种各样的软、硬件PBX,绝大部分其实都是基于两个开源项目:Asterisk和FreeSwitch,差异化无非体现在各种定制化的包装。如果想获取最大利益,应该是直接收购或者控制这两大项目。如果说“条条大路通罗马”,那Elastix、FreePBX不过是其中两条路而已,Asterisk/FreeSwitch才是目的地罗马。与其在道路上设卡收费,不如直接控制罗马。

从这个角度考虑,所有基于这些开源项目的软、硬件产品实际上都有风险。一旦项目被收购,产品就有被扼杀的危险。避免此类危险的唯一路径,大概就是控制住这些项目,或者另起炉灶。

幸运的是我们的产品(miniSIPServer)是完全自主开发,与这些项目毫无关系,因此不存在任何法律和潜在的风险,这也使得我们能够完全掌握自己产品的发展,从而为客户提供安全、可靠、一致、长久的体验。

希望这些开源项目能长久繁荣下去,毕竟一起做大蛋糕、做大市场对大家都有利。

量子通信卫星

量子通信卫星

据说今天晚上要发射一颗量子通信卫星,顾名思义就是进行量子通信。对此我感到非常震惊!作为一名科普爱好者,我有限的量子理论知识大概只有以下几点:

(1)量子纠缠态。一个量子分裂成两个量子,这两个量子就是自纠缠的。一个向左转,另一个就向右转。理论上讲,这种纠缠态是跨越空间限制的,即使两个量子分布在宇宙的两端,它们的纠缠状态也不会改变。我猜想量子通信大概就是基于量子纠缠态来通信,通过控制一个量子的状态,影响另一个量子,从而实现通信。从这个角度讲,量子通信简直就是超越光速的通信方式。

(2)测不准原理。这基本就是现代量子理论的基石之一,大概是指无法准确测量量子的状态,因为测量本身就是一种能量干扰,会影响到量子的状态。如果测量某个参数越准确,则其他参数的结果就越不准确。

既然测不准,因此实际上是无法准确控制量子的状态。既然无法准确控制,就不可能实现通信。反过来说,如果实现了通信,则必然有某种手段能准确控制量子的状态,也就是说“测不准原理”是不是可以over了?

理论基石居然有可能被动摇,这简直就是突破天际!怎么做到的?感觉有幸要见证历史,求科普。

2016-08-19 更新:看了一些新闻报道,貌似是利用光子的不可分割、不可复制特点,产生密钥,对通信进行加密。当然这仍然很厉害,但是完全不是『利用量子纠缠态进行远距离通信』。我觉得『量子通信』这个表达不准确,应该是『量子加密』才对,天际并没有被突破。

解决DNS污染

解决DNS污染

DNS污染可能是一个比较普遍的问题,主要表现在:(1)DNS查询返回的IP地址根本是错误的。“错误”意思是IP地址要么根本不存在,要么就是个和域名完全无关系的地址。(2)IP地址虽然可能是对的,但是其他参数被莫名修改,例如TTL参数等。

测试了各类DNS服务器,例如阿里DNS、DNSPod(鹅厂)、Google DNS等。无一例外,最终的结果都或多或少被一些看不见的手给修改、甚至屏蔽了。从技术角度上讲,DNS协议本身非常随意、非常粗糙,是典型的互联网蛮荒时代的产物,比如面向UDP、无加密、无鉴权等,因此网络上任何一只手都有可能修改DNS的查询结果。网络进化到IPv6阶段,当然能解决这个问题,不过既然目前绝大部分设备还只支持IPv4协议栈,因此我们还是不得不修修补补来解决这个污染问题。

最根本的解决方法就是加密DNS查询。目前有些DNS服务商提供了私有的加密DNS方式,不过不太通用,需要私有的客户端程序配合。实际上可以不用搞这么复杂,自己建立加密通道,传递DNS消息即可。例如,参考下图的拓扑逻辑:

实现简单DNS透传的逻辑单元
实现简单DNS透传的逻辑单元

在这个网络中,有两个关键程序:dnsProxy和dnsAgent。

dnsProxy顾名思义就是个Proxy,本身并不负责DNS协议的解析,也不保存DNS的查询结果等信息,只是单纯地将DNS消息传递给真正的DNS服务器,并返回相应的结果即可。dnsProxy另一个功能是对外提供加密的数据连接,例如TLS、SSL加密等,甚至可以只是简单地对数据包进行自定义的异或运算即可。另外就是对外提供非标准连接接口,这点非常重要。DNS采用标准UDP53接口作为DNS服务器接口,网络上那些看不见的手,往往就是扫描并篡改53接口的数据包。这个小程序跑在境外(大家都懂的)的一台VPS设备上,推荐采用DigitalOcean,专业的云计算服务商,采用SSD硬盘,价格公道,我们一直用TA,如果你有兴趣的话,请点击这里自行了解细节。

dnsAgent是另一个小程序,主要负责建立与dnsProxy的加密连接,接收普通设备的DNS请求并将其传递给dnsProxy,同时返回DNS结果给普通计算设备。对于网内设备而言,dnsAgent就是个伪装的DNS服务器。同样,dnsAgent其实也不需要关心、也不需要解析DNS协议细节。在我的网络中,dnsAgent跑在一台常年吃灰的树莓派上(还是第一代的)。

实现这些仅仅需要一点UDP、TCP的网络知识,甚至不需要了解DNS协议的细节,无需对DNS数据包做修改。完成后可以愉快地打开很多以前打不开的网站。当然,有些网站始终是打不开的,这是另一个与DNS无关的话题了。

Pi打开IPv6

Pi打开IPv6

默认已经安装了IPv6协议栈,但是没有打开IPv6功能,这点比较奇怪。不过打开IPv6功能也不复杂,一行命令即可:

sudo modprobe ipv6
6to4 tunnel

6to4 tunnel

今天无意中发现,居然不用翻墙就能打开google和gmail,这简直太不科学了,我朝气象从此焕然一新?

本着“怀着最大的恶意揣测他人”的精神,我对此充满疑虑。于是拉出wireshark仔细dig了一下,发现了IPv6数据包,竟然是通过IPv6访问google和gmail!墙没有封锁IPv6,这实在是个惊奇的发现。

难道电信已经开始部署IPv6网络了吗?对此同样充满疑虑。但是有一点是肯定的:最近买的路由器是支持IPv6的。进入路由器管理界面,发现电信仍然是分配IPv4地址,但是在路由器IPv6设置项中,配置了“自动检测IPv6类型”,检测结果是“6to4 tunnel”,而且配置了IPv6地址。

看起来电信的确升级了网络,但是仍然没有真正部署IPv6,还在半遮半掩中,并且猥琐地限速了。有“6to4 tunnel”也还好,至少能访问外部IPv6的服务器地址。google和gmail等都部署了IPv6,因此可以通过IPv6来访问这些服务。而对于没有部署IPv6的服务,例如twitter之流等,依然还是无法访问。

不管怎样,这已经是向前迈进了一步。真心希望步子能迈得更大些!

2016-03-10 更新:好吧,我承认我还是图样了。

为什么放弃了webRTC?

为什么放弃了webRTC?

首先必须要说明的是:webRTC是非常好的技术,以至于我现在仍然在怀疑,放弃webRTC是不是个明智的决定,内心依然是忐忑不安。

然而现状就是将webRTC从MSS新版本中砍掉了。下面试图说明清楚做决定时的一些考虑。

从webRTC技术的发展脉络看,感觉ta更适合于公共网络的通信,尤其是越来越像专为google hangouts服务。由于是服务于公共网络,因此“加密”提高到一个非常重要的位置,几乎达到了神经质的地步。webRTC设计之初就没有考虑过传统的VoIP网络(尽管该技术来自于收购的GIPS团队,而该团队本身就是为VoIP网络开发出身的),从传输到业务,基本都特立独行。当然,这没什么不好,只是由于与传统VoIP网络切割开来,实在让人经常感到惋惜。

既然没有考虑传统VoIP网络,当然就更不可能考虑网络部署的多样性。全方位加密固然让自己显得安全,带来心理上的安慰,但也增加了网络部署的复杂性(VoIP本身已经够复杂了)。就一般的VoIP应用而言,“salt+MD5”以及SRTP已经足够保证通信的安全性和私密性。而最新的webRTC(不特别说明的话,这里我们总是指Chrome的webRTC实现)要求getUserMedia等操作必须是来自加密(HTTPS)的网站,websocket也必须进行加密。这也就是说,用户必须要为webRTC服务器(通常也是呼叫服务器或者IPPBX)部署单独的签名加密(TLS或者SSL)。对于提供公众服务的网站而言,部署TLS/SSL不是太大问题,而要求中小企业申请签名并部署在呼叫服务器中,这毫无疑问极大地增加了系统的复杂度,增加了部署难度,而且这样做的意义又有多大了?在企业通信网络里与其这样增加复杂度(网络复杂度和管理复杂度),还不如干脆直接建立VPN网络,方案很成熟,加密更全面,保护更周到,方式方法也简单得多。

这就是我们感觉webRTC只适合公众网络并且已经神经质的原因之一。何况,“加密”有时候更多的是个管理问题而不是技术问题。

webRTC另一个宏大的目标是提供各平台统一的用户体验。愿望是很美好的,但是是否能实现值得商榷。把所有的处理都放入浏览器,固然增加了可移植性,但最大的问题也就是牺牲了平台的独特性和高效性。比如在移动平台,webRTC的耗电量就明显偏高。而且在移动平台各种莫名其妙的设定,极大地损害了用户体验,比如播放ring-back tone,居然要求用户必须点击一个按钮才能继续(是我out了么?),这实在是太扯了。

而最被我们诟病的是兼容性。webRTC基本没有什么兼容性可言。按道理已经发布这么久了,多少应该在一些关键点上考虑一下兼容性,然而并没有。这对一个像google hangouts这样的网站来说,不是大问题,用户无非升级一下chrome好了。而对部署在许许多多用户voip网络中的PBX或者webRTC服务器而言,就是噩梦了。用户升级了Chrome,就必须同时升级服务器,如果不升级服务器,就必须降级Chrome。与之相比,microsoft对兼容性的考虑简直贴心贴肺。

这种对兼容性的唾弃有时候甚至直接表现在产品本身。比如当初的Gtalk,直接就放弃了,我们花费大量时间精力对接Gtalk,最后也是然并卵。对webRTC也会产生同样的顾虑。

webRTC当然有很多非常优秀的技术特点,比如源自GIPS的回音消除技术、超强的语音编解码技术等。思虑再三,决定还是不再跟随,砍了算了,以后再说吧。