Merge remote-tracking branch 'origin/219-default-bridge-2'

This commit is contained in:
Solomon Hykes 2013-04-05 14:02:16 -07:00
commit 793c1ad990
6 changed files with 145 additions and 6 deletions

View file

@ -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()

View file

@ -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 = docker.DefaultNetworkBridge
}
if *flDebug {
os.Setenv("DEBUG", "1")
}

View file

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

View file

@ -12,10 +12,12 @@ import (
"sync"
)
var NetworkBridgeIface string
const (
networkBridgeIface = "lxcbr0"
portRangeStart = 49153
portRangeEnd = 65535
DefaultNetworkBridge = "lxcbr0"
portRangeStart = 49153
portRangeEnd = 65535
)
// Calculates the first and last IP addresses in an IPNet
@ -29,6 +31,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()))
@ -51,6 +66,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")
@ -63,6 +91,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 <bridgeName>")
} 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)
@ -406,7 +492,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)

View file

@ -253,3 +253,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)
}

View file

@ -267,7 +267,10 @@ func NewRuntimeFromDirectory(root string) (*Runtime, error) {
if err != nil {
return nil, fmt.Errorf("Couldn't create Tag store: %s", err)
}
netManager, err := newNetworkManager(networkBridgeIface)
if NetworkBridgeIface == "" {
NetworkBridgeIface = DefaultNetworkBridge
}
netManager, err := newNetworkManager(NetworkBridgeIface)
if err != nil {
return nil, err
}