diff --git a/ProxyChannel.cxx b/ProxyChannel.cxx index 56236a86..f3e45db3 100644 --- a/ProxyChannel.cxx +++ b/ProxyChannel.cxx @@ -1126,6 +1126,7 @@ class RTPLogicalChannel : public LogicalChannel { void GetRTPPorts(PIPSocket::Address & fSrcIP, PIPSocket::Address & fDestIP, PIPSocket::Address & rSrcIP, PIPSocket::Address & rDestIP, WORD & fSrcPort, WORD & dDestPort, WORD & rSrcPort, WORD & rDestPort) const; + bool IsRTPInactive() const; private: void SetNAT(bool); @@ -1250,8 +1251,8 @@ class H245ProxyHandler : public H245Handler { H245ProxyHandler * GetPeer() const { return peer; } void UpdateLogicalChannelSessionID(WORD flcn, WORD id); LogicalChannel * FindLogicalChannel(WORD flcn); - RTPLogicalChannel * FindRTPLogicalChannelBySessionID(WORD id); - RTPLogicalChannel * FindRTPLogicalChannelBySessionType(RTPSessionTypes sessionType); + RTPLogicalChannel * FindRTPLogicalChannelBySessionID(WORD id) const; + RTPLogicalChannel * FindRTPLogicalChannelBySessionType(RTPSessionTypes sessionType) const; bool UsesH46019() const { return m_useH46019; } void SetTraversalRole(H46019TraversalType type) { m_traversalType = type; m_useH46019 = (type != None); } H46019TraversalType GetTraversalRole() const { return m_traversalType; } @@ -1269,6 +1270,8 @@ class H245ProxyHandler : public H245Handler { void SetRoles(bool isCaller, bool isH245Master) { m_isCaller = isCaller; m_isH245Master = isH245Master; } bool IsCaller() const { return m_isCaller; } bool IsH245Master() const { return m_isH245Master; } + bool IsRTPInactive(short session) const; + protected: // override from class H245Handler @@ -9059,6 +9062,17 @@ void CallSignalSocket::SetCallTypePlan(Q931 *q931) } } +bool CallSignalSocket::IsRTPInactive(short session) const +{ + H245ProxyHandler * proxyhandler = dynamic_cast(m_h245handler); + if (proxyhandler) { + return proxyhandler->IsRTPInactive(session); + } else { + return false; + } +} + + // class H245Handler H245Handler::H245Handler(const PIPSocket::Address & local, const PIPSocket::Address & remote, const PIPSocket::Address & masq) : localAddr(local), remoteAddr(remote), masqAddr(masq), isH245ended(false), m_lastVideoFastUpdatePicture(0) @@ -10980,6 +10994,7 @@ UDPProxySocket::UDPProxySocket(const char *t, const H225_CallIdentifier & id) } m_lastPacketFromForwardSrc = time(NULL); m_lastPacketFromReverseSrc = time(NULL); + m_inactivityTimeout = GkConfig()->GetInteger(ProxySection, "RTPInactivityTimeout", 300); // 300 sec = 5 min } UDPProxySocket::~UDPProxySocket() @@ -11256,6 +11271,20 @@ void UDPProxySocket::SetMediaIP(bool isSRC, const Address & ip) } } +bool UDPProxySocket::IsRTPInactive() const +{ + time_t now = time(NULL); + if ( (fSrcIP != 0 && fSrcPort != 0) && (now - m_lastPacketFromForwardSrc > m_inactivityTimeout) ) { + PTRACE(1, "RTP\tTerminating call because of RTP inactivity from " << AsString(fSrcIP, fSrcPort) << " CallID " << AsString(m_callID.m_guid)); + return true; + } + if ( (rSrcIP != 0 && rSrcPort != 0) && (now - m_lastPacketFromReverseSrc > m_inactivityTimeout) ) { + PTRACE(1, "RTP\tTerminating call because of RTP inactivity from " << AsString(rSrcIP, rSrcPort) << " CallID " << AsString(m_callID.m_guid)); + return true; + } + return false; +} + // this method handles either RTP, RTCP or T.38 data ProxySocket::Result UDPProxySocket::ReceiveData() { @@ -12542,6 +12571,11 @@ void RTPLogicalChannel::GetRTPPorts(PIPSocket::Address & fSrcIP, PIPSocket::Addr } } +bool RTPLogicalChannel::IsRTPInactive() const +{ + return (rtp && rtp->IsRTPInactive()); +} + void RTPLogicalChannel::SetRTPSessionID(WORD id) { if (rtp) @@ -14330,15 +14364,15 @@ LogicalChannel * H245ProxyHandler::FindLogicalChannel(WORD flcn) return (iter != logicalChannels.end()) ? iter->second : NULL; } -RTPLogicalChannel * H245ProxyHandler::FindRTPLogicalChannelBySessionID(WORD id) +RTPLogicalChannel * H245ProxyHandler::FindRTPLogicalChannelBySessionID(WORD id) const { - siterator iter = sessionIDs.find(id); + const_siterator iter = sessionIDs.find(id); return (iter != sessionIDs.end()) ? iter->second : NULL; } -RTPLogicalChannel * H245ProxyHandler::FindRTPLogicalChannelBySessionType(RTPSessionTypes sessionType) +RTPLogicalChannel * H245ProxyHandler::FindRTPLogicalChannelBySessionType(RTPSessionTypes sessionType) const { - for (siterator iter = sessionIDs.begin(); iter != sessionIDs.end() ; ++iter) { + for (const_siterator iter = sessionIDs.begin(); iter != sessionIDs.end() ; ++iter) { if (iter->second->GetType() == sessionType) { RTPLogicalChannel * lc = iter->second; return lc; @@ -14347,6 +14381,16 @@ RTPLogicalChannel * H245ProxyHandler::FindRTPLogicalChannelBySessionType(RTPSess return NULL; } +bool H245ProxyHandler::IsRTPInactive(short session) const +{ + bool inactive = false; + RTPLogicalChannel * lc = FindRTPLogicalChannelBySessionID(session); + if (lc) { + inactive = lc->IsRTPInactive(); + } + return inactive; +} + //void H245ProxyHandler::DumpChannels(const PString & msg, bool dumpPeer) const //{ // if (PTrace::CanTrace(7)) { diff --git a/ProxyChannel.h b/ProxyChannel.h index 39bc47df..ce3db64c 100644 --- a/ProxyChannel.h +++ b/ProxyChannel.h @@ -239,6 +239,7 @@ class UDPProxySocket : public UDPSocket, public ProxySocket { WORD & _fSrcPort, WORD & _fDestPort, WORD & _rSrcPort, WORD & _rDestPort) const; void ZeroAllIPs(); void ForwardAndReverseSeen() { PTRACE(7, "JW RTP ForwardAndReverseSeen"); m_forwardAndReverseSeen = true; } + bool IsRTPInactive() const; protected: @@ -302,6 +303,7 @@ class UDPProxySocket : public UDPSocket, public ProxySocket { bool m_portDetectionDone; bool m_forwardAndReverseSeen; // did we see logical channels for both directions, yet ? bool m_legacyPortDetection; + unsigned m_inactivityTimeout; time_t m_lastPacketFromForwardSrc; time_t m_lastPacketFromReverseSrc; }; @@ -428,6 +430,8 @@ class CallSignalSocket : public TCPProxySocket { CallSignalSocket * GetRemote() const { return dynamic_cast(remote); } + bool IsRTPInactive(short session) const; + protected: void ForwardCall(FacilityMsg *msg); @@ -719,7 +723,7 @@ class MultiplexedRTPHandler : public Singleton { MultiplexedRTPHandler(); virtual ~MultiplexedRTPHandler(); - virtual void OnReload() { /* currently not runtime changable */ } + virtual void OnReload() { /* currently not runtime changeable */ } virtual void AddChannel(const H46019Session & cha); virtual void UpdateChannelSession(const H225_CallIdentifier & callid, WORD flcn, void * openedBy, WORD session); diff --git a/RasSrv.cxx b/RasSrv.cxx index e2b3980a..06107a78 100644 --- a/RasSrv.cxx +++ b/RasSrv.cxx @@ -1612,8 +1612,10 @@ void RasServer::HouseKeeping() ReadLock lock(ConfigReloadMutex); - if (!(count % 60)) // one minute + if (!(count % 60)) { // one minute RegistrationTable::Instance()->CheckEndpoints(); + CallTable::Instance()->CheckRTPInactive(); + } CallTable::Instance()->CheckCalls(this); diff --git a/RasTbl.cxx b/RasTbl.cxx index d34198ca..b41217e0 100644 --- a/RasTbl.cxx +++ b/RasTbl.cxx @@ -5117,6 +5117,14 @@ BYTE CallRec::GetNewDynamicPayloadType() } #endif +bool CallRec::IsRTPInactive(short session) const +{ + if (m_callingSocket) { + return m_callingSocket->IsRTPInactive(session); + } else { + return (m_calledSocket && m_calledSocket->IsRTPInactive(session)); + } +} /* bool CallRec::IsTimeout( @@ -5217,28 +5225,28 @@ void CallTable::LoadConfig() SetTotalBandwidth(GKCapacity); // will take into account ongoing calls m_minimumBandwidthPerCall = GkConfig()->GetInteger("MinimumBandwidthPerCall", -1); m_maximumBandwidthPerCall = GkConfig()->GetInteger("MaximumBandwidthPerCall", -1); - m_signalTimeout = std::max( - GkConfig()->GetInteger(RoutedSec, "SignalTimeout", DEFAULT_SIGNAL_TIMEOUT), - 5000L - ); - m_alertingTimeout = std::max( - GkConfig()->GetInteger(RoutedSec, "AlertingTimeout", DEFAULT_ALERTING_TIMEOUT), - 5000L - ); - m_defaultDurationLimit = GkConfig()->GetInteger( - CallTableSection, "DefaultCallDurationLimit", 0 - ); + m_signalTimeout = std::max(GkConfig()->GetInteger(RoutedSec, "SignalTimeout", DEFAULT_SIGNAL_TIMEOUT), 5000L); + m_alertingTimeout = std::max(GkConfig()->GetInteger(RoutedSec, "AlertingTimeout", DEFAULT_ALERTING_TIMEOUT), 5000L); + m_defaultDurationLimit = GkConfig()->GetInteger(CallTableSection, "DefaultCallDurationLimit", 0); // backward compatibility - check DefaultCallTimeout if (m_defaultDurationLimit == 0) - m_defaultDurationLimit = GkConfig()->GetInteger( - CallTableSection, "DefaultCallTimeout", 0 - ); + m_defaultDurationLimit = GkConfig()->GetInteger(CallTableSection, "DefaultCallTimeout", 0); m_acctUpdateInterval = GkConfig()->GetInteger(CallTableSection, "AcctUpdateInterval", 0); if( m_acctUpdateInterval != 0) m_acctUpdateInterval = std::max(m_acctUpdateInterval, 10L); m_timestampFormat = GkConfig()->GetString(CallTableSection, "TimestampFormat", "RFC822"); m_singleFailoverCDR = Toolkit::AsBool(GkConfig()->GetString(CallTableSection, "SingleFailoverCDR", "1")); + m_inactivityCheck = GkConfig()->GetBoolean(ProxySection, "RTPInactivityCheck", false); + PCaselessString sessionType = GkConfig()->GetString(ProxySection, "RTPInactivityCheckSession", "Audio"); + if (sessionType == "Audio") { + m_inactivityCheckSession = 1; + } else if (sessionType == "Video") { + m_inactivityCheckSession = 2; + } else { + PTRACE(1, "CallTable\tError: You can only check audio or video sessions for inactivity"); + m_inactivityCheckSession = 1; // default to audio + } } void CallTable::Insert(CallRec * NewRec) @@ -5374,12 +5382,11 @@ void CallTable::CheckCalls(RasServer * rassrv) { std::list m_callsToDisconnect; std::list m_callsToUpdate; - time_t now; + time_t now = time(NULL); { WriteLock lock(listLock); iterator Iter = CallList.begin(), eIter = CallList.end(); - now = time(0); while (Iter != eIter) { if ((*Iter)->IsTimeout(now)) m_callsToDisconnect.push_back(callptr(*Iter)); @@ -5397,10 +5404,9 @@ void CallTable::CheckCalls(RasServer * rassrv) std::list::iterator call = m_callsToDisconnect.begin(); while (call != m_callsToDisconnect.end()) { - (*call)->SetDisconnectCause((*call)->IsConnected() - ? Q931::ResourceUnavailable : Q931::TemporaryFailure - ); + (*call)->SetDisconnectCause((*call)->IsConnected() ? Q931::ResourceUnavailable : Q931::TemporaryFailure); (*call)->SetReleaseSource(CallRec::ReleasedByGatekeeper); + if (((*call)->GetNoRemainingRoutes() == 0) || (! (*call)->IsFailoverActive()) || (now - (*call)->GetSetupTime() > (GetSignalTimeout() / 1000) * 5)) { @@ -5425,6 +5431,17 @@ void CallTable::CheckCalls(RasServer * rassrv) } } +void CallTable::CheckRTPInactive() +{ + WriteLock lock(listLock); + for (iterator iter = CallList.begin(); iter != CallList.end(); ++iter) { + if (m_inactivityCheck && (*iter)->IsRTPInactive(m_inactivityCheckSession)) { + PTRACE(1, "CallTable\tTerminating call because of RTP inactivity CallID " << AsString((*iter)->GetCallIdentifier().m_guid)); + (*iter)->Disconnect(); + } + } +} + #ifdef HAS_H460 static PTextFile* OpenQoSFile(const PFilePath & fn) diff --git a/RasTbl.h b/RasTbl.h index 3a38c339..3ae85ab4 100644 --- a/RasTbl.h +++ b/RasTbl.h @@ -1214,8 +1214,8 @@ class CallRec { true if the address has been retrieved successfully, false otherwise. */ bool GetSrcSignalAddr( - PIPSocket::Address& addr, /// will receive the IP address - WORD& port /// will receive the port number + PIPSocket::Address & addr, /// will receive the IP address + WORD & port /// will receive the port number ) const; H225_TransportAddress GetDestSignalAddr() const; @@ -1441,7 +1441,7 @@ class CallRec { #endif // should we use TLS on the outgoing leg, incoming determined by port caller uses - bool ConnectWithTLS() const { return m_connectWithTLS || (m_Called && m_Called->UseTLS()); } // per call dynamicly and config setting + bool ConnectWithTLS() const { return m_connectWithTLS || (m_Called && m_Called->UseTLS()); } // per call dynamically and config setting void SetConnectWithTLS(bool val) { m_connectWithTLS = val; } #ifdef HAS_H235_MEDIA @@ -1453,6 +1453,8 @@ class CallRec { BYTE GetNewDynamicPayloadType(); #endif + bool IsRTPInactive(short session) const; + private: void SendDRQ(); void InternalSetEP(endptr &, const endptr &); @@ -1758,6 +1760,7 @@ class CallTable : public Singleton void CheckCalls( RasServer* rassrv // to avoid call RasServer::Instance every second ); + void CheckRTPInactive(); void RemoveCall(const H225_DisengageRequest & obj_drq, const endptr &); void RemoveCall(const callptr &); @@ -1850,9 +1853,11 @@ class CallTable : public Singleton PString m_timestampFormat; /// flag to trigger per call leg accounting bool m_singleFailoverCDR; + bool m_inactivityCheck; + short m_inactivityCheckSession; CallTable(const CallTable &); - CallTable& operator==(const CallTable &); + CallTable & operator==(const CallTable &); }; // inline functions of EndpointRec