source: source/ariba/communication/BaseCommunication.cpp@ 4438

Last change on this file since 4438 was 4438, checked in by Christoph Mayer, 15 years ago
File size: 20.6 KB
Line 
1// [License]
2// The Ariba-Underlay Copyright
3//
4// Copyright (c) 2008-2009, Institute of Telematics, UniversitÀt Karlsruhe (TH)
5//
6// Institute of Telematics
7// UniversitÀt Karlsruhe (TH)
8// Zirkel 2, 76128 Karlsruhe
9// Germany
10//
11// Redistribution and use in source and binary forms, with or without
12// modification, are permitted provided that the following conditions are
13// met:
14//
15// 1. Redistributions of source code must retain the above copyright
16// notice, this list of conditions and the following disclaimer.
17// 2. Redistributions in binary form must reproduce the above copyright
18// notice, this list of conditions and the following disclaimer in the
19// documentation and/or other materials provided with the distribution.
20//
21// THIS SOFTWARE IS PROVIDED BY THE INSTITUTE OF TELEMATICS ``AS IS'' AND
22// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE ARIBA PROJECT OR
25// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
26// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
27// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
28// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
29// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32//
33// The views and conclusions contained in the software and documentation
34// are those of the authors and should not be interpreted as representing
35// official policies, either expressed or implied, of the Institute of
36// Telematics.
37// [License]
38
39#include "BaseCommunication.h"
40
41#ifdef UNDERLAY_OMNET
42 #include "ariba/communication/modules/transport/omnet/AribaOmnetModule.h"
43 #include "ariba/communication/modules/network/omnet/OmnetNetworkProtocol.h"
44 #include "ariba/utility/system/StartupWrapper.h"
45
46 using ariba::communication::AribaOmnetModule;
47 using ariba::communication::OmnetNetworkProtocol;
48 using ariba::utility::StartupWrapper;
49#endif
50
51namespace ariba {
52namespace communication {
53
54use_logging_cpp(BaseCommunication);
55const BaseCommunication::LinkDescriptor BaseCommunication::LinkDescriptor::UNSPECIFIED;
56
57BaseCommunication::BaseCommunication()
58 : messageReceiver(NULL), network(NULL), transport(NULL){
59}
60
61BaseCommunication::~BaseCommunication(){
62}
63
64void BaseCommunication::start(const NetworkLocator* _locallocator, const uint16_t _listenport){
65
66 currentSeqnum = 0;
67 listenport = _listenport;
68
69 logging_info( "starting up base communication and creating transports ..." );
70 logging_info( "using port " << listenport );
71
72#ifdef UNDERLAY_OMNET
73 AribaOmnetModule* module = StartupWrapper::getCurrentModule();
74 module->setServerPort( listenport );
75
76 transport = module;
77 network = new OmnetNetworkProtocol( module );
78#else
79 transport = new TCPTransport( listenport );
80 network = new IPv4NetworkProtocol();
81#endif
82
83 logging_debug( "searching for local locators ..." );
84
85 NetworkProtocol::NetworkLocatorSet locators = network->getAddresses();
86 NetworkProtocol::NetworkLocatorSet::iterator i = locators.begin();
87 NetworkProtocol::NetworkLocatorSet::iterator iend = locators.end();
88
89 //
90 // choose the first locator that is not localhost
91 //
92
93 bool foundLocator = false;
94
95 for( ; i != iend; i++){
96 logging_debug( "local locator found " << (*i)->toString() );
97 IPv4Locator* ipv4locator = dynamic_cast<IPv4Locator*>(*i);
98
99 // TODO: which locators are find to bind to?
100 // localhost is not too bad, works when testing locally
101 // with several instances. the manual override currently
102 // enables to use an arbitrary address, guess this is fine.
103 // so the manual override also can use ANY, LOCALHOST, BROADCAST
104
105 if( *ipv4locator != IPv4Locator::LOCALHOST &&
106 *ipv4locator != IPv4Locator::ANY &&
107 *ipv4locator != IPv4Locator::BROADCAST ){
108
109 ipv4locator->setPort(listenport);
110 localDescriptor.locator = ipv4locator;
111 localDescriptor.isUnspec = false;
112 logging_info( "binding to addr = " << ipv4locator->toString() );
113 foundLocator = true;
114 break;
115 }
116 } // for( ; i != iend; i++)
117
118
119 if( _locallocator != NULL ) {
120 if( localDescriptor.locator != NULL) delete localDescriptor.locator;
121 localDescriptor.locator = new IPv4Locator( IPv4Locator::fromString( _locallocator->toString()) );
122 localDescriptor.isUnspec = false;
123 logging_debug( "manual locator override, using locator=" <<
124 localDescriptor.locator->toString() );
125 foundLocator = true;
126 }
127
128 // if we found no local locator, exit using logging fatal
129 if( !foundLocator )
130 logging_fatal( "did not find a useable local locator!" );
131
132 transport->addMessageReceiver( this );
133 transport->start();
134
135#ifndef UNDERLAY_OMNET
136 //
137 // bind to the network change detection
138 //
139
140 networkMonitor.registerNotification( this );
141#endif
142
143 //
144 // base comm startup done
145 //
146
147 logging_info( "base communication started up" );
148}
149
150void BaseCommunication::stop() {
151
152 logging_info( "stopping base communication and transport ..." );
153
154 transport->stop();
155 delete transport;
156 delete network;
157
158 logging_info( "base communication stopped" );
159}
160
161const LinkID BaseCommunication::establishLink(
162 const EndpointDescriptor& descriptor,
163 const LinkID& link_id,
164 const QoSParameterSet& qos,
165 const SecurityParameterSet& sec) {
166
167 // copy link id
168 LinkID linkid = link_id;
169
170 // debug
171 logging_debug( "request to establish link" );
172
173 //
174 // just use the first locator in the endp descriptors
175 //
176 if( descriptor.locator == NULL ){
177 logging_error( "invalid destination endpoint" );
178 return LinkID::UNSPECIFIED;
179 }
180
181 if( localDescriptor.locator == NULL ){
182 logging_error( "invalid local endpoint" );
183 return LinkID::UNSPECIFIED;
184 }
185
186 const NetworkLocator* remote = descriptor.locator;
187 const NetworkLocator* local = localDescriptor.locator;
188
189 // create link identifier and link descriptor
190 if (linkid.isUnspecified()) linkid = LinkID::create();
191 logging_debug( "creating new local descriptor entry with local link id " << linkid.toString() );
192 LinkDescriptor linkDescriptor( linkid, local, LinkID::UNSPECIFIED, remote, descriptor, false );
193 addLink( linkDescriptor );
194
195 //
196 // create a base msg with our link id and
197 // a request to open a link on the other side
198 //
199
200 logging_debug( "sending out base messages with request to open link to " << remote->toString() );
201 AribaBaseMsg baseMsg( remote, AribaBaseMsg::LINK_STATE_OPEN_REQUEST, linkid,
202 LinkID::UNSPECIFIED );
203 transport->sendMessage(&baseMsg);
204
205 return linkid;
206}
207
208void BaseCommunication::dropLink(const LinkID link) {
209
210 logging_debug( "starting to drop link " + link.toString() );
211
212 // see if we have the link
213 LinkDescriptor& descriptor = queryLocalLink( link );
214 if( descriptor.isUnspecified() ){
215 logging_error( "don't know the link you want to drop "+ link.toString() );
216 return;
217 }
218
219 // create message to drop the link
220 logging_debug( "sending out link close request. for us, the link is closed now" );
221 AribaBaseMsg msg(
222 descriptor.remoteLocator,
223 AribaBaseMsg::LINK_STATE_CLOSE_REQUEST,
224 descriptor.localLink,
225 descriptor.remoteLink
226 );
227
228 // send message to drop the link
229 transport->sendMessage( &msg );
230
231 // tell the registered listeners
232 BOOST_FOREACH( CommunicationEvents* i, eventListener ){
233 i->onLinkDown( link, descriptor.localLocator, descriptor.remoteLocator );
234 }
235
236 // remove from map
237 removeLink(link);
238}
239
240seqnum_t BaseCommunication::sendMessage( const LinkID lid, const Message* message) {
241
242 logging_debug( "sending out message to link " << lid.toString() );
243
244 // query local link info
245 LinkDescriptor& linkDesc = queryLocalLink(lid);
246 if( linkDesc.isUnspecified() ){
247 logging_error( "don't know the link with id " << lid.toString() );
248 return -1;
249 }
250
251 // create message
252 AribaBaseMsg msg(
253 linkDesc.remoteLocator,
254 AribaBaseMsg::LINK_STATE_DATA,
255 linkDesc.localLink,
256 linkDesc.remoteLink
257 );
258
259 // encapsulate the payload message
260 msg.encapsulate( const_cast<Message*>(message) );
261
262 if( !linkDesc.linkup ){
263 logging_error("cant send message on link " << lid.toString() << ", link not up");
264 return -1;
265 }
266
267 // send message
268 transport->sendMessage( &msg );
269 return ++currentSeqnum;
270}
271
272const EndpointDescriptor& BaseCommunication::getEndpointDescriptor(const LinkID link) const {
273
274 if( link == LinkID::UNSPECIFIED){
275 return localDescriptor;
276 } else {
277 LinkDescriptor& linkDesc = queryLocalLink(link);
278 if (linkDesc.isUnspecified()) return EndpointDescriptor::UNSPECIFIED;
279 return linkDesc.remoteEndpoint;
280 }
281}
282
283void BaseCommunication::registerMessageReceiver(MessageReceiver* _receiver) {
284 messageReceiver = _receiver;
285}
286
287void BaseCommunication::unregisterMessageReceiver(MessageReceiver* _receiver) {
288 messageReceiver = NULL;
289}
290
291void BaseCommunication::registerEventListener(CommunicationEvents* _events){
292
293 if( eventListener.find( _events ) == eventListener.end() )
294 eventListener.insert( _events );
295}
296
297void BaseCommunication::unregisterEventListener(CommunicationEvents* _events){
298
299 EventListenerSet::iterator i = eventListener.find( _events );
300 if( i != eventListener.end() )
301 eventListener.erase( i );
302}
303
304
305bool BaseCommunication::receiveMessage(const Message* message, const LinkID& /*invalid*/, const NodeID& ){
306
307 //
308 // these messages arrive from the Transport module
309 // and are incoming network messages. Unpack the
310 // AribaBaseMsg and handle control packets,
311 // deliver data packets to the overlay
312 //
313
314 AribaBaseMsg* spovmsg = ((Message*)message)->decapsulate<AribaBaseMsg>();
315 logging_debug( "receiving base comm message of type " << spovmsg->getTypeString() );
316
317 //
318 // deliver data to the overlays. we just give the
319 // inner packet to every registered overlay ...
320 //
321
322 if( spovmsg->getType() == AribaBaseMsg::LINK_STATE_DATA ){
323
324 logging_debug( "received data message, forwarding to overlay" );
325
326 //
327 // put the linkid as address into the message
328 // and sent it to the receiver
329 //
330
331 if( messageReceiver != NULL ) {
332 messageReceiver->receiveMessage(
333 spovmsg,
334 spovmsg->getRemoteLink(),
335 NodeID::UNSPECIFIED
336 );
337 }
338
339 } // LINK_STATE_DATA
340
341 //
342 // handle link open requests
343 //
344
345 else if( spovmsg->getType() == AribaBaseMsg::LINK_STATE_OPEN_REQUEST ){
346
347 logging_debug( "received link open request" );
348
349 //
350 // create a link context
351 //
352
353 // in an incoming packet the localLink is from
354 // the sender perspective local and from our
355 // perspective remote
356
357 logging_debug( "creating local link" );
358
359 LinkID localLink = LinkID::create();
360 LinkID remoteLink = spovmsg->getLocalLink();
361
362
363 const NetworkLocator* localLocator = dynamic_cast<const NetworkLocator*>(localDescriptor.locator);
364 const NetworkLocator* remoteLocator = dynamic_cast<const NetworkLocator*>(message->getSourceAddress());
365
366 logging_debug( "localLocator=" << localLocator->toString()
367 << " remoteLocator=" << remoteLocator->toString());
368
369 // ask the registered listeners if this link
370 // creation is fine. we will only allow the
371 // link if all of them agree
372
373 bool allowlink = true;
374 BOOST_FOREACH( CommunicationEvents* i, eventListener ){
375 allowlink &= i->onLinkRequest( localLink, localLocator, remoteLocator );
376 }
377
378 if( !allowlink ){
379 logging_warn( "overlay denied creation of link" );
380 return true;
381 }
382
383 //
384 // create and save the descriptor for the link
385 //
386
387 LinkDescriptor linkDescriptor(localLink, localLocator, remoteLink,
388 remoteLocator, EndpointDescriptor(remoteLocator), true);
389
390 logging_debug( "saving new link descriptor with " <<
391 "[local link " << localLink.toString() << "] " <<
392 "[local locator " << localLocator->toString() << "] " <<
393 "[remote link " << remoteLink.toString() << "] " <<
394 "[remote locator " << remoteLocator->toString() << "]" <<
395 "[link up true]" );
396
397 addLink( linkDescriptor );
398
399 //
400 // send out a link reply
401 //
402
403 logging_debug( "sending back link open reply for " <<
404 "[local link " << localLink.toString() << "] " <<
405 "[remote link " << remoteLink.toString() << "]" );
406
407 AribaBaseMsg reply(remoteLocator,
408 AribaBaseMsg::LINK_STATE_OPEN_REPLY,
409 localLink,
410 remoteLink);
411
412 transport->sendMessage( &reply );
413
414 //
415 // the link is now open
416 //
417
418 BOOST_FOREACH( CommunicationEvents* i, eventListener ){
419 i->onLinkUp( localLink, localLocator, remoteLocator );
420 }
421
422 } // LINK_STATE_OPEN_REQUEST
423
424 //
425 // handle link open replies
426 //
427
428 else if( spovmsg->getType() == AribaBaseMsg::LINK_STATE_OPEN_REPLY ){
429
430 logging_debug( "received link open reply for a link we initiated" );
431
432 // this is a reply to a link open request, so we have already
433 // a link mapping and can now set the remote link to valid
434 LinkDescriptor& linkDesc = queryLocalLink( spovmsg->getRemoteLink() );
435
436 if (linkDesc.isUnspecified()) {
437 logging_warn("failed to find local link " << spovmsg->getRemoteLink().toString());
438 return false;
439 }
440
441 linkDesc.remoteLink = spovmsg->getLocalLink();
442 linkDesc.linkup = true;
443
444 logging_debug( "the link is now up with local link id " << linkDesc.localLink.toString() <<
445 " and remote link id " << linkDesc.remoteLink.toString() );
446
447 // notify the baseoverlay that the link is up, so
448 // it can exchange nodeids over this link. then we
449 // can send the queued messages, as both nodes have
450 // to know their nodeids first
451
452 BOOST_FOREACH( CommunicationEvents* i, eventListener ){
453 i->onLinkUp( linkDesc.localLink, linkDesc.localLocator, linkDesc.remoteLocator );
454 }
455
456 } // LINK_STATE_OPEN_REPLY
457
458 //
459 // handle link close requests
460 //
461
462 else if( spovmsg->getType() == AribaBaseMsg::LINK_STATE_CLOSE_REQUEST ){
463
464 const LinkID& localLink = spovmsg->getRemoteLink();
465 logging_debug( "received link close request for link " << localLink.toString() );
466
467 //
468 // the link is closed immediately, we
469 // don't need to send out a reply, so we
470 // delete the mapping and inform
471 //
472
473 LinkDescriptor& linkDesc = queryLocalLink( localLink );
474 if (linkDesc.isUnspecified()) {
475 logging_warn("Failed to find local link " << localLink.toString());
476 return false;
477 }
478
479 BOOST_FOREACH( CommunicationEvents* i, eventListener ){
480 i->onLinkDown( linkDesc.localLink, linkDesc.localLocator, linkDesc.remoteLocator );
481 }
482
483 //
484 // remove the link descriptor
485 //
486
487 removeLink( localLink );
488
489 } // LINK_STATE_CLOSE_REQUEST
490
491 //
492 // handle locator updates
493 //
494
495 else if( spovmsg->getType() == AribaBaseMsg::LINK_STATE_UPDATE ){
496
497 const LinkID& localLink = spovmsg->getRemoteLink();
498 logging_debug( "received link update for link " << localLink.toString() );
499
500 //
501 // find the link description
502 //
503
504 LinkDescriptor& linkDesc = queryLocalLink( localLink );
505 if (linkDesc.isUnspecified()) {
506 logging_warn("Failed to update local link " << localLink.toString());
507 return false;
508 }
509
510 //
511 // update the remote locator
512 //
513
514 const NetworkLocator* oldremote = linkDesc.remoteLocator;
515 linkDesc.remoteLocator = dynamic_cast<const NetworkLocator*>(message->getSourceAddress());
516
517 //
518 // inform the listeners (local link has _not_ changed!)
519 //
520
521 BOOST_FOREACH( CommunicationEvents* i, eventListener ){
522 i->onLinkChanged(
523 linkDesc.localLink, // linkid
524 linkDesc.localLocator, // old local
525 linkDesc.localLocator, // new local
526 oldremote, // old remote
527 linkDesc.remoteLocator // new remote
528 );
529 }
530
531 } // LINK_STATE_UPDATE
532
533 return true;
534}
535
536void BaseCommunication::addLink( const LinkDescriptor& link ) {
537 linkSet.push_back( link );
538}
539
540void BaseCommunication::removeLink( const LinkID& localLink ) {
541
542 LinkSet::iterator i = linkSet.begin();
543 LinkSet::iterator iend = linkSet.end();
544
545 for( ; i != iend; i++){
546 if( (*i).localLink != localLink) continue;
547
548 linkSet.erase( i );
549 break;
550 }
551}
552
553BaseCommunication::LinkDescriptor& BaseCommunication::queryLocalLink( const LinkID& link ) const {
554 for (int i=0; i<linkSet.size();i++)
555 if (linkSet[i].localLink == link) return (LinkDescriptor&)linkSet[i];
556 return (LinkDescriptor&)LinkDescriptor::UNSPECIFIED;
557}
558
559BaseCommunication::LinkDescriptor& BaseCommunication::queryRemoteLink( const LinkID& link ) const {
560 for (int i=0; i<linkSet.size();i++)
561 if (linkSet[i].remoteLink == link) return (LinkDescriptor&)linkSet[i];
562 return (LinkDescriptor&)LinkDescriptor::UNSPECIFIED;
563}
564
565LinkIDs BaseCommunication::getLocalLinks( const EndpointDescriptor& ep ) const {
566 LinkIDs ids;
567
568 for (int i=0; i<linkSet.size(); i++){
569 if( ep == EndpointDescriptor::UNSPECIFIED ){
570 ids.push_back( linkSet[i].localLink );
571 } else {
572 if ( linkSet[i].remoteLocator == ep.locator )
573 ids.push_back( linkSet[i].localLink );
574 }
575 }
576
577 return ids;
578}
579
580void BaseCommunication::onNetworkChange(const NetworkChangeInterface::NetworkChangeInfo& info){
581
582#ifdef UNDERLAY_OMNET
583
584 // we have no mobility support for simulations
585 return
586
587#endif // UNDERLAY_OMNET
588
589 //
590 // we only care about address changes, not about interface changes
591 // as address changes are triggered by interface changes, we are safe here
592 //
593
594 if( info.type != NetworkChangeInterface::EventTypeAddressNew &&
595 info.type != NetworkChangeInterface::EventTypeAddressDelete ) return;
596
597 logging_info( "base communication is handling network address changes" );
598
599 //
600 // get all now available addresses
601 //
602
603 NetworkInformation networkInformation;
604 AddressInformation addressInformation;
605
606 NetworkInterfaceList interfaces = networkInformation.getInterfaces();
607 AddressList addresses;
608
609 for( NetworkInterfaceList::iterator i = interfaces.begin(); i != interfaces.end(); i++ ){
610 AddressList newaddr = addressInformation.getAddresses(*i);
611 addresses.insert( addresses.end(), newaddr.begin(), newaddr.end() );
612 }
613
614 //
615 // get current locators for the local endpoint
616 // TODO: this code is dublicate of the ctor code!!! cleanup!
617 //
618
619 NetworkProtocol::NetworkLocatorSet locators = network->getAddresses();
620 NetworkProtocol::NetworkLocatorSet::iterator i = locators.begin();
621 NetworkProtocol::NetworkLocatorSet::iterator iend = locators.end();
622
623 //
624 // remember the old local endpoint, in case it changes
625 //
626
627 EndpointDescriptor oldLocalDescriptor( localDescriptor );
628
629 //
630 // look for local locators that we can use in communication
631 //
632 // choose the first locator that is not localhost
633 //
634
635 bool foundLocator = false;
636 bool changedLocator = false;
637
638 for( ; i != iend; i++){
639 logging_debug( "local locator found " << (*i)->toString() );
640 IPv4Locator* ipv4locator = dynamic_cast<IPv4Locator*>(*i);
641
642 if( *ipv4locator != IPv4Locator::LOCALHOST &&
643 *ipv4locator != IPv4Locator::ANY &&
644 *ipv4locator != IPv4Locator::BROADCAST ){
645
646 ipv4locator->setPort( listenport );
647 changedLocator = *localDescriptor.locator != *ipv4locator;
648 localDescriptor.locator = ipv4locator;
649 logging_info( "binding to addr = " << ipv4locator->toString() );
650 foundLocator = true;
651 break;
652 }
653 } // for( ; i != iend; i++)
654
655 //
656 // if we found no locator, bind to localhost
657 //
658
659 if( !foundLocator ){
660 changedLocator = *localDescriptor.locator != IPv4Locator::LOCALHOST;
661 localDescriptor.locator = new IPv4Locator( IPv4Locator::LOCALHOST );
662 ((IPv4Locator*)(localDescriptor.locator))->setPort( listenport );
663 logging_info( "found no good local lcoator, binding to addr = " <<
664 localDescriptor.locator->toString() );
665 }
666
667 //
668 // if we have connections that have no more longer endpoints
669 // close these. they will be automatically built up again.
670 // also update the local locator in the linkset mapping
671 //
672
673 if( changedLocator ){
674
675 logging_debug( "local endp locator has changed to " << localDescriptor.toString() <<
676 ", resettings connections that end at old locator " <<
677 oldLocalDescriptor.toString());
678
679 LinkSet::iterator i = linkSet.begin();
680 LinkSet::iterator iend = linkSet.end();
681
682 for( ; i != iend; i++ ){
683
684 logging_debug( "checking connection for locator change: " <<
685 " local " << (*i).localLocator->toString() <<
686 " old " << oldLocalDescriptor.locator->toString() );
687
688 if( *((*i).localLocator) == *(oldLocalDescriptor.locator) ){
689
690 logging_debug("terminating connection to " << (*i).remoteLocator->toString() );
691 transport->terminate( oldLocalDescriptor.locator, (*i).remoteLocator );
692
693 (*i).localLocator = localDescriptor.locator;
694 }
695 } // for( ; i != iend; i++ )
696
697 // wait 500ms to give the sockets time to shut down
698 usleep( 500000 );
699
700 } else {
701
702 logging_debug( "locator has not changed, not resetting connections" );
703
704 }
705
706 //
707 // handle the connections that have no longer any
708 // valid locator. send update messages with the new
709 // locator, so the remote node updates its locator/link mapping
710 //
711
712 LinkSet::iterator iAffected = linkSet.begin();
713 LinkSet::iterator endAffected = linkSet.end();
714
715 for( ; iAffected != endAffected; iAffected++ ){
716 LinkDescriptor descr = *iAffected;
717 logging_debug( "sending out link locator update to " << descr.remoteLocator->toString() );
718
719 AribaBaseMsg updateMsg( descr.remoteLocator,
720 AribaBaseMsg::LINK_STATE_UPDATE,
721 descr.localLink, descr.remoteLink );
722
723 transport->sendMessage( &updateMsg );
724 }
725}
726
727}} // namespace ariba, communication
Note: See TracBrowser for help on using the repository browser.