SRT协议控制包处理源码解读
SRT协议控制包处理源码解读
本文为个人在看协议栈时候的理解,不一定对,还望发现错误能够及时指正
ACK包
1. 如果ACK包长度为SEND_LITE_ACK(4)
该包在srt中被称为SEND LITE ACK,仅包含ACK的seq
取出当前收到ACK seq(ack),同已发送的ACK seq(m_iSndLastAck)比较,使用seqcmp
函数比较,seqcmp
比较方式如下
(abs(seq1 - seq2) < m_iSeqNoTH) ? (seq1 - seq2) : (seq2 - seq1)
其中m_iSeqNoTH = 0x3FFFFFFF
为最大seq,超过最大seq从1开始
若比较返回值值大于等于0,则作出以下修改
-
滑动窗口大小(m_iFlowWindowSize)
重设为
m_iFlowWindowSize - CSeqNo::seqoff(m_iSndLastAck, ack)
其中
seqoff
为common.h
中定义的获取调整值的公用函数 -
上一个发送的ACK seq(m_iSndLastAck)调整,供下一次使用,
m_iSndLastAck = ack
扫描二维码关注公众号,回复: 9142085 查看本文章 -
上一次回复ACK时间(m_ullLastRspAckTime_tk)调整为
m_ullLastRspAckTime_tk = currtime_tk
-
重传包数量(m_iReXmitCount)重置,由于ACK seq已置为收到的ACK seq,所以无需重传,
m_iReXmitCount = 1
处理结束
2. 普通ACK包
普通ACK包处理过程
- 判断是否需要回复ACKACK包,两个判断条件其中之一为真则进行如下处理
- 回复ACK的ACKACK包回复时间是否大于最小时间间隔(COMM_SYN_INTERVAL_US = 10000)(推测应该是为了防止每次都发送ACKACK,同ACK一段时间再回复的原理一致),大于该间隔则重发ACKACK
- 判断ACK是否同上次回复的ACKACK相同,如果相同,说明ACKACK对方没有收到,重发
- 重发做两件事情,第一是重置ACKACK seq(m_iSndLastAck2)为当前回复的ACK seq,第二则是记录该ACKACK包回复时间(m_ullSndLastAck2Time)为当前时间,方便下次判断
- 验证ACK包seq的有效性,无效包则进行如下处理
- 设置
m_bBroken
为真,连接会断开 m_iBrokenCounter
置0(作用暂时未知)
- 设置
- 取出ACK seq,同上一次的ACK seq(m_iSndLastAck)比较,依旧是使用
seqcmp
函数比较- 滑动窗口
m_iFlowWindowSize
设置为ackdata[ACKD_BUFFERLEFT]
的值,推测是及时调整滑动窗口大小来达到限速的目的 m_iSndLastAck
调整为当前ACK seq的值- 上一次回复ACK时间(m_ullLastRspAckTime_tk)调整为
m_ullLastRspAckTime_tk = currtime_tk
- 重传包数量(m_iReXmitCount)重置,由于ACK seq已置为收到的ACK seq,所以无需重传,
m_iReXmitCount = 1
- 滑动窗口
- 判断ACK是否重复,重复则进行如下操作
- 直接丢弃当前包,不处理
- 处理至此,说明该ACK包前的序列为完整,则设置
m_iSndLastFullAck = ack
- 判断上一个ACK seq和当前ACK seq有无偏移,偏移大于0则做如下处理
- 移除当前的ACK seq之前的数据,并且调整上一个ACK seq
- 对srt信息做记录,总发送时间等
- 将当前ACK seq的包从重发列表移除
- 告知所有等待的epoll更新其状态,开始写
- 将当前socket插入等待队列(如果socket没有在队列的话)
- 对控制包长度进行判断
- 如果包长不是
int32_t
长度的整数倍,截取到整数倍为止 - 如果小于最小ACK包长度,报错,丢弃
- 如果包长不是
- 获取ACK包中的rtt,进行调整RTT方差和RTT
m_iRTTVar = (m_iRTTVar * (4 - 1) + abs(rtt - m_iRTT)) / 4
m_iRTT = (m_iRTT * (4 - 1) + rtt) / 4
- 判断包长度是否大于最小ACK包长度,如果这意味着ACKD_RCVSPEED和ACKD_BANDWIDTH字段是可用的
m_iBandwidth = (m_iBandwidth * (8 - 1) + bandwidth) / 8
m_iDeliveryRate = (m_iDeliveryRate * (8 - 1) + pktps) / 8
m_iByteDeliveryRate = (m_iByteDeliveryRate * (8 - 1) + bytesps) / 8
- 更新srt的总的状态信息
ACKACK包
回复ACK的包
-
调整RTT方差
m_iRTTVar = (m_iRTTVar * 3 + abs(rtt - m_iRTT)) >> 2
-
调整RTT
m_iRTT = (m_iRTT * 7 + rtt) >> 3
-
比较ACKACK和上一个收到的ACKACK,如果较新,则更新
LOSSREPORT包
只有该包会导致重传,用于报告重传的包
-
判断losslist的长度,losslist可能存在多个list,list可能是单个seq,也可能为一个range
-
循环处理单个seq或者一个seq range,判别依据为第一位是否设置,设置则为seq range
-
seq range
获取seq low和seq high,检查和上一个发送的ACK seq之间的差距,将差的部分添加至
m_pSndLosslist
,记录重发包数量 -
单个seq
判断是否大于上一个ACK,大于才重发,插入
m_pSndLosslist
,记录重发包数量 -
更新srt的重发包数量
-
通知
m_pSndUList
立即更新,发送这些丢失的包 -
更新srt状态中NAK包的数量
-
CGWARNING包
延时增加的警告包,srt自身并不会发送该类型的包,可能由用户控制,预测ACKACK有一个地方可能会用到这种类型的包,但是被注释掉了
- 调整包间时间间隔
m_ullInterval_tk = (uint64_t)ceil(m_ullInterval_tk * 1.125)
- 更新上一次减缓发送的seq序号
m_iLastDecSeq = m_iSndCurrSeqNo
KEEPALIVE包
不做任何处理,仅是一个提示包,1.3.4版本此包内部无逻辑
HANDSHAKE
握手的包有好几种,分别为INDUCTION
、CONCLUSION
、WAVEAHAND
和AGREEMENT
,其中INDUCTION
、WAVEAHAND
两种类型的包为第一部分握手发送的包,CONCLUSION
为第二部分握手时候发送的包,AGREEMENT
为额外的握手包
// Note: the client-server connection uses:
// --> INDUCTION (empty)
// <-- INDUCTION (cookie)
// --> CONCLUSION (cookie)
// <-- CONCLUSION (ok)
// The rendezvous HSv4 (legacy):
// --> WAVEAHAND (effective only if peer is also connecting)
// <-- CONCLUSION (empty) (consider yourself connected upon reception)
// --> AGREEMENT (sent as a response for conclusion, requires no response)
// The rendezvous HSv5 (using SRT extensions):
// --> WAVEAHAND (with cookie)
// --- (selecting INITIATOR/RESPONDER by cookie contest - comparing one another's cookie)
// <-- CONCLUSION (without extensions, if RESPONDER, with extensions, if INITIATOR)
// --> CONCLUSION (with response extensions, if RESPONDER)
// <-- AGREEMENT (sent exclusively by INITIATOR upon reception of CONCLUSIOn with response extensions)
-
判断包为第一部分的包(
INDUCTION
、WAVEAHAND
)或者rendezvous模式下的非AGREEMENT
包,如果均不是,则说明包非HANDSHAKE,忽略 -
初始化
ISN
、MSS
、socket
等,回复包类型设置为URQ_INDUCTION
或URQ_AGREEMENT
(依据连接方式决定) -
判断版本为
UDT4
还是UDT5
,将version更新到信息中如果版本为
UDT5
,除了设置版本之外,还将做额外的工作,判断解析内容,如果内容解析出现问题,则认为是非法的握手包,version置0,设置回复类型为SRT_REJ_ROGUE -
初始化extension参数,表明回复中有额外内容,如果是reject的包则该值为false
-
组织response,设置回复包为handshake包,申请空间,填充信息,PeerID,时间戳等,并插入队列等待发送,更新上一个包发送时间为当前时间,解析完成
SHUTDOWN
收到SHUTDOWN包,连接将会断开
- 设置
shutdown
、closing
、broken
、brokenCounter
等参数,为关闭做准备 - 给发送者和接受者发送信号,如果他们处于等待数据的状态,其实就是个上锁做一些工作再释放的过程
- 更新事件,让事件队列清空
- 通知事件线程
DROPREQ
丢弃包消息,收到该包会将丢包重传等从丢包列表去除,不再重发
- 收到该消息,上锁,删除msg
- 清理loss list等,重置lossttl等
PEERERROR
用来告知peer端出现错误
- 更新
PeerHealth
为false
EXT
该包作为保留包和用户定义包
- 判断包内容是否能够解析,能够解析且只有包的extended type为
SRT_CMD_HSREQ
、SRT_CMD_HSRSP
才认为包有效并进行操作
源码
源码位于core.cpp
void CUDT::processCtrl(CPacket& ctrlpkt)
{
// Just heard from the peer, reset the expiration count.
m_iEXPCount = 1;
uint64_t currtime_tk;
CTimer::rdtsc(currtime_tk);
m_ullLastRspTime_tk = currtime_tk;
bool using_rexmit_flag = m_bPeerRexmitFlag;
HLOGC(mglog.Debug, log << CONID() << "incoming UMSG:" << ctrlpkt.getType() << " ("
<< MessageTypeStr(ctrlpkt.getType(), ctrlpkt.getExtendedType()) << ") socket=%" << ctrlpkt.m_iID);
switch (ctrlpkt.getType())
{
case UMSG_ACK: //010 - Acknowledgement
{
int32_t ack;
int32_t* ackdata = (int32_t*)ctrlpkt.m_pcData;
// process a lite ACK
if (ctrlpkt.getLength() == (size_t)SEND_LITE_ACK)
{
ack = *ackdata;
if (CSeqNo::seqcmp(ack, m_iSndLastAck) >= 0)
{
m_iFlowWindowSize -= CSeqNo::seqoff(m_iSndLastAck, ack);
HLOGC(mglog.Debug, log << CONID() << "ACK covers: " << m_iSndLastDataAck << " - " << ack << " [ACK=" << m_iSndLastAck << "] (FLW: " << m_iFlowWindowSize << ") [LITE]");
m_iSndLastAck = ack;
m_ullLastRspAckTime_tk = currtime_tk;
m_iReXmitCount = 1; // Reset re-transmit count since last ACK
}
break;
}
// read ACK seq. no.
ack = ctrlpkt.getAckSeqNo();
// send ACK acknowledgement
// number of ACK2 can be much less than number of ACK
uint64_t now = CTimer::getTime();
if ((now - m_ullSndLastAck2Time > (uint64_t)COMM_SYN_INTERVAL_US) || (ack == m_iSndLastAck2))
{
sendCtrl(UMSG_ACKACK, &ack);
m_iSndLastAck2 = ack;
m_ullSndLastAck2Time = now;
}
// Got data ACK
ack = ackdata[ACKD_RCVLASTACK];
// New code, with TLPKTDROP
// protect packet retransmission
CGuard::enterCS(m_AckLock);
// check the validation of the ack
if (CSeqNo::seqcmp(ack, CSeqNo::incseq(m_iSndCurrSeqNo)) > 0)
{
CGuard::leaveCS(m_AckLock);
//this should not happen: attack or bug
LOGC(glog.Error, log << CONID() << "ATTACK/IPE: incoming ack seq " << ack << " exceeds current "
<< m_iSndCurrSeqNo << " by " << (CSeqNo::seqoff(m_iSndCurrSeqNo, ack)-1) << "!");
m_bBroken = true;
m_iBrokenCounter = 0;
break;
}
if (CSeqNo::seqcmp(ack, m_iSndLastAck) >= 0)
{
// Update Flow Window Size, must update before and together with m_iSndLastAck
m_iFlowWindowSize = ackdata[ACKD_BUFFERLEFT];
m_iSndLastAck = ack;
m_ullLastRspAckTime_tk = currtime_tk;
m_iReXmitCount = 1; // Reset re-transmit count since last ACK
}
/*
* We must not ignore full ack received by peer
* if data has been artificially acked by late packet drop.
* Therefore, a distinct ack state is used for received Ack (iSndLastFullAck)
* and ack position in send buffer (m_iSndLastDataAck).
* Otherwise, when severe congestion causing packet drops (and m_iSndLastDataAck update)
* occures, we drop received acks (as duplicates) and do not update stats like RTT,
* which may go crazy and stay there, preventing proper stream recovery.
*/
if (CSeqNo::seqoff(m_iSndLastFullAck, ack) <= 0)
{
// discard it if it is a repeated ACK
CGuard::leaveCS(m_AckLock);
break;
}
m_iSndLastFullAck = ack;
int offset = CSeqNo::seqoff(m_iSndLastDataAck, ack);
// IF distance between m_iSndLastDataAck and ack is nonempty...
if (offset > 0) {
// acknowledge the sending buffer (remove data that predate 'ack')
m_pSndBuffer->ackData(offset);
const int64_t currtime = CTimer::getTime();
// record total time used for sending
CGuard::enterCS(m_StatsLock);
m_stats.sndDuration += currtime - m_stats.sndDurationCounter;
m_stats.m_sndDurationTotal += currtime - m_stats.sndDurationCounter;
m_stats.sndDurationCounter = currtime;
CGuard::leaveCS(m_StatsLock);
HLOGC(mglog.Debug, log << CONID() << "ACK covers: " << m_iSndLastDataAck << " - " << ack
<< " [ACK=" << m_iSndLastAck << "] BUFr=" << m_iFlowWindowSize
<< " RTT=" << ackdata[ACKD_RTT] << " RTT*=" << ackdata[ACKD_RTTVAR]
<< " BW=" << ackdata[ACKD_BANDWIDTH] << " Vrec=" << ackdata[ACKD_RCVSPEED]);
// update sending variables
m_iSndLastDataAck = ack;
// remove any loss that predates 'ack' (not to be considered loss anymore)
m_pSndLossList->remove(CSeqNo::decseq(m_iSndLastDataAck));
}
CGuard::leaveCS(m_AckLock);
if (m_bSynSending)
{
CGuard lk(m_SendBlockLock);
pthread_cond_signal(&m_SendBlockCond);
}
// acknowledde any waiting epolls to write
s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_OUT, true);
// insert this socket to snd list if it is not on the list yet
m_pSndQueue->m_pSndUList->update(this, CSndUList::DONT_RESCHEDULE);
size_t acksize = ctrlpkt.getLength(); // TEMPORARY VALUE FOR CHECKING
bool wrongsize = 0 != (acksize % ACKD_FIELD_SIZE);
acksize = acksize / ACKD_FIELD_SIZE; // ACTUAL VALUE
if ( wrongsize )
{
// Issue a log, but don't do anything but skipping the "odd" bytes from the payload.
LOGC(mglog.Error, log << CONID() << "Received UMSG_ACK payload is not evened up to 4-byte based field size - cutting to " << acksize << " fields");
}
// Start with checking the base size.
if ( acksize < ACKD_TOTAL_SIZE_SMALL )
{
LOGC(mglog.Error, log << CONID() << "Invalid ACK size " << acksize << " fields - less than minimum required!");
// Ack is already interpreted, just skip further parts.
break;
}
// This check covers fields up to ACKD_BUFFERLEFT.
// Update RTT
//m_iRTT = ackdata[ACKD_RTT];
//m_iRTTVar = ackdata[ACKD_RTTVAR];
// XXX These ^^^ commented-out were blocked in UDT;
// the current RTT calculations are exactly the same as in UDT4.
int rtt = ackdata[ACKD_RTT];
m_iRTTVar = avg_iir<4>(m_iRTTVar, abs(rtt - m_iRTT));
m_iRTT = avg_iir<8>(m_iRTT, rtt);
/* Version-dependent fields:
* Original UDT (total size: ACKD_TOTAL_SIZE_SMALL):
* ACKD_RCVLASTACK
* ACKD_RTT
* ACKD_RTTVAR
* ACKD_BUFFERLEFT
* Additional UDT fields, not always attached:
* ACKD_RCVSPEED
* ACKD_BANDWIDTH
* SRT extension version 1.0.2 (bstats):
* ACKD_RCVRATE
* SRT extension version 1.0.4:
* ACKD_XMRATE
*/
if (acksize > ACKD_TOTAL_SIZE_SMALL)
{
// This means that ACKD_RCVSPEED and ACKD_BANDWIDTH fields are available.
int pktps = ackdata[ACKD_RCVSPEED];
int bandwidth = ackdata[ACKD_BANDWIDTH];
int bytesps;
/* SRT v1.0.2 Bytes-based stats: bandwidth (pcData[ACKD_XMRATE]) and delivery rate (pcData[ACKD_RCVRATE]) in bytes/sec instead of pkts/sec */
/* SRT v1.0.3 Bytes-based stats: only delivery rate (pcData[ACKD_RCVRATE]) in bytes/sec instead of pkts/sec */
if (acksize > ACKD_TOTAL_SIZE_UDTBASE)
bytesps = ackdata[ACKD_RCVRATE];
else
bytesps = pktps * m_iMaxSRTPayloadSize;
m_iBandwidth = avg_iir<8>(m_iBandwidth, bandwidth);
m_iDeliveryRate = avg_iir<8>(m_iDeliveryRate, pktps);
m_iByteDeliveryRate = avg_iir<8>(m_iByteDeliveryRate, bytesps);
// XXX not sure if ACKD_XMRATE is of any use. This is simply
// calculated as ACKD_BANDWIDTH * m_iMaxSRTPayloadSize.
// Update Estimated Bandwidth and packet delivery rate
// m_iRcvRate = m_iDeliveryRate;
// ^^ This has been removed because with the SrtCongestion class
// instead of reading the m_iRcvRate local field this will read
// cudt->deliveryRate() instead.
}
checkSndTimers(REGEN_KM);
updateCC(TEV_ACK, ack);
CGuard::enterCS(m_StatsLock);
++ m_stats.recvACK;
++ m_stats.recvACKTotal;
CGuard::leaveCS(m_StatsLock);
break;
}
case UMSG_ACKACK: //110 - Acknowledgement of Acknowledgement
{
int32_t ack = 0;
int rtt = -1;
// update RTT
rtt = m_ACKWindow.acknowledge(ctrlpkt.getAckSeqNo(), ack);
if (rtt <= 0)
{
LOGC(mglog.Error, log << "IPE: ACK node overwritten when acknowledging " <<
ctrlpkt.getAckSeqNo() << " (ack extracted: " << ack << ")");
break;
}
//if increasing delay detected...
// sendCtrl(UMSG_CGWARNING);
// RTT EWMA
m_iRTTVar = (m_iRTTVar * 3 + abs(rtt - m_iRTT)) >> 2;
m_iRTT = (m_iRTT * 7 + rtt) >> 3;
updateCC(TEV_ACKACK, ack);
// This function will put a lock on m_RecvLock by itself, as needed.
// It must be done inside because this function reads the current time
// and if waiting for the lock has caused a delay, the time will be
// inaccurate. Additionally it won't lock if TSBPD mode is off, and
// won't update anything. Note that if you set TSBPD mode and use
// srt_recvfile (which doesn't make any sense), you'll have e deadlock.
m_pRcvBuffer->addRcvTsbPdDriftSample(ctrlpkt.getMsgTimeStamp(), m_RecvLock);
// update last ACK that has been received by the sender
if (CSeqNo::seqcmp(ack, m_iRcvLastAckAck) > 0)
m_iRcvLastAckAck = ack;
break;
}
case UMSG_LOSSREPORT: //011 - Loss Report
{
int32_t* losslist = (int32_t *)(ctrlpkt.m_pcData);
size_t losslist_len = ctrlpkt.getLength() / 4;
updateCC(TEV_LOSSREPORT, EventVariant(losslist, losslist_len));
bool secure = true;
// protect packet retransmission
CGuard::enterCS(m_AckLock);
// decode loss list message and insert loss into the sender loss list
for (int i = 0, n = (int)(ctrlpkt.getLength() / 4); i < n; ++ i)
{
if (IsSet(losslist[i], LOSSDATA_SEQNO_RANGE_FIRST))
{
// Then it's this is a <lo, hi> specification with HI in a consecutive cell.
int32_t losslist_lo = SEQNO_VALUE::unwrap(losslist[i]);
int32_t losslist_hi = losslist[i+1];
// <lo, hi> specification means that the consecutive cell has been already interpreted.
++ i;
HLOGF(mglog.Debug, "received UMSG_LOSSREPORT: %d-%d (%d packets)...",
losslist_lo, losslist_hi, CSeqNo::seqoff(losslist_lo, losslist_hi)+1);
if ((CSeqNo::seqcmp(losslist_lo, losslist_hi) > 0) || (CSeqNo::seqcmp(losslist_hi, m_iSndCurrSeqNo) > 0))
{
// seq_a must not be greater than seq_b; seq_b must not be greater than the most recent sent seq
secure = false;
// XXX leaveCS: really necessary? 'break' will break the 'for' loop, not the 'switch' statement.
// and the leaveCS is done again next to the 'for' loop end.
CGuard::leaveCS(m_AckLock);
break;
}
int num = 0;
if (CSeqNo::seqcmp(losslist_lo, m_iSndLastAck) >= 0)
num = m_pSndLossList->insert(losslist_lo, losslist_hi);
else if (CSeqNo::seqcmp(losslist_hi, m_iSndLastAck) >= 0)
{
// This should be theoretically impossible because this would mean
// that the received packet loss report informs about the loss that predates
// the ACK sequence.
// However, this can happen if the packet reordering has caused the earlier sent
// LOSSREPORT will be delivered after later sent ACK. Whatever, ACK should be
// more important, so simply drop the part that predates ACK.
num = m_pSndLossList->insert(m_iSndLastAck, losslist_hi);
}
CGuard::enterCS(m_StatsLock);
m_stats.traceSndLoss += num;
m_stats.sndLossTotal += num;
CGuard::leaveCS(m_StatsLock);
}
else if (CSeqNo::seqcmp(losslist[i], m_iSndLastAck) >= 0)
{
HLOGF(mglog.Debug, "received UMSG_LOSSREPORT: %d (1 packet)...", losslist[i]);
if (CSeqNo::seqcmp(losslist[i], m_iSndCurrSeqNo) > 0)
{
//seq_a must not be greater than the most recent sent seq
secure = false;
CGuard::leaveCS(m_AckLock);
break;
}
int num = m_pSndLossList->insert(losslist[i], losslist[i]);
CGuard::enterCS(m_StatsLock);
m_stats.traceSndLoss += num;
m_stats.sndLossTotal += num;
CGuard::leaveCS(m_StatsLock);
}
}
CGuard::leaveCS(m_AckLock);
if (!secure)
{
HLOGF(mglog.Debug, "WARNING: out-of-band LOSSREPORT received; considered bug or attack");
//this should not happen: attack or bug
m_bBroken = true;
m_iBrokenCounter = 0;
break;
}
// the lost packet (retransmission) should be sent out immediately
m_pSndQueue->m_pSndUList->update(this, CSndUList::DO_RESCHEDULE);
CGuard::enterCS(m_StatsLock);
++ m_stats.recvNAK;
++ m_stats.recvNAKTotal;
CGuard::leaveCS(m_StatsLock);
break;
}
case UMSG_CGWARNING: //100 - Delay Warning
// One way packet delay is increasing, so decrease the sending rate
m_ullInterval_tk = (uint64_t)ceil(m_ullInterval_tk * 1.125);
m_iLastDecSeq = m_iSndCurrSeqNo;
// XXX Note as interesting fact: this is only prepared for handling,
// but nothing in the code is sending this message. Probably predicted
// for a custom congctl. There's a predicted place to call it under
// UMSG_ACKACK handling, but it's commented out.
break;
case UMSG_KEEPALIVE: //001 - Keep-alive
// The only purpose of keep-alive packet is to tell that the peer is still alive
// nothing needs to be done.
break;
case UMSG_HANDSHAKE: //000 - Handshake
{
CHandShake req;
req.load_from(ctrlpkt.m_pcData, ctrlpkt.getLength());
HLOGC(mglog.Debug, log << "processCtrl: got HS: " << req.show());
if ((req.m_iReqType > URQ_INDUCTION_TYPES) // acually it catches URQ_INDUCTION and URQ_ERROR_* symbols...???
|| (m_bRendezvous && (req.m_iReqType != URQ_AGREEMENT))) // rnd sends AGREEMENT in rsp to CONCLUSION
{
// The peer side has not received the handshake message, so it keeps querying
// resend the handshake packet
// This condition embraces cases when:
// - this is normal accept() and URQ_INDUCTION was received
// - this is rendezvous accept() and there's coming any kind of URQ except AGREEMENT (should be RENDEZVOUS or CONCLUSION)
// - this is any of URQ_ERROR_* - well...
CHandShake initdata;
initdata.m_iISN = m_iISN;
initdata.m_iMSS = m_iMSS;
initdata.m_iFlightFlagSize = m_iFlightFlagSize;
// For rendezvous we do URQ_WAVEAHAND/URQ_CONCLUSION --> URQ_AGREEMENT.
// For client-server we do URQ_INDUCTION --> URQ_CONCLUSION.
initdata.m_iReqType = (!m_bRendezvous) ? URQ_CONCLUSION : URQ_AGREEMENT;
initdata.m_iID = m_SocketID;
uint32_t kmdata[SRTDATA_MAXSIZE];
size_t kmdatasize = SRTDATA_MAXSIZE;
bool have_hsreq = false;
if ( req.m_iVersion > HS_VERSION_UDT4 )
{
initdata.m_iVersion = HS_VERSION_SRT1; // if I remember correctly, this is induction/listener...
int hs_flags = SrtHSRequest::SRT_HSTYPE_HSFLAGS::unwrap(m_ConnRes.m_iType);
if ( hs_flags != 0 ) // has SRT extensions
{
HLOGC(mglog.Debug, log << "processCtrl/HS: got HS reqtype=" << RequestTypeStr(req.m_iReqType) << " WITH SRT ext");
have_hsreq = interpretSrtHandshake(req, ctrlpkt, kmdata, &kmdatasize);
if ( !have_hsreq )
{
initdata.m_iVersion = 0;
m_RejectReason = SRT_REJ_ROGUE;
initdata.m_iReqType = URQFailure(m_RejectReason);
}
else
{
// Extensions are added only in case of CONCLUSION (not AGREEMENT).
// Actually what is expected here is that this may either process the
// belated-repeated handshake from a caller (and then it's CONCLUSION,
// and should be added with HSRSP/KMRSP), or it's a belated handshake
// of Rendezvous when it has already considered itself connected.
// Sanity check - according to the rules, there should be no such situation
if (m_bRendezvous && m_SrtHsSide == HSD_RESPONDER)
{
LOGC(mglog.Error, log << "processCtrl/HS: IPE???: RESPONDER should receive all its handshakes in handshake phase.");
}
// The 'extension' flag will be set from this variable; set it to false
// in case when the AGREEMENT response is to be sent.
have_hsreq = initdata.m_iReqType == URQ_CONCLUSION;
HLOGC(mglog.Debug, log << "processCtrl/HS: processing ok, reqtype="
<< RequestTypeStr(initdata.m_iReqType) << " kmdatasize=" << kmdatasize);
}
}
else
{
HLOGC(mglog.Debug, log << "processCtrl/HS: got HS reqtype=" << RequestTypeStr(req.m_iReqType));
}
}
else
{
initdata.m_iVersion = HS_VERSION_UDT4;
}
initdata.m_extension = have_hsreq;
HLOGC(mglog.Debug, log << CONID() << "processCtrl: responding HS reqtype=" << RequestTypeStr(initdata.m_iReqType) << (have_hsreq ? " WITH SRT HS response extensions" : ""));
// XXX here interpret SRT handshake extension
CPacket response;
response.setControl(UMSG_HANDSHAKE);
response.allocate(m_iMaxSRTPayloadSize);
// If createSrtHandshake failed, don't send anything. Actually it can only fail on IPE.
// There is also no possible IPE condition in case of HSv4 - for this version it will always return true.
if ( createSrtHandshake(Ref(response), Ref(initdata), SRT_CMD_HSRSP, SRT_CMD_KMRSP, kmdata, kmdatasize) )
{
response.m_iID = m_PeerID;
response.m_iTimeStamp = int(CTimer::getTime() - m_stats.startTime);
int nbsent = m_pSndQueue->sendto(m_pPeerAddr, response);
if (nbsent)
{
uint64_t currtime_tk;
CTimer::rdtsc(currtime_tk);
m_ullLastSndTime_tk = currtime_tk;
}
}
}
else
{
HLOGC(mglog.Debug, log << "processCtrl: ... not INDUCTION, not ERROR, not rendezvous - IGNORED.");
}
break;
}
case UMSG_SHUTDOWN: //101 - Shutdown
m_bShutdown = true;
m_bClosing = true;
m_bBroken = true;
m_iBrokenCounter = 60;
// Signal the sender and recver if they are waiting for data.
releaseSynch();
// Unblock any call so they learn the connection_broken error
s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_ERR, true);
CTimer::triggerEvent();
break;
case UMSG_DROPREQ: //111 - Msg drop request
CGuard::enterCS(m_RecvLock);
m_pRcvBuffer->dropMsg(ctrlpkt.getMsgSeq(using_rexmit_flag), using_rexmit_flag);
CGuard::leaveCS(m_RecvLock);
unlose(*(int32_t*)ctrlpkt.m_pcData, *(int32_t*)(ctrlpkt.m_pcData + 4));
// move forward with current recv seq no.
if ((CSeqNo::seqcmp(*(int32_t*)ctrlpkt.m_pcData, CSeqNo::incseq(m_iRcvCurrSeqNo)) <= 0)
&& (CSeqNo::seqcmp(*(int32_t*)(ctrlpkt.m_pcData + 4), m_iRcvCurrSeqNo) > 0))
{
m_iRcvCurrSeqNo = *(int32_t*)(ctrlpkt.m_pcData + 4);
}
break;
case UMSG_PEERERROR: // 1000 - An error has happened to the peer side
//int err_type = packet.getAddInfo();
// currently only this error is signalled from the peer side
// if recvfile() failes (e.g., due to disk fail), blcoked sendfile/send should return immediately
// giving the app a chance to fix the issue
m_bPeerHealth = false;
break;
case UMSG_EXT: //0x7FFF - reserved and user defined messages
HLOGF(mglog.Debug, "CONTROL EXT MSG RECEIVED: %08X\n", ctrlpkt.getExtendedType());
{
// This has currently two roles in SRT:
// - HSv4 (legacy) handshake
// - refreshed KMX (initial KMX is done still in the HS process in HSv5)
bool understood = processSrtMsg(&ctrlpkt);
// CAREFUL HERE! This only means that this update comes from the UMSG_EXT
// message received, REGARDLESS OF WHAT IT IS. This version doesn't mean
// the handshake version, but the reason of calling this function.
//
// Fortunately, the only messages taken into account in this function
// are HSREQ and HSRSP, which should *never* be interchanged when both
// parties are HSv5.
if ( understood )
{
updateAfterSrtHandshake(ctrlpkt.getExtendedType(), HS_VERSION_UDT4);
}
else
{
updateCC(TEV_CUSTOM, &ctrlpkt);
}
}
break;
default:
break;
}
}