背景
最近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):
為了排除其它網絡問題,Gemfield在同一個容器裡使用python腳本進行同一invite指令的模拟,發現指令的目的IP變成國标IPC的了(192.168.2.103),信令也能正常工作:
模拟該信令的腳本如下所示:
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++語言編寫,性能穩定。- 項目位址為:https://github.com/resiprocate/resiprocate;
- ReSIProcate feature清單(非常值得一看):https://www.resiprocate.org/ReSIProcate_Current_Features;
- 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庫的消息處理架構如下所示:
ReSIProcate庫中的類層次如下圖所示:
編譯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庫進行業務的開發。主要有以下幾個階段:
程式的初始化程式主程式的初始化階段要進行如下的設定:
- Create a Security Object (if required)
- Create SipStack
- Create DialogUsageManager
- Add transports
- Create/Set MasterProfile
- Supported Methods, Mime Types, etc.
- Validation Settings, Advertised Capabilities
- Set Outbound Decorators
- Set Handlers (ie. InviteSessionHandler)
- Set Managers (ie. ClientAuth, KeepAlive)
- Set AppDialogSet Factory
- Start Stack Processing / Thread
- Start DUM Processing / Thread
使用DUM的Shutdown Handler:
DUM Application Shutdownwww.resiprocate.org
DUM的handler- Invite Session Handler
- Client/Server Registration Handlers
- Client/Server Subscription Handlers
- Default Server Refer Handler (?)
- Client/Server Publication Handlers
- Pager Message Handler
- Redirect Handler
- DialogSet Handler
- Out Of Dialog Handler
- Shutdown Handler
如果是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部分的字段含義如下:
- v= 為protocol version;
- o= owner/creator and session identifier;
- s= 為Session name,如Play代表點播,Playback代表曆史回放;Download代表檔案下載下傳;Talk代表語音對講;
- u=* 為URI of description;
- c=* 為connection information-not required if included in all media;
- t= 為time the session is active;點播時00,回放或下載下傳時,t的值為開始時間和結束時間,用空格分開;
- m= 為media name and transport address;
- c=* 為connection information-optional if included at session-level;
- b=* bandwidth information;
- a=* zero or more media attribute lines;這個字段的格式為rtpmap:(payload type)(encoding name)/(clock rate)[/(encoding parameters)]
- y=* SSRC;十進制整數字元串,格式為Dddddddddd(第一位為曆史或實時媒體流的辨別位,1為曆史,0為實時);
- f=* 媒體描述:f=v/編碼格式/分辨率/幀率/碼率類型/碼率大小 a/編碼格式/碼率大小/采樣率。