From c2a96a91e85fe4d202c23c2d91e9fa0f22b11078 Mon Sep 17 00:00:00 2001 From: Jan Willamowius Date: Thu, 4 Jun 2020 17:10:12 +0200 Subject: [PATCH] new feature GnuGkAssignedGatekeeper to push endpoints back to their intended home gatekeepers --- Neighbor.cxx | 12 +++++ Neighbor.h | 3 ++ RasSrv.cxx | 12 ++++- RasSrv.h | 2 +- RasTbl.cxx | 42 +++++++++++++-- RasTbl.h | 21 ++++++-- Toolkit.cxx | 120 +++++++++++++++++++++++++++++++++++++++++-- Toolkit.h | 32 ++++++++++-- changes.txt | 1 + docs/manual/ras.sgml | 72 +++++++++++++++++++++++--- gk.cxx | 14 +++++ 11 files changed, 304 insertions(+), 27 deletions(-) diff --git a/Neighbor.cxx b/Neighbor.cxx index cd326e0e..57897945 100644 --- a/Neighbor.cxx +++ b/Neighbor.cxx @@ -1740,6 +1740,18 @@ bool NeighborList::IsTraversalServer(const PIPSocket::Address & addr) const return find_if(m_neighbors.begin(), m_neighbors.end(), bind2nd(mem_fun(&Neighbor::IsTraversalServer), &addr)) != m_neighbors.end(); } +// is IP a neighbor and is it not disabled ? +bool NeighborList::IsAvailable(const PIPSocket::Address & ip) +{ + // Attempt to find the neighbor in the list + List::iterator findNeighbor = find_if(m_neighbors.begin(), m_neighbors.end(), bind2nd(mem_fun(&Neighbor::IsFrom), &ip)); + if (findNeighbor == m_neighbors.end()) + { + return false; + } + return !(*findNeighbor)->IsDisabled(); +} + PString NeighborList::GetNeighborIdBySigAdr(const H225_TransportAddress & sigAd) { PIPSocket::Address ipaddr; diff --git a/Neighbor.h b/Neighbor.h index db3d672a..c95699e0 100644 --- a/Neighbor.h +++ b/Neighbor.h @@ -169,6 +169,9 @@ class NeighborList { bool IsTraversalClient(const PIPSocket::Address &) const; bool IsTraversalServer(const PIPSocket::Address &) const; + // is IP a neighbor and is it not disabled ? + bool IsAvailable(const PIPSocket::Address & ip); + // return the neighbor's ID from the list by signal address PString GetNeighborIdBySigAdr(const H225_TransportAddress & sigAd); PString GetNeighborIdBySigAdr(const PIPSocket::Address & sigAd); diff --git a/RasSrv.cxx b/RasSrv.cxx index c8f89b07..352fce9d 100644 --- a/RasSrv.cxx +++ b/RasSrv.cxx @@ -2644,7 +2644,7 @@ bool RegistrationRequestPDU::Process() PTRACE(3, "RAS\tRRQ rejected by unknown reason from " << rx_addr); return BuildRRJ(H225_RegistrationRejectReason::e_undefinedReason); } - ep->SetRasServerIP(m_msg->m_localAddr); // remember which of our IPs the endpoint has sent the RRQ to (need for H.460.18 SCI) + ep->SetRasServerIP(m_msg->m_localAddr); // remember which of our IPs the endpoint has sent the RRQ to (needed for H.460.18 SCI) #ifdef HAS_H46017 if (usesH46017) { @@ -2878,8 +2878,16 @@ bool RegistrationRequestPDU::Process() #endif // Alternate GKs - if (request.HasOptionalField(H225_RegistrationRequest::e_supportsAltGK)) + if (request.HasOptionalField(H225_RegistrationRequest::e_supportsAltGK)) { RasSrv->SetAlternateGK(rcf, m_msg->m_peerAddr); +#if HAS_DATABASE + // check if we have GnuGk-assigned home gatekeepers + // TODO: make sure this DB query only runs on new registrations + if (Toolkit::Instance()->GnuGkAssignedGKs().HasAssignedGk(ep, m_msg->m_peerAddr)) { + PTRACE(2, "RCF\tEndpoint is assigned to other gatekeeper - will try to re-home"); + } +#endif // HAS_DATABASE + } // Call credit display if (ep->AddCallCreditServiceControl(rcf.m_serviceControl, diff --git a/RasSrv.h b/RasSrv.h index e5c453de..e770aa64 100644 --- a/RasSrv.h +++ b/RasSrv.h @@ -217,7 +217,7 @@ class RasServer : public Singleton, public SocketsReader { } #ifdef h323v6 - template bool HasAssignedGK(const PString & alias,const PIPSocket::Address & ip, RAS & ras) + template bool HasAssignedGK(const PString & alias, const PIPSocket::Address & ip, RAS & ras) { H225_ArrayOf_AlternateGK assignedGK; assignedGK.SetSize(0); diff --git a/RasTbl.cxx b/RasTbl.cxx index 53a2f581..024c3495 100644 --- a/RasTbl.cxx +++ b/RasTbl.cxx @@ -105,10 +105,12 @@ EndpointRec::EndpointRec( m_H46024a(false), m_H46024b(false), m_natproxy(GkConfig()->GetBoolean(proxysection, "ProxyForNAT", false)), m_internal(false), m_remote(false), m_h46017disabled(false), m_h46018disabled(false), m_usesH460P(false), m_hasH460PData(false), m_usesH46017(false), m_usesH46026(false), m_traversalType(None), m_bandwidth(0), m_maxBandwidth(-1), m_useTLS(false), - m_useIPSec(false), m_additiveRegistrant(false), m_addCallingPartyToSourceAddress(false), m_authenticators(NULL) + m_useIPSec(false), m_additiveRegistrant(false), m_addCallingPartyToSourceAddress(false), m_authenticators(NULL), + m_hasGnuGkAssignedGk(false) { static H225_EndpointType defaultTermType; // nouse m_terminalType = &defaultTermType; + m_registrationTime = PTime(); switch (m_RasMsg.GetTag()) { @@ -953,6 +955,13 @@ EndpointRec *EndpointRec::Unregister() return this; } +EndpointRec *EndpointRec::UnregisterWithAlternate(H225_ArrayOf_AlternateGK * setAlternate) +{ + if (!IsPermanent()) + SendURQ(H225_UnregRequestReason::e_maintenance, 0, setAlternate); + return this; +} + EndpointRec *EndpointRec::Expired() { SendURQ(H225_UnregRequestReason::e_ttlExpired, 0); @@ -1016,7 +1025,7 @@ PString EndpointRec::PrintOn(bool verbose) const return msg; } -bool EndpointRec::SendURQ(H225_UnregRequestReason::Choices reason, int preemption) +bool EndpointRec::SendURQ(H225_UnregRequestReason::Choices reason, int preemption, H225_ArrayOf_AlternateGK * setAlternate) { if ((GetRasAddress().GetTag() != H225_TransportAddress::e_ipAddress) && (GetRasAddress().GetTag() != H225_TransportAddress::e_ip6Address) @@ -1034,6 +1043,12 @@ bool EndpointRec::SendURQ(H225_UnregRequestReason::Choices reason, int preemptio urq.m_endpointIdentifier = GetEndpointIdentifier(); urq.m_callSignalAddress.SetSize(1); urq.m_callSignalAddress[0] = GetCallSignalAddress(); + // alternate given as parameter + if (setAlternate) { + urq.IncludeOptionalField(H225_UnregistrationRequest::e_alternateGatekeeper); + urq.m_alternateGatekeeper = *setAlternate; + } + // when in maintenance mode, use that as alternate if (Toolkit::Instance()->IsMaintenanceMode()) { H225_ArrayOf_AlternateGK alternates = Toolkit::Instance()->GetMaintenanceAlternate(); if (alternates.GetSize() > 0) { @@ -2373,11 +2388,32 @@ void RegistrationTable::UpdateTable() void RegistrationTable::CheckEndpoints() { PTime now; + RasServer * RasSrv = RasServer::Instance(); + time_t rehomingWait = GkConfig()->GetInteger("GnuGkAssignedGatekeepers::SQL", "RehomingWait", 300); // in sec, default 5 min WriteLock lock(listLock); iterator Iter = EndpointList.begin(); while (Iter != EndpointList.end()) { EndpointRec *ep = *Iter; + // check GnuGk-Assigned gatekeeper + if (ep->HasGnuGkAssignedGk()) { + // check how long EP is registered here, don't try sending it away before n sec/min (switch!) + if (now - ep->GetRegistrationTime() > rehomingWait) { + callptr call = CallTable::Instance()->FindCallRec(endptr(ep)); + if (!call) { + if (RasSrv->GetNeighbors()->IsAvailable(ep->GetGnuGkAssignedGk())) { + H225_ArrayOf_AlternateGK alternate; + alternate.SetSize(1); + alternate[0].m_rasAddress = SocketToH225TransportAddr(ep->GetGnuGkAssignedGk(), GK_DEF_UNICAST_RAS_PORT); + alternate[0].m_needToRegister = true; + alternate[0].m_priority = 1; + ep->UnregisterWithAlternate(&alternate); + } + } + } + } + + // check expired registrations if (!ep->IsUpdated(&now) && !ep->SendIRQ()) { if (!Toolkit::AsBool(GkConfig()->GetString("Gatekeeper::Main", "TTLExpireDropCall", "1")) && CallTable::Instance()->FindCallRec(endptr(ep))) { @@ -2388,7 +2424,7 @@ void RegistrationTable::CheckEndpoints() } SoftPBX::DisconnectEndpoint(endptr(ep)); ep->Expired(); - RasServer::Instance()->LogAcctEvent(GkAcctLogger::AcctUnregister, endptr(ep)); + RasSrv->LogAcctEvent(GkAcctLogger::AcctUnregister, endptr(ep)); RemovedList.push_back(ep); Iter = EndpointList.erase(Iter); --regSize; diff --git a/RasTbl.h b/RasTbl.h index 19717ba6..04ffccaa 100644 --- a/RasTbl.h +++ b/RasTbl.h @@ -181,6 +181,7 @@ class EndpointRec virtual EndpointRec *Unregisterpreempt(int type); virtual EndpointRec *Reregister(); virtual EndpointRec *Unregister(); + virtual EndpointRec *UnregisterWithAlternate(H225_ArrayOf_AlternateGK * setAlternate); virtual EndpointRec *Expired(); virtual PString PrintOn(bool verbose) const; @@ -242,6 +243,7 @@ class EndpointRec int Priority() const { return m_registrationPriority; } PTime GetUpdatedTime() const; + PTime GetRegistrationTime() const; void SetUsesH460P(bool uses); bool UsesH460P() const { return m_usesH460P; } @@ -345,6 +347,10 @@ class EndpointRec bool AddCallingPartyToSourceAddress() const { return m_addCallingPartyToSourceAddress; } PString GetDisabledCodecs() const { return m_disabledcodecs; } + void SetGnuGkAssignedGk(const PIPSocket::Address & addr) { m_hasGnuGkAssignedGk = true; m_GnuGkAssignedGk = addr; } + bool HasGnuGkAssignedGk() const { return m_hasGnuGkAssignedGk; } + PIPSocket::Address GetGnuGkAssignedGk() const { return m_GnuGkAssignedGk; } + // smart pointer for EndpointRec typedef SmartPtr Ptr; @@ -355,7 +361,7 @@ class EndpointRec void SetEndpointRec(H225_LocationConfirm &); void SetEndpointRec(H225_UnregistrationRequest &); // used for temp objects - bool SendURQ(H225_UnregRequestReason::Choices, int preemption); + bool SendURQ(H225_UnregRequestReason::Choices, int preemption, H225_ArrayOf_AlternateGK * setAlternate = NULL); private: /// Load general endpoint settings from the config @@ -390,7 +396,8 @@ class EndpointRec int m_pollCount, m_usedCount; mutable PMutex m_usedLock; - PTime m_updatedTime; + PTime m_updatedTime; // last update from EP + PTime m_registrationTime; // time when the EP registered bool m_fromParent, m_nat; PIPSocket::Address m_natip; CallSignalSocket *m_natsocket; @@ -443,6 +450,8 @@ class EndpointRec PString m_disabledcodecs; /// H.235 used to authenticate this endpoint GkH235Authenticators * m_authenticators; + bool m_hasGnuGkAssignedGk; + PIPSocket::Address m_GnuGkAssignedGk; }; typedef EndpointRec::Ptr endptr; @@ -460,7 +469,7 @@ class GatewayRec : public EndpointRec { virtual void Update(const H225_RasMessage & lightweightRRQ); virtual bool IsGateway() const { return true; } - /// Overiden from EndpointRec + /// Overridden from EndpointRec virtual bool LoadConfig(); /** Find if at least one of the given aliases matches any prefix @@ -2078,6 +2087,12 @@ inline PTime EndpointRec::GetUpdatedTime() const return m_updatedTime; } +inline PTime EndpointRec::GetRegistrationTime() const +{ + PWaitAndSignal lock(m_usedLock); + return m_registrationTime; +} + inline H225_RasMessage EndpointRec::GetCompleteRegistrationRequest() const { PWaitAndSignal lock(m_usedLock); diff --git a/Toolkit.cxx b/Toolkit.cxx index 6db2bb15..45dc4ad0 100644 --- a/Toolkit.cxx +++ b/Toolkit.cxx @@ -1975,6 +1975,7 @@ PConfig* Toolkit::ReloadConfig() #endif #if HAS_DATABASE m_AlternateGKs.LoadConfig(m_Config); + m_GnuGkAssignedGKs.LoadConfig(m_Config); m_qosMonitor.LoadConfig(m_Config); #ifdef HAS_LANGUAGE m_assignedLanguage.LoadSQL(m_Config); @@ -2837,7 +2838,7 @@ bool Toolkit::AssignedGatekeepers::DatabaseLookup( SNMP_TRAP(4, SNMPError, Database, "AssignedGatekeepers query failed"); continue; } - if (!success) success = true; + success = true; PTRACE(5, "AssignSQL\tQuery result: " << retval[0]); PStringArray adr_parts = SplitIPAndPort(retval[0],GK_DEF_UNICAST_RAS_PORT); @@ -2976,13 +2977,13 @@ bool Toolkit::AssignedGatekeepers::GetAssignedGK(const PString & alias, const PI // prefix match if (MatchPrefix(alias,assignedGKList[j].first)) { assignedGK.AppendString(assignedGKList[j].second); - if (!found) found = true; + found = true; } } else { // regex match for IP address if (MatchRegex(ip.AsString(), match)) { assignedGK.AppendString(assignedGKList[j].second); - if (!found) found = true; + found = true; } } } @@ -3024,7 +3025,7 @@ bool Toolkit::AssignedGatekeepers::GetAssignedGK(const PString & alias, const PI int sz = gklist.GetSize(); gklist.SetSize(sz+1); H225_AlternateGK & alt = gklist[sz]; - alt.m_rasAddress = SocketToH225TransportAddr(PIPSocket::Address(tokens[0]),port); + alt.m_rasAddress = SocketToH225TransportAddr(PIPSocket::Address(tokens[0]), port); alt.m_needToRegister = true; alt.m_priority = k; } @@ -3118,6 +3119,7 @@ bool Toolkit::AlternateGatekeepers::QueryAlternateGK(const PIPSocket::Address & std::map params; params["i"] = ip.AsString(); + params["g"] = GKName(); GkSQLResult* result = m_sqlConn->ExecuteQuery(m_query, params, m_timeout); if (result == NULL) { PTRACE(2, "AltGKSQL\tQuery failed - timeout or fatal error"); @@ -3148,7 +3150,7 @@ bool Toolkit::AlternateGatekeepers::QueryAlternateGK(const PIPSocket::Address & SNMP_TRAP(5, SNMPError, Database, "AlternateGatekeepers query failed"); continue; } - if (!success) success = true; + success = true; PTRACE(5, "AltGKSQL\tQuery result: " << retval[0]); addresses.AppendString(retval[0]); } @@ -3159,6 +3161,113 @@ bool Toolkit::AlternateGatekeepers::QueryAlternateGK(const PIPSocket::Address & } #endif +///////////////////////////////////////////////////////////////////////////////////////// + +#if HAS_DATABASE +Toolkit::GnuGkAssignedGatekeepers::GnuGkAssignedGatekeepers() + : m_sqlactive(false), m_sqlConn(NULL), m_timeout(-1) +{ +} + +Toolkit::GnuGkAssignedGatekeepers::~GnuGkAssignedGatekeepers() +{ +} + +void Toolkit::GnuGkAssignedGatekeepers::LoadConfig(PConfig * cfg) +{ + delete m_sqlConn; + const PString authName = "GnuGkAssignedGatekeepers::SQL"; + + if (cfg->GetSections().GetStringsIndex(authName) == P_MAX_INDEX) + return; + + const PString driverName = cfg->GetString(authName, "Driver", ""); + if (driverName.IsEmpty()) { + PTRACE(1, "GnuGkAssignedGkSQL\tModule creation failed: no SQL driver selected"); + SNMP_TRAP(4, SNMPError, Database, authName + " creation failed"); + PTRACE(0, "GnuGkAssignedGkSQL\tFATAL: Shutting down"); + return; + } + + m_sqlConn = GkSQLConnection::Create(driverName, authName); + if (m_sqlConn == NULL) { + PTRACE(1, "GnuGkAssignedGkSQL\tModule creation failed: " + "Could not find " << driverName << " database driver"); + SNMP_TRAP(4, SNMPError, Database, authName + " creation failed"); + PTRACE(0, "GnuGkAssignedGkSQL\tFATAL: Shutting down"); + return; + } + + m_query = cfg->GetString(authName, "Query", ""); + if (m_query.IsEmpty()) { + PTRACE(1, "GnuGkAssignedGkSQL\tModule creation failed: No query configured"); + SNMP_TRAP(4, SNMPError, Database, authName + " creation failed"); + PTRACE(0, "GnuGkAssignedGkSQL\tFATAL: Shutting down"); + return; + } else + PTRACE(4, "GnuGkAssignedGkSQL\tQuery: " << m_query); + + if (!m_sqlConn->Initialize(cfg, authName)) { + PTRACE(1, "GnuGkAssignedGkSQL\tModule creation failed: Could not connect to the database"); + SNMP_TRAP(4, SNMPError, Database, authName + " creation failed"); + return; + } + + m_sqlactive = true; +} + + +bool Toolkit::GnuGkAssignedGatekeepers::HasAssignedGk(endptr ep, const PIPSocket::Address & ip) +{ + if (!m_sqlactive) + return false; + + std::map params; + if (ep->GetAliases().GetSize() > 0) { + params["u"] = ::AsString((ep->GetAliases()[0]), false); // TODO: repeat query for all aliases ??? + } + params["i"] = ip.AsString(); + params["g"] = GKName(); + GkSQLResult* result = m_sqlConn->ExecuteQuery(m_query, params, m_timeout); + if (result == NULL) { + PTRACE(2, "GnuGkAssignedGkSQL\tQuery failed - timeout or fatal error"); + SNMP_TRAP(5, SNMPError, Database, "GnuGkAssignedGatekeeper query failed"); + return false; + } + + if (!result->IsValid()) { + PTRACE(2, "GnuGkAssignedGkSQL\tQuery failed (" << result->GetErrorCode() + << ") - " << result->GetErrorMessage()); + SNMP_TRAP(5, SNMPError, Database, "GnuGkAssignedGatekeeper query failed"); + delete result; + return false; + } + + bool found = false; + + if (result->GetNumRows() < 1) + PTRACE(3, "GnuGkAssignedGkSQL\tQuery returned no rows"); + else if (result->GetNumRows() > 0 && result->GetNumFields() < 1) { + PTRACE(2, "GnuGkAssignedGkSQL\tBad-formed query - no columns found in the result set"); + SNMP_TRAP(5, SNMPError, Database, "GnuGkAssignedGatekeeper query failed"); + } else { + PStringArray retval; + while (result->FetchRow(retval)) { // TODO: if instead of while ? + PTRACE(5, "GnuGkAssignedGkSQL\tQuery result: " << retval[0]); + PIPSocket::Address gkip = retval[0]; + if (!retval[0].IsEmpty() && !Toolkit::Instance()->IsGKHome(gkip)) { + // if an assigned GK IP is set and it is not one of our IPs, then save it in the EpRec and throw the endpoint back once the assigned gk is ready + ep->SetGnuGkAssignedGk(gkip); + found = true; + } + } + } + delete result; + + return found; +} +#endif + /////////////////////////////////////////////////////////////////////////////////////// #ifdef HAS_LANGUAGE @@ -3736,6 +3845,7 @@ H225_ArrayOf_AlternateGK Toolkit::GetMaintenanceAlternate() const return gklist; } + PString Toolkit::GetExternalIP() const { diff --git a/Toolkit.h b/Toolkit.h index 10f4cfca..6c4585c3 100644 --- a/Toolkit.h +++ b/Toolkit.h @@ -291,7 +291,7 @@ class Toolkit : public Singleton bool m_sqlactive; // connection to the SQL database GkSQLConnection* m_sqlConn; - // parametrized query string for the auth condition string retrieval + // parametrized query string PString m_query; // query timeout long m_timeout; @@ -313,9 +313,9 @@ class Toolkit : public Singleton void LoadConfig(PConfig *); bool QueryAssignedGK(const PString & alias, const PIPSocket::Address & ip, PStringArray & addresses); #ifdef H323_H350 - bool QueryH350Directory(const PString & alias,const PIPSocket::Address & ip, PStringArray & addresses); + bool QueryH350Directory(const PString & alias, const PIPSocket::Address & ip, PStringArray & addresses); #endif - bool GetAssignedGK(const PString & alias,const PIPSocket::Address & ip, H225_ArrayOf_AlternateGK & gklist); + bool GetAssignedGK(const PString & alias, const PIPSocket::Address & ip, H225_ArrayOf_AlternateGK & gklist); protected: std::vector< std::pair > assignedGKList; @@ -325,7 +325,7 @@ class Toolkit : public Singleton bool m_sqlactive; // connection to the SQL database GkSQLConnection* m_sqlConn; - // parametrized query string for the auth condition string retrieval + // parametrized query string PString m_query; // query timeout long m_timeout; @@ -350,7 +350,7 @@ class Toolkit : public Singleton bool m_sqlactive; // connection to the SQL database GkSQLConnection* m_sqlConn; - // parametrized query string for the auth condition string retrieval + // parametrized query string PString m_query; // query timeout long m_timeout; @@ -358,6 +358,27 @@ class Toolkit : public Singleton AlternateGatekeepers AlternateGKs() const { return m_AlternateGKs; } #endif +#if HAS_DATABASE + class GnuGkAssignedGatekeepers { + public: + GnuGkAssignedGatekeepers(); + ~GnuGkAssignedGatekeepers(); + + void LoadConfig(PConfig *); + bool HasAssignedGk(endptr ep, const PIPSocket::Address & ip); + + private: + bool m_sqlactive; + // connection to the SQL database + GkSQLConnection* m_sqlConn; + // parametrized query string + PString m_query; + // query timeout + long m_timeout; + }; + GnuGkAssignedGatekeepers GnuGkAssignedGKs() const { return m_GnuGkAssignedGKs; } +#endif + #if HAS_DATABASE class QoSMonitor { public: @@ -724,6 +745,7 @@ class Toolkit : public Singleton VendorModeTool m_venderMode; #if HAS_DATABASE AlternateGatekeepers m_AlternateGKs; + GnuGkAssignedGatekeepers m_GnuGkAssignedGKs; QoSMonitor m_qosMonitor; #endif RouteTable m_RouteTable; diff --git a/changes.txt b/changes.txt index 4df91426..a71d5687 100644 --- a/changes.txt +++ b/changes.txt @@ -1,5 +1,6 @@ Changes from 5.4 to 5.5 ======================= +- new feature GnuGkAssignedGatekeeper to push endpoints back to their intended home gatekeepers - BUGFIX(RasSrv.h) set altGKisPermanent=true when redirecting endpoints - BUGFIX(RasSrv.cxx) fix RRJ to include alternates when RedirectGK=Endpoints limit is reached - manual translated into Simplified Chinese by Zhang Tao diff --git a/docs/manual/ras.sgml b/docs/manual/ras.sgml index 53cb7d52..0dd26acf 100644 --- a/docs/manual/ras.sgml +++ b/docs/manual/ras.sgml @@ -302,6 +302,33 @@ all other networks will use the globally defined alternate gatekeeper. +Section [AlternateGatekeepers::SQL] +

+This section allows to read the alternate gatekeepers from a database. + +Use the +to define your database connection for this module. + + + +Default: N/A +

+Defines the SQL query used to retrieve the alternate gatekeepers from the database. + +These parameters are defined: + + +

+Sample query string: + +SELECT alternategatekeeper FROM users WHERE ip = '%i' AND active + + + + + Section [RasSrv::AssignedGatekeeper]

This allows the assigning of a gatekeeper based upon the H323ID or the @@ -333,7 +360,9 @@ is assigned as the primary and others are then the alternates. Section [AssignedGatekeepers::SQL]

This section allows to read the assigned gatekeepers from a database. -You can use the same database parameters as defined in . + +Use the +to define your database connection for this module. @@ -355,26 +384,53 @@ SELECT assignedgatekeeper FROM users WHERE alias = '%u' AND active - -Section [AlternateGatekeepers::SQL] +Section [GnuGkAssignedGatekeepers::SQL]

-This section allows to read the alternate gatekeepers from a database. -You can use the same database parameters as defined in . +Many endpoints that support alternate gatekeepers will move to the +alternate once they can't reach their curent gatekeeper, but will +never change back, causing uneven distribution of endpoints between +gatekeepers. +Many older endpoints don't support the assigned gatekeeper procedures +defined by the ITU to move back, either. + +With this section, you can have GnuGk look up the intended home gatekeeper +for each endpoint and GnuGk will push the endpoints back to their intended +home gatekeeper. + +For this to work, the endpoint must signal support alternate gatekeepers +in their RegistrationRequest (RRQ) and obey the alternate gatekeeper +information provided in UnregistrationRequests (URQ) from GnuGk. + +The home gatekeepers must be defined as neighbors to GnuGk and should +have the neighbor ping enabled so GnuGk will know when they are up or down. + +GnuGk will wait a defined amount of time before trying to move the endpoint +back and will also not try to move it while it is actively in a call. + +Use the +to define your database connection for this module. + +Default: 300 +

+The number of seconds to wait until GnuGk tries ro send the endpoint back to it's home gatekeeper. + Default: N/A

-Defines the SQL query used to retrieve the alternate gatekeepers from the database. +Defines the SQL query used to retrieve the intended home gatekeepers from the database. -One parameter is defined: +These parameters are defined: +

Sample query string: -SELECT alternategatekeeper FROM users WHERE ip = '%i' AND active +SELECT home from users where alias = "%u" diff --git a/gk.cxx b/gk.cxx index fadcde7d..0e05adf1 100644 --- a/gk.cxx +++ b/gk.cxx @@ -367,6 +367,20 @@ const char * KnownConfigEntries[][2] = { { "GkStatus::Message", "Compact" }, { "GkStatus::Message", "RCF" }, { "GkStatus::Message", "URQ" }, +#ifdef HAS_DATABASE + { "GnuGkAssignedGatekeepers::SQL", "CacheTimeout" }, + { "GnuGkAssignedGatekeepers::SQL", "ConnectTimeout" }, + { "GnuGkAssignedGatekeepers::SQL", "Database" }, + { "GnuGkAssignedGatekeepers::SQL", "Driver" }, + { "GnuGkAssignedGatekeepers::SQL", "Host" }, + { "GnuGkAssignedGatekeepers::SQL", "Library" }, + { "GnuGkAssignedGatekeepers::SQL", "MinPoolSize" }, + { "GnuGkAssignedGatekeepers::SQL", "RehomingWait" }, + { "GnuGkAssignedGatekeepers::SQL", "Password" }, + { "GnuGkAssignedGatekeepers::SQL", "Query" }, + { "GnuGkAssignedGatekeepers::SQL", "ReadTimeout" }, + { "GnuGkAssignedGatekeepers::SQL", "Username" }, +#endif // HAS_DATABASE #ifdef H323_H235 { "H235", "CheckSendersID" }, { "H235", "FullQ931Checking" },