Exemplo n.º 1
0
func TestEndpointNodeID(t *testing.T) {
	for _, bad := range []string{
		clientAddressNodeID,
		serverAddressNodeID,
		unknownAddressNodeID,
		clientHostNodeID,
		serverHostNodeID,
		"host.com;1.2.3.4",
		"a;b",
		"a;",
		";b",
		";",
		"",
	} {
		if haveName, haveAddress, havePort, ok := report.ParseEndpointNodeID(bad); ok {
			t.Errorf("%q: expected failure, but got {%q, %q, %q}", bad, haveName, haveAddress, havePort)
		}
	}

	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"},
		"a;b;c": {"a", "b", "c"},
	} {
		haveName, haveAddress, havePort, ok := report.ParseEndpointNodeID(input)
		if !ok {
			t.Errorf("%q: not OK", input)
			continue
		}
		if want.name != haveName ||
			want.address != haveAddress ||
			want.port != havePort {
			t.Errorf("%q: want %q, have {%q, %q, %q}", input, want, haveName, haveAddress, havePort)
		}
	}
}
Exemplo n.º 2
0
// MapEndpoint2IP maps endpoint nodes to their IP address, for joining
// with container nodes.  We drop endpoint nodes with pids, as they
// will be joined to containers through the process topology, and we
// don't want to double count edges.
func MapEndpoint2IP(m RenderableNode, local report.Networks) RenderableNodes {
	// Don't include procspied connections, to prevent double counting
	_, ok := m.Latest.Lookup(endpoint.Procspied)
	if ok {
		return RenderableNodes{}
	}
	scope, addr, port, ok := report.ParseEndpointNodeID(m.ID)
	if !ok {
		return RenderableNodes{}
	}
	if ip := net.ParseIP(addr); ip != nil && !local.Contains(ip) {
		return RenderableNodes{TheInternetID: newDerivedPseudoNode(TheInternetID, TheInternetMajor, m)}
	}

	// We don't always know what port a container is listening on, and
	// container-to-container communications can be unambiguously identified
	// without ports. OTOH, connections to the host IPs which have been port
	// mapped to a container can only be unambiguously identified with the port.
	// So we need to emit two nodes, for two different cases.
	id := report.MakeScopedEndpointNodeID(scope, addr, "")
	idWithPort := report.MakeScopedEndpointNodeID(scope, addr, port)
	m = m.WithParents(report.EmptySets)
	return RenderableNodes{
		id:         NewRenderableNodeWith(id, "", "", "", m),
		idWithPort: NewRenderableNodeWith(idWithPort, "", "", "", m),
	}
}
Exemplo n.º 3
0
func connectionDetailsRows(topology report.Topology, originID string) []Row {
	rows := []Row{}
	labeler := func(nodeID string, meta map[string]string) (string, bool) {
		if _, addr, port, ok := report.ParseEndpointNodeID(nodeID); ok {
			if name, ok := meta["name"]; ok {
				return fmt.Sprintf("%s:%s", name, port), true
			}
			return fmt.Sprintf("%s:%s", addr, port), true
		}
		if _, addr, ok := report.ParseAddressNodeID(nodeID); ok {
			return addr, true
		}
		return "", false
	}
	local, ok := labeler(originID, topology.Nodes[originID].Metadata)
	if !ok {
		return rows
	}
	// Firstly, collection outgoing connections from this node.
	for _, serverNodeID := range topology.Nodes[originID].Adjacency {
		remote, ok := labeler(serverNodeID, topology.Nodes[serverNodeID].Metadata)
		if !ok {
			continue
		}
		rows = append(rows, Row{
			Key:        local,
			ValueMajor: remote,
			Expandable: true,
		})
	}
	// Next, scan the topology for incoming connections to this node.
	for clientNodeID, clientNode := range topology.Nodes {
		if clientNodeID == originID {
			continue
		}
		serverNodeIDs := clientNode.Adjacency
		if !serverNodeIDs.Contains(originID) {
			continue
		}
		remote, ok := labeler(clientNodeID, clientNode.Metadata)
		if !ok {
			continue
		}
		rows = append(rows, Row{
			Key:        remote,
			ValueMajor: local,
			ValueMinor: "",
			Expandable: true,
		})
	}
	return rows
}
Exemplo n.º 4
0
func connectionDetailsRows(topology report.Topology, originID string) []Row {
	rows := []Row{}
	labeler := func(nodeID string) (string, bool) {
		if _, addr, port, ok := report.ParseEndpointNodeID(nodeID); ok {
			return fmt.Sprintf("%s:%s", addr, port), true
		}
		if _, addr, ok := report.ParseAddressNodeID(nodeID); ok {
			return addr, true
		}
		return "", false
	}
	local, ok := labeler(originID)
	if !ok {
		return rows
	}
	// Firstly, collection outgoing connections from this node.
	originAdjID := report.MakeAdjacencyID(originID)
	for _, serverNodeID := range topology.Adjacency[originAdjID] {
		remote, ok := labeler(serverNodeID)
		if !ok {
			continue
		}
		rows = append(rows, Row{
			Key:        local,
			ValueMajor: remote,
			Expandable: true,
		})
	}
	// Next, scan the topology for incoming connections to this node.
	for clientAdjID, serverNodeIDs := range topology.Adjacency {
		if clientAdjID == originAdjID {
			continue
		}
		if !serverNodeIDs.Contains(originID) {
			continue
		}
		clientNodeID, ok := report.ParseAdjacencyID(clientAdjID)
		if !ok {
			continue
		}
		remote, ok := labeler(clientNodeID)
		if !ok {
			continue
		}
		rows = append(rows, Row{
			Key:        remote,
			ValueMajor: local,
			Expandable: true,
		})
	}
	return rows
}
Exemplo n.º 5
0
func connectionDetailsRows(endpointTopology report.Topology, originID string, nmd report.NodeMetadata) []Row {
	rows := []Row{}
	local := fmt.Sprintf("%s:%s", nmd.Metadata[endpoint.Addr], nmd.Metadata[endpoint.Port])
	adjacencies := endpointTopology.Adjacency[report.MakeAdjacencyID(originID)]
	sort.Strings(adjacencies)
	for _, adj := range adjacencies {
		if _, address, port, ok := report.ParseEndpointNodeID(adj); ok {
			rows = append(rows, Row{
				Key:        local,
				ValueMajor: fmt.Sprintf("%s:%s", address, port),
			})
		}
	}
	return rows
}
Exemplo n.º 6
0
// ShortLivedConnectionJoin joins the given renderer with short lived connections
// from the endpoints topology, using the toIPs function to extract IPs from
// the nodes.
func ShortLivedConnectionJoin(r Renderer, toIPs func(report.Node) []string) Renderer {
	nodeToIP := func(n report.Node, _ report.Networks) report.Nodes {
		result := report.Nodes{}
		for _, ip := range toIPs(n) {
			result[ip] = NewDerivedNode(ip, n).
				WithTopology(IP).
				WithLatests(map[string]string{
					originalNodeID:       n.ID,
					originalNodeTopology: n.Topology,
				}).
				WithCounters(map[string]int{IP: 1})
		}
		return result
	}

	ipToNode := func(n report.Node, _ report.Networks) report.Nodes {
		// If an IP is shared between multiple nodes, we can't
		// reliably attribute an connection based on its IP
		if count, _ := n.Counters.Lookup(IP); count > 1 {
			return report.Nodes{}
		}

		// Propagate the internet pseudo node
		if strings.HasSuffix(n.ID, TheInternetID) {
			return report.Nodes{n.ID: n}
		}

		// If this node is not of the original type, exclude it.
		// This excludes all the nodes we've dragged in from endpoint
		// that we failed to join to a node.
		id, ok := n.Latest.Lookup(originalNodeID)
		if !ok {
			return report.Nodes{}
		}
		topology, ok := n.Latest.Lookup(originalNodeTopology)
		if !ok {
			return report.Nodes{}
		}

		return report.Nodes{
			id: NewDerivedNode(id, n).
				WithTopology(topology),
		}
	}

	// MapEndpoint2IP maps endpoint nodes to their IP address, for joining
	// with container nodes.  We drop endpoint nodes with pids, as they
	// will be joined to containers through the process topology, and we
	// don't want to double count edges.
	endpoint2IP := func(m report.Node, local report.Networks) report.Nodes {
		// Don't include procspied connections, to prevent double counting
		_, ok := m.Latest.Lookup(endpoint.Procspied)
		if ok {
			return report.Nodes{}
		}
		scope, addr, port, ok := report.ParseEndpointNodeID(m.ID)
		if !ok {
			return report.Nodes{}
		}
		if ip := net.ParseIP(addr); ip != nil && !local.Contains(ip) {
			node := theInternetNode(m)
			return report.Nodes{node.ID: node}
		}

		// We also allow for joining on ip:port pairs.  This is useful
		// for connections to the host IPs which have been port
		// mapped to a container can only be unambiguously identified with the port.
		// So we need to emit two nodes, for two different cases.
		id := report.MakeScopedEndpointNodeID(scope, addr, "")
		idWithPort := report.MakeScopedEndpointNodeID(scope, addr, port)
		return report.Nodes{
			id:         NewDerivedNode(id, m).WithTopology(IP),
			idWithPort: NewDerivedNode(idWithPort, m).WithTopology(IP),
		}
	}

	return FilterUnconnected(MakeMap(
		ipToNode,
		MakeReduce(
			MakeMap(
				nodeToIP,
				r,
			),
			MakeMap(
				endpoint2IP,
				SelectEndpoint,
			),
		),
	))
}