aboutsummaryrefslogtreecommitdiffstats
path: root/p2p
diff options
context:
space:
mode:
Diffstat (limited to 'p2p')
-rw-r--r--p2p/dial.go4
-rw-r--r--p2p/metrics.go49
-rw-r--r--p2p/peer.go11
-rw-r--r--p2p/peer_test.go95
-rw-r--r--p2p/protocol.go10
-rw-r--r--p2p/server.go6
6 files changed, 166 insertions, 9 deletions
diff --git a/p2p/dial.go b/p2p/dial.go
index b82d6d1f5..45cd8116b 100644
--- a/p2p/dial.go
+++ b/p2p/dial.go
@@ -196,7 +196,9 @@ func (t *dialTask) Do(srv *Server) {
glog.V(logger.Detail).Infof("dial error: %v", err)
return
}
- srv.setupConn(fd, t.flags, t.dest)
+ mfd := newMeteredConn(fd, false)
+
+ srv.setupConn(mfd, t.flags, t.dest)
}
func (t *dialTask) String() string {
return fmt.Sprintf("%v %x %v:%d", t.flags, t.dest.ID[:8], t.dest.IP, t.dest.TCP)
diff --git a/p2p/metrics.go b/p2p/metrics.go
new file mode 100644
index 000000000..4b519e438
--- /dev/null
+++ b/p2p/metrics.go
@@ -0,0 +1,49 @@
+// Contains the meters and timers used by the networking layer.
+
+package p2p
+
+import (
+ "net"
+
+ "github.com/ethereum/go-ethereum/metrics"
+)
+
+var (
+ ingressConnectMeter = metrics.NewMeter("p2p/InboundConnects")
+ ingressTrafficMeter = metrics.NewMeter("p2p/InboundTraffic")
+ egressConnectMeter = metrics.NewMeter("p2p/OutboundConnects")
+ egressTrafficMeter = metrics.NewMeter("p2p/OutboundTraffic")
+)
+
+// meteredConn is a wrapper around a network TCP connection that meters both the
+// inbound and outbound network traffic.
+type meteredConn struct {
+ *net.TCPConn // Network connection to wrap with metering
+}
+
+// newMeteredConn creates a new metered connection, also bumping the ingress or
+// egress connection meter.
+func newMeteredConn(conn net.Conn, ingress bool) net.Conn {
+ if ingress {
+ ingressConnectMeter.Mark(1)
+ } else {
+ egressConnectMeter.Mark(1)
+ }
+ return &meteredConn{conn.(*net.TCPConn)}
+}
+
+// Read delegates a network read to the underlying connection, bumping the ingress
+// traffic meter along the way.
+func (c *meteredConn) Read(b []byte) (n int, err error) {
+ n, err = c.TCPConn.Read(b)
+ ingressTrafficMeter.Mark(int64(n))
+ return
+}
+
+// Write delegates a network write to the underlying connection, bumping the
+// egress traffic meter along the way.
+func (c *meteredConn) Write(b []byte) (n int, err error) {
+ n, err = c.TCPConn.Write(b)
+ egressTrafficMeter.Mark(int64(n))
+ return
+}
diff --git a/p2p/peer.go b/p2p/peer.go
index 40466cf84..e1bda1d03 100644
--- a/p2p/peer.go
+++ b/p2p/peer.go
@@ -249,15 +249,22 @@ func countMatchingProtocols(protocols []Protocol, caps []Cap) int {
// matchProtocols creates structures for matching named subprotocols.
func matchProtocols(protocols []Protocol, caps []Cap, rw MsgReadWriter) map[string]*protoRW {
- sort.Sort(capsByName(caps))
+ sort.Sort(capsByNameAndVersion(caps))
offset := baseProtocolLength
result := make(map[string]*protoRW)
+
outer:
for _, cap := range caps {
for _, proto := range protocols {
- if proto.Name == cap.Name && proto.Version == cap.Version && result[cap.Name] == nil {
+ if proto.Name == cap.Name && proto.Version == cap.Version {
+ // If an old protocol version matched, revert it
+ if old := result[cap.Name]; old != nil {
+ offset -= old.Length
+ }
+ // Assign the new match
result[cap.Name] = &protoRW{Protocol: proto, offset: offset, in: make(chan Msg), w: rw}
offset += proto.Length
+
continue outer
}
}
diff --git a/p2p/peer_test.go b/p2p/peer_test.go
index 575d0ff79..d849c925f 100644
--- a/p2p/peer_test.go
+++ b/p2p/peer_test.go
@@ -196,3 +196,98 @@ func TestNewPeer(t *testing.T) {
p.Disconnect(DiscAlreadyConnected) // Should not hang
}
+
+func TestMatchProtocols(t *testing.T) {
+ tests := []struct {
+ Remote []Cap
+ Local []Protocol
+ Match map[string]protoRW
+ }{
+ {
+ // No remote capabilities
+ Local: []Protocol{{Name: "a"}},
+ },
+ {
+ // No local protocols
+ Remote: []Cap{{Name: "a"}},
+ },
+ {
+ // No mutual protocols
+ Remote: []Cap{{Name: "a"}},
+ Local: []Protocol{{Name: "b"}},
+ },
+ {
+ // Some matches, some differences
+ Remote: []Cap{{Name: "local"}, {Name: "match1"}, {Name: "match2"}},
+ Local: []Protocol{{Name: "match1"}, {Name: "match2"}, {Name: "remote"}},
+ Match: map[string]protoRW{"match1": {Protocol: Protocol{Name: "match1"}}, "match2": {Protocol: Protocol{Name: "match2"}}},
+ },
+ {
+ // Various alphabetical ordering
+ Remote: []Cap{{Name: "aa"}, {Name: "ab"}, {Name: "bb"}, {Name: "ba"}},
+ Local: []Protocol{{Name: "ba"}, {Name: "bb"}, {Name: "ab"}, {Name: "aa"}},
+ Match: map[string]protoRW{"aa": {Protocol: Protocol{Name: "aa"}}, "ab": {Protocol: Protocol{Name: "ab"}}, "ba": {Protocol: Protocol{Name: "ba"}}, "bb": {Protocol: Protocol{Name: "bb"}}},
+ },
+ {
+ // No mutual versions
+ Remote: []Cap{{Version: 1}},
+ Local: []Protocol{{Version: 2}},
+ },
+ {
+ // Multiple versions, single common
+ Remote: []Cap{{Version: 1}, {Version: 2}},
+ Local: []Protocol{{Version: 2}, {Version: 3}},
+ Match: map[string]protoRW{"": {Protocol: Protocol{Version: 2}}},
+ },
+ {
+ // Multiple versions, multiple common
+ Remote: []Cap{{Version: 1}, {Version: 2}, {Version: 3}, {Version: 4}},
+ Local: []Protocol{{Version: 2}, {Version: 3}},
+ Match: map[string]protoRW{"": {Protocol: Protocol{Version: 3}}},
+ },
+ {
+ // Various version orderings
+ Remote: []Cap{{Version: 4}, {Version: 1}, {Version: 3}, {Version: 2}},
+ Local: []Protocol{{Version: 2}, {Version: 3}, {Version: 1}},
+ Match: map[string]protoRW{"": {Protocol: Protocol{Version: 3}}},
+ },
+ {
+ // Versions overriding sub-protocol lengths
+ Remote: []Cap{{Version: 1}, {Version: 2}, {Version: 3}, {Name: "a"}},
+ Local: []Protocol{{Version: 1, Length: 1}, {Version: 2, Length: 2}, {Version: 3, Length: 3}, {Name: "a"}},
+ Match: map[string]protoRW{"": {Protocol: Protocol{Version: 3}}, "a": {Protocol: Protocol{Name: "a"}, offset: 3}},
+ },
+ }
+
+ for i, tt := range tests {
+ result := matchProtocols(tt.Local, tt.Remote, nil)
+ if len(result) != len(tt.Match) {
+ t.Errorf("test %d: negotiation mismatch: have %v, want %v", i, len(result), len(tt.Match))
+ continue
+ }
+ // Make sure all negotiated protocols are needed and correct
+ for name, proto := range result {
+ match, ok := tt.Match[name]
+ if !ok {
+ t.Errorf("test %d, proto '%s': negotiated but shouldn't have", i, name)
+ continue
+ }
+ if proto.Name != match.Name {
+ t.Errorf("test %d, proto '%s': name mismatch: have %v, want %v", i, name, proto.Name, match.Name)
+ }
+ if proto.Version != match.Version {
+ t.Errorf("test %d, proto '%s': version mismatch: have %v, want %v", i, name, proto.Version, match.Version)
+ }
+ if proto.offset-baseProtocolLength != match.offset {
+ t.Errorf("test %d, proto '%s': offset mismatch: have %v, want %v", i, name, proto.offset-baseProtocolLength, match.offset)
+ }
+ }
+ // Make sure no protocols missed negotiation
+ for name, _ := range tt.Match {
+ if _, ok := result[name]; !ok {
+ t.Errorf("test %d, proto '%s': not negotiated, should have", i, name)
+ continue
+ }
+ }
+ }
+}
diff --git a/p2p/protocol.go b/p2p/protocol.go
index 5fa395eda..a229ba911 100644
--- a/p2p/protocol.go
+++ b/p2p/protocol.go
@@ -43,8 +43,10 @@ func (cap Cap) String() string {
return fmt.Sprintf("%s/%d", cap.Name, cap.Version)
}
-type capsByName []Cap
+type capsByNameAndVersion []Cap
-func (cs capsByName) Len() int { return len(cs) }
-func (cs capsByName) Less(i, j int) bool { return cs[i].Name < cs[j].Name }
-func (cs capsByName) Swap(i, j int) { cs[i], cs[j] = cs[j], cs[i] }
+func (cs capsByNameAndVersion) Len() int { return len(cs) }
+func (cs capsByNameAndVersion) Swap(i, j int) { cs[i], cs[j] = cs[j], cs[i] }
+func (cs capsByNameAndVersion) Less(i, j int) bool {
+ return cs[i].Name < cs[j].Name || (cs[i].Name == cs[j].Name && cs[i].Version < cs[j].Version)
+}
diff --git a/p2p/server.go b/p2p/server.go
index 5eff70345..9078841a8 100644
--- a/p2p/server.go
+++ b/p2p/server.go
@@ -548,9 +548,11 @@ func (srv *Server) listenLoop() {
if err != nil {
return
}
- glog.V(logger.Debug).Infof("Accepted conn %v\n", fd.RemoteAddr())
+ mfd := newMeteredConn(fd, true)
+
+ glog.V(logger.Debug).Infof("Accepted conn %v\n", mfd.RemoteAddr())
go func() {
- srv.setupConn(fd, inboundConn, nil)
+ srv.setupConn(mfd, inboundConn, nil)
slots <- struct{}{}
}()
}