1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU Affero General Public License as
6 // published by the Free Software Foundation, either version 3 of the
7 // License, or (at your option) any later version.
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU Affero General Public License for more details.
14 // You should have received a copy of the GNU Affero General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
18 #include "nel/net/module_gateway.h"
19 #include "nel/net/module.h"
20 #include "nel/net/module_manager.h"
21 #include "nel/net/module_socket.h"
22 #include "nel/net/module_message.h"
23 #include "nel/net/unified_network.h"
24 #include "nel/net/service.h"
25 #include "nel/net/net_log.h"
29 using namespace NLMISC
;
35 typedef uint8 TL5TransportId
;
39 TL5TransportId TransportId
;
43 void serial(NLMISC::IStream
&s
)
45 s
.serial(TransportId
);
51 /** the specialized route for l5 transport */
52 class CL5Route
: public CGatewayRoute
55 /// the service ID of the outbound service
58 /// The transport ID at the outbound
59 TL5TransportId ForeignTransportId
;
62 CL5Route(IGatewayTransport
*transport
)
63 : CGatewayRoute(transport
)
67 void sendMessage(const CMessage
&message
) const;
70 /** Utility class that generate 8bits unique transport id.
71 * The total L5 transport instance is limited to 256.
72 * This really should be enough or you have a problem in
74 * The allocator keep released ID as long as possible
75 * and reallocated them only when all other ids
76 * have been used/allocated.
78 class CTransportIdAllocator
80 NLMISC_SAFE_SINGLETON_DECL(CTransportIdAllocator
);
82 deque
<TL5TransportId
> _FreeIds
;
83 set
<TL5TransportId
> _UsedIds
;
85 CTransportIdAllocator()
87 // fill the list of free ids;
88 for (TL5TransportId i
=0; i
<TL5TransportId(UINT64_CONSTANT(0xffffffffffffffff)); ++i
)
90 _FreeIds
.push_back(i
);
95 TL5TransportId
allocateId()
97 nlassert(!_FreeIds
.empty());
98 TL5TransportId ret
= _FreeIds
.front();
100 nlassert(_UsedIds
.find(ret
) == _UsedIds
.end());
101 _UsedIds
.insert(ret
);
105 void releaseId(TL5TransportId id
)
107 nlassert(_UsedIds
.find(id
) != _UsedIds
.end());
109 _FreeIds
.push_back(id
);
113 NLMISC_SAFE_SINGLETON_IMPL(CTransportIdAllocator
);
115 #define LAYER5_CLASS_NAME "L5Transport"
117 /** Gateway transport using layer 5 */
118 class CGatewayL5Transport
: public IGatewayTransport
120 friend class CL5Route
;
122 /// This transport ID
123 TL5TransportId _TransportId
;
125 /// current open status
130 typedef std::map
<TServiceId
, CL5Route
*> TRouteMap
;
131 /// The table that keep track of all routes
134 typedef std::map
<TL5TransportId
, CGatewayL5Transport
*> TTransportDispatcher
;
135 /// Global index of transport use to dispatch received message
136 static TTransportDispatcher _TransportDispatcher
;
139 CGatewayL5Transport(const IGatewayTransport::TCtorParam
¶m
)
140 : IGatewayTransport(param
),
143 // allocate a transport unique ID
144 _TransportId
= CTransportIdAllocator::getInstance().allocateId();
146 // store the transport in the dispatcher
147 _TransportDispatcher
.insert(make_pair(_TransportId
, this));
149 // L5 transport is always peer invisible
150 PeerInvisible
= true;
153 ~CGatewayL5Transport()
157 // the transport is still open, close it before destruction
161 // remove the transport from the dispatcher
162 nlassert(_TransportDispatcher
.find(_TransportId
) != _TransportDispatcher
.end());
163 _TransportDispatcher
.erase(_TransportId
);
164 // release the unique id
165 CTransportIdAllocator::getInstance().releaseId(_TransportId
);
168 const std::string
&getClassName() const
170 static string
className(LAYER5_CLASS_NAME
);
174 virtual void update()
178 virtual uint32
getRouteCount() const
180 return (uint32
)_Routes
.size();
183 void dump(NLMISC::CLog
&log
) const
185 IModuleManager
&mm
= IModuleManager::getInstance();
186 log
.displayNL(" NeL Net layer 5 transport");
189 log
.displayNL(" The transport is currently closed.");
193 log
.displayNL(" The transport is open and support %u routes :",
195 TRouteMap::const_iterator
first(_Routes
.begin()), last(_Routes
.end());
196 for (; first
!= last
; ++first
)
198 TServiceId sid
= first
->first
;
199 CL5Route
*route
= first
->second
;
200 log
.displayNL(" + route to service %hu('%s'), %u entries in the proxy translation table :",
202 CUnifiedNetwork::getInstance()->getServiceName(sid
).c_str(),
203 route
->ForeignToLocalIdx
.getAToBMap().size());
206 CGatewayRoute::TForeignToLocalIdx::TAToBMap::const_iterator
first(route
->ForeignToLocalIdx
.getAToBMap().begin()), last(route
->ForeignToLocalIdx
.getAToBMap().end());
207 for (; first
!= last
; ++first
)
209 IModuleProxy
*modProx
= mm
.getModuleProxy(first
->second
);
211 log
.displayNL(" - Proxy '%s' : local proxy id %u => foreign module id %u",
212 modProx
!= NULL
? modProx
->getModuleName().c_str() : "ERROR, invalid module",
221 void onCommand(const CMessage
&/* command */)
223 // nothing done for now
224 throw EInvalidCommand();
226 /// The gateway send a textual command to the transport
227 bool onCommand(const TParsedCommandLine
&command
)
229 if (command
.SubParams
.size() < 1)
230 throw EInvalidCommand();
232 const std::string
&commandName
= command
.SubParams
[0]->ParamName
;
233 if (commandName
== "open")
237 /// look for an optional sub network name
238 const TParsedCommandLine
*netName
= command
.SubParams
[0]->getParam("SubNet");
241 subNetName
= netName
->ParamValue
;
246 else if (commandName
== "close")
257 /// Open the server by establishing route with all known services
258 void open(const std::string
&subNetName
)
262 static TUnifiedCallbackItem L5TransportCallback
[] =
264 {"GW_L5_MSG", CGatewayL5Transport::cbDispatchL5Message
},
265 {"GW_L5_ADDTP", CGatewayL5Transport::cbL5AddTransport
},
266 {"GW_L5_REMTP", CGatewayL5Transport::cbL5RemoveTransport
},
270 throw ETransportError("Transport already open");
272 _SubNetName
= subNetName
;
275 CUnifiedNetwork
*un
= CUnifiedNetwork::getInstance();
277 static bool callbackRegistered
= false;
279 if (!callbackRegistered
)
281 LNETL6_DEBUG("LNETL6: L5 transport open : registering callbacks");
282 // set the service con/disconnect callback
283 un
->setServiceUpCallback("*", CGatewayL5Transport::cbOnServiceUp
);
284 un
->setServiceDownCallback("*", CGatewayL5Transport::cbOnServiceDown
);
285 // set the message callback
286 un
->addCallbackArray(L5TransportCallback
, sizeof(L5TransportCallback
) / sizeof(TUnifiedCallbackItem
));
288 callbackRegistered
= true;
292 // create route and open route for each existing service
293 const vector
<TServiceId
> &connList
= un
->getConnectionList();
295 set
<TServiceId
> uniqueService(connList
.begin(), connList
.end());
297 while (!uniqueService
.empty())
299 TServiceId sid
= *(uniqueService
.begin());
300 uniqueService
.erase(uniqueService
.begin());
302 if ( un
->isConnectionConnected(sid
))
304 // send transport descriptor to other service
305 onServiceUp(un
->getServiceName(sid
), sid
);
309 // the Connection is not established right now. We wait for the ServiceUp callback
317 /// Close the server, this will close all route
323 throw ETransportError("closeServer : The server is not open");
325 // close all client connections
326 while (!_Routes
.empty())
328 CL5Route
*route
= _Routes
.begin()->second
;
329 TServiceId sid
= route
->ServiceId
;
330 onServiceDown(CUnifiedNetwork::getInstance()->getServiceName(sid
), sid
);
337 /***************************************************/
338 /** Event management **/
339 /***************************************************/
341 void onServiceUp(const std::string
&serviceName
, TServiceId sid
)
343 H_AUTO(L5_onServiceUp
);
345 LNETL6_DEBUG("LNETL6: L5 transport onServiceUp('%s')", serviceName
.c_str());
346 // send the transport descriptor to the new service
348 desc
.SubNetName
= _SubNetName
;
349 desc
.TransportId
= _TransportId
;
350 desc
.InResponse
= false;
352 CMessage
msg("GW_L5_ADDTP");
355 CUnifiedNetwork::getInstance()->send(sid
, msg
);
357 // the route will be created by receiving this message
360 void onServiceDown(const std::string
&/* serviceName */, TServiceId sid
)
362 H_AUTO(L5_onServicedown
);
364 LNETL6_DEBUG("LNETL6: L5 transport onServiceDown('%hu')", sid
.get());
365 // retrieve the route
366 TRouteMap::iterator
it(_Routes
.find(sid
));
367 if (it
== _Routes
.end())
369 nlinfo("Transport L5 : service down, can't find a route for the service");
372 CL5Route
*route
= it
->second
;
375 _Gateway
->onRouteRemoved(route
);
382 // Called to dispatch an incoming message to the gateway
383 void onDispatchMessage(const CMessage
&msgin
, TServiceId sid
)
385 H_AUTO(L5_onDispatchMessage
);
387 LNETL6_DEBUG("LNETL6: L5 transport onDispatchMessage from service %hu", sid
.get());
388 /// retrieve the route for dispatching
389 TRouteMap::iterator
it(_Routes
.find(sid
));
390 if (it
== _Routes
.end())
392 nlwarning("Gateway '%s' : Can't find route for service %hu for dispatching, message is discarded",
393 _Gateway
->getGatewayName().c_str(),
398 // read the message size
400 nlRead(msgin
, serial
, msgLen
);
402 // lock the sub message
403 msgin
.lockSubMessage(msgLen
);
405 _Gateway
->onReceiveMessage(it
->second
, msgin
);
407 // unlock the sub message
408 msgin
.unlockSubMessage();
411 void onAddTransport(TServiceId sid
, TTransportDesc
&desc
)
413 H_AUTO(L5_onAddTransport
);
415 LNETL6_DEBUG("LNETL6: L5 transport onAddTransport from service %hu", sid
.get());
416 // we need to create a route for this transport
417 // create a new route and send the route open message
419 if (_Routes
.find(sid
) != _Routes
.end())
421 LNETL6_DEBUG("LNETL6: L5 transport onAddTransport a route for this service alredy exist");
425 CL5Route
*route
= new CL5Route(this);
427 route
->ServiceId
= sid
;
428 route
->ForeignTransportId
= desc
.TransportId
;
430 // store the route infos
431 _Routes
.insert(make_pair(sid
, route
));
433 // notify the gateway about the new route
434 _Gateway
->onRouteAdded(route
);
436 if (desc
.InResponse
== false)
438 // we need to send back this transport info to this service
440 desc
.InResponse
= true;
441 desc
.SubNetName
= _SubNetName
;
442 desc
.TransportId
= _TransportId
;
444 CMessage
msg("GW_L5_ADDTP");
447 CUnifiedNetwork::getInstance()->send(sid
, msg
);
451 void onRemoveTransport(TServiceId sid
, TTransportDesc
&desc
)
453 H_AUTO(L5_onRemoveTransport
);
455 LNETL6_DEBUG("LNETL6: L5 transport onRemoveTransport from service %hu", sid
.get());
457 TRouteMap::iterator
it(_Routes
.find(sid
));
458 if (it
== _Routes
.end())
460 nlwarning("onRemoveTransport : can't find a route to the transport %hu on service %u",
466 CL5Route
*route
= it
->second
;
468 // notify the gateway about the removed route
469 _Gateway
->onRouteRemoved(route
);
471 // erase the route info and delete the route
476 /***************************************************/
477 /** static callback forwarder **/
478 /***************************************************/
479 /// callback from layer 5
481 static void cbL5AddTransport(CMessage
&msgin
, const std::string
&/* serviceName */, TServiceId sid
)
483 LNETL6_DEBUG("LNETL6: L5 transport cbL5AddTransport from service %hu", sid
.get());
484 // Receive a transport descriptor from another service, create
491 // for each existing transport here, check if they are in the
492 // same sub net, if so, callback them for route creation
494 TTransportDispatcher::iterator
first(_TransportDispatcher
.begin()), last(_TransportDispatcher
.end());
495 for (; first
!= last
; ++first
)
497 CGatewayL5Transport
*transport
= first
->second
;
499 && transport
->_SubNetName
== desc
.SubNetName
500 && (sid
!= IService::getInstance()->getServiceId()
501 || desc
.TransportId
!= transport
->_TransportId
))
503 // this one is on the same subnet
504 transport
->onAddTransport(sid
, desc
);
509 static void cbL5RemoveTransport(CMessage
&msgin
, const std::string
&/* serviceName */, TServiceId sid
)
511 LNETL6_DEBUG("LNETL6: L5 transport cbL5RemoveTransport from service %hu", sid
.get());
512 // Receive a transport descriptor from another service, delete
519 // for each existing transport here, check if they are in the
520 // same sub net, if so, callback them for route creation
522 TTransportDispatcher::iterator
first(_TransportDispatcher
.begin()), last(_TransportDispatcher
.end());
523 for (; first
!= last
; ++first
)
525 CGatewayL5Transport
*transport
= first
->second
;
527 && transport
->_SubNetName
== desc
.SubNetName
528 && desc
.TransportId
!= transport
->_TransportId
)
530 // this one is on the same subnet
531 transport
->onRemoveTransport(sid
, desc
);
536 static void cbDispatchL5Message (CMessage
&msgin
, const std::string
&serviceName
, TServiceId sid
)
538 LNETL6_DEBUG("LNETL6: L5 transport cbDispatch called, receiving from %s", serviceName
.c_str());
539 // dispatch the message to the route associated with the service
540 // the first info in the message is the transport id
541 TL5TransportId transportId
;
542 msgin
.serial(transportId
);
544 // look for a corresponding transport
545 TTransportDispatcher::iterator
it(_TransportDispatcher
.find(transportId
));
546 if (it
== _TransportDispatcher
.end())
548 nlwarning("ReceiveL5Message, can't find transport id %u for dispatching, message is discarded",
553 CGatewayL5Transport
*transport
= it
->second
;
554 transport
->onDispatchMessage(msgin
, sid
);
557 static void cbOnServiceUp (const std::string
&serviceName
, TServiceId sid
, void * /* arg */)
559 LNETL6_DEBUG("LNETL6: L5 transport cbOnServiceUp called, service up for %s", serviceName
.c_str());
560 // callback all open transport about the new service
561 TTransportDispatcher::iterator
first(_TransportDispatcher
.begin()), last(_TransportDispatcher
.end());
562 for (; first
!= last
; ++first
)
564 CGatewayL5Transport
*transport
= first
->second
;
566 if (transport
->_Open
)
567 transport
->onServiceUp(serviceName
, sid
);
571 static void cbOnServiceDown (const std::string
&serviceName
, TServiceId sid
, void * /* arg */)
573 LNETL6_DEBUG("LNETL6: L5 transport cbOnServicedown called, service down for %s", serviceName
.c_str());
574 // callback all open transport about the removed service
575 TTransportDispatcher::iterator
first(_TransportDispatcher
.begin()), last(_TransportDispatcher
.end());
576 for (; first
!= last
; ++first
)
578 CGatewayL5Transport
*transport
= first
->second
;
580 if (transport
->_Open
)
581 transport
->onServiceDown(serviceName
, sid
);
587 CGatewayL5Transport::TTransportDispatcher
CGatewayL5Transport::_TransportDispatcher
;
589 // register this class in the transport factory
590 NLMISC_REGISTER_OBJECT(IGatewayTransport
, CGatewayL5Transport
, std::string
, string(LAYER5_CLASS_NAME
));
592 void CL5Route::sendMessage(const CMessage
&message
) const
594 NLNET_AUTO_DELTE_ASSERT
;
595 H_AUTO(L5Route_sendMessage
);
597 CGatewayL5Transport
*trpt
= static_cast<CGatewayL5Transport
*>(_Transport
);
599 // create a transport message
600 CMessage
wrapper("GW_L5_MSG");
601 // serial the transport identifier
602 wrapper
.serial(trpt
->_TransportId
);;
604 // insert the message in the wrapper
605 nlWrite(wrapper
, serialMessage
, message
);
607 CUnifiedNetwork::getInstance()->send(ServiceId
, wrapper
);
612 void forceGatewayL5TransportLink()