天天看點

開源協定棧 rlc rrc_從ReSIProcate SIP協定棧庫到GB28181

背景

最近Gemfield團隊在使用其它部門的某三方庫進行GB28181協定的适配,然後在Docker化的過程中遇到了問題:SIP信令在Docker網絡上無法正常工作。具體來說,當服務部署在主控端(192.168.2.102)上的時候,UAS給UAC發送invite指令是沒有問題的;但是服務一旦部署在Docker容器裡,網絡使用Docker自身的bridge進行SIP協定的收發時,UAS發送給UAC的invite指令的目的位址就是錯誤的(通過wireshark抓包分析),本來應該是UAC(國标IPC,位址192.168.2.103)的IP,但不知道什麼原因,這個IP變成了主控端的IP(192.168.2.102):

開源協定棧 rlc rrc_從ReSIProcate SIP協定棧庫到GB28181

為了排除其它網絡問題,Gemfield在同一個容器裡使用python腳本進行同一invite指令的模拟,發現指令的目的IP變成國标IPC的了(192.168.2.103),信令也能正常工作:

開源協定棧 rlc rrc_從ReSIProcate SIP協定棧庫到GB28181

模拟該信令的腳本如下所示:

import socket
def main():
    # filename = sys.argv[1]
    port = 5060
    ip = '192.168.2.103'
    # ts, pkt = dpkt.pcap.Reader(open(filename, 'r'))
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    gemfield_payload = bytearray.fromhex('30333a3...<copy the payload from wireshark as hex>...0300d0a0d0a')
    sock.sendto(gemfield_payload, (ip, port))

    buf, addr = sock.recvfrom(2048, 0)
    print(addr)

if __name__ == '__main__':
    main()           

于是排除了網絡的原因。但因為種種原因,主要因為其它部門太忙了,無法進一步debug下去,問題遲遲得不到解決。這個時候,Gemfield設定了一個獨立的task,在小組内部來并行推動這個事情的解決:使用開源的SIP協定棧庫來進行這個問題的模拟和複現。這個開源的SIP協定棧,Gemfield選擇了ReSIProcate。

ReSIProcate庫的簡介

ReSIProcate來源于功能完善的開源SIP協定棧VOCAL,基于RFC3261和RFC2327标準,使用C++語言編寫,性能穩定。
  1. 項目位址為:https://github.com/resiprocate/resiprocate;
  2. ReSIProcate feature清單(非常值得一看):https://www.resiprocate.org/ReSIProcate_Current_Features;
  3. Ubuntu上的ReSIProcate package:https://launchpad.net/ubuntu/+source/resiprocat;

ReSIProcate庫主要分為

reSIProcate stack

(核心協定棧)、

DUM

(The Dialog Usage Manager )

、recon

(Conversation Manager )、

repro

(SIP Proxy)這四部分。

reSIProcate stack

項目的resip/stack目錄為核心的SIP棧的實作,基于該項目進行開發的普通程式員不應該涉及這部分底層的東西,相反,應該使用DUM、repro、recon這些層。核心協定棧的UTcase和參考例子在ReSIProcate項目的rutil/test 和 resip/stack/test 目錄下,特别注意testStack.cxx這個檔案。

The Dialog Usage Manager (DUM)

這部分代碼在resip/dum目錄下,UT case和參考例子在resip/dum/test目錄下。DUM在核心的SIP棧之上,用來SIP Dialog的建立和處理。這可以用來設定Registrations、Calls/InviteSessions、Subscriptions、Publications等。對于Invite Sessions來說,DUM會将收到的SDP bodies交給上層的應用程式處理。 大多數SIP應用程式都基于DUM這一層進行開發,因為DUM提供了極高的靈活性和可擴充性。

Conversation Manager (recon)

代碼在resip/recon目錄下,UT case和參考例子在resip/recon/test目錄下,可以特别留神testUA.cxx檔案。recon在核心SIP棧和DUM層之上,用來和media以及相關的sip信令打交道。recon使用sipXtapi media棧提供audio RTP的處理。

SIP Proxy(repro)

repro是一個獨立的SIP服務程式,該部分代碼在項目的repro目錄下,repro在核心SIP棧之上,使用DUM來進行SIP注冊的處理,使用核心SIP棧來進行SIP信令的proxy。

ReSIProcate庫的消息處理架構如下所示:

開源協定棧 rlc rrc_從ReSIProcate SIP協定棧庫到GB28181

ReSIProcate庫中的類層次如下圖所示:

開源協定棧 rlc rrc_從ReSIProcate SIP協定棧庫到GB28181

編譯ReSIProcate庫

使用如下步驟進行基本的編譯:

[email protected]:/home/gemfield/github/resiprocate-master# autoreconf -fi
[email protected]:/home/gemfield/github/resiprocate-master# ./configure
[email protected]:/home/gemfield/github/resiprocate-master# make -j4
[email protected]:/home/gemfield/github/resiprocate-master# make check
[email protected]:/home/gemfield/github/resiprocate-master# make install           

上述configure的時候可以指定安裝的目錄,比如:./configure --prefix=/home/gemfield/github/tmp_install/ 将會導緻最後的安裝如下所示:

[email protected]:/home/gemfield/github/Gemfield/resiprocate# make install
Making install in rutil
......
Libraries have been installed in:
   /home/gemfield/github/tmp_install/lib           

生成的install目錄如下所示:

[email protected]:/home/gemfield/github/tmp_install# ls -l
total 12
drwxr-xr-x 4 root root 4096 Nov 25 08:04 include
drwxr-xr-x 2 root root 4096 Nov 25 08:04 lib
drwxr-xr-x 3 root root 4096 Nov 25 08:04 share           

有頭檔案、庫檔案和文檔。其中庫檔案如下所示:

[email protected]:/home/gemfield/github/tmp_install# ls -l lib/
total 140876
-rwxr-xr-x 1 root root 13431224 Nov 25 08:04 libdum-1.12.so
-rw-r--r-- 1 root root 36667544 Nov 25 08:04 libdum.a
-rwxr-xr-x 1 root root     1183 Nov 25 08:04 libdum.la
lrwxrwxrwx 1 root root       14 Nov 25 08:04 libdum.so -> libdum-1.12.so
-rwxr-xr-x 1 root root 20110880 Nov 25 08:04 libresip-1.12.so
-rw-r--r-- 1 root root 54648650 Nov 25 08:04 libresip.a
-rwxr-xr-x 1 root root     1147 Nov 25 08:04 libresip.la
lrwxrwxrwx 1 root root       16 Nov 25 08:04 libresip.so -> libresip-1.12.so
-rwxr-xr-x 1 root root   181088 Nov 25 08:04 libresipares-1.12.so
-rw-r--r-- 1 root root   353168 Nov 25 08:04 libresipares.a
-rwxr-xr-x 1 root root     1061 Nov 25 08:04 libresipares.la
lrwxrwxrwx 1 root root       20 Nov 25 08:04 libresipares.so -> libresipares-1.12.so
-rwxr-xr-x 1 root root  5323416 Nov 25 08:04 librutil-1.12.so
-rw-r--r-- 1 root root 13493638 Nov 25 08:04 librutil.a
-rwxr-xr-x 1 root root     1097 Nov 25 08:04 librutil.la
lrwxrwxrwx 1 root root       16 Nov 25 08:04 librutil.so -> librutil-1.12.so           

這些庫和檔案都來自:

#動态庫
./resip/stack/.libs/libresip.so
./resip/stack/.libs/libresip-1.12.so
./resip/dum/.libs/libdum-1.12.so
./resip/dum/.libs/libdum.so
./rutil/dns/ares/.libs/libresipares-1.12.so
./rutil/dns/ares/.libs/libresipares.so
./rutil/.libs/librutil.so
./rutil/.libs/librutil-1.12.so
#靜态庫
./contrib/popt/win32/lib/libpopt.dll.a
./contrib/popt/win32/lib/libpopt.a
./resip/stack/.libs/libresip.a
./resip/dum/.libs/libdum.a
./rutil/dns/ares/.libs/libresipares.a
./rutil/.libs/librutil.a
#可執行檔案
./resip/certs/makeCA
./resip/certs/makeCert
......           

如果要使用這些庫,則需要在編譯的時候設定LD_RUN_PATH,而在運作的時候設定LD_LIBRARY_PATH;如果需要一個更多功能的編譯,則在configure的時候加上更多的參數:

[email protected]:/home/gemfield/github/resiprocate-master# ./configure --enable-ipv6 --with-ssl --with-geoip --with-tfm --with-repro --with-mysql --with-popt CXXFLAGS="-I`pwd`/contrib/cajun/include"           

或者,仿照github項目中的建構腳本,先安裝如下依賴:

sudo add-apt-repository -y "deb http://archive.ubuntu.com/ubuntu/ bionic main universe"
sudo apt-get update -qq
sudo apt-get install -qq gperf libasio-dev libboost-dev libc-ares-dev libdb++-dev 
    libpopt-dev libssl-dev perl libmysqlclient-dev libpq-dev libcppunit-dev autotools-dev 
    libpcre3-dev libxerces-c-dev curl libcajun-dev python-cxx-dev libsrtp-dev libgloox-dev libtelepathy-qt5-dev
sudo apt-get install -qq debian-archive-keyring
wget -q -O - https://ftp-master.debian.org/keys/release-10.asc | sudo apt-key add -
wget -q -O - https://ftp-master.debian.org/keys/archive-key-10.asc | sudo apt-key add -
sudo add-apt-repository -y "deb http://http.us.debian.org/debian/ buster main"
sudo add-apt-repository -y "deb http://http.us.debian.org/debian/ buster-backports main"
curl -O http://apt.debify.org/debify/pool/main/d/debify-archive-keyring/debify-archive-keyring_2019.01_all.deb
sudo dpkg -i debify-archive-keyring_2019.01_all.deb
sudo add-apt-repository -y "deb http://apt.debify.org/debify debify-buster-backports main"
sudo apt-get update -qq
sudo apt-get install -qq --force-yes -t buster libradcli-dev libsoci-dev libqpid-proton-cpp12-dev
sudo apt-get install -qq --force-yes -t debify-buster-backports libsipxtapi-dev           

再進行configure:

#build/travis/bootstrap
autoreconf --install
rm resip/stack/gen/*
ls -l /usr/include/asio.hpp

#build/travis/configure
./configure 
  --enable-assert-syslog 
  --with-popt 
  --with-ssl 
  --with-mysql 
  --with-postgresql 
  --with-apps 
  --with-telepathy 
  --enable-dtls 
  --enable-ipv6 
  --with-radcli 
  --with-qpid-proton 
  --with-repro 
  --enable-repro-plugins 
  DEPS_PYTHON_CFLAGS="`/usr/bin/python2.7-config --cflags`" 
  DEPS_PYTHON_LIBS="`/usr/bin/python2.7-config --ldflags`" 
  PYCXX_SRCDIR=/usr/share/python2.7/CXX/Python2 
  --with-python 
  --with-rend 
  --with-recon 
  CPPFLAGS="-I/usr/include/postgresql -I/usr/include/sipxtapi -D__pingtel_on_posix__ -D_linux_ -D_REENTRANT -D_FILE_OFFS -DDEFAULT_BRIDGE_MAX_IN_OUTPUTS=20 -DRESIP_DIGEST_LOGGING -I/usr/include/soci -I/usr/include/mysql" 
  CXXFLAGS="-Wformat -Werror=format-security -fpermissive" 
  --with-c-ares           

基于DUM庫開始進行應用程式的開發

大多數應用都是基于DUM庫進行業務的開發。主要有以下幾個階段:

程式的初始化

程式主程式的初始化階段要進行如下的設定:

  1. Create a Security Object (if required)
  2. Create SipStack
  3. Create DialogUsageManager
  4. Add transports
  5. Create/Set MasterProfile
  6. Supported Methods, Mime Types, etc.
  7. Validation Settings, Advertised Capabilities
  8. Set Outbound Decorators
  9. Set Handlers (ie. InviteSessionHandler)
  10. Set Managers (ie. ClientAuth, KeepAlive)
  11. Set AppDialogSet Factory
  12. Start Stack Processing / Thread
  13. Start DUM Processing / Thread
程式的關閉

使用DUM的Shutdown Handler:

DUM Application Shutdown​www.resiprocate.org

DUM的handler
  1. Invite Session Handler
  2. Client/Server Registration Handlers
  3. Client/Server Subscription Handlers
  4. Default Server Refer Handler (?)
  5. Client/Server Publication Handlers
  6. Pager Message Handler
  7. Redirect Handler
  8. DialogSet Handler
  9. Out Of Dialog Handler
  10. Shutdown Handler
DUM的注冊和鑒權

如果是UAS,則繼承并實作ServerAuthManager中的相關API;如果是UAC,則繼承并實作ClientAuthManager中的相關API。

GB28181 UAS的開發

裝置注冊

很簡單,繼承并實作resip::ServerAuthManager中的

requiresChallenge
requestCredential
onAuthSuccess
onAuthFailure
           
實時點播的invite信令

按照GB28181的标準,invite消息格式為:

INVITE sip:媒體流發送者裝置編碼@目的域名或IP位址端口 SIP/2.0

To:sip:媒體流發送者裝置編碼@目的域名
Content-Length:消息實體的位元組長度
Contact:<sip:媒體流接收者裝置編碼@源IP位址端口>
CSeq:1 INVITE
Call-ID:[email protected]
Via:SIP/2.0/UDP源域名或IP位址
From:<sip:媒體流接收者裝置編碼@源域名;tag=gemfield
Subject:媒體流發送者裝置編碼:發送端媒體流序列号,媒體流接收者裝置編碼:接收端媒體流序列号
Content-Type:application/sdp
Max-Forwards:70
#下面是SDP的定義,INVITE消息體中攜帶的SDP内容應符合RFC2327的相關要求,有如下字段:
------Session description, by Gemfield------
v=0
o=
s=Play
c=IN IP4 192.168.
--------Time description, by Gemfield--------
t=0 0
--------Media description,by Gemfield---------
m=video 6000 RTP/AVP 96 98 97
a=recvonly
a=rtpmap:96 PS/90000
a=rtpmap:98 H264/90000
a=rtpmap:97 MPEG4/90000
y=           

其中,SDP部分的字段含義如下:

  1. v= 為protocol version;
  2. o= owner/creator and session identifier;
  3. s= 為Session name,如Play代表點播,Playback代表曆史回放;Download代表檔案下載下傳;Talk代表語音對講;
  4. u=* 為URI of description;
  5. c=* 為connection information-not required if included in all media;
  6. t= 為time the session is active;點播時00,回放或下載下傳時,t的值為開始時間和結束時間,用空格分開;
  7. m= 為media name and transport address;
  8. c=* 為connection information-optional if included at session-level;
  9. b=* bandwidth information;
  10. a=* zero or more media attribute lines;這個字段的格式為rtpmap:(payload type)(encoding name)/(clock rate)[/(encoding parameters)]
  11. y=* SSRC;十進制整數字元串,格式為Dddddddddd(第一位為曆史或實時媒體流的辨別位,1為曆史,0為實時);
  12. f=* 媒體描述:f=v/編碼格式/分辨率/幀率/碼率類型/碼率大小 a/編碼格式/碼率大小/采樣率。