Skip to content

Commit 40fb3a7

Browse files
committed
[FAB-2007] Gossip:Add support of external endpoint
Peers in the same organization might need to communicate by connecting to endpoints that are only route-able from within the organization, and when communication with peers in different organizations, they should connect to endpoints that are route-able from outside the organization. For example if the peers are behind a NAT with port-forwarding rules for the peers. This commit adds an internal endpoint to the Member proto message. The endpoint is signed separately and when a message would cross an organization, the inner endpoint would be deleted from the last peer that was in the source organization. The access control and filtering will be done in upcoming commits. This commit only adds the internal endpoint, and changes the gossip logic to prefer using the internal endpoint over the external one. Unit tests: I changed the discovery and gossip unit tests accordingly, and added a check in the discovery unit test that both endpoints are gossiped to peers. In the gossip unit-tests, made sure that the external endpoint is never used, and only the internal one is used. In upcoming commits, I would add tests that have several organizations. How was this tested? This commit doesn't add any new functionality, so I just tested that the communication and discovery between peers wasn't damaged. I ran a modified docs/channel-setup.md with an additional peer (peer1) - Once with CORE_PEER_GOSSIP_BOOTSTRAP=ip-of-peer-0:7051 and saw that peers were able to connect to one another - Once without any bootstrap set, but created an anchor peer file with the ip address of peer0, and made peer0 and peer1 join the channel and saw in the logs that channel-based messages were sent between the peers. Signed-off-by: Yacov Manevich <[email protected]> Change-Id: I243b55fc192c3d7e516598a899cd58039ded7587
1 parent 7a09dfb commit 40fb3a7

14 files changed

+378
-211
lines changed

gossip/discovery/discovery.go

+18-3
Original file line numberDiff line numberDiff line change
@@ -54,14 +54,29 @@ type CommService interface {
5454

5555
// NetworkMember is a peer's representation
5656
type NetworkMember struct {
57-
Endpoint string
58-
Metadata []byte
59-
PKIid common.PKIidType
57+
Endpoint string
58+
Metadata []byte
59+
PKIid common.PKIidType
60+
InternalEndpoint *proto.SignedEndpoint
61+
}
62+
63+
// PreferredEndpoint computes the endpoint to connect to,
64+
// while preferring internal endpoint over the standard
65+
// endpoint
66+
func (nm NetworkMember) PreferredEndpoint() string {
67+
if nm.InternalEndpoint != nil && nm.InternalEndpoint.Endpoint != "" {
68+
return nm.InternalEndpoint.Endpoint
69+
}
70+
return nm.Endpoint
6071
}
6172

6273
// Discovery is the interface that represents a discovery module
6374
type Discovery interface {
6475

76+
// Exists returns whether a peer with given
77+
// PKI-ID is known
78+
Exists(PKIID common.PKIidType) bool
79+
6580
// Self returns this instance's membership information
6681
Self() NetworkMember
6782

gossip/discovery/discovery_impl.go

+71-53
Original file line numberDiff line numberDiff line change
@@ -71,13 +71,9 @@ func (ts *timestamp) String() string {
7171
}
7272

7373
type gossipDiscoveryImpl struct {
74-
pkiID common.PKIidType
75-
endpoint string
76-
incTime uint64
77-
metadata []byte
78-
79-
seqNum uint64
80-
74+
incTime uint64
75+
seqNum uint64
76+
self NetworkMember
8177
deadLastTS map[string]*timestamp // H
8278
aliveLastTS map[string]*timestamp // V
8379
id2Member map[string]*NetworkMember // all known members
@@ -97,10 +93,8 @@ type gossipDiscoveryImpl struct {
9793
// NewDiscoveryService returns a new discovery service with the comm module passed and the crypto service passed
9894
func NewDiscoveryService(bootstrapPeers []string, self NetworkMember, comm CommService, crypt CryptoService) Discovery {
9995
d := &gossipDiscoveryImpl{
100-
endpoint: self.Endpoint,
96+
self: self,
10197
incTime: uint64(time.Now().UnixNano()),
102-
metadata: self.Metadata,
103-
pkiID: self.PKIid,
10498
seqNum: uint64(0),
10599
deadLastTS: make(map[string]*timestamp),
106100
aliveLastTS: make(map[string]*timestamp),
@@ -109,13 +103,12 @@ func NewDiscoveryService(bootstrapPeers []string, self NetworkMember, comm CommS
109103
Alive: make([]*proto.GossipMessage, 0),
110104
Dead: make([]*proto.GossipMessage, 0),
111105
},
112-
crypt: crypt,
113-
bootstrapPeers: bootstrapPeers,
114-
comm: comm,
115-
lock: &sync.RWMutex{},
116-
toDieChan: make(chan struct{}, 1),
117-
toDieFlag: int32(0),
118-
logger: util.GetLogger(util.LoggingDiscoveryModule, self.Endpoint),
106+
crypt: crypt,
107+
comm: comm,
108+
lock: &sync.RWMutex{},
109+
toDieChan: make(chan struct{}, 1),
110+
toDieFlag: int32(0),
111+
logger: util.GetLogger(util.LoggingDiscoveryModule, self.InternalEndpoint.Endpoint),
119112
}
120113

121114
go d.periodicalSendAlive()
@@ -131,6 +124,15 @@ func NewDiscoveryService(bootstrapPeers []string, self NetworkMember, comm CommS
131124
return d
132125
}
133126

127+
// Exists returns whether a peer with given
128+
// PKI-ID is known
129+
func (d *gossipDiscoveryImpl) Exists(PKIID common.PKIidType) bool {
130+
d.lock.RLock()
131+
defer d.lock.RUnlock()
132+
_, exists := d.id2Member[string(PKIID)]
133+
return exists
134+
}
135+
134136
func (d *gossipDiscoveryImpl) Connect(member NetworkMember) {
135137
d.logger.Debug("Entering", member)
136138
defer d.logger.Debug("Exiting")
@@ -167,6 +169,9 @@ func (d *gossipDiscoveryImpl) connect2BootstrapPeers(endpoints []string) {
167169
defer wg.Done()
168170
peer := &NetworkMember{
169171
Endpoint: endpoint,
172+
InternalEndpoint: &proto.SignedEndpoint{
173+
Endpoint: endpoint,
174+
},
170175
}
171176
d.comm.SendToPeer(peer, req)
172177
}(endpoint)
@@ -192,9 +197,10 @@ func (d *gossipDiscoveryImpl) InitiateSync(peerNum int) {
192197
for _, i := range util.GetRandomIndices(k, n-1) {
193198
pulledPeer := d.cachedMembership.Alive[i].GetAliveMsg().Membership
194199
netMember := &NetworkMember{
195-
Endpoint: pulledPeer.Endpoint,
196-
Metadata: pulledPeer.Metadata,
197-
PKIid: pulledPeer.PkiID,
200+
Endpoint: pulledPeer.Endpoint,
201+
Metadata: pulledPeer.Metadata,
202+
PKIid: pulledPeer.PkiID,
203+
InternalEndpoint: pulledPeer.InternalEndpoint,
198204
}
199205
peers2SendTo = append(peers2SendTo, netMember)
200206
}
@@ -285,7 +291,7 @@ func (d *gossipDiscoveryImpl) handleMsgFromComm(m *proto.GossipMessage) {
285291

286292
for _, dm := range memResp.Dead {
287293
if !d.crypt.ValidateAliveMsg(m) {
288-
d.logger.Warningf("Alive message isn't authentic, someone spoofed %s's identity", dm.GetAliveMsg().Membership.Endpoint)
294+
d.logger.Warningf("Alive message isn't authentic, someone spoofed %s's identity", dm.GetAliveMsg().Membership)
289295
continue
290296
}
291297

@@ -308,9 +314,10 @@ func (d *gossipDiscoveryImpl) sendMemResponse(member *proto.Member, known [][]by
308314
defer d.logger.Debug("Exiting, replying with", memResp)
309315

310316
d.comm.SendToPeer(&NetworkMember{
311-
Endpoint: member.Endpoint,
312-
Metadata: member.Metadata,
313-
PKIid: member.PkiID,
317+
Endpoint: member.Endpoint,
318+
Metadata: member.Metadata,
319+
PKIid: member.PkiID,
320+
InternalEndpoint: member.InternalEndpoint,
314321
}, &proto.GossipMessage{
315322
Tag: proto.GossipMessage_EMPTY,
316323
Nonce: uint64(0),
@@ -353,12 +360,12 @@ func (d *gossipDiscoveryImpl) handleAliveMessage(m *proto.GossipMessage) {
353360
defer d.logger.Debug("Exiting")
354361

355362
if !d.crypt.ValidateAliveMsg(m) {
356-
d.logger.Warningf("Alive message isn't authentic, someone must be spoofing %s's identity", m.GetAliveMsg().Membership.Endpoint)
363+
d.logger.Warningf("Alive message isn't authentic, someone must be spoofing %s's identity", m.GetAliveMsg())
357364
return
358365
}
359366

360367
pkiID := m.GetAliveMsg().Membership.PkiID
361-
if equalPKIid(pkiID, d.pkiID) {
368+
if equalPKIid(pkiID, d.self.PKIid) {
362369
d.logger.Debug("Got alive message about ourselves,", m)
363370
return
364371
}
@@ -385,7 +392,7 @@ func (d *gossipDiscoveryImpl) handleAliveMessage(m *proto.GossipMessage) {
385392
}
386393

387394
if isAlive && isDead {
388-
d.logger.Panicf("Member %s is both alive and dead at the same time", m.GetAliveMsg().Membership.Endpoint)
395+
d.logger.Panicf("Member %s is both alive and dead at the same time", m.GetAliveMsg().Membership)
389396
return
390397
}
391398

@@ -394,7 +401,7 @@ func (d *gossipDiscoveryImpl) handleAliveMessage(m *proto.GossipMessage) {
394401
// resurrect peer
395402
d.resurrectMember(m, *ts)
396403
} else if !same(lastDeadTS, ts) {
397-
d.logger.Debug(m.GetAliveMsg().Membership.Endpoint, "lastDeadTS:", lastDeadTS, "but got ts:", ts)
404+
d.logger.Debug(m.GetAliveMsg().Membership, "lastDeadTS:", lastDeadTS, "but got ts:", ts)
398405
}
399406
return
400407
}
@@ -407,7 +414,7 @@ func (d *gossipDiscoveryImpl) handleAliveMessage(m *proto.GossipMessage) {
407414
if before(lastAliveTS, ts) {
408415
d.learnExistingMembers([]*proto.GossipMessage{m})
409416
} else if !same(lastAliveTS, ts) {
410-
d.logger.Debug(m.GetAliveMsg().Membership.Endpoint, "lastAliveTS:", lastAliveTS, "but got ts:", ts)
417+
d.logger.Debug(m.GetAliveMsg().Membership, "lastAliveTS:", lastAliveTS, "but got ts:", ts)
411418
}
412419

413420
}
@@ -429,9 +436,10 @@ func (d *gossipDiscoveryImpl) resurrectMember(am *proto.GossipMessage, t proto.P
429436
}
430437

431438
d.id2Member[string(pkiID)] = &NetworkMember{
432-
Endpoint: member.Endpoint,
433-
Metadata: member.Metadata,
434-
PKIid: member.PkiID,
439+
Endpoint: member.Endpoint,
440+
Metadata: member.Metadata,
441+
PKIid: member.PkiID,
442+
InternalEndpoint: member.InternalEndpoint,
435443
}
436444
delete(d.deadLastTS, string(pkiID))
437445

@@ -570,7 +578,7 @@ func (d *gossipDiscoveryImpl) expireDeadMembers(dead []common.PKIidType) {
570578
d.lock.Unlock()
571579

572580
for _, member2Expire := range deadMembers2Expire {
573-
d.logger.Warning("Closing connection to", member2Expire.Endpoint)
581+
d.logger.Warning("Closing connection to", member2Expire)
574582
d.comm.CloseConn(member2Expire)
575583
}
576584
}
@@ -605,9 +613,10 @@ func (d *gossipDiscoveryImpl) createAliveMessage() *proto.GossipMessage {
605613
d.seqNum++
606614
seqNum := d.seqNum
607615

608-
endpoint := d.endpoint
609-
meta := d.metadata
610-
pkiID := d.pkiID
616+
endpoint := d.self.Endpoint
617+
meta := d.self.Metadata
618+
pkiID := d.self.PKIid
619+
internalEndpoint := d.self.InternalEndpoint
611620

612621
d.lock.Unlock()
613622

@@ -616,9 +625,10 @@ func (d *gossipDiscoveryImpl) createAliveMessage() *proto.GossipMessage {
616625
Content: &proto.GossipMessage_AliveMsg{
617626
AliveMsg: &proto.AliveMessage{
618627
Membership: &proto.Member{
619-
Endpoint: endpoint,
620-
Metadata: meta,
621-
PkiID: pkiID,
628+
Endpoint: endpoint,
629+
Metadata: meta,
630+
PkiID: pkiID,
631+
InternalEndpoint: internalEndpoint,
622632
},
623633
Timestamp: &proto.PeerTime{
624634
IncNumber: uint64(d.incTime),
@@ -649,14 +659,15 @@ func (d *gossipDiscoveryImpl) learnExistingMembers(aliveArr []*proto.GossipMessa
649659
member := d.id2Member[string(am.Membership.PkiID)]
650660
member.Endpoint = am.Membership.Endpoint
651661
member.Metadata = am.Membership.Metadata
662+
member.InternalEndpoint = am.Membership.InternalEndpoint
652663

653664
if _, isKnownAsDead := d.deadLastTS[string(am.Membership.PkiID)]; isKnownAsDead {
654-
d.logger.Warning(am.Membership.Endpoint, "has already expired")
665+
d.logger.Warning(am.Membership, "has already expired")
655666
continue
656667
}
657668

658669
if _, isKnownAsAlive := d.aliveLastTS[string(am.Membership.PkiID)]; !isKnownAsAlive {
659-
d.logger.Warning(am.Membership.Endpoint, "has already expired")
670+
d.logger.Warning(am.Membership, "has already expired")
660671
continue
661672
} else {
662673
d.logger.Debug("Updating aliveness data:", am)
@@ -686,7 +697,7 @@ func (d *gossipDiscoveryImpl) learnNewMembers(aliveMembers []*proto.GossipMessag
686697
defer d.lock.Unlock()
687698

688699
for _, am := range aliveMembers {
689-
if equalPKIid(am.GetAliveMsg().Membership.PkiID, d.pkiID) {
700+
if equalPKIid(am.GetAliveMsg().Membership.PkiID, d.self.PKIid) {
690701
continue
691702
}
692703
d.aliveLastTS[string(am.GetAliveMsg().Membership.PkiID)] = &timestamp{
@@ -700,7 +711,7 @@ func (d *gossipDiscoveryImpl) learnNewMembers(aliveMembers []*proto.GossipMessag
700711
}
701712

702713
for _, dm := range deadMembers {
703-
if equalPKIid(dm.GetAliveMsg().Membership.PkiID, d.pkiID) {
714+
if equalPKIid(dm.GetAliveMsg().Membership.PkiID, d.self.PKIid) {
704715
continue
705716
}
706717
d.deadLastTS[string(dm.GetAliveMsg().Membership.PkiID)] = &timestamp{
@@ -722,9 +733,10 @@ func (d *gossipDiscoveryImpl) learnNewMembers(aliveMembers []*proto.GossipMessag
722733
return
723734
}
724735
d.id2Member[string(member.Membership.PkiID)] = &NetworkMember{
725-
Endpoint: member.Membership.Endpoint,
726-
Metadata: member.Membership.Metadata,
727-
PKIid: member.Membership.PkiID,
736+
Endpoint: member.Membership.Endpoint,
737+
Metadata: member.Membership.Metadata,
738+
PKIid: member.Membership.PkiID,
739+
InternalEndpoint: member.Membership.InternalEndpoint,
728740
}
729741
}
730742
}
@@ -741,9 +753,10 @@ func (d *gossipDiscoveryImpl) GetMembership() []NetworkMember {
741753
for _, m := range d.cachedMembership.Alive {
742754
member := m.GetAliveMsg()
743755
response = append(response, NetworkMember{
744-
PKIid: member.Membership.PkiID,
745-
Endpoint: member.Membership.Endpoint,
746-
Metadata: member.Membership.Metadata,
756+
PKIid: member.Membership.PkiID,
757+
Endpoint: member.Membership.Endpoint,
758+
Metadata: member.Membership.Metadata,
759+
InternalEndpoint: member.Membership.InternalEndpoint,
747760
})
748761
}
749762
return response
@@ -757,18 +770,23 @@ func tsToTime(ts uint64) time.Time {
757770
func (d *gossipDiscoveryImpl) UpdateMetadata(md []byte) {
758771
d.lock.Lock()
759772
defer d.lock.Unlock()
760-
d.metadata = md
773+
d.self.Metadata = md
761774
}
762775

763776
func (d *gossipDiscoveryImpl) UpdateEndpoint(endpoint string) {
764777
d.lock.Lock()
765778
defer d.lock.Unlock()
766779

767-
d.endpoint = endpoint
780+
d.self.Endpoint = endpoint
768781
}
769782

770783
func (d *gossipDiscoveryImpl) Self() NetworkMember {
771-
return NetworkMember{Endpoint: d.endpoint, Metadata: d.metadata, PKIid: d.pkiID}
784+
return NetworkMember{
785+
Endpoint: d.self.Endpoint,
786+
Metadata: d.self.Metadata,
787+
PKIid: d.self.PKIid,
788+
InternalEndpoint: d.self.InternalEndpoint,
789+
}
772790
}
773791

774792
func (d *gossipDiscoveryImpl) toDie() bool {
@@ -788,7 +806,7 @@ func equalPKIid(a, b common.PKIidType) bool {
788806
}
789807

790808
func same(a *timestamp, b *proto.PeerTime) bool {
791-
return (uint64(a.incTime.UnixNano()) == b.IncNumber && a.seqNum == b.SeqNum)
809+
return uint64(a.incTime.UnixNano()) == b.IncNumber && a.seqNum == b.SeqNum
792810
}
793811

794812
func before(a *timestamp, b *proto.PeerTime) bool {

gossip/discovery/discovery_test.go

+20
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,10 @@ func createDiscoveryInstanceThatGossips(port int, id string, bootstrapPeers []st
234234
Metadata: []byte{},
235235
PKIid: []byte(endpoint),
236236
Endpoint: endpoint,
237+
InternalEndpoint: &proto.SignedEndpoint{
238+
Endpoint: endpoint,
239+
Signature: []byte{},
240+
},
237241
}
238242

239243
listenAddress := fmt.Sprintf("%s:%d", "", port)
@@ -414,6 +418,22 @@ func TestGetFullMembership(t *testing.T) {
414418
}
415419

416420
assertMembership(t, instances, nodeNum-1)
421+
422+
// Ensure that internal endpoint was propagated to everyone
423+
for _, inst := range instances {
424+
for _, member := range inst.GetMembership() {
425+
assert.NotEmpty(t, member.InternalEndpoint.Endpoint)
426+
assert.NotEmpty(t, member.Endpoint)
427+
}
428+
}
429+
430+
// Check that Exists() is valid
431+
for _, inst := range instances {
432+
for _, member := range inst.GetMembership() {
433+
assert.True(t, inst.Exists(member.PKIid))
434+
}
435+
}
436+
417437
stopInstances(t, instances)
418438
}
419439

gossip/filter/filter.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ func SelectPeers(k int, peerPool []discovery.NetworkMember, filters ...RoutingFi
4444
var filteredPeers []*comm.RemotePeer
4545
for _, peer := range peerPool {
4646
if CombineRoutingFilters(filters...)(peer) {
47-
filteredPeers = append(filteredPeers, &comm.RemotePeer{PKIID: peer.PKIid, Endpoint: peer.Endpoint})
47+
filteredPeers = append(filteredPeers, &comm.RemotePeer{PKIID: peer.PKIid, Endpoint: peer.PreferredEndpoint()})
4848
}
4949
}
5050

gossip/gossip/gossip.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,6 @@ type Gossip interface {
6969
type Config struct {
7070
BindPort int // Port we bind to, used only for tests
7171
ID string // ID of this instance
72-
SelfEndpoint string // Endpoint we publish to remote peers
7372
BootstrapPeers []string // Peers we connect to at startup
7473
PropagateIterations int // Number of times a message is pushed to remote peers
7574
PropagatePeerNum int // Number of peers selected to push messages to
@@ -89,4 +88,7 @@ type Config struct {
8988
PublishStateInfoInterval time.Duration // Determines frequency of pushing state info messages to peers
9089
RequestStateInfoInterval time.Duration // Determines frequency of pulling state info messages from peers
9190
TLSServerCert *tls.Certificate // TLS certificate of the peer
91+
92+
InternalEndpoint string // Endpoint we publish to peers in our organization
93+
ExternalEndpoint string // Peer publishes this endpoint instead of SelfEndpoint to foreign organizations
9294
}

0 commit comments

Comments
 (0)