mirror of
https://github.com/weaveworks/scope.git
synced 2026-07-19 21:39:26 +00:00
probe: fetch container IP addresses from inside its namespace
So that we can pick up addresses added via CNI or other mechanisms that Docker is not aware of.
This commit is contained in:
@@ -268,6 +268,15 @@ func isIPv4(addr string) bool {
|
||||
return ip != nil && ip.To4() != nil
|
||||
}
|
||||
|
||||
func contains(strs []string, str string) bool {
|
||||
for _, s := range strs {
|
||||
if s == str {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *container) NetworkInfo(localAddrs []net.IP) report.Sets {
|
||||
c.RLock()
|
||||
defer c.RUnlock()
|
||||
@@ -277,6 +286,18 @@ func (c *container) NetworkInfo(localAddrs []net.IP) report.Sets {
|
||||
ips = append(ips, c.container.NetworkSettings.IPAddress)
|
||||
}
|
||||
|
||||
// Fetch IP addresses from the container's namespace
|
||||
cidrs, err := namespaceIPAddresses(c.container.State.Pid)
|
||||
if err != nil {
|
||||
log.Debugf("container %s: failed to get addresses: %s", c.container.ID, err)
|
||||
}
|
||||
for _, cidr := range cidrs {
|
||||
ip := cidr.IP.String()
|
||||
if !contains(ips, ip) {
|
||||
ips = append(ips, ip)
|
||||
}
|
||||
}
|
||||
|
||||
// For now, for the proof-of-concept, we just add networks as a set of
|
||||
// names. For the next iteration, we will probably want to create a new
|
||||
// Network topology, populate the network nodes with all of the details
|
||||
|
||||
85
probe/docker/network.go
Normal file
85
probe/docker/network.go
Normal file
@@ -0,0 +1,85 @@
|
||||
// withNetNS function requires a fix that first appeared in Go version 1.10
|
||||
// +build go1.10
|
||||
|
||||
package docker
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"runtime"
|
||||
|
||||
"github.com/vishvananda/netlink"
|
||||
"github.com/vishvananda/netns"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// Code adapted from github.com/weaveworks/weave/net/netdev.go
|
||||
|
||||
// Return any non-local IP addresses for processID if in a non-root namespace
|
||||
func namespaceIPAddresses(processID int) ([]*net.IPNet, error) {
|
||||
// Ignore if this process is running in the root namespace
|
||||
netnsRoot, err := netns.GetFromPid(1)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to open root namespace: %s", err)
|
||||
}
|
||||
defer netnsRoot.Close()
|
||||
netnsContainer, err := netns.GetFromPid(processID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer netnsContainer.Close()
|
||||
if netnsRoot.Equal(netnsContainer) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var cidrs []*net.IPNet
|
||||
err = withNetNS(netnsContainer, func() error {
|
||||
cidrs, err = allNonLocalAddresses()
|
||||
return err
|
||||
})
|
||||
|
||||
return cidrs, err
|
||||
}
|
||||
|
||||
// return all non-local IP addresses from the current namespace
|
||||
func allNonLocalAddresses() ([]*net.IPNet, error) {
|
||||
var cidrs []*net.IPNet
|
||||
links, err := netlink.LinkList()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, link := range links {
|
||||
addrs, err := netlink.AddrList(link, netlink.FAMILY_ALL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, addr := range addrs {
|
||||
// Exclude link-local ipv6 addresses, localhost, etc. Hope this is the correct test.
|
||||
if addr.Scope == unix.RT_SCOPE_UNIVERSE {
|
||||
cidrs = append(cidrs, addr.IPNet)
|
||||
}
|
||||
}
|
||||
}
|
||||
return cidrs, nil
|
||||
}
|
||||
|
||||
// Run the 'work' function in a different network namespace
|
||||
func withNetNS(ns netns.NsHandle, work func() error) error {
|
||||
runtime.LockOSThread()
|
||||
defer runtime.UnlockOSThread()
|
||||
|
||||
oldNs, err := netns.Get()
|
||||
if err == nil {
|
||||
defer oldNs.Close()
|
||||
|
||||
err = netns.Set(ns)
|
||||
if err == nil {
|
||||
defer netns.Set(oldNs)
|
||||
|
||||
err = work()
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
Reference in New Issue
Block a user