From 49ba30ca253446807ecde92621cf68effec35fb5 Mon Sep 17 00:00:00 2001 From: Antony Chazapis Date: Fri, 2 Mar 2018 23:41:02 +0200 Subject: [PATCH] Introduce the XRF peer type XRF peers can now be defined in the xlxd.interlink file, allowing linking of a local module to a remote using the DExtra protocol. XRF peers are shown in the peer list, along with their XLX siblings. Changes affect only how the reflector initiates the link, as xlxd already supports incoming DExtra links. Each peer is associated internally with a remote client, so code changes include distinguishing peer connect acks from non-peer ones and handling timeouts differently for peer clients to clean up the peer data structure. The DExtra disconnect packet is now properly implemented. --- config/xlxd.interlink | 8 +- src/cbmpeer.cpp | 2 +- src/cbmpeer.h | 2 +- src/cdextrapeer.cpp | 99 +++++++++++++++++++++ src/cdextrapeer.h | 61 +++++++++++++ src/cdextraprotocol.cpp | 192 ++++++++++++++++++++++++++++++++++------ src/cdextraprotocol.h | 5 +- src/cpeer.cpp | 2 +- src/cpeer.h | 4 +- src/cpeers.cpp | 2 +- src/cxlxpeer.cpp | 2 +- src/cxlxpeer.h | 2 +- src/cxlxprotocol.cpp | 2 + src/cxlxprotocol.h | 2 +- src/main.h | 1 + 15 files changed, 347 insertions(+), 39 deletions(-) create mode 100644 src/cdextrapeer.cpp create mode 100644 src/cdextrapeer.h diff --git a/config/xlxd.interlink b/config/xlxd.interlink index fdba77c..b77ce32 100644 --- a/config/xlxd.interlink +++ b/config/xlxd.interlink @@ -2,13 +2,15 @@ # XLXD interlink file # # one line per entry -# each entry specify a remote XLX to peer with +# each entry specifies a remote XLX or XRF to peer with # format: -# +# +# # example: # XLX270 158.64.26.132 ACD +# XRF270 158.64.26.132 BB # -# note: the remote XLX must list this XLX in it's interlink file +# note: the remote XLX must list this XLX in its interlink file # for the link to be established # ############################################################################# diff --git a/src/cbmpeer.cpp b/src/cbmpeer.cpp index 36c38fb..cef9661 100644 --- a/src/cbmpeer.cpp +++ b/src/cbmpeer.cpp @@ -34,7 +34,7 @@ CBmPeer::CBmPeer() { } -CBmPeer::CBmPeer(const CCallsign &callsign, const CIp &ip, char *modules, const CVersion &version) +CBmPeer::CBmPeer(const CCallsign &callsign, const CIp &ip, const char *modules, const CVersion &version) : CPeer(callsign, ip, modules, version) { std::cout << "Adding BM peer" << std::endl; diff --git a/src/cbmpeer.h b/src/cbmpeer.h index b92d238..ee90a9b 100644 --- a/src/cbmpeer.h +++ b/src/cbmpeer.h @@ -41,7 +41,7 @@ class CBmPeer : public CPeer public: // constructors CBmPeer(); - CBmPeer(const CCallsign &, const CIp &, char *, const CVersion &); + CBmPeer(const CCallsign &, const CIp &, const char *, const CVersion &); CBmPeer(const CBmPeer &); // destructor diff --git a/src/cdextrapeer.cpp b/src/cdextrapeer.cpp new file mode 100644 index 0000000..e6a13e6 --- /dev/null +++ b/src/cdextrapeer.cpp @@ -0,0 +1,99 @@ +// +// cdextrapeer.cpp +// xlxd +// +// Created by Antony Chazapis (SV9OAN) on 25/2/2018. +// Copyright © 2016 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#include "main.h" +#include +#include "creflector.h" +#include "cdextrapeer.h" +#include "cdextraclient.h" + + +//////////////////////////////////////////////////////////////////////////////////////// +// constructor + + +CDextraPeer::CDextraPeer() +{ +} + +CDextraPeer::CDextraPeer(const CCallsign &callsign, const CIp &ip, const char *modules, const CVersion &version) +: CPeer(callsign, ip, modules, version) +{ + std::cout << "Adding DExtra peer" << std::endl; + + // and construct the DExtra clients + for ( int i = 0; i < ::strlen(modules); i++ ) + { + // create + CDextraClient *client = new CDextraClient(callsign, ip, modules[i], version.GetMajor()); + // and append to vector + m_Clients.push_back(client); + } +} + +CDextraPeer::CDextraPeer(const CDextraPeer &peer) +: CPeer(peer) +{ + for ( int i = 0; i < peer.m_Clients.size(); i++ ) + { + CDextraClient *client = new CDextraClient((const CDextraClient &)*(peer.m_Clients[i])); + // grow vector capacity if needed + if ( m_Clients.capacity() == m_Clients.size() ) + { + m_Clients.reserve(m_Clients.capacity()+10); + } + // and append + m_Clients.push_back(client); + + } +} + +//////////////////////////////////////////////////////////////////////////////////////// +// destructors + +CDextraPeer::~CDextraPeer() +{ +} + +//////////////////////////////////////////////////////////////////////////////////////// +// status + +bool CDextraPeer::IsAlive(void) const +{ + bool alive = true; + for ( int i = 0; (i < m_Clients.size()) && alive ; i++ ) + { + alive &= m_Clients[i]->IsAlive(); + } + return alive; +} + +//////////////////////////////////////////////////////////////////////////////////////// +// revision helper + +int CDextraPeer::GetProtocolRevision(const CVersion &version) +{ + return version.GetMajor(); +} + diff --git a/src/cdextrapeer.h b/src/cdextrapeer.h new file mode 100644 index 0000000..ff3b24e --- /dev/null +++ b/src/cdextrapeer.h @@ -0,0 +1,61 @@ +// +// cdextrapeer.h +// xlxd +// +// Created by Antony Chazapis (SV9OAN) on 25/2/2018. +// Copyright © 2016 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#ifndef cdextrapeer_h +#define cdextrapeer_h + +#include "cpeer.h" +#include "cdextraclient.h" + +//////////////////////////////////////////////////////////////////////////////////////// +// define + + +//////////////////////////////////////////////////////////////////////////////////////// +// class + +class CDextraPeer : public CPeer +{ +public: + // constructors + CDextraPeer(); + CDextraPeer(const CCallsign &, const CIp &, const char *, const CVersion &); + CDextraPeer(const CDextraPeer &); + + // destructor + ~CDextraPeer(); + + // status + bool IsAlive(void) const; + + // identity + int GetProtocol(void) const { return PROTOCOL_DEXTRA; } + const char *GetProtocolName(void) const { return "DExtra"; } + + // revision helper + static int GetProtocolRevision(const CVersion &); +}; + +//////////////////////////////////////////////////////////////////////////////////////// +#endif /* cdextrapeer_h */ diff --git a/src/cdextraprotocol.cpp b/src/cdextraprotocol.cpp index 8aa88ab..c8c887c 100644 --- a/src/cdextraprotocol.cpp +++ b/src/cdextraprotocol.cpp @@ -24,6 +24,7 @@ #include "main.h" #include +#include "cdextrapeer.h" #include "cdextraclient.h" #include "cdextraprotocol.h" #include "creflector.h" @@ -52,6 +53,7 @@ bool CDextraProtocol::Init(void) // update time m_LastKeepaliveTime.Now(); + m_LastPeersLinkTime.Now(); // done return ok; @@ -115,16 +117,41 @@ void CDextraProtocol::Task(void) // valid module ? if ( g_Reflector.IsValidModule(ToLinkModule) ) { - // acknowledge the request - EncodeConnectAckPacket(&Buffer, ProtRev); - m_Socket.Send(Buffer, Ip); - - // create the client - CDextraClient *client = new CDextraClient(Callsign, Ip, ToLinkModule, ProtRev); - - // and append - g_Reflector.GetClients()->AddClient(client); - g_Reflector.ReleaseClients(); + // is this an ack for a link request? + CPeerCallsignList *list = g_GateKeeper.GetPeerList(); + CCallsignListItem *item = list->FindListItem(Callsign); + if ( item != NULL && Callsign.GetModule() == item->GetModules()[1] && ToLinkModule == item->GetModules()[0] ) + { + std::cout << "DExtra ack packet for module " << ToLinkModule << " from " << Callsign << " at " << Ip << std::endl; + + // already connected ? + CPeers *peers = g_Reflector.GetPeers(); + if ( peers->FindPeer(Callsign, Ip, PROTOCOL_DEXTRA) == NULL ) + { + // create the new peer + // this also create one client per module + CPeer *peer = new CDextraPeer(Callsign, Ip, std::string(1, ToLinkModule).c_str(), CVersion(2, 0, 0)); + + // append the peer to reflector peer list + // this also add all new clients to reflector client list + peers->AddPeer(peer); + } + g_Reflector.ReleasePeers(); + } + else + { + // acknowledge the request + EncodeConnectAckPacket(&Buffer, ProtRev); + m_Socket.Send(Buffer, Ip); + + // create the client + CDextraClient *client = new CDextraClient(Callsign, Ip, ToLinkModule, ProtRev); + + // and append + g_Reflector.GetClients()->AddClient(client); + g_Reflector.ReleaseClients(); + } + g_GateKeeper.ReleasePeerList(); } else { @@ -193,15 +220,25 @@ void CDextraProtocol::Task(void) // handle queue from reflector HandleQueue(); - // keep client alive + // keep alive if ( m_LastKeepaliveTime.DurationSinceNow() > DEXTRA_KEEPALIVE_PERIOD ) { - // + // handle keep alives HandleKeepalives(); // update time m_LastKeepaliveTime.Now(); } + + // peer connections + if ( m_LastPeersLinkTime.DurationSinceNow() > DEXTRA_RECONNECT_PERIOD ) + { + // handle remote peers connections + HandlePeerLinks(); + + // update time + m_LastPeersLinkTime.Now(); + } } //////////////////////////////////////////////////////////////////////////////////////// @@ -257,7 +294,7 @@ void CDextraProtocol::HandleKeepalives(void) // so, send keepalives to all CBuffer keepalive; EncodeKeepAlivePacket(&keepalive); - + // iterate on clients CClients *clients = g_Reflector.GetClients(); int index = -1; @@ -276,18 +313,109 @@ void CDextraProtocol::HandleKeepalives(void) // otherwise check if still with us else if ( !client->IsAlive() ) { - // no, disconnect + CPeers *peers = g_Reflector.GetPeers(); + CPeer *peer = peers->FindPeer(client->GetCallsign(), client->GetIp(), PROTOCOL_DEXTRA); + if ( peer != NULL && peer->GetReflectorModules()[0] == client->GetReflectorModule() ) + { + // no, but this is a peer client, so it will be handled below + } + else + { + // no, disconnect + CBuffer disconnect; + EncodeDisconnectPacket(&disconnect, client->GetReflectorModule()); + m_Socket.Send(disconnect, client->GetIp()); + + // remove it + std::cout << "DExtra client " << client->GetCallsign() << " keepalive timeout" << std::endl; + clients->RemoveClient(client); + } + g_Reflector.ReleasePeers(); + } + + } + g_Reflector.ReleaseClients(); + + // iterate on peers + CPeers *peers = g_Reflector.GetPeers(); + index = -1; + CPeer *peer = NULL; + while ( (peer = peers->FindNextPeer(PROTOCOL_DEXTRA, &index)) != NULL ) + { + // keepalives are sent between clients + + // some client busy or still with us ? + if ( !peer->IsAMaster() && !peer->IsAlive() ) + { + // no, disconnect all clients CBuffer disconnect; - EncodeDisconnectPacket(&disconnect); - m_Socket.Send(disconnect, client->GetIp()); + EncodeDisconnectPacket(&disconnect, peer->GetReflectorModules()[0]); + CClients *clients = g_Reflector.GetClients(); + for ( int i = 0; i < peer->GetNbClients(); i++ ) + { + m_Socket.Send(disconnect, peer->GetClient(i)->GetIp()); + } + g_Reflector.ReleaseClients(); // remove it - std::cout << "DExtra client " << client->GetCallsign() << " keepalive timeout" << std::endl; - clients->RemoveClient(client); + std::cout << "DExtra peer " << peer->GetCallsign() << " keepalive timeout" << std::endl; + peers->RemovePeer(peer); + } + } + g_Reflector.ReleasePeers(); +} + +//////////////////////////////////////////////////////////////////////////////////////// +// Peers helpers + +void CDextraProtocol::HandlePeerLinks(void) +{ + CBuffer buffer; + + // get the list of peers + CPeerCallsignList *list = g_GateKeeper.GetPeerList(); + CPeers *peers = g_Reflector.GetPeers(); + + // check if all our connected peers are still listed by gatekeeper + // if not, disconnect + int index = -1; + CPeer *peer = NULL; + while ( (peer = peers->FindNextPeer(PROTOCOL_DEXTRA, &index)) != NULL ) + { + if ( list->FindListItem(peer->GetCallsign()) == NULL ) + { + // send disconnect packet + EncodeDisconnectPacket(&buffer, peer->GetReflectorModules()[0]); + m_Socket.Send(buffer, peer->GetIp()); + std::cout << "Sending disconnect packet to XRF peer " << peer->GetCallsign() << std::endl; + // remove client + peers->RemovePeer(peer); } - } - g_Reflector.ReleaseClients(); + + // check if all ours peers listed by gatekeeper are connected + // if not, connect or reconnect + for ( int i = 0; i < list->size(); i++ ) + { + CCallsignListItem *item = &((list->data())[i]); + if ( !item->GetCallsign().HasSameCallsignWithWildcard(CCallsign("XRF*")) ) + continue; + if ( strlen(item->GetModules()) != 2 ) + continue; + if ( peers->FindPeer(item->GetCallsign(), PROTOCOL_DEXTRA) == NULL ) + { + // resolve again peer's IP in case it's a dynamic IP + item->ResolveIp(); + // send connect packet to re-initiate peer link + EncodeConnectPacket(&buffer, item->GetModules()); + m_Socket.Send(buffer, item->GetIp(), DEXTRA_PORT); + std::cout << "Sending connect packet to XRF peer " << item->GetCallsign() << " @ " << item->GetIp() << " for module " << item->GetModules()[1] << " (module " << item->GetModules()[0] << ")" << std::endl; + } + } + + // done + g_Reflector.ReleasePeers(); + g_GateKeeper.ReleasePeerList(); } //////////////////////////////////////////////////////////////////////////////////////// @@ -388,7 +516,7 @@ bool CDextraProtocol::IsValidDisconnectPacket(const CBuffer &Buffer, CCallsign * { callsign->SetCallsign(Buffer.data(), 8); callsign->SetModule(Buffer.data()[8]); - valid = callsign->IsValid(); + valid = callsign->IsValid(); } return valid; } @@ -471,12 +599,22 @@ CDvLastFramePacket *CDextraProtocol::IsValidDvLastFramePacket(const CBuffer &Buf void CDextraProtocol::EncodeKeepAlivePacket(CBuffer *Buffer) { - Buffer->Set(GetReflectorCallsign()); + Buffer->Set(GetReflectorCallsign()); +} + +void CDextraProtocol::EncodeConnectPacket(CBuffer *Buffer, const char *Modules) +{ + uint8 lm = (uint8)Modules[0]; + uint8 rm = (uint8)Modules[1]; + Buffer->Set((uint8 *)(const char *)GetReflectorCallsign(), CALLSIGN_LEN); + Buffer->Append(lm); + Buffer->Append(rm); + Buffer->Append((uint8)0); } void CDextraProtocol::EncodeConnectAckPacket(CBuffer *Buffer, int ProtRev) { - // is it for a XRF or repeater + // is it for a XRF or repeater if ( ProtRev == 2 ) { // XRFxxx @@ -504,10 +642,12 @@ void CDextraProtocol::EncodeConnectNackPacket(CBuffer *Buffer) Buffer->Append(tag, sizeof(tag)); } -void CDextraProtocol::EncodeDisconnectPacket(CBuffer *Buffer) +void CDextraProtocol::EncodeDisconnectPacket(CBuffer *Buffer, char Module) { - uint8 tag[] = { ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',0 }; - Buffer->Set(tag, sizeof(tag)); + uint8 tag[] = { ' ',0 }; + Buffer->Set((uint8 *)(const char *)GetReflectorCallsign(), CALLSIGN_LEN); + Buffer->Append((uint8)Module); + Buffer->Append(tag, sizeof(tag)); } void CDextraProtocol::EncodeDisconnectedPacket(CBuffer *Buffer) diff --git a/src/cdextraprotocol.h b/src/cdextraprotocol.h index 3c623fc..4512295 100644 --- a/src/cdextraprotocol.h +++ b/src/cdextraprotocol.h @@ -75,6 +75,7 @@ protected: void HandleQueue(void); // keepalive helpers + void HandlePeerLinks(void); void HandleKeepalives(void); // stream helpers @@ -90,9 +91,10 @@ protected: // packet encoding helpers void EncodeKeepAlivePacket(CBuffer *); + void EncodeConnectPacket(CBuffer *, const char *); void EncodeConnectAckPacket(CBuffer *, int); void EncodeConnectNackPacket(CBuffer *); - void EncodeDisconnectPacket(CBuffer *); + void EncodeDisconnectPacket(CBuffer *, char); void EncodeDisconnectedPacket(CBuffer *); bool EncodeDvHeaderPacket(const CDvHeaderPacket &, CBuffer *) const; bool EncodeDvFramePacket(const CDvFramePacket &, CBuffer *) const; @@ -101,6 +103,7 @@ protected: protected: // time CTimePoint m_LastKeepaliveTime; + CTimePoint m_LastPeersLinkTime; }; //////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/cpeer.cpp b/src/cpeer.cpp index 037325e..96f9c9a 100644 --- a/src/cpeer.cpp +++ b/src/cpeer.cpp @@ -40,7 +40,7 @@ CPeer::CPeer() m_LastHeardTime = std::time(NULL); } -CPeer::CPeer(const CCallsign &callsign, const CIp &ip, char *modules, const CVersion &version) +CPeer::CPeer(const CCallsign &callsign, const CIp &ip, const char *modules, const CVersion &version) { m_Callsign = callsign; m_Ip = ip; diff --git a/src/cpeer.h b/src/cpeer.h index ffb68a5..dc1a7b9 100644 --- a/src/cpeer.h +++ b/src/cpeer.h @@ -42,7 +42,7 @@ class CPeer public: // constructors CPeer(); - CPeer(const CCallsign &, const CIp &, char *, const CVersion &); + CPeer(const CCallsign &, const CIp &, const char *, const CVersion &); CPeer(const CPeer &); // destructor @@ -54,7 +54,7 @@ public: // get const CCallsign &GetCallsign(void) const { return m_Callsign; } const CIp &GetIp(void) const { return m_Ip; } - char *GetModulesModules(void) { return m_ReflectorModules; } + char *GetReflectorModules(void) { return m_ReflectorModules; } // set diff --git a/src/cpeers.cpp b/src/cpeers.cpp index 35c5547..0a83cdb 100644 --- a/src/cpeers.cpp +++ b/src/cpeers.cpp @@ -106,7 +106,7 @@ void CPeers::RemovePeer(CPeer *peer) bool found = false; for ( int i = 0; (i < m_Peers.size()) && !found; i++ ) { - // compare objetc pointers + // compare object pointers if ( (m_Peers[i]) == peer ) { // found it ! diff --git a/src/cxlxpeer.cpp b/src/cxlxpeer.cpp index aa214c4..264d1f6 100644 --- a/src/cxlxpeer.cpp +++ b/src/cxlxpeer.cpp @@ -37,7 +37,7 @@ CXlxPeer::CXlxPeer() { } -CXlxPeer::CXlxPeer(const CCallsign &callsign, const CIp &ip, char *modules, const CVersion &version) +CXlxPeer::CXlxPeer(const CCallsign &callsign, const CIp &ip, const char *modules, const CVersion &version) : CPeer(callsign, ip, modules, version) { // get protocol revision diff --git a/src/cxlxpeer.h b/src/cxlxpeer.h index 482f3ec..497d7f2 100644 --- a/src/cxlxpeer.h +++ b/src/cxlxpeer.h @@ -40,7 +40,7 @@ class CXlxPeer : public CPeer public: // constructors CXlxPeer(); - CXlxPeer(const CCallsign &, const CIp &, char *, const CVersion &); + CXlxPeer(const CCallsign &, const CIp &, const char *, const CVersion &); CXlxPeer(const CXlxPeer &); // destructor diff --git a/src/cxlxprotocol.cpp b/src/cxlxprotocol.cpp index f01fb9a..c347275 100644 --- a/src/cxlxprotocol.cpp +++ b/src/cxlxprotocol.cpp @@ -384,6 +384,8 @@ void CXlxProtocol::HandlePeerLinks(void) for ( int i = 0; i < list->size(); i++ ) { CCallsignListItem *item = &((list->data())[i]); + if ( item->GetCallsign().HasSameCallsignWithWildcard(CCallsign("XRF*")) ) + continue; if ( peers->FindPeer(item->GetCallsign(), PROTOCOL_XLX) == NULL ) { // resolve again peer's IP in case it's a dynamic IP diff --git a/src/cxlxprotocol.h b/src/cxlxprotocol.h index 1b8d8d9..c9b66a1 100644 --- a/src/cxlxprotocol.h +++ b/src/cxlxprotocol.h @@ -56,7 +56,7 @@ protected: // queue helper void HandleQueue(void); - // keepalive helper + // keepalive helpers void HandlePeerLinks(void); void HandleKeepalives(void); diff --git a/src/main.h b/src/main.h index fa7acab..f564162 100644 --- a/src/main.h +++ b/src/main.h @@ -80,6 +80,7 @@ #define DEXTRA_PORT 30001 // UDP port #define DEXTRA_KEEPALIVE_PERIOD 3 // in seconds #define DEXTRA_KEEPALIVE_TIMEOUT (DEXTRA_KEEPALIVE_PERIOD*10) // in seconds +#define DEXTRA_RECONNECT_PERIOD 5 // in seconds // DPlus #define DPLUS_PORT 20001 // UDP port