From ae8058f403d50e62f8aba6f57e673c9c96ab1fee Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Wed, 3 Apr 2013 14:53:09 -0700 Subject: [PATCH 1/5] Put the bridge interface name in the command line Upstream-commit: f39af7e05da437d6ac95b19ccfa401598d1fb5d9 Component: engine --- components/engine/docker/docker.go | 6 ++++++ components/engine/network.go | 7 ++++--- components/engine/runtime.go | 2 +- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/components/engine/docker/docker.go b/components/engine/docker/docker.go index e9522ba429..85bfc8e551 100644 --- a/components/engine/docker/docker.go +++ b/components/engine/docker/docker.go @@ -22,7 +22,13 @@ func main() { // FIXME: Switch d and D ? (to be more sshd like) flDaemon := flag.Bool("d", false, "Daemon mode") flDebug := flag.Bool("D", false, "Debug mode") + bridgeName := flag.String("br", "", "") flag.Parse() + if *bridgeName != "" { + docker.NetworkBridgeIface = *bridgeName + } else { + docker.NetworkBridgeIface = "lxcbr0" + } if *flDebug { os.Setenv("DEBUG", "1") } diff --git a/components/engine/network.go b/components/engine/network.go index fe700bd61c..4a583e2f1e 100644 --- a/components/engine/network.go +++ b/components/engine/network.go @@ -11,10 +11,11 @@ import ( "strings" ) +var NetworkBridgeIface string + const ( - networkBridgeIface = "lxcbr0" - portRangeStart = 49153 - portRangeEnd = 65535 + portRangeStart = 49153 + portRangeEnd = 65535 ) // Calculates the first and last IP addresses in an IPNet diff --git a/components/engine/runtime.go b/components/engine/runtime.go index 950ea404a1..97752a2ab5 100644 --- a/components/engine/runtime.go +++ b/components/engine/runtime.go @@ -267,7 +267,7 @@ func NewRuntimeFromDirectory(root string) (*Runtime, error) { if err != nil { return nil, fmt.Errorf("Couldn't create Tag store: %s", err) } - netManager, err := newNetworkManager(networkBridgeIface) + netManager, err := newNetworkManager(NetworkBridgeIface) if err != nil { return nil, err } From d25d9d9cff4167850e4f48961e1ef7b5e194e445 Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Wed, 3 Apr 2013 14:53:54 -0700 Subject: [PATCH 2/5] Add an helper function to check if two network overlaps. Also add unit tests for this function Upstream-commit: 90a6e310fe4c70b99f9fa5c8d8615e2382edba0b Component: engine --- components/engine/network.go | 13 ++++++++++++ components/engine/network_test.go | 35 +++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/components/engine/network.go b/components/engine/network.go index 4a583e2f1e..d6b7537c2b 100644 --- a/components/engine/network.go +++ b/components/engine/network.go @@ -29,6 +29,19 @@ func networkRange(network *net.IPNet) (net.IP, net.IP) { return firstIP, lastIP } +// Detects overlap between one IPNet and another +func networkOverlaps(netX *net.IPNet, netY *net.IPNet) bool { + firstIP, _ := networkRange(netX) + if netY.Contains(firstIP) { + return true + } + firstIP, _ = networkRange(netY) + if netX.Contains(firstIP) { + return true + } + return false +} + // Converts a 4 bytes IP into a 32 bit integer func ipToInt(ip net.IP) int32 { return int32(binary.BigEndian.Uint32(ip.To4())) diff --git a/components/engine/network_test.go b/components/engine/network_test.go index a9d3cac454..7c2a4c2272 100644 --- a/components/engine/network_test.go +++ b/components/engine/network_test.go @@ -217,3 +217,38 @@ func assertIPEquals(t *testing.T, ip1, ip2 net.IP) { t.Fatalf("Expected IP %s, got %s", ip1, ip2) } } + +func AssertOverlap(CIDRx string, CIDRy string, t *testing.T) { + _, netX, _ := net.ParseCIDR(CIDRx) + _, netY, _ := net.ParseCIDR(CIDRy) + if !networkOverlaps(netX, netY) { + t.Errorf("%v and %v should overlap", netX, netY) + } +} + +func AssertNoOverlap(CIDRx string, CIDRy string, t *testing.T) { + _, netX, _ := net.ParseCIDR(CIDRx) + _, netY, _ := net.ParseCIDR(CIDRy) + if networkOverlaps(netX, netY) { + t.Errorf("%v and %v should not overlap", netX, netY) + } +} + +func TestNetworkOverlaps(t *testing.T) { + //netY starts at same IP and ends within netX + AssertOverlap("172.16.0.1/24", "172.16.0.1/25", t) + //netY starts within netX and ends at same IP + AssertOverlap("172.16.0.1/24", "172.16.0.128/25", t) + //netY starts and ends within netX + AssertOverlap("172.16.0.1/24", "172.16.0.64/25", t) + //netY starts at same IP and ends outside of netX + AssertOverlap("172.16.0.1/24", "172.16.0.1/23", t) + //netY starts before and ends at same IP of netX + AssertOverlap("172.16.1.1/24", "172.16.0.1/23", t) + //netY starts before and ends outside of netX + AssertOverlap("172.16.1.1/24", "172.16.0.1/23", t) + //netY starts and ends before netX + AssertNoOverlap("172.16.1.1/25", "172.16.0.1/24", t) + //netX starts and ends before netY + AssertNoOverlap("172.16.1.1/25", "172.16.2.1/24", t) +} From 04e71efb01a314baf0531b5848984220e91de86e Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Wed, 3 Apr 2013 15:05:03 -0700 Subject: [PATCH 3/5] Make LXC aware of custom bridge Upstream-commit: d9a9bfc9c7bee2e4316debcd9f393b8f2fb1e45a Component: engine --- components/engine/container.go | 2 ++ components/engine/lxc_template.go | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/components/engine/container.go b/components/engine/container.go index 981b9f29c0..fccdbf0b68 100644 --- a/components/engine/container.go +++ b/components/engine/container.go @@ -135,6 +135,7 @@ type NetworkSettings struct { IpAddress string IpPrefixLen int Gateway string + Bridge string PortMapping map[string]string } @@ -491,6 +492,7 @@ func (container *Container) allocateNetwork() error { } } container.network = iface + container.NetworkSettings.Bridge = container.runtime.networkManager.bridgeIface container.NetworkSettings.IpAddress = iface.IPNet.IP.String() container.NetworkSettings.IpPrefixLen, _ = iface.IPNet.Mask.Size() container.NetworkSettings.Gateway = iface.Gateway.String() diff --git a/components/engine/lxc_template.go b/components/engine/lxc_template.go index e3beb037f9..c6849cb0df 100644 --- a/components/engine/lxc_template.go +++ b/components/engine/lxc_template.go @@ -16,7 +16,7 @@ lxc.utsname = {{.Id}} # network configuration lxc.network.type = veth lxc.network.flags = up -lxc.network.link = lxcbr0 +lxc.network.link = {{.NetworkSettings.Bridge}} lxc.network.name = eth0 lxc.network.mtu = 1500 lxc.network.ipv4 = {{.NetworkSettings.IpAddress}}/{{.NetworkSettings.IpPrefixLen}} From ad744d80e7c4224fcc238a5e37136c0ff7620cf0 Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Wed, 3 Apr 2013 15:57:57 -0700 Subject: [PATCH 4/5] If bridge does not exists, try to create it Upstream-commit: aa4bf4284b4d18874544368349cb9575d8412de8 Component: engine --- components/engine/network.go | 80 +++++++++++++++++++++++++++++++++++- 1 file changed, 79 insertions(+), 1 deletion(-) diff --git a/components/engine/network.go b/components/engine/network.go index d6b7537c2b..cde78e7aa6 100644 --- a/components/engine/network.go +++ b/components/engine/network.go @@ -64,6 +64,19 @@ func networkSize(mask net.IPMask) int32 { return int32(binary.BigEndian.Uint32(m)) + 1 } +//Wrapper around the ip command +func ip(args ...string) (string, error) { + path, err := exec.LookPath("ip") + if err != nil { + return "", fmt.Errorf("command not found: ip") + } + output, err := exec.Command(path, args...).CombinedOutput() + if err != nil { + return "", fmt.Errorf("ip failed: ip %v", strings.Join(args, " ")) + } + return string(output), nil +} + // Wrapper around the iptables command func iptables(args ...string) error { path, err := exec.LookPath("iptables") @@ -76,6 +89,64 @@ func iptables(args ...string) error { return nil } +func checkRouteOverlaps(dockerNetwork *net.IPNet) error { + output, err := ip("route") + if err != nil { + return err + } + Debugf("Routes:\n\n%s", output) + for _, line := range strings.Split(output, "\n") { + if strings.Trim(line, "\r\n\t ") == "" || strings.Contains(line, "default") { + continue + } + if _, network, err := net.ParseCIDR(strings.Split(line, " ")[0]); err != nil { + return fmt.Errorf("Unexpected ip route output: %s (%s)", err, line) + } else if networkOverlaps(dockerNetwork, network) { + return fmt.Errorf("Network %s is already routed: '%s'", dockerNetwork.String(), line) + } + } + return nil +} + +func CreateBridgeIface(ifaceName string) error { + addrs := []string{"172.16.42.1/24", "10.0.42.1/24", "192.168.42.1/24"} + + var ifaceAddr string + for _, addr := range addrs { + _, dockerNetwork, err := net.ParseCIDR(addr) + if err != nil { + return err + } + if err := checkRouteOverlaps(dockerNetwork); err == nil { + ifaceAddr = addr + break + } else { + Debugf("%s: %s", addr, err) + } + } + if ifaceAddr == "" { + return fmt.Errorf("Impossible to create a bridge. Please create a bridge manually and restart docker with -br ") + } else { + Debugf("Creating bridge %s with network %s", ifaceName, ifaceAddr) + } + + if output, err := ip("link", "add", ifaceName, "type", "bridge"); err != nil { + return fmt.Errorf("Error creating bridge: %s (output: %s)", err, output) + } + + if output, err := ip("addr", "add", ifaceAddr, "dev", ifaceName); err != nil { + return fmt.Errorf("Unable to add private network: %s (%s)", err, output) + } + if output, err := ip("link", "set", ifaceName, "up"); err != nil { + return fmt.Errorf("Unable to start network bridge: %s (%s)", err, output) + } + if err := iptables("-t", "nat", "-A", "POSTROUTING", "-s", ifaceAddr, + "!", "-d", ifaceAddr, "-j", "MASQUERADE"); err != nil { + return fmt.Errorf("Unable to enable network bridge NAT: %s", err) + } + return nil +} + // Return the IPv4 address of a network interface func getIfaceAddr(name string) (net.Addr, error) { iface, err := net.InterfaceByName(name) @@ -371,7 +442,14 @@ func (manager *NetworkManager) Allocate() (*NetworkInterface, error) { func newNetworkManager(bridgeIface string) (*NetworkManager, error) { addr, err := getIfaceAddr(bridgeIface) if err != nil { - return nil, fmt.Errorf("Couldn't find bridge interface %s (%s).\nPlease create it with 'ip link add lxcbr0 type bridge; ip addr add ADDRESS/MASK dev lxcbr0'", bridgeIface, err) + // If the iface is not found, try to create it + if err := CreateBridgeIface(bridgeIface); err != nil { + return nil, err + } + addr, err = getIfaceAddr(bridgeIface) + if err != nil { + return nil, err + } } network := addr.(*net.IPNet) From 5140fcb35ffb3508aa63c8990de61d86d8a08672 Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Thu, 4 Apr 2013 05:33:28 -0700 Subject: [PATCH 5/5] Move the default bridge name to a constant Upstream-commit: 1b370f9d8dbd1c476db766e8587617e795570d97 Component: engine --- components/engine/docker/docker.go | 2 +- components/engine/network.go | 5 +++-- components/engine/runtime.go | 3 +++ 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/components/engine/docker/docker.go b/components/engine/docker/docker.go index 85bfc8e551..d38d67216c 100644 --- a/components/engine/docker/docker.go +++ b/components/engine/docker/docker.go @@ -27,7 +27,7 @@ func main() { if *bridgeName != "" { docker.NetworkBridgeIface = *bridgeName } else { - docker.NetworkBridgeIface = "lxcbr0" + docker.NetworkBridgeIface = docker.DefaultNetworkBridge } if *flDebug { os.Setenv("DEBUG", "1") diff --git a/components/engine/network.go b/components/engine/network.go index cde78e7aa6..105c0056f5 100644 --- a/components/engine/network.go +++ b/components/engine/network.go @@ -14,8 +14,9 @@ import ( var NetworkBridgeIface string const ( - portRangeStart = 49153 - portRangeEnd = 65535 + DefaultNetworkBridge = "lxcbr0" + portRangeStart = 49153 + portRangeEnd = 65535 ) // Calculates the first and last IP addresses in an IPNet diff --git a/components/engine/runtime.go b/components/engine/runtime.go index 97752a2ab5..0e5bcbfc69 100644 --- a/components/engine/runtime.go +++ b/components/engine/runtime.go @@ -267,6 +267,9 @@ func NewRuntimeFromDirectory(root string) (*Runtime, error) { if err != nil { return nil, fmt.Errorf("Couldn't create Tag store: %s", err) } + if NetworkBridgeIface == "" { + NetworkBridgeIface = DefaultNetworkBridge + } netManager, err := newNetworkManager(NetworkBridgeIface) if err != nil { return nil, err