一 SCTP协议简介
通信领域常用的信令传输协议Sigtran(Signaling Transport Protocol)协议簇是IETF的Sigtran工作组制定的PSTN信令与IP互通的规范。该协议簇支持通过IP网络传输传统电路交换网SCN(Switched Circuit Network)信令。
Sigtran协议簇可以分为PSTN信令适配协议和通用信令传输协议两大类。前者是针对SCN中现存的各种信令协议而产生的信令适配协议,例如M2UA(No.7 MTP2-User Adaptation Layer)、M3UA(No.7 MTP3-User Adaptation Layer)以及IUA(ISDN Q.921-User Adaptation Layer)等等。
而后者通用信令传输协议便是今天介绍的主角SCTP(Stream Control Transmission Protocol,流控制传输协议),该协议的主要目的是实现PSTN信令在IP网上高效、可靠的传输。
IP网络上的传输协议大家比较熟悉的是UDP和TCP协议。UDP协议的特点是无连接的传输协议,传输质量不能保证。TCP协议的特点是有连接的传输协议,但是实时性差,无法支持多归属,容易受到DoS攻击。与之相比,SCTP协议是面向连接的基于分组的可靠实时传输协议,具有高可靠性、优实时性、多归属支持、攻击防备等多种优点。
![](https://img.haomeiwen.com/i1347404/a406e52a7ef0324f.png)
二 PYSCTP简介与安装
PYSCTP是基于Python的SCTP协议栈,用于建立偶联和发送SCTP数据包。
下载以后是一个名为pysctp-master.zip的压缩文件包,文件内容如下。
![](https://img.haomeiwen.com/i1347404/c4eb472d3dfed308.png)
PYSCTP安装命令为:sudo python setup.py install
三 SCTP偶联的建立
假设需要建立SCTP偶联的服务器上的M2UA配置如下,其中XX.XX.14.109和XX.XX.15.109是服务器的本地IP地址。
然后ASP(Application Server Process)上配置的需要建立偶联的对端地址是XX.XX.217.43。这个XX.XX.217.43就是安装并计划运行PYSCTP的另一台服务器地址。
![](https://img.haomeiwen.com/i1347404/7a1dd32607f9d7bb.png)
在XX.XX.217.43服务器的PYSCTP安装目录下增加一个名为client.py的文件,代码内容如下。
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# client.py - Create SCTP association
import _sctp
from sctp import *
server = 'XX.XX.14.109'
sctpport = 2904
if _sctp.getconstant("IPPROTO_SCTP") != 132:
raise "getconstant failed"
client_sock = sctpsocket(socket.AF_INET, socket.SOCK_STREAM, None)
saddr = (server, sctpport)
print "SCTP", saddr, " ----------------------------------------------"
client_sock.initparams.max_instreams = 3
client_sock.initparams.num_ostreams = 3
client = 'XX.XX.217.43'
cport = 2904
caddr = (client, cport)
client_sock.bindx([caddr])
client_sock.events.clear()
client_sock.events.data_io = 1
client_sock.connect(saddr)
while 1:
while True:
data = raw_input('INPUT: ')
if not data:
break
client_sock.sctp_send(data)
fromaddr, flags, msg, notif = client_sock.sctp_recv(1024)
print '%s__' %msg
break
client_sock.close()
执行client.py以后即可以与服务器端建立偶联 。
colvin@ubuntu:~/git/pysctp$ sudo python client.py
SCTP ('XX.XX.14.109', 2904) ----------------------------------------------
INPUT:
服务器侧可以看到有新的偶联建立 。
# vi sctp open
Open Associations
-------------------------------------------------
...
AssocID: 2 Prim. Destination Address: XX.XX.217.43 Port: 2904
如果需要建立偶联的服务器被配置成了clinet端,PYSCTP对应的server.py代码内容如下:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# server.py - Create SCTP association
import socket
import sctp
from time import ctime
HOST = ''
PORT = 3868
BUFSIZE = 1024
ADDR = (HOST, PORT)
socket_serv = sctp.sctpsocket(socket.AF_INET, socket.SOCK_STREAM, None)
socket_serv.initparams.max_instreams = 3
socket_serv.initparams.num_ostreams = 3
socket_serv.bindx([ADDR])
socket_serv.listen(5)
socket_serv.events.data_io = 1
socket_serv.events.clear()
while True:
print
'Waiting for user connecting'
conn_sock, addr = socket_serv.accept()
print
'connecting: ', addr
msg = ''
while True:
fromaddr, flags, msg, notif = conn_sock.sctp_recv(BUFSIZE)
print
'recv: %s' % msg
if not msg:
break
conn_sock.sctp_send('current time: %s, message is %s' % (ctime(), msg))
conn_sock.close()
socket_serv.close()
四 verification tag
通过SCTP协议传输消息时,需要保证同一偶联内部verification tag保持一致,否则会当做错包丢弃。
所以通过PYSCTP建立偶联以后,如果需要进一步验证发包功能,需要在建立偶联的时候就记录下偶联的verification tag。
colvin@ubuntu:~$ sudo tcpdump host XX.XX.14.109 -w /tmp/sunny.pcap
[sudo] password for colvin:
tcpdump: listening on ens33, link-type EN10MB (Ethernet), capture size 262144 bytes
^C6 packets captured
6 packets received by filter
0 packets dropped by kernel
得到的抓包的COOKIE里面的verification tag就是我们需要的vTag
![](https://img.haomeiwen.com/i1347404/9e778e11b746ce4f.png)
将获取的verification tag保存在文件Vtag.py中供其他python脚本调用,便可以实现在同一偶联中发送SCTP消息了。
#!/usr/bin/python
# -*- coding: utf-8 -*-
#verification tag
vtag = 0x619f0836
五 发送退出偶联的Abort消息
文件abort.py的代码内容如下:
#!/usr/bin/python
# -*- coding: utf-8 -*-
from scapy.all import *
from Vtag import vtag
#casue=1, invalid stream identifier
data = struct.pack('=BBHI', 0x06, 0x00, 0x0800, 0x04000700)
#(type, chunk flag, length, cause code, cause)
pkt = IP(dst="XX.XX.14.109")/SCTP(dport=2904, sport=2904, tag=vtag)/data
print pkt.dst
print pkt.ttl
print repr(pkt)
pkt.show()
send(pkt)
执行$ sudo python abort.py即可以实现发送退出偶联的Abort消息。
【说明】根据SCTP标准,ABORT数据块的格式如下图所示。
![](https://img.haomeiwen.com/i1347404/c52c0580fed9e23c.png)
数据块标志位(8比特):其中高7比特备用,在发送方设置为全0,并在接收方忽略。
T比特(1比特):当发送方有一个TCB被破坏时则该T比特设置为0,如果发送方没有TCB则把该比特设置为1。
长度(16比特无符号整数):设置为该数据块的长度,包括数据块头和所有包含的差错原因字段。
data = struct.pack('=BBHI', 0x06, 0x00, 0x0800, 0x04000700)
消息体中的0x06指的是消息类型6,0x0800指的是消息长度一共8个字节。
六 发送操作差错(ERROR)数据块
【说明】根据SCTP标准,操作差错(ERROR)数据块的格式如下图所示。
![](https://img.haomeiwen.com/i1347404/243060fb3d9497d4.png)
数据块标志位(8比特):在发送方设置为全0,并在接收方忽略。
可变长度(16比特无符号整数):设置为该数据块的字节数,包括数据块头和所有包含的差错原因字段的长度。
然后对于无效的流标识符原因的差错数据块,其差错原因的格式如下。
![](https://img.haomeiwen.com/i1347404/050c89806aa7d327.png)
流识别符:(16比特无符号整数):包含了接收的差错的DATA数据块的流标识符
备用字段(16比特):由发送方设为全0,在接收方忽略
由此,可以得到对于无效的流标识符原因的差错数据块,其消息体编码为
data = struct.pack('=BBHHHHH', 0x09, 0x00, 0x0c00, 0x0100, 0x0800, 0x1200, 0x0000)
其中0x09是消息类型9,0x0c00是消息体长度12个字节,0x0100是差错原因编码1,0x0800是原因长度8个字节。
最终的文件1InvalidStramID.py内容如下。
#!/usr/bin/python
# -*- coding: utf-8 -*-
from scapy.all import *
import Vtag
#casue=1, invalid stream identifier
data = struct.pack('=BBHHHHH', 0x09, 0x00, 0x0c00, 0x0100, 0x0800, 0x1200, 0x0000)
#(type, chunk flag, length, cause code, cause)
pkt = IP(dst="XX.XX.14.109")/SCTP(dport=2904, sport=2904, tag=Vtag.vtag)/data
print pkt.dst
print pkt.ttl
print repr(pkt)
pkt.show()
send(pkt)
执行$ sudo python 1InvalidStramI.py即可以实现发送无效的流标识符原因的差错数据消息。
其他原因的差错数据块可依照上例完成。
七 下一步的方向
1 建立偶联发送消息时verification tag的抓取很不方便,可以考虑用代码同步实现。
2 考虑用PyQt5对操作界面做优化,方便测试人员使用。
最后,需要感谢同事@claire对PYSCTP的研究。
网友评论