Add support for HEP3 packet capture in SIP.

This commit is contained in:
Ioana Stanciu 2023-05-23 14:27:45 +03:00
parent 87bd925d49
commit 3539304e63
3 changed files with 261 additions and 3 deletions

View File

@ -39,6 +39,13 @@
; Default true ; Default true
; sips: Boolean. Use SIPS URI for register/contact. Transport defaults to TLS if enabled ; sips: Boolean. Use SIPS URI for register/contact. Transport defaults to TLS if enabled
; ;
; For TCP SIP HEP3 capturing, setup the following parameters:
; capture_filter: Boolean, default false. Enable it if you want HEP3 capture of packets
; capture_agent: String, mandatory if capture_filter is set to true. Name of capture
; capture_server: String, mandatory if capture_filter is set to true. Name of HEP3 server where to send packets
; capture_compress: Boolean, default false. Set to true to compress captured packets
; If not set, capture settings will default to SIP global capture settings.
;
; NOTE: Default port is 5060 for udp/tcp and 5061 for tls ; NOTE: Default port is 5060 for udp/tcp and 5061 for tls
; ;
; Jabber: ; Jabber:

View File

@ -388,6 +388,31 @@
; This parameter is applied on reload ; This parameter is applied on reload
;warn_no_default_udp_transport=yes ;warn_no_default_udp_transport=yes
; capture_filter: boolean. Enable global HEP3 capture of SIP packets
; NOTE: This setting can be overridden by listener settings or by account settings
; in case of outgoing TCP connections.
; This setting applies on reload.
;capture_filter=false
; capture_agent: string, mandatory if capture_filter is set to true. Name of capture agent.
; NOTE: This setting can be overridden by listener settings or by account settings
; in case of outgoing TCP connections.
; This is for internal tracking.
;capture_agent=
; capture_server: string, mandatory if capture_filter is set to true.
; Name of HEP3 server where to send packets. The server with this name must be configured
; in HEP3 module configuration.
; NOTE: This setting can be overridden by listener settings or by account settings
; in case of outgoing TCP connections.
;capture_server=
; capture_compress: boolean. Set to true to compress captured packets.
; If not set, it will use the HEP server configuration 'compress' configured value.
; NOTE: This setting can be overridden by listener settings or by account settings
; in case of outgoing TCP connections.
;capture_compress=false
[options] [options]
; Controls the behaviour for SIP options retrieval ; Controls the behaviour for SIP options retrieval
@ -625,3 +650,21 @@
; role: string: Role to be set in messages sent by connections using this listener ; role: string: Role to be set in messages sent by connections using this listener
; This parameter is applied on reload ; This parameter is applied on reload
;role= ;role=
; capture_filter: boolean. Enable HEP3 capture of packets on this listener.
; NOTE: for outgoing TCP connections, these settings must be made in accfile.conf.
; This setting applies on reload.
;capture_filter=false
; capture_agent: string, mandatory if capture_filter is set to true. Name of capture agent
; This is for internal tracking.
;capture_agent=
; capture_server: string, mandatory if capture_filter is set to true.
; Name of HEP3 server where to send packets. The server with this name must be configured
; in HEP3 module configuration
;capture_server=
; capture_compress: boolean. Set to true to compress captured packets.
; If not set, it will use the HEP server configuration 'compress' configured value
;capture_compress=false

View File

@ -214,6 +214,47 @@ protected:
int m_methods[MethodCount]; int m_methods[MethodCount];
}; };
class CaptureFilter : virtual public SocketFilter
{
public:
CaptureFilter(const char* name = 0)
:m_name(name), m_lock("CaptureFilter"), m_captureAgent(0), m_warned(false)
{
DDebug(DebugAll,"CaptureFilter(%s) [%p]",TelEngine::c_safe(name),this);
}
virtual ~CaptureFilter()
{
DDebug(DebugAll,"~CaptureFilter(%s) [%p]",m_name.c_str(),this);
TelEngine::destruct(m_captureAgent);
}
inline void setLocalAddr(const SocketAddr& lAddr)
{
WLock l(m_lock);
m_local = lAddr;
}
inline void setRemoteAddr(const SocketAddr& rAddr)
{
WLock l(m_lock);
m_remote = rAddr;
}
bool init(YateSIPTransport* transp, const NamedList& params, const SocketAddr& lAddr, const SocketAddr& rAddr);
virtual void* getObject(const String& name) const;
virtual bool received(const void* buffer, int length, int flags, const struct sockaddr* addr, socklen_t adrlen);
virtual bool sent(const void* buffer, int length, int flags, const struct sockaddr* addr, socklen_t adrlen);
private:
String m_name;
RWLock m_lock;
SocketAddr m_local;
SocketAddr m_remote;
Capture* m_captureAgent;
bool m_warned;
};
// A SIP party holder // A SIP party holder
class YateSIPPartyHolder : public ProtocolHolder class YateSIPPartyHolder : public ProtocolHolder
{ {
@ -221,7 +262,7 @@ public:
inline YateSIPPartyHolder(DebugEnabler* enabler, Mutex* mutex = 0, const String& traceId = String::empty()) inline YateSIPPartyHolder(DebugEnabler* enabler, Mutex* mutex = 0, const String& traceId = String::empty())
: ProtocolHolder(Udp), : ProtocolHolder(Udp),
m_party(0), m_partyMutex(mutex), m_sips(false), m_transLocalPort(0), m_transRemotePort(0), m_party(0), m_partyMutex(mutex), m_sips(false), m_transLocalPort(0), m_transRemotePort(0),
m_enabler(enabler), m_traceId(traceId) m_capture(false), m_captZipped(false), m_enabler(enabler), m_traceId(traceId)
{} {}
virtual ~YateSIPPartyHolder() virtual ~YateSIPPartyHolder()
{ setParty(); } { setParty(); }
@ -296,6 +337,11 @@ protected:
// Failure // Failure
String m_partyInvalidRemote; String m_partyInvalidRemote;
bool m_capture;
String m_captAgent;
String m_captServer;
bool m_captZipped;
private: private:
DebugEnabler* m_enabler; DebugEnabler* m_enabler;
String m_traceId; String m_traceId;
@ -463,6 +509,8 @@ protected:
String m_protoAddr; // Proto + addr: used for debug (send/recv msg) String m_protoAddr; // Proto + addr: used for debug (send/recv msg)
String m_role; String m_role;
bool m_ignoreVia; // Ignore VIA header (override from global) bool m_ignoreVia; // Ignore VIA header (override from global)
CaptureFilter* m_capture;
private: private:
YateSIPTransport() : ProtocolHolder(Udp) {} // No default constructor YateSIPTransport() : ProtocolHolder(Udp) {} // No default constructor
}; };
@ -1335,6 +1383,12 @@ static int s_expires_def = EXPIRES_DEF;
static int s_expires_max = EXPIRES_MAX; static int s_expires_max = EXPIRES_MAX;
static int s_defEncoding = SipHandler::BodyBase64; static int s_defEncoding = SipHandler::BodyBase64;
// SIP packet capture global parameters
static bool s_captureFilter = false;
static String s_captureAgent = "sip";
static String s_captureServer;
static bool s_captureZipped = false;
static const String s_statusCmd = "status"; static const String s_statusCmd = "status";
static const String s_noAutoAuth = "noautoauth"; static const String s_noAutoAuth = "noautoauth";
static const String s_username = "username"; static const String s_username = "username";
@ -2719,7 +2773,12 @@ bool YateSIPPartyHolder::buildParty(bool force, bool isTemp)
} }
if (tcpTrans && initTcp) { if (tcpTrans && initTcp) {
// TODO: handle other params: maxpkt, thread prio // TODO: handle other params: maxpkt, thread prio
tcpTrans->init(NamedList::empty(),true); NamedList p("extra");
p.setParam(YSTRING("capture_filter"),m_capture);
p.setParam(YSTRING("capture_agent"),m_captAgent);
p.setParam(YSTRING("capture_server"),m_captServer);
p.setParam(YSTRING("capture_compress"),m_captZipped);
tcpTrans->init(p,true);
} }
if (!isTemp || m_party) if (!isTemp || m_party)
TraceDebug(m_traceId,m_enabler,m_party ? DebugAll : DebugNote, TraceDebug(m_traceId,m_enabler,m_party ? DebugAll : DebugNote,
@ -3092,6 +3151,92 @@ void YateSIPListener::initialize(const NamedList& params, bool first)
} }
} }
class CaptureRef : public RefObject
{
public:
inline CaptureRef(Capture** capt)
: m_capture(capt)
{ }
virtual void* getObject(const String& name) const
{ return (name == YATOM("Capture*")) ? m_capture : RefObject::getObject(name); }
private:
CaptureRef();
void* m_capture;
};
void* CaptureFilter::getObject(const String& name) const
{
if (name == "CaptureFilter")
return (void*) this;
return GenObject::getObject(name);
}
bool CaptureFilter::init(YateSIPTransport* transp, const NamedList& params,
const SocketAddr& lAddr, const SocketAddr& rAddr)
{
if (!transp)
return false;
WLock l(m_lock);
if (m_captureAgent && m_captureAgent->valid())
return true;
TelEngine::destruct(m_captureAgent);
m_local = lAddr;
m_remote = rAddr;
DDebug(&plugin,DebugAll,"CaptureFilter::init() '%s' [%p]",m_name.c_str(),this);
Message m("hep3.capture");
CaptureRef* cRef = new CaptureRef(&m_captureAgent);
m.userData(cRef);
TelEngine::destruct(cRef);
m.copyParams(params);
m.setParam("payload_proto","sip");
m.setParam("ip_type",lAddr.familyName());
m.setParam("ip_proto",transp->protoName());
Engine::dispatch(m);
if (!m_captureAgent) {
Debug(&plugin,DebugConf,"Failed to obtain capture agent '%s' [%p]",m_name.c_str(),this);
return false;
}
return true;
}
bool CaptureFilter::received(const void* buffer, int length, int flags,
const struct sockaddr* addr, socklen_t adrlen)
{
if (!m_captureAgent)
return false;
DDebug(&plugin,DebugAll,"CaptureFilter::received(%p,%d,%x,%p,%u) [%p]",
buffer,length,flags,addr,adrlen,this);
SocketAddr a(addr,adrlen);
CaptureInfo info(Time::now(),addr && adrlen ? &a : &m_remote,&m_local);
if (!m_captureAgent->write((const uint8_t*)buffer,length,info)) {
if (!m_captureAgent->valid() && !m_warned) {
Debug(&plugin,DebugWarn,"Capture filter '%s' has become invalid [%p]",m_captureAgent->toString().c_str(),this);
m_warned = true;
}
}
return false;
}
bool CaptureFilter::sent(const void* buffer, int length, int flags,
const struct sockaddr* addr, socklen_t adrlen)
{
if (!m_captureAgent)
return false;
DDebug(&plugin,DebugAll,"CaptureFilter::sent(%p,%d,%x,%p,%u) [%p]",
buffer,length,flags,addr,adrlen,this);
SocketAddr a(addr,adrlen);
CaptureInfo info(Time::now(),&m_local,addr && adrlen ? &a : &m_remote);
if (!m_captureAgent->write((const uint8_t*)buffer,length,info)) {
if (!m_captureAgent->valid() && !m_warned) {
Debug(&plugin,DebugWarn,"Capture filter '%s' has become invalid [%p]",m_captureAgent->toString().c_str(),this);
m_warned = true;
}
}
return false;
}
YateSIPTransport::YateSIPTransport(int proto, const String& id, Socket* sock, int stat) YateSIPTransport::YateSIPTransport(int proto, const String& id, Socket* sock, int stat)
: Mutex(true,"YateSIPTransport"), : Mutex(true,"YateSIPTransport"),
@ -3099,7 +3244,7 @@ YateSIPTransport::YateSIPTransport(int proto, const String& id, Socket* sock, in
m_id(id), m_status(stat), m_statusChgTime(Time::secNow()), m_id(id), m_status(stat), m_statusChgTime(Time::secNow()),
m_sock(sock), m_maxpkt(1500), m_sock(sock), m_maxpkt(1500),
m_worker(0), m_initialized(false), m_worker(0), m_initialized(false),
m_ignoreVia(s_ignoreVia) m_ignoreVia(s_ignoreVia), m_capture(0)
{ {
} }
@ -3127,6 +3272,38 @@ bool YateSIPTransport::init(const NamedList& params, const NamedList& defs,
} }
m_rtpNatAddr = params.getValue(YSTRING("nat_address")); m_rtpNatAddr = params.getValue(YSTRING("nat_address"));
m_role = params[YSTRING("role")]; m_role = params[YSTRING("role")];
bool capture = params.getBoolValue(YSTRING("capture_filter"),s_captureFilter);
if (capture) {
NamedList captParams("capture");
Lock lck(s_globalMutex);
captParams.addParam(YSTRING("capture"),params.getValue(YSTRING("capture_agent"),s_captureAgent));
captParams.addParam(YSTRING("server"),params.getValue(YSTRING("capture_server"),s_captureServer));
captParams.addParam(YSTRING("compress"),params.getBoolValue(YSTRING("capture_compress"),s_captureZipped));
lck.drop();
bool install = false;
if (!m_capture) {
m_capture = new CaptureFilter(captParams.getValue("agent"));
install = true;
}
if (!m_capture->init(this,captParams,m_local,m_remote)) {
if (m_sock)
m_sock->removeFilter(m_capture);
TelEngine::destruct(m_capture);
}
else if (install && m_sock) {
if (!m_sock->installFilter(m_capture)) {
Debug(&plugin,DebugNote,"Transport(%s) failed to install capture filter [%p]",
m_id.c_str(),this);
}
}
}
else if (m_capture) {
if (m_sock)
m_sock->removeFilter(m_capture);
TelEngine::destruct(m_capture);
}
unlock(); unlock();
// Done if not first // Done if not first
if (!first) if (!first)
@ -3135,6 +3312,7 @@ bool YateSIPTransport::init(const NamedList& params, const NamedList& defs,
m_sock->getSockName(m_local); m_sock->getSockName(m_local);
m_sock->getPeerName(m_remote); m_sock->getPeerName(m_remote);
} }
return true; return true;
} }
@ -3237,6 +3415,8 @@ void YateSIPTransport::resetSocket(Socket*& sock, int linger)
if (!sock) if (!sock)
return; return;
sock->setLinger(linger); sock->setLinger(linger);
// socket does not own capture filter, avoid being destroyed
sock->clearFilters(false);
delete sock; delete sock;
sock = 0; sock = 0;
} }
@ -3245,6 +3425,7 @@ void YateSIPTransport::destroyed()
{ {
terminate("Destroyed"); terminate("Destroyed");
resetSocket(m_sock,-1); resetSocket(m_sock,-1);
TelEngine::destruct(m_capture);
Debug(&plugin,DebugAll,"Transport(%s) destroyed [%p]",m_id.c_str(),this); Debug(&plugin,DebugAll,"Transport(%s) destroyed [%p]",m_id.c_str(),this);
RefObject::destroyed(); RefObject::destroyed();
} }
@ -3462,6 +3643,14 @@ int YateSIPUDPTransport::process()
lock(); lock();
m_sock = sock; m_sock = sock;
m_local = addr; m_local = addr;
if (m_capture) {
m_capture->setLocalAddr(addr);
// doesn't matter if we installed if before, install filter checks
if (!m_sock->installFilter(m_capture))
Debug(&plugin,DebugNote,
"Transport(%s) failed to install capture filter [%p]",
m_id.c_str(),this);
}
m_reason.clear(); m_reason.clear();
unlock(); unlock();
setProtoAddr(true); setProtoAddr(true);
@ -4192,6 +4381,14 @@ void YateSIPTCPTransport::resetConnection(Socket* sock)
setProtoAddr(true); setProtoAddr(true);
Debug(&plugin,DebugAll,"Transport(%s) connected local=%s remote=%s [%p]", Debug(&plugin,DebugAll,"Transport(%s) connected local=%s remote=%s [%p]",
m_id.c_str(),m_local.addr().c_str(),m_remote.addr().c_str(),this); m_id.c_str(),m_local.addr().c_str(),m_remote.addr().c_str(),this);
if (m_capture) {
m_capture->setLocalAddr(m_local);
m_capture->setRemoteAddr(m_remote);
// doesn't matter if we installed if before, install filter checks
if (!m_sock->installFilter(m_capture))
Debug(&plugin,DebugNote,"Transport(%s)) failed to install capture filter [%p]",
m_id.c_str(),this);
}
} }
// Update party local/remote ip/port // Update party local/remote ip/port
if (m_party) if (m_party)
@ -9212,6 +9409,12 @@ bool YateSIPLine::update(const Message& msg)
chg = change(m_password,msg.getValue(YSTRING("password"))) || chg; chg = change(m_password,msg.getValue(YSTRING("password"))) || chg;
chg = change(m_domain,msg.getValue(YSTRING("domain"))) || chg; chg = change(m_domain,msg.getValue(YSTRING("domain"))) || chg;
chg = change(m_flags,msg.getIntValue(YSTRING("xsip_flags"),-1)) || chg; chg = change(m_flags,msg.getIntValue(YSTRING("xsip_flags"),-1)) || chg;
s_globalMutex.lock();
chg = change(m_capture,msg.getBoolValue(YSTRING("capture_filter"),s_captureFilter)) || chg;
m_captAgent = msg.getValue(YSTRING("capture_agent"),s_captureAgent);
m_captServer = msg.getValue(YSTRING("capture_server"),s_captureServer);
m_captZipped = msg.getBoolValue(YSTRING("capture_compress"),s_captureZipped);
s_globalMutex.unlock();
m_trans = msg.getIntValue(YSTRING("xsip_trans_count"),-1); m_trans = msg.getIntValue(YSTRING("xsip_trans_count"),-1);
m_display = msg.getValue(YSTRING("description")); m_display = msg.getValue(YSTRING("description"));
m_interval = msg.getIntValue(YSTRING("interval"),600); m_interval = msg.getIntValue(YSTRING("interval"),600);
@ -9761,6 +9964,11 @@ void SIPDriver::initialize()
s_tcpIdle = tcpIdleInterval(s_cfg.getIntValue("general","tcp_idle",TCP_IDLE_DEF)); s_tcpIdle = tcpIdleInterval(s_cfg.getIntValue("general","tcp_idle",TCP_IDLE_DEF));
s_tcpKeepalive = s_cfg.getIntValue("general","tcp_keepalive",s_tcpIdle); s_tcpKeepalive = s_cfg.getIntValue("general","tcp_keepalive",s_tcpIdle);
s_tcpKeepaliveFirst = s_cfg.getIntValue("general","tcp_keepalive_first",0,0); s_tcpKeepaliveFirst = s_cfg.getIntValue("general","tcp_keepalive_first",0,0);
// SIP capture parameters
s_captureFilter = s_cfg.getBoolValue("general","capture_filter",s_captureFilter);
s_captureAgent = s_cfg.getValue("general","capture_agent","sip");
s_captureServer = s_cfg.getValue("general","capture_server",s_captureServer);
s_captureZipped = s_cfg.getBoolValue("general","capture_compress",s_captureZipped);
// Mark listeners // Mark listeners
m_endpoint->initializing(true); m_endpoint->initializing(true);
// Setup general listener // Setup general listener