diff --git a/.gitmodules b/.gitmodules index 6819c6f7..feedaad8 100644 --- a/.gitmodules +++ b/.gitmodules @@ -52,3 +52,6 @@ [submodule "vendor/github.com/weaveworks/mesh"] path = vendor/github.com/weaveworks/mesh url = https://github.com/weaveworks/mesh +[submodule "vendor/github.com/vishvananda/netns"] + path = vendor/github.com/vishvananda/netns + url = https://github.com/vishvananda/netns.git diff --git a/common/utils.go b/common/utils.go index 82959321..ce9e09e3 100644 --- a/common/utils.go +++ b/common/utils.go @@ -1,6 +1,11 @@ package common import ( + "fmt" + "github.com/vishvananda/netlink" + "github.com/vishvananda/netns" + "net" + "runtime" "strings" ) @@ -18,3 +23,76 @@ func ErrorMessages(errors []error) string { } return strings.Join(result, "\n") } + +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 nil +} + +type NetDev struct { + MAC net.HardwareAddr + CIDRs []*net.IPNet +} + +// Search the network namespace of a process for interfaces matching a predicate +func FindNetDevs(procPath string, processID int, match func(string) bool) ([]NetDev, error) { + var netDevs []NetDev + + ns, err := netns.GetFromPath(fmt.Sprintf("%s/%d/ns/net", procPath, processID)) + if err != nil { + return nil, err + } + defer ns.Close() + + err = WithNetNS(ns, func() error { + links, err := netlink.LinkList() + if err != nil { + return err + } + for _, link := range links { + if match(link.Attrs().Name) { + addrs, err := netlink.AddrList(link, netlink.FAMILY_V4) + if err != nil { + return err + } + + netDev := NetDev{MAC: link.Attrs().HardwareAddr} + for _, addr := range addrs { + netDev.CIDRs = append(netDev.CIDRs, addr.IPNet) + } + netDevs = append(netDevs, netDev) + } + } + return nil + }) + + return netDevs, err +} + +// Lookup the weave interface of a container +func GetWeaveNetDevs(procPath string, processID int) ([]NetDev, error) { + return FindNetDevs(procPath, processID, func(name string) bool { + return strings.HasPrefix(name, "ethwe") + }) +} + +// Get the weave bridge interface +func GetBridgeNetDev(procPath, bridgeName string) ([]NetDev, error) { + return FindNetDevs(procPath, 1, func(name string) bool { + return name == bridgeName + }) +} diff --git a/prog/weaveproxy/main.go b/prog/weaveproxy/main.go index 3eb66d19..58dd29bc 100644 --- a/prog/weaveproxy/main.go +++ b/prog/weaveproxy/main.go @@ -14,6 +14,7 @@ import ( var ( version = "(unreleased version)" defaultDockerHost = "unix:///var/run/docker.sock" + defaultProcPath = "/proc" ) func main() { @@ -68,6 +69,11 @@ func main() { c.DockerHost = dockerHost } + c.ProcPath = defaultProcPath + if procPath := os.Getenv("PROCFS"); procPath != "" { + c.ProcPath = procPath + } + p, err := proxy.NewProxy(c) if err != nil { Log.Fatalf("Could not start proxy: %s", err) diff --git a/prog/weaveutil/addrs.go b/prog/weaveutil/addrs.go new file mode 100644 index 00000000..068f37e5 --- /dev/null +++ b/prog/weaveutil/addrs.go @@ -0,0 +1,55 @@ +package main + +import ( + "fmt" + "github.com/fsouza/go-dockerclient" + "github.com/weaveworks/weave/common" +) + +func containerAddrs(args []string) error { + if len(args) < 2 { + cmdUsage("container-addrs", " [containerID ...]") + } + + procPath := args[0] + bridgeName := args[1] + + c, err := docker.NewVersionedClientFromEnv("1.18") + if err != nil { + return err + } + + for _, containerID := range args[2:] { + netDevs, err := getNetDevs(procPath, bridgeName, c, containerID) + if err != nil { + if _, ok := err.(*docker.NoSuchContainer); ok { + continue + } + return err + } + + for _, netDev := range netDevs { + fmt.Printf("%12s %s", containerID, netDev.MAC.String()) + for _, cidr := range netDev.CIDRs { + prefixLength, _ := cidr.Mask.Size() + fmt.Printf(" %v/%v", cidr.IP, prefixLength) + } + fmt.Println() + } + } + + return nil +} + +func getNetDevs(procPath, bridgeName string, c *docker.Client, containerID string) ([]common.NetDev, error) { + if containerID == "weave:expose" { + return common.GetBridgeNetDev(procPath, bridgeName) + } + + container, err := c.InspectContainer(containerID) + if err != nil { + return nil, err + } + + return common.GetWeaveNetDevs(procPath, container.State.Pid) +} diff --git a/prog/weaveutil/main.go b/prog/weaveutil/main.go index 2cdf02d7..1d737341 100644 --- a/prog/weaveutil/main.go +++ b/prog/weaveutil/main.go @@ -18,6 +18,7 @@ func init() { "add-datapath-interface": addDatapathInterface, "create-plugin-network": createPluginNetwork, "remove-plugin-network": removePluginNetwork, + "container-addrs": containerAddrs, } } diff --git a/proxy/common.go b/proxy/common.go index a4810c66..b2755220 100644 --- a/proxy/common.go +++ b/proxy/common.go @@ -3,10 +3,8 @@ package proxy import ( "bytes" "encoding/json" - "errors" "fmt" "io/ioutil" - "net" "net/http" "os" "os/exec" @@ -128,35 +126,6 @@ func inspectContainerInPath(client *docker.Client, path string) (*docker.Contain return container, err } -func weaveContainerIPs(containerID string) (mac string, ips []net.IP, nets []*net.IPNet, err error) { - stdout, stderr, err := callWeave("ps", containerID) - if err != nil || len(stderr) > 0 { - err = errors.New(string(stderr)) - return - } - if len(stdout) <= 0 { - return - } - - fields := strings.Fields(string(stdout)) - if len(fields) < 2 { - return - } - mac = fields[1] - - var ip net.IP - var ipnet *net.IPNet - for _, cidr := range fields[2:] { - ip, ipnet, err = net.ParseCIDR(cidr) - if err != nil { - return - } - ips = append(ips, ip) - nets = append(nets, ipnet) - } - return -} - func addVolume(hostConfig jsonObject, source, target, mode string) error { configBinds, err := hostConfig.StringArray("Binds") if err != nil { diff --git a/proxy/json.go b/proxy/json.go index f736a77b..33995fca 100644 --- a/proxy/json.go +++ b/proxy/json.go @@ -1,6 +1,7 @@ package proxy import ( + "encoding/json" "fmt" ) @@ -45,6 +46,25 @@ func (j jsonObject) String(key string) (string, error) { return result, nil } +func (j jsonObject) Int(key string) (int, error) { + iface, ok := j[key] + if !ok || iface == nil { + return 0, nil + } + + result, ok := iface.(json.Number) + if !ok { + return 0, &UnmarshalWrongTypeError{key, "json.Number", iface} + } + + i64, err := result.Int64() + if err != nil { + return 0, err + } + + return int(i64), nil +} + func (j jsonObject) StringArray(key string) ([]string, error) { iface, ok := j[key] if !ok || iface == nil { diff --git a/proxy/proxy.go b/proxy/proxy.go index 1045fd51..588bbf33 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -62,6 +62,7 @@ type Config struct { WithoutDNS bool NoMulticastRoute bool DockerHost string + ProcPath string } type wait struct { @@ -590,11 +591,21 @@ func (proxy *Proxy) updateContainerNetworkSettings(container jsonObject) error { return err } + state, err := container.Object("State") + if err != nil { + return err + } + + pid, err := state.Int("Pid") + if err != nil { + return err + } + if err := proxy.waitForStartByIdent(containerID); err != nil { return err } - mac, ips, nets, err := weaveContainerIPs(containerID) - if err != nil || len(ips) == 0 { + netDevs, err := GetWeaveNetDevs(proxy.ProcPath, pid) + if err != nil || len(netDevs) == 0 || len(netDevs[0].CIDRs) == 0 { return err } @@ -602,9 +613,9 @@ func (proxy *Proxy) updateContainerNetworkSettings(container jsonObject) error { if err != nil { return err } - networkSettings["MacAddress"] = mac - networkSettings["IPAddress"] = ips[0].String() - networkSettings["IPPrefixLen"], _ = nets[0].Mask.Size() + networkSettings["MacAddress"] = netDevs[0].MAC.String() + networkSettings["IPAddress"] = netDevs[0].CIDRs[0].IP.String() + networkSettings["IPPrefixLen"], _ = netDevs[0].CIDRs[0].Mask.Size() return nil } diff --git a/vendor/github.com/vishvananda/netns b/vendor/github.com/vishvananda/netns new file mode 160000 index 00000000..f0b8d0aa --- /dev/null +++ b/vendor/github.com/vishvananda/netns @@ -0,0 +1 @@ +Subproject commit f0b8d0aad33f2e47337fb9e7de620cc9c6243d3c diff --git a/weave b/weave index cc49ace5..729e4f20 100755 --- a/weave +++ b/weave @@ -914,10 +914,6 @@ detach() { netnsenter ip link del $CONTAINER_IFNAME type veth } -container_ip_addr_show() { - ! container_in_host_ns && netnsenter ip addr show -} - ###################################################################### # functions for interacting with containers ###################################################################### @@ -1043,34 +1039,13 @@ wait_for_status() { done } -# Parse the output of `ip addr show`, outputing weave addresses of kind $1 -addrs_from_ip_addr_show() { - while read first second rest ; do - if [ "$first" != "${first%:}" ] ; then - iname="${second%:}" - elif [ "$first" = "$1" -a \( "$iname" = "weave" -o "$iname" != "${iname#ethwe}" \) ] ; then - echo $second - fi - done -} - # Call $1 for all containers, passing container ID, all MACs and all IPs with_container_addresses() { COMMAND=$1 shift 1 - for CONTAINER_ID in "$@" ; do - if [ "$CONTAINER_ID" = "weave:expose" ] ; then - ADDRS_CMD="ip addr show dev $BRIDGE" - else - ADDRS_CMD="with_container_netns $CONTAINER_ID container_ip_addr_show" - fi - if CONTAINER_ADDRS=$($ADDRS_CMD 2>/dev/null) ; then - CONTAINER_MAC=$(echo "$CONTAINER_ADDRS" | addrs_from_ip_addr_show 'link/ether') - CONTAINER_IPS=$(echo "$CONTAINER_ADDRS" | addrs_from_ip_addr_show 'inet') - if [ -n "$CONTAINER_MAC$CONTAINER_IPS" ] ; then - $COMMAND "$CONTAINER_ID" "$CONTAINER_MAC" "$CONTAINER_IPS" - fi - fi + + util_op container-addrs $PROCFS $BRIDGE "$@" | while read CONTAINER_ID CONTAINER_MAC CONTAINER_IPS; do + $COMMAND "$CONTAINER_ID" "$CONTAINER_MAC" "$CONTAINER_IPS" done }