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:
Bryan Boreham
2018-06-02 21:24:17 +00:00
parent 5ab00ae664
commit ff5b2affe0
2 changed files with 106 additions and 0 deletions

View File

@@ -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
View 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
}