func TestSpyNoProcesses(t *testing.T) { procspy.SetFixtures(fixConnections) const ( nodeID = "heinz-tomato-ketchup" // TODO rename to hostID nodeName = "frenchs-since-1904" // TODO rename to hostNmae ) reporter := endpoint.NewReporter(nodeID, nodeName, false, false) r, _ := reporter.Report() //buf, _ := json.MarshalIndent(r, "", " ") //t.Logf("\n%s\n", buf) // No process nodes, please if want, have := 0, len(r.Endpoint.Nodes); want != have { t.Fatalf("want %d, have %d", want, have) } var ( scopedLocal = report.MakeAddressNodeID(nodeID, fixLocalAddress.String()) scopedRemote = report.MakeAddressNodeID(nodeID, fixRemoteAddress.String()) ) if want, have := nodeName, r.Address.Nodes[scopedLocal].Metadata[docker.Name]; want != have { t.Fatalf("want %q, have %q", want, have) } if want, have := 1, len(r.Address.Nodes[scopedRemote].Adjacency); want != have { t.Fatalf("want %d, have %d", want, have) } if want, have := scopedLocal, r.Address.Nodes[scopedRemote].Adjacency[0]; want != have { t.Fatalf("want %q, have %q", want, have) } }
func TestMerge(t *testing.T) { var ( hostID = "xyz" src = newMockSource([]byte{}, nil) on = time.Millisecond off = time.Millisecond rpt = report.MakeReport() p = sniff.Packet{ SrcIP: "1.0.0.0", SrcPort: "1000", DstIP: "2.0.0.0", DstPort: "2000", Network: 512, Transport: 256, } _, ipnet, _ = net.ParseCIDR(p.SrcIP + "/24") // ;) localNets = report.Networks([]*net.IPNet{ipnet}) ) sniff.New(hostID, localNets, src, on, off).Merge(p, &rpt) var ( srcEndpointNodeID = report.MakeEndpointNodeID(hostID, p.SrcIP, p.SrcPort) dstEndpointNodeID = report.MakeEndpointNodeID(hostID, p.DstIP, p.DstPort) ) if want, have := (report.Topology{ Nodes: report.Nodes{ srcEndpointNodeID: report.MakeNode().WithEdge(dstEndpointNodeID, report.EdgeMetadata{ EgressPacketCount: newu64(1), EgressByteCount: newu64(256), }), dstEndpointNodeID: report.MakeNode(), }, }), rpt.Endpoint; !reflect.DeepEqual(want, have) { t.Errorf("%s", test.Diff(want, have)) } var ( srcAddressNodeID = report.MakeAddressNodeID(hostID, p.SrcIP) dstAddressNodeID = report.MakeAddressNodeID(hostID, p.DstIP) ) if want, have := (report.Topology{ Nodes: report.Nodes{ srcAddressNodeID: report.MakeNode().WithEdge(dstAddressNodeID, report.EdgeMetadata{ EgressPacketCount: newu64(1), EgressByteCount: newu64(512), }), dstAddressNodeID: report.MakeNode(), }, }), rpt.Address; !reflect.DeepEqual(want, have) { t.Errorf("%s", test.Diff(want, have)) } }
func (rep *reporter) addConnection(r *report.Report, c *procspy.Connection) { var ( scopedLocal = report.MakeAddressNodeID(rep.hostID, c.LocalAddress.String()) scopedRemote = report.MakeAddressNodeID(rep.hostID, c.RemoteAddress.String()) key = report.MakeAdjacencyID(scopedLocal) edgeKey = report.MakeEdgeID(scopedLocal, scopedRemote) ) r.Address.Adjacency[key] = r.Address.Adjacency[key].Add(scopedRemote) if _, ok := r.Address.NodeMetadatas[scopedLocal]; !ok { r.Address.NodeMetadatas[scopedLocal] = report.NodeMetadata{ "name": rep.hostName, "addr": c.LocalAddress.String(), } } // Count the TCP connection. edgeMeta := r.Address.EdgeMetadatas[edgeKey] edgeMeta.WithConnCountTCP = true edgeMeta.MaxConnCountTCP++ r.Address.EdgeMetadatas[edgeKey] = edgeMeta if c.Proc.PID > 0 { var ( scopedLocal = report.MakeEndpointNodeID(rep.hostID, c.LocalAddress.String(), strconv.Itoa(int(c.LocalPort))) scopedRemote = report.MakeEndpointNodeID(rep.hostID, c.RemoteAddress.String(), strconv.Itoa(int(c.RemotePort))) key = report.MakeAdjacencyID(scopedLocal) edgeKey = report.MakeEdgeID(scopedLocal, scopedRemote) ) r.Endpoint.Adjacency[key] = r.Endpoint.Adjacency[key].Add(scopedRemote) if _, ok := r.Endpoint.NodeMetadatas[scopedLocal]; !ok { // First hit establishes NodeMetadata for scoped local address + port md := report.NodeMetadata{ "addr": c.LocalAddress.String(), "port": strconv.Itoa(int(c.LocalPort)), "pid": fmt.Sprintf("%d", c.Proc.PID), } r.Endpoint.NodeMetadatas[scopedLocal] = md } // Count the TCP connection. edgeMeta := r.Endpoint.EdgeMetadatas[edgeKey] edgeMeta.WithConnCountTCP = true edgeMeta.MaxConnCountTCP++ r.Endpoint.EdgeMetadatas[edgeKey] = edgeMeta } }
func addScopeToIPs(hostID string, ips []string) []string { ipsWithScopes := []string{} for _, ip := range ips { ipsWithScopes = append(ipsWithScopes, report.MakeAddressNodeID(hostID, ip)) } return ipsWithScopes }
func TestEdgeID(t *testing.T) { for _, bad := range []string{ client54001EndpointNodeID, client54002EndpointNodeID, unknown1EndpointNodeID, unknown2EndpointNodeID, unknown3EndpointNodeID, clientAddressNodeID, serverAddressNodeID, unknownAddressNodeID, clientHostNodeID, serverHostNodeID, ">1.2.3.4", ">", ";", "", } { if srcNodeID, dstNodeID, ok := report.ParseEdgeID(bad); ok { t.Errorf("%q: expected failure, but got (%q, %q)", bad, srcNodeID, dstNodeID) } } for input, want := range map[string]struct{ srcNodeID, dstNodeID string }{ report.MakeEdgeID("a", report.MakeEndpointNodeID("a", "b", "c")): {"a", report.MakeEndpointNodeID("a", "b", "c")}, report.MakeEdgeID("a", report.MakeAddressNodeID("a", "b")): {"a", report.MakeAddressNodeID("a", "b")}, report.MakeEdgeID("a", report.MakeProcessNodeID("a", "b")): {"a", report.MakeProcessNodeID("a", "b")}, report.MakeEdgeID("a", report.MakeHostNodeID("a")): {"a", report.MakeHostNodeID("a")}, "host.com|1.2.3.4": {"host.com", "1.2.3.4"}, "a|b;c": {"a", "b;c"}, "a|b": {"a", "b"}, "a|": {"a", ""}, "|b": {"", "b"}, "|": {"", ""}, } { srcNodeID, dstNodeID, ok := report.ParseEdgeID(input) if !ok { t.Errorf("%q: not OK", input) continue } if want, have := want.srcNodeID, srcNodeID; want != have { t.Errorf("%q: want %q, have %q", input, want, have) } if want, have := want.dstNodeID, dstNodeID; want != have { t.Errorf("%q: want %q, have %q", input, want, have) } } }
func (r *Reporter) addConnection(rpt *report.Report, c *procspy.Connection) { var ( scopedLocal = report.MakeAddressNodeID(r.hostID, c.LocalAddress.String()) scopedRemote = report.MakeAddressNodeID(r.hostID, c.RemoteAddress.String()) key = report.MakeAdjacencyID(scopedLocal) edgeKey = report.MakeEdgeID(scopedLocal, scopedRemote) ) rpt.Address.Adjacency[key] = rpt.Address.Adjacency[key].Add(scopedRemote) if _, ok := rpt.Address.NodeMetadatas[scopedLocal]; !ok { rpt.Address.NodeMetadatas[scopedLocal] = report.NewNodeMetadata(map[string]string{ docker.Name: r.hostName, docker.Addr: c.LocalAddress.String(), }) } countTCPConnection(rpt.Address.EdgeMetadatas, edgeKey) if c.Proc.PID > 0 { var ( scopedLocal = report.MakeEndpointNodeID(r.hostID, c.LocalAddress.String(), strconv.Itoa(int(c.LocalPort))) scopedRemote = report.MakeEndpointNodeID(r.hostID, c.RemoteAddress.String(), strconv.Itoa(int(c.RemotePort))) key = report.MakeAdjacencyID(scopedLocal) edgeKey = report.MakeEdgeID(scopedLocal, scopedRemote) ) rpt.Endpoint.Adjacency[key] = rpt.Endpoint.Adjacency[key].Add(scopedRemote) if _, ok := rpt.Endpoint.NodeMetadatas[scopedLocal]; !ok { // First hit establishes NodeMetadata for scoped local address + port md := report.NewNodeMetadata(map[string]string{ "addr": c.LocalAddress.String(), "port": strconv.Itoa(int(c.LocalPort)), "pid": fmt.Sprintf("%d", c.Proc.PID), }) rpt.Endpoint.NodeMetadatas[scopedLocal] = md } countTCPConnection(rpt.Endpoint.EdgeMetadatas, edgeKey) } }
func (r *Reporter) addConnection(rpt *report.Report, c *procspy.Connection) { var ( localAddressNodeID = report.MakeAddressNodeID(r.hostID, c.LocalAddress.String()) remoteAddressNodeID = report.MakeAddressNodeID(r.hostID, c.RemoteAddress.String()) adjecencyID = report.MakeAdjacencyID(localAddressNodeID) edgeID = report.MakeEdgeID(localAddressNodeID, remoteAddressNodeID) ) rpt.Address.Adjacency[adjecencyID] = rpt.Address.Adjacency[adjecencyID].Add(remoteAddressNodeID) if _, ok := rpt.Address.NodeMetadatas[localAddressNodeID]; !ok { rpt.Address.NodeMetadatas[localAddressNodeID] = report.MakeNodeMetadataWith(map[string]string{ "name": r.hostName, Addr: c.LocalAddress.String(), }) } countTCPConnection(rpt.Address.EdgeMetadatas, edgeID) if c.Proc.PID > 0 { var ( localEndpointNodeID = report.MakeEndpointNodeID(r.hostID, c.LocalAddress.String(), strconv.Itoa(int(c.LocalPort))) remoteEndpointNodeID = report.MakeEndpointNodeID(r.hostID, c.RemoteAddress.String(), strconv.Itoa(int(c.RemotePort))) adjecencyID = report.MakeAdjacencyID(localEndpointNodeID) edgeID = report.MakeEdgeID(localEndpointNodeID, remoteEndpointNodeID) ) rpt.Endpoint.Adjacency[adjecencyID] = rpt.Endpoint.Adjacency[adjecencyID].Add(remoteEndpointNodeID) if _, ok := rpt.Endpoint.NodeMetadatas[localEndpointNodeID]; !ok { // First hit establishes NodeMetadata for scoped local address + port md := report.MakeNodeMetadataWith(map[string]string{ Addr: c.LocalAddress.String(), Port: strconv.Itoa(int(c.LocalPort)), process.PID: fmt.Sprint(c.Proc.PID), }) rpt.Endpoint.NodeMetadatas[localEndpointNodeID] = md } countTCPConnection(rpt.Endpoint.EdgeMetadatas, edgeID) } }
func TestCollector(t *testing.T) { log.SetOutput(ioutil.Discard) // Swap out ticker publish := make(chan time.Time) oldTick := tick tick = func(time.Duration) <-chan time.Time { return publish } defer func() { tick = oldTick }() // Build a collector collector := NewCollector(time.Second, "id") defer collector.Stop() concreteCollector, ok := collector.(*realCollector) if !ok { t.Fatal("type assertion failure") } // Build a test publisher reports := make(chan interface{}) ln := testPublisher(t, reports) defer ln.Close() // Connect the collector to the test publisher addr := ln.Addr().String() collector.Add(addr) collector.Add(addr) // test duplicate case runtime.Gosched() // make sure it connects // Push a report through everything r := report.Report{ Address: report.Topology{ NodeMetadatas: report.NodeMetadatas{ report.MakeAddressNodeID("a", "b"): report.NodeMetadata{}, }, }, } reports <- r poll(t, 100*time.Millisecond, func() bool { return len(concreteCollector.peek().Address.NodeMetadatas) == 1 }, "missed the report") go func() { publish <- time.Now() }() collected := <-collector.Reports() if reflect.DeepEqual(r, collected) { t.Errorf(test.Diff(r, collected)) } collector.Remove(addr) collector.Remove(addr) // test duplicate case }
func TestAddressIDAddresser(t *testing.T) { if nodeID := "1.2.4.5"; report.AddressIDAddresser(nodeID) != nil { t.Errorf("%q: bad node ID parsed as good", nodeID) } var ( nodeID = report.MakeAddressNodeID(clientHostID, clientAddress) want = net.ParseIP(clientAddress) have = report.AddressIDAddresser(nodeID) ) if !reflect.DeepEqual(want, have) { t.Errorf("want %s, have %s", want, have) } }
// Tag implements Tagger. func (w *Weave) Tag(r report.Report) (report.Report, error) { w.mtx.RLock() defer w.mtx.RUnlock() // Put information from weaveDNS on the container nodes for _, entry := range w.statusCache.DNS.Entries { if entry.Tombstone > 0 { continue } nodeID := report.MakeContainerNodeID(entry.ContainerID) node, ok := r.Container.Nodes[nodeID] if !ok { continue } w, _ := node.Latest.Lookup(WeaveDNSHostname) hostnames := report.IDList(strings.Fields(w)) hostnames = hostnames.Add(strings.TrimSuffix(entry.Hostname, ".")) r.Container.Nodes[nodeID] = node.WithLatests(map[string]string{WeaveDNSHostname: strings.Join(hostnames, " ")}) } // Put information from weave ps on the container nodes const maxPrefixSize = 12 for id, node := range r.Container.Nodes { prefix, ok := node.Latest.Lookup(docker.ContainerID) if !ok { continue } if len(prefix) > maxPrefixSize { prefix = prefix[:maxPrefixSize] } entry, ok := w.psCache[prefix] if !ok { continue } ipsWithScope := report.MakeStringSet() for _, ip := range entry.IPs { ipsWithScope = ipsWithScope.Add(report.MakeAddressNodeID("", ip)) } node = node.WithSet(docker.ContainerIPs, report.MakeStringSet(entry.IPs...)) node = node.WithSet(docker.ContainerIPsWithScopes, ipsWithScope) node = node.WithLatests(map[string]string{ WeaveMACAddress: entry.MACAddress, }) r.Container.Nodes[id] = node } return r, nil }
// Tag implements Tagger. func (w *Weave) Tag(r report.Report) (report.Report, error) { w.mtx.RLock() defer w.mtx.RUnlock() // Put information from weaveDNS on the container nodes for _, entry := range w.status.DNS.Entries { if entry.Tombstone > 0 { continue } nodeID := report.MakeContainerNodeID(w.hostID, entry.ContainerID) node, ok := r.Container.Nodes[nodeID] if !ok { continue } hostnames := report.IDList(strings.Fields(node.Metadata[WeaveDNSHostname])) hostnames = hostnames.Add(strings.TrimSuffix(entry.Hostname, ".")) node.Metadata[WeaveDNSHostname] = strings.Join(hostnames, " ") } // Put information from weave ps on the container nodes psEntries, err := w.ps() if err != nil { return r, nil } psEntriesByPrefix := map[string]psEntry{} for _, entry := range psEntries { psEntriesByPrefix[entry.containerIDPrefix] = entry } for id, node := range r.Container.Nodes { prefix := node.Metadata[docker.ContainerID][:12] entry, ok := psEntriesByPrefix[prefix] if !ok { continue } ipsWithScope := report.MakeStringSet() for _, ip := range entry.ips { ipsWithScope = ipsWithScope.Add(report.MakeAddressNodeID("", ip)) } node = node.WithSet(docker.ContainerIPs, report.MakeStringSet(entry.ips...)) node = node.WithSet(docker.ContainerIPsWithScopes, ipsWithScope) node.Metadata[WeaveMACAddress] = entry.macAddress r.Container.Nodes[id] = node } return r, nil }
ServerProcessNodeID = report.MakeProcessNodeID(ServerHostID, ServerPID) NonContainerProcessNodeID = report.MakeProcessNodeID(ServerHostID, NonContainerPID) ClientContainerID = "a1b2c3d4e5" ServerContainerID = "5e4d3c2b1a" ClientContainerNodeID = report.MakeContainerNodeID(ClientHostID, ClientContainerID) ServerContainerNodeID = report.MakeContainerNodeID(ServerHostID, ServerContainerID) ClientContainerImageID = "imageid123" ServerContainerImageID = "imageid456" ClientContainerImageNodeID = report.MakeContainerNodeID(ClientHostID, ClientContainerImageID) ServerContainerImageNodeID = report.MakeContainerNodeID(ServerHostID, ServerContainerImageID) ClientContainerImageName = "image/client" ServerContainerImageName = "image/server" ClientAddressNodeID = report.MakeAddressNodeID(ClientHostID, ClientIP) ServerAddressNodeID = report.MakeAddressNodeID(ServerHostID, ServerIP) UnknownAddress1NodeID = report.MakeAddressNodeID(ServerHostID, UnknownClient1IP) UnknownAddress2NodeID = report.MakeAddressNodeID(ServerHostID, UnknownClient2IP) UnknownAddress3NodeID = report.MakeAddressNodeID(ServerHostID, UnknownClient3IP) RandomAddressNodeID = report.MakeAddressNodeID(ServerHostID, RandomClientIP) // this should become an internet node Report = report.Report{ Endpoint: report.Topology{ Nodes: report.Nodes{ // Node is arbitrary. We're free to put only precisely what we // care to test into the fixture. Just be sure to include the bits // that the mapping funcs extract :) Client54001NodeID: report.MakeNode().WithMetadata(map[string]string{ endpoint.Addr: ClientIP, endpoint.Port: ClientPort54001,
ClientProcessNodeID = report.MakeProcessNodeID(ClientHostID, ClientPID) ServerProcessNodeID = report.MakeProcessNodeID(ServerHostID, ServerPID) NonContainerProcessNodeID = report.MakeProcessNodeID(ServerHostID, NonContainerPID) ClientContainerID = "a1b2c3d4e5" ServerContainerID = "5e4d3c2b1a" ClientContainerNodeID = report.MakeContainerNodeID(ClientHostID, ClientContainerID) ServerContainerNodeID = report.MakeContainerNodeID(ServerHostID, ServerContainerID) ClientContainerImageID = "imageid123" ServerContainerImageID = "imageid456" ClientContainerImageNodeID = report.MakeContainerNodeID(ClientHostID, ClientContainerImageID) ServerContainerImageNodeID = report.MakeContainerNodeID(ServerHostID, ServerContainerImageID) ClientAddressNodeID = report.MakeAddressNodeID(ClientHostID, "10.10.10.20") ServerAddressNodeID = report.MakeAddressNodeID(ServerHostID, "192.168.1.1") UnknownAddress1NodeID = report.MakeAddressNodeID(ServerHostID, "10.10.10.10") UnknownAddress2NodeID = report.MakeAddressNodeID(ServerHostID, "10.10.10.11") RandomAddressNodeID = report.MakeAddressNodeID(ServerHostID, "51.52.53.54") // this should become an internet node Report = report.Report{ Endpoint: report.Topology{ Adjacency: report.Adjacency{ report.MakeAdjacencyID(Client54001NodeID): report.MakeIDList(Server80NodeID), report.MakeAdjacencyID(Client54002NodeID): report.MakeIDList(Server80NodeID), report.MakeAdjacencyID(Server80NodeID): report.MakeIDList( Client54001NodeID, Client54002NodeID, UnknownClient1NodeID, UnknownClient2NodeID, UnknownClient3NodeID, RandomClientNodeID), }, NodeMetadatas: report.NodeMetadatas{
func (r *Reporter) addConnection(rpt *report.Report, localAddr, remoteAddr string, localPort, remotePort uint16, extraLocalNode, extraRemoteNode *report.Node) { localIsClient := int(localPort) > int(remotePort) // Update address topology { var ( localAddressNodeID = report.MakeAddressNodeID(r.hostID, localAddr) remoteAddressNodeID = report.MakeAddressNodeID(r.hostID, remoteAddr) localNode = report.MakeNodeWith(map[string]string{ "name": r.hostName, Addr: localAddr, }) remoteNode = report.MakeNodeWith(map[string]string{ Addr: remoteAddr, }) ) // In case we have a reverse resolution for the IP, we can use it for // the name... if revRemoteName, err := r.revResolver.Get(remoteAddr); err == nil { remoteNode = remoteNode.WithMetadata(map[string]string{ "name": revRemoteName, }) } if localIsClient { // New nodes are merged into the report so we don't need to do any // counting here; the merge does it for us. localNode = localNode.WithEdge(remoteAddressNodeID, report.EdgeMetadata{ MaxConnCountTCP: newu64(1), }) } else { remoteNode = localNode.WithEdge(localAddressNodeID, report.EdgeMetadata{ MaxConnCountTCP: newu64(1), }) } if extraLocalNode != nil { localNode = localNode.Merge(*extraLocalNode) } if extraRemoteNode != nil { remoteNode = remoteNode.Merge(*extraRemoteNode) } rpt.Address = rpt.Address.AddNode(localAddressNodeID, localNode) rpt.Address = rpt.Address.AddNode(remoteAddressNodeID, remoteNode) } // Update endpoint topology if r.includeProcesses { var ( localEndpointNodeID = report.MakeEndpointNodeID(r.hostID, localAddr, strconv.Itoa(int(localPort))) remoteEndpointNodeID = report.MakeEndpointNodeID(r.hostID, remoteAddr, strconv.Itoa(int(remotePort))) localNode = report.MakeNodeWith(map[string]string{ Addr: localAddr, Port: strconv.Itoa(int(localPort)), }) remoteNode = report.MakeNodeWith(map[string]string{ Addr: remoteAddr, Port: strconv.Itoa(int(remotePort)), }) ) // In case we have a reverse resolution for the IP, we can use it for // the name... if revRemoteName, err := r.revResolver.Get(remoteAddr); err == nil { remoteNode = remoteNode.WithMetadata(map[string]string{ "name": revRemoteName, }) } if localIsClient { // New nodes are merged into the report so we don't need to do any // counting here; the merge does it for us. localNode = localNode.WithEdge(remoteEndpointNodeID, report.EdgeMetadata{ MaxConnCountTCP: newu64(1), }) } else { remoteNode = remoteNode.WithEdge(localEndpointNodeID, report.EdgeMetadata{ MaxConnCountTCP: newu64(1), }) } if extraLocalNode != nil { localNode = localNode.Merge(*extraLocalNode) } if extraRemoteNode != nil { remoteNode = remoteNode.Merge(*extraRemoteNode) } rpt.Endpoint = rpt.Endpoint.AddNode(localEndpointNodeID, localNode) rpt.Endpoint = rpt.Endpoint.AddNode(remoteEndpointNodeID, remoteNode) } }
// DemoReport makes up a report. func DemoReport(nodeCount int) report.Report { r := report.MakeReport() // Make up some plausible IPv4 numbers hosts := []string{} ip := [4]int{192, 168, 1, 1} for range make([]struct{}, nodeCount) { hosts = append(hosts, fmt.Sprintf("%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3])) ip[3]++ if ip[3] > 200 { ip[2]++ ip[3] = 1 } } // Some non-local ones. hosts = append(hosts, []string{"1.2.3.4", "2.3.4.5"}...) _, localNet, err := net.ParseCIDR("192.168.0.0/16") if err != nil { panic(err) } type conn struct { srcProc, dstProc string dstPort int } procPool := []conn{ {srcProc: "curl", dstPort: 80, dstProc: "apache"}, {srcProc: "wget", dstPort: 80, dstProc: "apache"}, {srcProc: "curl", dstPort: 80, dstProc: "nginx"}, {srcProc: "curl", dstPort: 8080, dstProc: "app1"}, {srcProc: "nginx", dstPort: 8080, dstProc: "app1"}, {srcProc: "nginx", dstPort: 8080, dstProc: "app2"}, {srcProc: "nginx", dstPort: 8080, dstProc: "app3"}, } connectionCount := nodeCount * 8 for i := 0; i < connectionCount; i++ { var ( c = procPool[rand.Intn(len(procPool))] src = hosts[rand.Intn(len(hosts))] dst = hosts[rand.Intn(len(hosts))] srcPort = rand.Intn(50000) + 10000 srcPortID = report.MakeEndpointNodeID("", src, strconv.Itoa(srcPort)) dstPortID = report.MakeEndpointNodeID("", dst, strconv.Itoa(c.dstPort)) srcAddressID = report.MakeAddressNodeID("", src) dstAddressID = report.MakeAddressNodeID("", dst) ) // Endpoint topology r.Endpoint = r.Endpoint.WithNode(srcPortID, report.MakeNode().WithMetadata(map[string]string{ "pid": "4000", "name": c.srcProc, "domain": "node-" + src, }).WithEdge(dstPortID, report.EdgeMetadata{ MaxConnCountTCP: newu64(uint64(rand.Intn(100) + 10)), })) r.Endpoint = r.Endpoint.WithNode(dstPortID, report.MakeNode().WithMetadata(map[string]string{ "pid": "4000", "name": c.dstProc, "domain": "node-" + dst, }).WithEdge(srcPortID, report.EdgeMetadata{ MaxConnCountTCP: newu64(uint64(rand.Intn(100) + 10)), })) // Address topology r.Address = r.Address.WithNode(srcAddressID, report.MakeNode().WithMetadata(map[string]string{ "name": src, }).WithAdjacent(dstAddressID)) r.Address = r.Address.WithNode(dstAddressID, report.MakeNode().WithMetadata(map[string]string{ "name": dst, }).WithAdjacent(srcAddressID)) // Host data r.Host = r.Host.WithNode("hostX", report.MakeNodeWith(map[string]string{ "ts": time.Now().UTC().Format(time.RFC3339Nano), "host_name": "host-x", "local_networks": localNet.String(), "os": "linux", })) } return r }
endpoint.Port: duplicatedPort, endpoint.Conntracked: "true", }). WithTopology(report.Endpoint), }, }, Container: report.Topology{ Nodes: report.Nodes{ container1NodeID: report.MakeNodeWith(container1NodeID, map[string]string{ docker.ContainerID: container1ID, docker.ContainerName: container1Name, report.HostNodeID: serverHostNodeID, }). WithSets(report.EmptySets. Add(docker.ContainerIPs, report.MakeStringSet(container1IP)). Add(docker.ContainerIPsWithScopes, report.MakeStringSet(report.MakeAddressNodeID("", container1IP))). Add(docker.ContainerPorts, report.MakeStringSet(fmt.Sprintf("%s:%s->%s/tcp", serverIP, serverPort, serverPort))), ).WithTopology(report.Container), container2NodeID: report.MakeNodeWith(container2NodeID, map[string]string{ docker.ContainerID: container2ID, docker.ContainerName: container2Name, report.HostNodeID: serverHostNodeID, }). WithSets(report.EmptySets. Add(docker.ContainerIPs, report.MakeStringSet(container2IP)). Add(docker.ContainerIPsWithScopes, report.MakeStringSet(report.MakeAddressNodeID("", container2IP))), ).WithTopology(report.Container), pauseContainerNodeID: report.MakeNodeWith(pauseContainerNodeID, map[string]string{ docker.ContainerID: pauseContainerID, docker.ContainerName: pauseContainerName, report.HostNodeID: serverHostNodeID,
// DemoReport makes up a report. func DemoReport(nodeCount int) report.Report { r := report.MakeReport() // Make up some plausible IPv4 numbers hosts := []string{} ip := [4]int{192, 168, 1, 1} for range make([]struct{}, nodeCount) { hosts = append(hosts, fmt.Sprintf("%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3])) ip[3]++ if ip[3] > 200 { ip[2]++ ip[3] = 1 } } // Some non-local ones. hosts = append(hosts, []string{"1.2.3.4", "2.3.4.5"}...) _, localNet, err := net.ParseCIDR("192.168.0.0/16") if err != nil { panic(err) } type conn struct { srcProc, dstProc string dstPort int } procPool := []conn{ {srcProc: "curl", dstPort: 80, dstProc: "apache"}, {srcProc: "wget", dstPort: 80, dstProc: "apache"}, {srcProc: "curl", dstPort: 80, dstProc: "nginx"}, {srcProc: "curl", dstPort: 8080, dstProc: "app1"}, {srcProc: "nginx", dstPort: 8080, dstProc: "app1"}, {srcProc: "nginx", dstPort: 8080, dstProc: "app2"}, {srcProc: "nginx", dstPort: 8080, dstProc: "app3"}, } connectionCount := nodeCount * 2 for i := 0; i < connectionCount; i++ { var ( c = procPool[rand.Intn(len(procPool))] src = hosts[rand.Intn(len(hosts))] dst = hosts[rand.Intn(len(hosts))] srcPort = rand.Intn(50000) + 10000 srcPortID = report.MakeEndpointNodeID("", src, strconv.Itoa(srcPort)) dstPortID = report.MakeEndpointNodeID("", dst, strconv.Itoa(c.dstPort)) srcID = report.MakeAdjacencyID(srcPortID) dstID = report.MakeAdjacencyID(dstPortID) srcAddressID = report.MakeAddressNodeID("", src) dstAddressID = report.MakeAddressNodeID("", dst) nodeSrcAddressID = report.MakeAdjacencyID(srcAddressID) nodeDstAddressID = report.MakeAdjacencyID(dstAddressID) ) // Endpoint topology if _, ok := r.Endpoint.NodeMetadatas[srcPortID]; !ok { r.Endpoint.NodeMetadatas[srcPortID] = report.NewNodeMetadata(map[string]string{ docker.PID: "4000", docker.Name: c.srcProc, docker.Domain: "node-" + src, }) } r.Endpoint.Adjacency[srcID] = r.Endpoint.Adjacency[srcID].Add(dstPortID) if _, ok := r.Endpoint.NodeMetadatas[dstPortID]; !ok { r.Endpoint.NodeMetadatas[dstPortID] = report.NewNodeMetadata(map[string]string{ docker.PID: "4000", docker.Name: c.dstProc, docker.Domain: "node-" + dst, }) } r.Endpoint.Adjacency[dstID] = r.Endpoint.Adjacency[dstID].Add(srcPortID) var ( edgeKeyEgress = report.MakeEdgeID(srcPortID, dstPortID) edgeKeyIngress = report.MakeEdgeID(dstPortID, srcPortID) ) r.Endpoint.EdgeMetadatas[edgeKeyEgress] = report.EdgeMetadata{ WithConnCountTCP: true, MaxConnCountTCP: uint(rand.Intn(100) + 10), } r.Endpoint.EdgeMetadatas[edgeKeyIngress] = report.EdgeMetadata{ WithConnCountTCP: true, MaxConnCountTCP: uint(rand.Intn(100) + 10), } // Address topology if _, ok := r.Address.NodeMetadatas[srcAddressID]; !ok { r.Address.NodeMetadatas[srcAddressID] = report.NewNodeMetadata(map[string]string{ docker.Name: src, }) } r.Address.Adjacency[nodeSrcAddressID] = r.Address.Adjacency[nodeSrcAddressID].Add(dstAddressID) if _, ok := r.Address.NodeMetadatas[dstAddressID]; !ok { r.Address.NodeMetadatas[dstAddressID] = report.NewNodeMetadata(map[string]string{ docker.Name: dst, }) } r.Address.Adjacency[nodeDstAddressID] = r.Address.Adjacency[nodeDstAddressID].Add(srcAddressID) // Host data r.Host.NodeMetadatas["hostX"] = report.NewNodeMetadata(map[string]string{ "ts": time.Now().UTC().Format(time.RFC3339Nano), "host_name": "host-x", "local_networks": localNet.String(), "os": "linux", }) } return r }
clientAddress = "10.10.10.20" serverHostID = "server.host.com" serverHostName = serverHostID serverHostNodeID = report.MakeHostNodeID(serverHostID) serverAddress = "10.10.10.1" 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") clientAddressNodeID = report.MakeAddressNodeID(clientHostID, clientAddress) serverAddressNodeID = report.MakeAddressNodeID(serverHostID, serverAddress) unknownAddressNodeID = report.MakeAddressNodeID(unknownHostID, unknownAddress) ) func TestAdjacencyID(t *testing.T) { for _, bad := range []string{ client54001EndpointNodeID, client54002EndpointNodeID, unknown1EndpointNodeID, unknown2EndpointNodeID, unknown3EndpointNodeID, clientAddressNodeID, serverAddressNodeID, unknownAddressNodeID, clientHostNodeID,
func (r *Reporter) addConnection(rpt *report.Report, localAddr, remoteAddr string, localPort, remotePort uint16, proc *procspy.Proc) { localIsClient := int(localPort) > int(remotePort) // Update address topology { var ( localAddressNodeID = report.MakeAddressNodeID(r.hostID, localAddr) remoteAddressNodeID = report.MakeAddressNodeID(r.hostID, remoteAddr) adjacencyID = "" edgeID = "" ) if localIsClient { adjacencyID = report.MakeAdjacencyID(localAddressNodeID) rpt.Address.Adjacency[adjacencyID] = rpt.Address.Adjacency[adjacencyID].Add(remoteAddressNodeID) edgeID = report.MakeEdgeID(localAddressNodeID, remoteAddressNodeID) } else { adjacencyID = report.MakeAdjacencyID(remoteAddressNodeID) rpt.Address.Adjacency[adjacencyID] = rpt.Address.Adjacency[adjacencyID].Add(localAddressNodeID) edgeID = report.MakeEdgeID(remoteAddressNodeID, localAddressNodeID) } countTCPConnection(rpt.Address.EdgeMetadatas, edgeID) if _, ok := rpt.Address.NodeMetadatas[localAddressNodeID]; !ok { rpt.Address.NodeMetadatas[localAddressNodeID] = report.MakeNodeMetadataWith(map[string]string{ "name": r.hostName, Addr: localAddr, }) } } // Update endpoint topology if r.includeProcesses { var ( localEndpointNodeID = report.MakeEndpointNodeID(r.hostID, localAddr, strconv.Itoa(int(localPort))) remoteEndpointNodeID = report.MakeEndpointNodeID(r.hostID, remoteAddr, strconv.Itoa(int(remotePort))) adjacencyID = "" edgeID = "" ) if localIsClient { adjacencyID = report.MakeAdjacencyID(localEndpointNodeID) rpt.Endpoint.Adjacency[adjacencyID] = rpt.Endpoint.Adjacency[adjacencyID].Add(remoteEndpointNodeID) edgeID = report.MakeEdgeID(localEndpointNodeID, remoteEndpointNodeID) } else { adjacencyID = report.MakeAdjacencyID(remoteEndpointNodeID) rpt.Endpoint.Adjacency[adjacencyID] = rpt.Endpoint.Adjacency[adjacencyID].Add(localEndpointNodeID) edgeID = report.MakeEdgeID(remoteEndpointNodeID, localEndpointNodeID) } countTCPConnection(rpt.Endpoint.EdgeMetadatas, edgeID) md, ok := rpt.Endpoint.NodeMetadatas[localEndpointNodeID] updated := !ok if !ok { md = report.MakeNodeMetadataWith(map[string]string{ Addr: localAddr, Port: strconv.Itoa(int(localPort)), }) } if proc != nil && proc.PID > 0 { pid := strconv.FormatUint(uint64(proc.PID), 10) updated = updated || md.Metadata[process.PID] != pid md.Metadata[process.PID] = pid } if updated { rpt.Endpoint.NodeMetadatas[localEndpointNodeID] = md } } }
// Merge puts the packet into the report. // // Note that, for the moment, we encode bidirectional traffic as ingress and // egress traffic on a single edge whose src is local and dst is remote. That // is, if we see a packet from the remote addr 9.8.7.6 to the local addr // 1.2.3.4, we apply it as *ingress* on the edge (1.2.3.4 -> 9.8.7.6). func (s *Sniffer) Merge(p Packet, rpt *report.Report) { if p.SrcIP == "" || p.DstIP == "" { return } // One end of the traffic has to be local. Otherwise, we don't know how to // construct the edge. // // If we need to get around this limitation, we may be able to change the // semantics of the report, and allow the src side of edges to be from // anywhere. But that will have ramifications throughout Scope (read: it // may violate implicit invariants) and needs to be thought through. var ( srcLocal = s.localNets.Contains(net.ParseIP(p.SrcIP)) dstLocal = s.localNets.Contains(net.ParseIP(p.DstIP)) localIP string remoteIP string localPort string remotePort string egress bool ) switch { case srcLocal && !dstLocal: localIP, localPort, remoteIP, remotePort, egress = p.SrcIP, p.SrcPort, p.DstIP, p.DstPort, true case !srcLocal && dstLocal: localIP, localPort, remoteIP, remotePort, egress = p.DstIP, p.DstPort, p.SrcIP, p.SrcPort, false case srcLocal && dstLocal: localIP, localPort, remoteIP, remotePort, egress = p.SrcIP, p.SrcPort, p.DstIP, p.DstPort, true // loopback case !srcLocal && !dstLocal: log.Printf("sniffer ignoring remote-to-remote (%s -> %s) traffic", p.SrcIP, p.DstIP) return } addAdjacency := func(t report.Topology, srcNodeID, dstNodeID string) report.Topology { result := t.WithNode(srcNodeID, report.MakeNode().WithAdjacent(dstNodeID)) result = result.WithNode(dstNodeID, report.MakeNode()) return result } // For sure, we can add to the address topology. { var ( srcNodeID = report.MakeAddressNodeID(s.hostID, localIP) dstNodeID = report.MakeAddressNodeID(s.hostID, remoteIP) ) rpt.Address = addAdjacency(rpt.Address, srcNodeID, dstNodeID) emd := rpt.Address.Nodes[srcNodeID].Edges[dstNodeID] if egress { if emd.EgressPacketCount == nil { emd.EgressPacketCount = new(uint64) } *emd.EgressPacketCount++ if emd.EgressByteCount == nil { emd.EgressByteCount = new(uint64) } *emd.EgressByteCount += uint64(p.Network) } else { if emd.IngressPacketCount == nil { emd.IngressPacketCount = new(uint64) } *emd.IngressPacketCount++ if emd.IngressByteCount == nil { emd.IngressByteCount = new(uint64) } *emd.IngressByteCount += uint64(p.Network) } rpt.Address.Nodes[srcNodeID].Edges[dstNodeID] = emd } // If we have ports, we can add to the endpoint topology, too. if p.SrcPort != "" && p.DstPort != "" { var ( srcNodeID = report.MakeEndpointNodeID(s.hostID, localIP, localPort) dstNodeID = report.MakeEndpointNodeID(s.hostID, remoteIP, remotePort) ) rpt.Endpoint = addAdjacency(rpt.Endpoint, srcNodeID, dstNodeID) emd := rpt.Endpoint.Nodes[srcNodeID].Edges[dstNodeID] if egress { if emd.EgressPacketCount == nil { emd.EgressPacketCount = new(uint64) } *emd.EgressPacketCount++ if emd.EgressByteCount == nil { emd.EgressByteCount = new(uint64) } *emd.EgressByteCount += uint64(p.Transport) } else { if emd.IngressPacketCount == nil { emd.IngressPacketCount = new(uint64) } *emd.IngressPacketCount++ if emd.IngressByteCount == nil { emd.IngressByteCount = new(uint64) } *emd.IngressByteCount += uint64(p.Transport) } rpt.Endpoint.Nodes[srcNodeID].Edges[dstNodeID] = emd } }
func (r *Reporter) addConnection(rpt *report.Report, localAddr, remoteAddr string, localPort, remotePort uint16, proc *procspy.Proc) { var ( localIsClient = int(localPort) > int(remotePort) hostNodeID = report.MakeHostNodeID(r.hostID) ) // Update address topology { var ( localAddressNodeID = report.MakeAddressNodeID(r.hostID, localAddr) remoteAddressNodeID = report.MakeAddressNodeID(r.hostID, remoteAddr) localNode = report.MakeNodeWith(map[string]string{ "name": r.hostName, Addr: localAddr, report.HostNodeID: hostNodeID, }) remoteNode = report.MakeNodeWith(map[string]string{ Addr: remoteAddr, }) ) if localIsClient { // New nodes are merged into the report so we don't need to do any counting here; the merge does it for us. localNode = localNode.WithEdge(remoteAddressNodeID, report.EdgeMetadata{ MaxConnCountTCP: newu64(1), }) } else { remoteNode = localNode.WithEdge(localAddressNodeID, report.EdgeMetadata{ MaxConnCountTCP: newu64(1), }) } rpt.Address = rpt.Address.WithNode(localAddressNodeID, localNode) rpt.Address = rpt.Address.WithNode(remoteAddressNodeID, remoteNode) } // Update endpoint topology if r.includeProcesses { var ( localEndpointNodeID = report.MakeEndpointNodeID(r.hostID, localAddr, strconv.Itoa(int(localPort))) remoteEndpointNodeID = report.MakeEndpointNodeID(r.hostID, remoteAddr, strconv.Itoa(int(remotePort))) localNode = report.MakeNodeWith(map[string]string{ Addr: localAddr, Port: strconv.Itoa(int(localPort)), report.HostNodeID: hostNodeID, }) remoteNode = report.MakeNodeWith(map[string]string{ Addr: remoteAddr, Port: strconv.Itoa(int(remotePort)), }) ) if localIsClient { // New nodes are merged into the report so we don't need to do any counting here; the merge does it for us. localNode = localNode.WithEdge(remoteEndpointNodeID, report.EdgeMetadata{ MaxConnCountTCP: newu64(1), }) } else { remoteNode = remoteNode.WithEdge(localEndpointNodeID, report.EdgeMetadata{ MaxConnCountTCP: newu64(1), }) } if proc != nil && proc.PID > 0 { localNode.Metadata[process.PID] = strconv.FormatUint(uint64(proc.PID), 10) } rpt.Endpoint = rpt.Endpoint.WithNode(localEndpointNodeID, localNode) rpt.Endpoint = rpt.Endpoint.WithNode(remoteEndpointNodeID, remoteNode) } }