mirror of
https://github.com/weaveworks/scope.git
synced 2026-07-28 17:51:21 +00:00
Merge pull request #1780 from weaveworks/1733-scope-loopback-connections
Handle loopback addresses correctly when tracking connections
This commit is contained in:
@@ -52,9 +52,9 @@ func (n natMapper) applyNAT(rpt report.Report, scope string) {
|
||||
n.flowWalker.walkFlows(func(f flow) {
|
||||
var (
|
||||
mapping = toMapping(f)
|
||||
realEndpointID = report.MakeEndpointNodeID(scope, mapping.originalIP, strconv.Itoa(mapping.originalPort))
|
||||
realEndpointID = report.MakeEndpointNodeID(scope, "", mapping.originalIP, strconv.Itoa(mapping.originalPort))
|
||||
copyEndpointPort = strconv.Itoa(mapping.rewrittenPort)
|
||||
copyEndpointID = report.MakeEndpointNodeID(scope, mapping.rewrittenIP, copyEndpointPort)
|
||||
copyEndpointID = report.MakeEndpointNodeID(scope, "", mapping.rewrittenIP, copyEndpointPort)
|
||||
node, ok = rpt.Endpoint.Nodes[realEndpointID]
|
||||
)
|
||||
if !ok {
|
||||
|
||||
@@ -43,7 +43,7 @@ func TestNat(t *testing.T) {
|
||||
}
|
||||
|
||||
have := report.MakeReport()
|
||||
originalID := report.MakeEndpointNodeID("host1", "10.0.47.1", "80")
|
||||
originalID := report.MakeEndpointNodeID("host1", "", "10.0.47.1", "80")
|
||||
have.Endpoint.AddNode(report.MakeNodeWith(originalID, map[string]string{
|
||||
Addr: "10.0.47.1",
|
||||
Port: "80",
|
||||
@@ -52,7 +52,7 @@ func TestNat(t *testing.T) {
|
||||
}))
|
||||
|
||||
want := have.Copy()
|
||||
wantID := report.MakeEndpointNodeID("host1", "1.2.3.4", "80")
|
||||
wantID := report.MakeEndpointNodeID("host1", "", "1.2.3.4", "80")
|
||||
want.Endpoint.AddNode(report.MakeNodeWith(wantID, map[string]string{
|
||||
Addr: "1.2.3.4",
|
||||
Port: "80",
|
||||
@@ -78,7 +78,7 @@ func TestNat(t *testing.T) {
|
||||
}
|
||||
|
||||
have := report.MakeReport()
|
||||
originalID := report.MakeEndpointNodeID("host2", "10.0.47.2", "22222")
|
||||
originalID := report.MakeEndpointNodeID("host2", "", "10.0.47.2", "22222")
|
||||
have.Endpoint.AddNode(report.MakeNodeWith(originalID, map[string]string{
|
||||
Addr: "10.0.47.2",
|
||||
Port: "22222",
|
||||
@@ -87,7 +87,7 @@ func TestNat(t *testing.T) {
|
||||
}))
|
||||
|
||||
want := have.Copy()
|
||||
want.Endpoint.AddNode(report.MakeNodeWith(report.MakeEndpointNodeID("host2", "2.3.4.5", "22223"), map[string]string{
|
||||
want.Endpoint.AddNode(report.MakeNodeWith(report.MakeEndpointNodeID("host2", "", "2.3.4.5", "22223"), map[string]string{
|
||||
Addr: "2.3.4.5",
|
||||
Port: "22223",
|
||||
"copy_of": originalID,
|
||||
|
||||
@@ -126,7 +126,7 @@ func readProcessConnections(buf *bytes.Buffer, namespaceProcs []*process.Process
|
||||
}
|
||||
|
||||
// walkNamespace does the work of walk for a single namespace
|
||||
func (w pidWalker) walkNamespace(buf *bytes.Buffer, sockets map[uint64]*Proc, namespaceProcs []*process.Process) error {
|
||||
func (w pidWalker) walkNamespace(namespaceID uint64, buf *bytes.Buffer, sockets map[uint64]*Proc, namespaceProcs []*process.Process) error {
|
||||
|
||||
if found, err := readProcessConnections(buf, namespaceProcs); err != nil || !found {
|
||||
return err
|
||||
@@ -181,8 +181,9 @@ func (w pidWalker) walkNamespace(buf *bytes.Buffer, sockets map[uint64]*Proc, na
|
||||
// garbage
|
||||
if proc == nil {
|
||||
proc = &Proc{
|
||||
PID: uint(p.PID),
|
||||
Name: p.Name,
|
||||
PID: uint(p.PID),
|
||||
Name: p.Name,
|
||||
NetNamespaceID: namespaceID,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -225,10 +226,10 @@ func (w pidWalker) walk(buf *bytes.Buffer) (map[uint64]*Proc, error) {
|
||||
namespaces[namespaceID] = append(namespaces[namespaceID], &p)
|
||||
})
|
||||
|
||||
for _, procs := range namespaces {
|
||||
for namespaceID, procs := range namespaces {
|
||||
select {
|
||||
case <-w.tickc:
|
||||
w.walkNamespace(buf, sockets, procs)
|
||||
w.walkNamespace(namespaceID, buf, sockets, procs)
|
||||
case <-w.stopc:
|
||||
break // abort
|
||||
}
|
||||
|
||||
@@ -24,8 +24,9 @@ type Connection struct {
|
||||
|
||||
// Proc is a single process with PID and process name.
|
||||
type Proc struct {
|
||||
PID uint
|
||||
Name string
|
||||
PID uint
|
||||
Name string
|
||||
NetNamespaceID uint64
|
||||
}
|
||||
|
||||
// ConnIter is returned by Connections().
|
||||
|
||||
@@ -132,7 +132,7 @@ func (r *Reporter) Report() (report.Report, error) {
|
||||
}
|
||||
|
||||
seenTuples[tuple.key()] = tuple
|
||||
r.addConnection(&rpt, tuple, extraNodeInfo, extraNodeInfo)
|
||||
r.addConnection(&rpt, tuple, "", extraNodeInfo, extraNodeInfo)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -143,7 +143,8 @@ func (r *Reporter) Report() (report.Report, error) {
|
||||
}
|
||||
for conn := conns.Next(); conn != nil; conn = conns.Next() {
|
||||
var (
|
||||
tuple = fourTuple{
|
||||
namespaceID string
|
||||
tuple = fourTuple{
|
||||
conn.LocalAddress.String(),
|
||||
conn.RemoteAddress.String(),
|
||||
conn.LocalPort,
|
||||
@@ -157,6 +158,10 @@ func (r *Reporter) Report() (report.Report, error) {
|
||||
fromNodeInfo[report.HostNodeID] = hostNodeID
|
||||
}
|
||||
|
||||
if conn.Proc.NetNamespaceID > 0 {
|
||||
namespaceID = strconv.FormatUint(conn.Proc.NetNamespaceID, 10)
|
||||
}
|
||||
|
||||
// If we've already seen this connection, we should know the direction
|
||||
// (or have already figured it out), so we normalize and use the
|
||||
// canonical direction. Otherwise, we can use a port-heuristic to guess
|
||||
@@ -166,7 +171,7 @@ func (r *Reporter) Report() (report.Report, error) {
|
||||
tuple.reverse()
|
||||
toNodeInfo, fromNodeInfo = fromNodeInfo, toNodeInfo
|
||||
}
|
||||
r.addConnection(&rpt, tuple, fromNodeInfo, toNodeInfo)
|
||||
r.addConnection(&rpt, tuple, namespaceID, fromNodeInfo, toNodeInfo)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,10 +179,10 @@ func (r *Reporter) Report() (report.Report, error) {
|
||||
return rpt, nil
|
||||
}
|
||||
|
||||
func (r *Reporter) addConnection(rpt *report.Report, t fourTuple, extraFromNode, extraToNode map[string]string) {
|
||||
func (r *Reporter) addConnection(rpt *report.Report, t fourTuple, namespaceID string, extraFromNode, extraToNode map[string]string) {
|
||||
var (
|
||||
fromEndpointNodeID = report.MakeEndpointNodeID(r.hostID, t.fromAddr, strconv.Itoa(int(t.fromPort)))
|
||||
toEndpointNodeID = report.MakeEndpointNodeID(r.hostID, t.toAddr, strconv.Itoa(int(t.toPort)))
|
||||
fromEndpointNodeID = report.MakeEndpointNodeID(r.hostID, namespaceID, t.fromAddr, strconv.Itoa(int(t.fromPort)))
|
||||
toEndpointNodeID = report.MakeEndpointNodeID(r.hostID, namespaceID, t.toAddr, strconv.Itoa(int(t.toPort)))
|
||||
|
||||
fromNode = report.MakeNodeWith(fromEndpointNodeID, map[string]string{
|
||||
Addr: t.fromAddr,
|
||||
|
||||
@@ -91,8 +91,8 @@ func TestSpyWithProcesses(t *testing.T) {
|
||||
// buf, _ := json.MarshalIndent(r, "", " ") ; t.Logf("\n%s\n", buf)
|
||||
|
||||
var (
|
||||
scopedLocal = report.MakeEndpointNodeID(nodeID, fixLocalAddress.String(), strconv.Itoa(int(fixLocalPort)))
|
||||
scopedRemote = report.MakeEndpointNodeID(nodeID, fixRemoteAddress.String(), strconv.Itoa(int(fixRemotePort)))
|
||||
scopedLocal = report.MakeEndpointNodeID(nodeID, "", fixLocalAddress.String(), strconv.Itoa(int(fixLocalPort)))
|
||||
scopedRemote = report.MakeEndpointNodeID(nodeID, "", fixRemoteAddress.String(), strconv.Itoa(int(fixRemotePort)))
|
||||
)
|
||||
|
||||
if want, have := 1, len(r.Endpoint.Nodes[scopedRemote].Adjacency); want != have {
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
func TestTagger(t *testing.T) {
|
||||
var (
|
||||
hostID = "foo"
|
||||
endpointNodeID = report.MakeEndpointNodeID(hostID, "1.2.3.4", "56789") // hostID ignored
|
||||
endpointNodeID = report.MakeEndpointNodeID(hostID, "", "1.2.3.4", "56789") // hostID ignored
|
||||
node = report.MakeNodeWith(endpointNodeID, map[string]string{"foo": "bar"})
|
||||
)
|
||||
|
||||
|
||||
@@ -264,6 +264,11 @@ func MapContainer2IP(m report.Node) []string {
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
// loopback addresses are shared among all namespaces
|
||||
// so we can't use them to attribute connections to a container
|
||||
if report.IsLoopback(addr) {
|
||||
continue
|
||||
}
|
||||
id := report.MakeScopedEndpointNodeID(scope, addr, "")
|
||||
result = append(result, id)
|
||||
}
|
||||
|
||||
@@ -19,11 +19,11 @@ var (
|
||||
|
||||
randomIP = "3.4.5.6"
|
||||
randomPort = "56789"
|
||||
randomEndpointNodeID = report.MakeEndpointNodeID(serverHostID, randomIP, randomPort)
|
||||
randomEndpointNodeID = report.MakeEndpointNodeID(serverHostID, "", randomIP, randomPort)
|
||||
|
||||
serverIP = "192.168.1.1"
|
||||
serverPort = "80"
|
||||
serverEndpointNodeID = report.MakeEndpointNodeID(serverHostID, serverIP, serverPort)
|
||||
serverEndpointNodeID = report.MakeEndpointNodeID(serverHostID, "", serverIP, serverPort)
|
||||
|
||||
container1ID = "11b2c3d4e5"
|
||||
container1IP = "192.168.0.1"
|
||||
@@ -31,11 +31,11 @@ var (
|
||||
container1NodeID = report.MakeContainerNodeID(container1ID)
|
||||
|
||||
container1Port = "16782"
|
||||
container1EndpointNodeID = report.MakeEndpointNodeID(serverHostID, container1IP, container1Port)
|
||||
container1EndpointNodeID = report.MakeEndpointNodeID(serverHostID, "", container1IP, container1Port)
|
||||
|
||||
duplicatedIP = "192.168.0.2"
|
||||
duplicatedPort = "80"
|
||||
duplicatedEndpointNodeID = report.MakeEndpointNodeID(serverHostID, duplicatedIP, duplicatedPort)
|
||||
duplicatedEndpointNodeID = report.MakeEndpointNodeID(serverHostID, "", duplicatedIP, duplicatedPort)
|
||||
|
||||
container2ID = "21b2c3d4e5"
|
||||
container2IP = duplicatedIP
|
||||
|
||||
74
report/id.go
74
report/id.go
@@ -1,13 +1,8 @@
|
||||
package report
|
||||
|
||||
import (
|
||||
"hash"
|
||||
"hash/fnv"
|
||||
"net"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/bluele/gcache"
|
||||
)
|
||||
|
||||
// TheInternet is used as a node ID to indicate a remote IP.
|
||||
@@ -29,66 +24,46 @@ const (
|
||||
DoesNotMakeConnections = "does_not_make_connections"
|
||||
)
|
||||
|
||||
var (
|
||||
idCache = gcache.New(1024).LRU().Build()
|
||||
hashers = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return fnv.New64a()
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func lookupID(part1, part2, part3 string, f func() string) string {
|
||||
h := hashers.Get().(hash.Hash64)
|
||||
h.Write([]byte(part1))
|
||||
h.Write([]byte(part2))
|
||||
h.Write([]byte(part3))
|
||||
sum := h.Sum64()
|
||||
var result string
|
||||
if id, err := idCache.Get(sum); id != nil && err != nil {
|
||||
result = id.(string)
|
||||
} else {
|
||||
result = f()
|
||||
idCache.Set(sum, result)
|
||||
}
|
||||
h.Reset()
|
||||
hashers.Put(h)
|
||||
return result
|
||||
}
|
||||
|
||||
// MakeEndpointNodeID produces an endpoint node ID from its composite parts.
|
||||
func MakeEndpointNodeID(hostID, address, port string) string {
|
||||
return lookupID(hostID, address, port, func() string {
|
||||
return MakeAddressNodeID(hostID, address) + ScopeDelim + port
|
||||
})
|
||||
func MakeEndpointNodeID(hostID, namespaceID, address, port string) string {
|
||||
return makeAddressID(hostID, namespaceID, address) + ScopeDelim + port
|
||||
}
|
||||
|
||||
// MakeAddressNodeID produces an address node ID from its composite parts.
|
||||
func MakeAddressNodeID(hostID, address string) string {
|
||||
return makeAddressID(hostID, "", address)
|
||||
}
|
||||
|
||||
func makeAddressID(hostID, namespaceID, address string) string {
|
||||
var scope string
|
||||
|
||||
// Loopback addresses and addresses explicitly marked as
|
||||
// local get scoped by hostID
|
||||
// Loopback addresses and addresses explicitly marked as local get
|
||||
// scoped by hostID
|
||||
// Loopback addresses are also scoped by the networking
|
||||
// namespace if available, since they can clash.
|
||||
addressIP := net.ParseIP(address)
|
||||
if addressIP != nil && LocalNetworks.Contains(addressIP) {
|
||||
scope = hostID
|
||||
} else if isLoopback(address) {
|
||||
} else if IsLoopback(address) {
|
||||
scope = hostID
|
||||
if namespaceID != "" {
|
||||
scope += "-" + namespaceID
|
||||
}
|
||||
}
|
||||
|
||||
return scope + ScopeDelim + address
|
||||
}
|
||||
|
||||
// MakeScopedEndpointNodeID is like MakeEndpointNodeID, but it always
|
||||
// prefixes the ID witha scope.
|
||||
func MakeScopedEndpointNodeID(hostID, address, port string) string {
|
||||
return hostID + ScopeDelim + address + ScopeDelim + port
|
||||
// prefixes the ID with a scope.
|
||||
func MakeScopedEndpointNodeID(scope, address, port string) string {
|
||||
return scope + ScopeDelim + address + ScopeDelim + port
|
||||
}
|
||||
|
||||
// MakeScopedAddressNodeID is like MakeAddressNodeID, but it always
|
||||
// prefixes the ID witha scope.
|
||||
func MakeScopedAddressNodeID(hostID, address string) string {
|
||||
return hostID + ScopeDelim + address
|
||||
func MakeScopedAddressNodeID(scope, address string) string {
|
||||
return scope + ScopeDelim + address
|
||||
}
|
||||
|
||||
// MakeProcessNodeID produces a process node ID from its composite parts.
|
||||
@@ -174,14 +149,14 @@ func ParseNodeID(nodeID string) (hostID string, remainder string, ok bool) {
|
||||
return fields[0], fields[1], true
|
||||
}
|
||||
|
||||
// ParseEndpointNodeID produces the host ID, address, and port and remainder
|
||||
// (typically an address) from an endpoint node ID. Note that hostID may be
|
||||
// blank.
|
||||
func ParseEndpointNodeID(endpointNodeID string) (hostID, address, port string, ok bool) {
|
||||
// ParseEndpointNodeID produces the scope, address, and port and remainder.
|
||||
// Note that hostID may be blank.
|
||||
func ParseEndpointNodeID(endpointNodeID string) (scope, address, port string, ok bool) {
|
||||
fields := strings.SplitN(endpointNodeID, ScopeDelim, 3)
|
||||
if len(fields) != 3 {
|
||||
return "", "", "", false
|
||||
}
|
||||
|
||||
return fields[0], fields[1], fields[2], true
|
||||
}
|
||||
|
||||
@@ -201,7 +176,8 @@ func ExtractHostID(m Node) string {
|
||||
return hostID
|
||||
}
|
||||
|
||||
func isLoopback(address string) bool {
|
||||
// IsLoopback ascertains if an address comes from a loopback interface.
|
||||
func IsLoopback(address string) bool {
|
||||
ip := net.ParseIP(address)
|
||||
return ip != nil && ip.IsLoopback()
|
||||
}
|
||||
|
||||
@@ -18,12 +18,12 @@ var (
|
||||
unknownHostID = "" // by definition, we don't know it
|
||||
unknownAddress = "172.16.93.112" // will be a pseudonode, no corresponding host
|
||||
|
||||
client54001EndpointNodeID = report.MakeEndpointNodeID(clientHostID, clientAddress, "54001") // i.e. curl
|
||||
client54002EndpointNodeID = report.MakeEndpointNodeID(clientHostID, clientAddress, "54002") // also curl
|
||||
server80EndpointNodeID = report.MakeEndpointNodeID(serverHostID, serverAddress, "80") // i.e. apache
|
||||
unknown1EndpointNodeID = report.MakeEndpointNodeID(unknownHostID, unknownAddress, "10001")
|
||||
unknown2EndpointNodeID = report.MakeEndpointNodeID(unknownHostID, unknownAddress, "10002")
|
||||
unknown3EndpointNodeID = report.MakeEndpointNodeID(unknownHostID, unknownAddress, "10003")
|
||||
client54001EndpointNodeID = report.MakeEndpointNodeID(clientHostID, "", clientAddress, "54001") // i.e. curl
|
||||
client54002EndpointNodeID = report.MakeEndpointNodeID(clientHostID, "", clientAddress, "54002") // also curl
|
||||
server80EndpointNodeID = report.MakeEndpointNodeID(serverHostID, "", serverAddress, "80") // i.e. apache
|
||||
unknown1EndpointNodeID = report.MakeEndpointNodeID(unknownHostID, "", unknownAddress, "10001")
|
||||
unknown2EndpointNodeID = report.MakeEndpointNodeID(unknownHostID, "", unknownAddress, "10002")
|
||||
unknown3EndpointNodeID = report.MakeEndpointNodeID(unknownHostID, "", unknownAddress, "10003")
|
||||
|
||||
clientAddressNodeID = report.MakeAddressNodeID(clientHostID, clientAddress)
|
||||
serverAddressNodeID = report.MakeAddressNodeID(serverHostID, serverAddress)
|
||||
@@ -50,7 +50,8 @@ func TestEndpointNodeID(t *testing.T) {
|
||||
}
|
||||
|
||||
for input, want := range map[string]struct{ name, address, port string }{
|
||||
report.MakeEndpointNodeID("host.com", "1.2.3.4", "c"): {"", "1.2.3.4", "c"},
|
||||
report.MakeEndpointNodeID("host.com", "namespaceid", "127.0.0.1", "c"): {"host.com-namespaceid", "127.0.0.1", "c"},
|
||||
report.MakeEndpointNodeID("host.com", "", "1.2.3.4", "c"): {"", "1.2.3.4", "c"},
|
||||
"a;b;c": {"a", "b", "c"},
|
||||
} {
|
||||
haveName, haveAddress, havePort, ok := report.ParseEndpointNodeID(input)
|
||||
|
||||
@@ -57,15 +57,15 @@ var (
|
||||
ClientHostNodeID = report.MakeHostNodeID(ClientHostID)
|
||||
ServerHostNodeID = report.MakeHostNodeID(ServerHostID)
|
||||
|
||||
Client54001NodeID = report.MakeEndpointNodeID(ClientHostID, ClientIP, ClientPort54001) // curl (1)
|
||||
Client54002NodeID = report.MakeEndpointNodeID(ClientHostID, ClientIP, ClientPort54002) // curl (2)
|
||||
Server80NodeID = report.MakeEndpointNodeID(ServerHostID, ServerIP, ServerPort) // apache
|
||||
UnknownClient1NodeID = report.MakeEndpointNodeID(ServerHostID, UnknownClient1IP, UnknownClient1Port) // we want to ensure two unknown clients, connnected
|
||||
UnknownClient2NodeID = report.MakeEndpointNodeID(ServerHostID, UnknownClient2IP, UnknownClient2Port) // to the same server, are deduped.
|
||||
UnknownClient3NodeID = report.MakeEndpointNodeID(ServerHostID, UnknownClient3IP, UnknownClient3Port) // Check this one isn't deduped
|
||||
RandomClientNodeID = report.MakeEndpointNodeID(ServerHostID, RandomClientIP, RandomClientPort) // this should become an internet node
|
||||
NonContainerNodeID = report.MakeEndpointNodeID(ServerHostID, ServerIP, NonContainerClientPort)
|
||||
GoogleEndpointNodeID = report.MakeEndpointNodeID(ServerHostID, GoogleIP, GooglePort)
|
||||
Client54001NodeID = report.MakeEndpointNodeID(ClientHostID, "", ClientIP, ClientPort54001) // curl (1)
|
||||
Client54002NodeID = report.MakeEndpointNodeID(ClientHostID, "", ClientIP, ClientPort54002) // curl (2)
|
||||
Server80NodeID = report.MakeEndpointNodeID(ServerHostID, "", ServerIP, ServerPort) // apache
|
||||
UnknownClient1NodeID = report.MakeEndpointNodeID(ServerHostID, "", UnknownClient1IP, UnknownClient1Port) // we want to ensure two unknown clients, connnected
|
||||
UnknownClient2NodeID = report.MakeEndpointNodeID(ServerHostID, "", UnknownClient2IP, UnknownClient2Port) // to the same server, are deduped.
|
||||
UnknownClient3NodeID = report.MakeEndpointNodeID(ServerHostID, "", UnknownClient3IP, UnknownClient3Port) // Check this one isn't deduped
|
||||
RandomClientNodeID = report.MakeEndpointNodeID(ServerHostID, "", RandomClientIP, RandomClientPort) // this should become an internet node
|
||||
NonContainerNodeID = report.MakeEndpointNodeID(ServerHostID, "", ServerIP, NonContainerClientPort)
|
||||
GoogleEndpointNodeID = report.MakeEndpointNodeID(ServerHostID, "", GoogleIP, GooglePort)
|
||||
|
||||
ClientProcess1NodeID = report.MakeProcessNodeID(ClientHostID, Client1PID)
|
||||
ClientProcess2NodeID = report.MakeProcessNodeID(ClientHostID, Client2PID)
|
||||
|
||||
Reference in New Issue
Block a user