commit
735dbcf3ab
26 changed files with 4992 additions and 5 deletions
18
libnetwork/Godeps/Godeps.json
generated
18
libnetwork/Godeps/Godeps.json
generated
|
@ -1,17 +1,35 @@
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/docker/libnetwork",
|
"ImportPath": "github.com/docker/libnetwork",
|
||||||
"GoVersion": "go1.4.2",
|
"GoVersion": "go1.4.2",
|
||||||
|
"Packages": [
|
||||||
|
"./..."
|
||||||
|
],
|
||||||
"Deps": [
|
"Deps": [
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/Sirupsen/logrus",
|
"ImportPath": "github.com/Sirupsen/logrus",
|
||||||
"Comment": "v0.6.4-12-g467d9d5",
|
"Comment": "v0.6.4-12-g467d9d5",
|
||||||
"Rev": "467d9d55c2d2c17248441a8fc661561161f40d5e"
|
"Rev": "467d9d55c2d2c17248441a8fc661561161f40d5e"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/docker/docker/daemon/networkdriver",
|
||||||
|
"Comment": "v1.4.1-1379-g8e107a9",
|
||||||
|
"Rev": "8e107a93210c54f22ec1354d969c771b1abfbe05"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/docker/docker/pkg/parsers/kernel",
|
||||||
|
"Comment": "v1.4.1-1379-g8e107a9",
|
||||||
|
"Rev": "8e107a93210c54f22ec1354d969c771b1abfbe05"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/docker/docker/pkg/reexec",
|
"ImportPath": "github.com/docker/docker/pkg/reexec",
|
||||||
"Comment": "v1.4.1-1379-g8e107a9",
|
"Comment": "v1.4.1-1379-g8e107a9",
|
||||||
"Rev": "8e107a93210c54f22ec1354d969c771b1abfbe05"
|
"Rev": "8e107a93210c54f22ec1354d969c771b1abfbe05"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/docker/libcontainer/netlink",
|
||||||
|
"Comment": "v1.4.0-324-g88989e6",
|
||||||
|
"Rev": "88989e66d3a1ab960deb37f3dd7f824d85e1b9bc"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/vishvananda/netlink",
|
"ImportPath": "github.com/vishvananda/netlink",
|
||||||
"Rev": "8eb64238879fed52fd51c5b30ad20b928fb4c36c"
|
"Rev": "8eb64238879fed52fd51c5b30ad20b928fb4c36c"
|
||||||
|
|
720
libnetwork/Godeps/_workspace/src/github.com/docker/docker/daemon/networkdriver/bridge/driver.go
generated
vendored
Normal file
720
libnetwork/Godeps/_workspace/src/github.com/docker/docker/daemon/networkdriver/bridge/driver.go
generated
vendored
Normal file
|
@ -0,0 +1,720 @@
|
||||||
|
package bridge
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/hex"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
log "github.com/Sirupsen/logrus"
|
||||||
|
"github.com/docker/docker/daemon/networkdriver"
|
||||||
|
"github.com/docker/docker/daemon/networkdriver/ipallocator"
|
||||||
|
"github.com/docker/docker/daemon/networkdriver/portmapper"
|
||||||
|
"github.com/docker/docker/engine"
|
||||||
|
"github.com/docker/docker/nat"
|
||||||
|
"github.com/docker/docker/pkg/iptables"
|
||||||
|
"github.com/docker/docker/pkg/networkfs/resolvconf"
|
||||||
|
"github.com/docker/docker/pkg/parsers/kernel"
|
||||||
|
"github.com/docker/libcontainer/netlink"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
DefaultNetworkBridge = "docker0"
|
||||||
|
MaxAllocatedPortAttempts = 10
|
||||||
|
)
|
||||||
|
|
||||||
|
// Network interface represents the networking stack of a container
|
||||||
|
type networkInterface struct {
|
||||||
|
IP net.IP
|
||||||
|
IPv6 net.IP
|
||||||
|
PortMappings []net.Addr // there are mappings to the host interfaces
|
||||||
|
}
|
||||||
|
|
||||||
|
type ifaces struct {
|
||||||
|
c map[string]*networkInterface
|
||||||
|
sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *ifaces) Set(key string, n *networkInterface) {
|
||||||
|
i.Lock()
|
||||||
|
i.c[key] = n
|
||||||
|
i.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *ifaces) Get(key string) *networkInterface {
|
||||||
|
i.Lock()
|
||||||
|
res := i.c[key]
|
||||||
|
i.Unlock()
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
addrs = []string{
|
||||||
|
// Here we don't follow the convention of using the 1st IP of the range for the gateway.
|
||||||
|
// This is to use the same gateway IPs as the /24 ranges, which predate the /16 ranges.
|
||||||
|
// In theory this shouldn't matter - in practice there's bound to be a few scripts relying
|
||||||
|
// on the internal addressing or other stupid things like that.
|
||||||
|
// They shouldn't, but hey, let's not break them unless we really have to.
|
||||||
|
"172.17.42.1/16", // Don't use 172.16.0.0/16, it conflicts with EC2 DNS 172.16.0.23
|
||||||
|
"10.0.42.1/16", // Don't even try using the entire /8, that's too intrusive
|
||||||
|
"10.1.42.1/16",
|
||||||
|
"10.42.42.1/16",
|
||||||
|
"172.16.42.1/24",
|
||||||
|
"172.16.43.1/24",
|
||||||
|
"172.16.44.1/24",
|
||||||
|
"10.0.42.1/24",
|
||||||
|
"10.0.43.1/24",
|
||||||
|
"192.168.42.1/24",
|
||||||
|
"192.168.43.1/24",
|
||||||
|
"192.168.44.1/24",
|
||||||
|
}
|
||||||
|
|
||||||
|
bridgeIface string
|
||||||
|
bridgeIPv4Network *net.IPNet
|
||||||
|
bridgeIPv6Addr net.IP
|
||||||
|
globalIPv6Network *net.IPNet
|
||||||
|
|
||||||
|
defaultBindingIP = net.ParseIP("0.0.0.0")
|
||||||
|
currentInterfaces = ifaces{c: make(map[string]*networkInterface)}
|
||||||
|
)
|
||||||
|
|
||||||
|
func InitDriver(job *engine.Job) engine.Status {
|
||||||
|
var (
|
||||||
|
networkv4 *net.IPNet
|
||||||
|
networkv6 *net.IPNet
|
||||||
|
addrv4 net.Addr
|
||||||
|
addrsv6 []net.Addr
|
||||||
|
enableIPTables = job.GetenvBool("EnableIptables")
|
||||||
|
enableIPv6 = job.GetenvBool("EnableIPv6")
|
||||||
|
icc = job.GetenvBool("InterContainerCommunication")
|
||||||
|
ipMasq = job.GetenvBool("EnableIpMasq")
|
||||||
|
ipForward = job.GetenvBool("EnableIpForward")
|
||||||
|
bridgeIP = job.Getenv("BridgeIP")
|
||||||
|
bridgeIPv6 = "fe80::1/64"
|
||||||
|
fixedCIDR = job.Getenv("FixedCIDR")
|
||||||
|
fixedCIDRv6 = job.Getenv("FixedCIDRv6")
|
||||||
|
)
|
||||||
|
|
||||||
|
if defaultIP := job.Getenv("DefaultBindingIP"); defaultIP != "" {
|
||||||
|
defaultBindingIP = net.ParseIP(defaultIP)
|
||||||
|
}
|
||||||
|
|
||||||
|
bridgeIface = job.Getenv("BridgeIface")
|
||||||
|
usingDefaultBridge := false
|
||||||
|
if bridgeIface == "" {
|
||||||
|
usingDefaultBridge = true
|
||||||
|
bridgeIface = DefaultNetworkBridge
|
||||||
|
}
|
||||||
|
|
||||||
|
addrv4, addrsv6, err := networkdriver.GetIfaceAddr(bridgeIface)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
// No Bridge existent, create one
|
||||||
|
// If we're not using the default bridge, fail without trying to create it
|
||||||
|
if !usingDefaultBridge {
|
||||||
|
return job.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the iface is not found, try to create it
|
||||||
|
if err := configureBridge(bridgeIP, bridgeIPv6, enableIPv6); err != nil {
|
||||||
|
return job.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
addrv4, addrsv6, err = networkdriver.GetIfaceAddr(bridgeIface)
|
||||||
|
if err != nil {
|
||||||
|
return job.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if fixedCIDRv6 != "" {
|
||||||
|
// Setting route to global IPv6 subnet
|
||||||
|
log.Infof("Adding route to IPv6 network %q via device %q", fixedCIDRv6, bridgeIface)
|
||||||
|
if err := netlink.AddRoute(fixedCIDRv6, "", "", bridgeIface); err != nil {
|
||||||
|
log.Fatalf("Could not add route to IPv6 network %q via device %q", fixedCIDRv6, bridgeIface)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Bridge exists already, getting info...
|
||||||
|
// validate that the bridge ip matches the ip specified by BridgeIP
|
||||||
|
if bridgeIP != "" {
|
||||||
|
networkv4 = addrv4.(*net.IPNet)
|
||||||
|
bip, _, err := net.ParseCIDR(bridgeIP)
|
||||||
|
if err != nil {
|
||||||
|
return job.Error(err)
|
||||||
|
}
|
||||||
|
if !networkv4.IP.Equal(bip) {
|
||||||
|
return job.Errorf("bridge ip (%s) does not match existing bridge configuration %s", networkv4.IP, bip)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// a bridge might exist but not have any IPv6 addr associated with it yet
|
||||||
|
// (for example, an existing Docker installation that has only been used
|
||||||
|
// with IPv4 and docker0 already is set up) In that case, we can perform
|
||||||
|
// the bridge init for IPv6 here, else we will error out below if --ipv6=true
|
||||||
|
if len(addrsv6) == 0 && enableIPv6 {
|
||||||
|
if err := setupIPv6Bridge(bridgeIPv6); err != nil {
|
||||||
|
return job.Error(err)
|
||||||
|
}
|
||||||
|
// recheck addresses now that IPv6 is setup on the bridge
|
||||||
|
addrv4, addrsv6, err = networkdriver.GetIfaceAddr(bridgeIface)
|
||||||
|
if err != nil {
|
||||||
|
return job.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Check if route to fixedCIDRv6 is set
|
||||||
|
}
|
||||||
|
|
||||||
|
if enableIPv6 {
|
||||||
|
bip6, _, err := net.ParseCIDR(bridgeIPv6)
|
||||||
|
if err != nil {
|
||||||
|
return job.Error(err)
|
||||||
|
}
|
||||||
|
found := false
|
||||||
|
for _, addrv6 := range addrsv6 {
|
||||||
|
networkv6 = addrv6.(*net.IPNet)
|
||||||
|
if networkv6.IP.Equal(bip6) {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
return job.Errorf("bridge IPv6 does not match existing bridge configuration %s", bip6)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
networkv4 = addrv4.(*net.IPNet)
|
||||||
|
|
||||||
|
if enableIPv6 {
|
||||||
|
if len(addrsv6) == 0 {
|
||||||
|
return job.Error(errors.New("IPv6 enabled but no IPv6 detected"))
|
||||||
|
}
|
||||||
|
bridgeIPv6Addr = networkv6.IP
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure iptables for link support
|
||||||
|
if enableIPTables {
|
||||||
|
if err := setupIPTables(addrv4, icc, ipMasq); err != nil {
|
||||||
|
return job.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if ipForward {
|
||||||
|
// Enable IPv4 forwarding
|
||||||
|
if err := ioutil.WriteFile("/proc/sys/net/ipv4/ip_forward", []byte{'1', '\n'}, 0644); err != nil {
|
||||||
|
job.Logf("WARNING: unable to enable IPv4 forwarding: %s\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if fixedCIDRv6 != "" {
|
||||||
|
// Enable IPv6 forwarding
|
||||||
|
if err := ioutil.WriteFile("/proc/sys/net/ipv6/conf/default/forwarding", []byte{'1', '\n'}, 0644); err != nil {
|
||||||
|
job.Logf("WARNING: unable to enable IPv6 default forwarding: %s\n", err)
|
||||||
|
}
|
||||||
|
if err := ioutil.WriteFile("/proc/sys/net/ipv6/conf/all/forwarding", []byte{'1', '\n'}, 0644); err != nil {
|
||||||
|
job.Logf("WARNING: unable to enable IPv6 all forwarding: %s\n", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We can always try removing the iptables
|
||||||
|
if err := iptables.RemoveExistingChain("DOCKER", iptables.Nat); err != nil {
|
||||||
|
return job.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if enableIPTables {
|
||||||
|
_, err := iptables.NewChain("DOCKER", bridgeIface, iptables.Nat)
|
||||||
|
if err != nil {
|
||||||
|
return job.Error(err)
|
||||||
|
}
|
||||||
|
chain, err := iptables.NewChain("DOCKER", bridgeIface, iptables.Filter)
|
||||||
|
if err != nil {
|
||||||
|
return job.Error(err)
|
||||||
|
}
|
||||||
|
portmapper.SetIptablesChain(chain)
|
||||||
|
}
|
||||||
|
|
||||||
|
bridgeIPv4Network = networkv4
|
||||||
|
if fixedCIDR != "" {
|
||||||
|
_, subnet, err := net.ParseCIDR(fixedCIDR)
|
||||||
|
if err != nil {
|
||||||
|
return job.Error(err)
|
||||||
|
}
|
||||||
|
log.Debugf("Subnet: %v", subnet)
|
||||||
|
if err := ipallocator.RegisterSubnet(bridgeIPv4Network, subnet); err != nil {
|
||||||
|
return job.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if fixedCIDRv6 != "" {
|
||||||
|
_, subnet, err := net.ParseCIDR(fixedCIDRv6)
|
||||||
|
if err != nil {
|
||||||
|
return job.Error(err)
|
||||||
|
}
|
||||||
|
log.Debugf("Subnet: %v", subnet)
|
||||||
|
if err := ipallocator.RegisterSubnet(subnet, subnet); err != nil {
|
||||||
|
return job.Error(err)
|
||||||
|
}
|
||||||
|
globalIPv6Network = subnet
|
||||||
|
}
|
||||||
|
|
||||||
|
// Block BridgeIP in IP allocator
|
||||||
|
ipallocator.RequestIP(bridgeIPv4Network, bridgeIPv4Network.IP)
|
||||||
|
|
||||||
|
// https://github.com/docker/docker/issues/2768
|
||||||
|
job.Eng.Hack_SetGlobalVar("httpapi.bridgeIP", bridgeIPv4Network.IP)
|
||||||
|
|
||||||
|
for name, f := range map[string]engine.Handler{
|
||||||
|
"allocate_interface": Allocate,
|
||||||
|
"release_interface": Release,
|
||||||
|
"allocate_port": AllocatePort,
|
||||||
|
"link": LinkContainers,
|
||||||
|
} {
|
||||||
|
if err := job.Eng.Register(name, f); err != nil {
|
||||||
|
return job.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return engine.StatusOK
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupIPTables(addr net.Addr, icc, ipmasq bool) error {
|
||||||
|
// Enable NAT
|
||||||
|
|
||||||
|
if ipmasq {
|
||||||
|
natArgs := []string{"POSTROUTING", "-t", "nat", "-s", addr.String(), "!", "-o", bridgeIface, "-j", "MASQUERADE"}
|
||||||
|
|
||||||
|
if !iptables.Exists(natArgs...) {
|
||||||
|
if output, err := iptables.Raw(append([]string{"-I"}, natArgs...)...); err != nil {
|
||||||
|
return fmt.Errorf("Unable to enable network bridge NAT: %s", err)
|
||||||
|
} else if len(output) != 0 {
|
||||||
|
return &iptables.ChainError{Chain: "POSTROUTING", Output: output}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
args = []string{"FORWARD", "-i", bridgeIface, "-o", bridgeIface, "-j"}
|
||||||
|
acceptArgs = append(args, "ACCEPT")
|
||||||
|
dropArgs = append(args, "DROP")
|
||||||
|
)
|
||||||
|
|
||||||
|
if !icc {
|
||||||
|
iptables.Raw(append([]string{"-D"}, acceptArgs...)...)
|
||||||
|
|
||||||
|
if !iptables.Exists(dropArgs...) {
|
||||||
|
log.Debugf("Disable inter-container communication")
|
||||||
|
if output, err := iptables.Raw(append([]string{"-I"}, dropArgs...)...); err != nil {
|
||||||
|
return fmt.Errorf("Unable to prevent intercontainer communication: %s", err)
|
||||||
|
} else if len(output) != 0 {
|
||||||
|
return fmt.Errorf("Error disabling intercontainer communication: %s", output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
iptables.Raw(append([]string{"-D"}, dropArgs...)...)
|
||||||
|
|
||||||
|
if !iptables.Exists(acceptArgs...) {
|
||||||
|
log.Debugf("Enable inter-container communication")
|
||||||
|
if output, err := iptables.Raw(append([]string{"-I"}, acceptArgs...)...); err != nil {
|
||||||
|
return fmt.Errorf("Unable to allow intercontainer communication: %s", err)
|
||||||
|
} else if len(output) != 0 {
|
||||||
|
return fmt.Errorf("Error enabling intercontainer communication: %s", output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Accept all non-intercontainer outgoing packets
|
||||||
|
outgoingArgs := []string{"FORWARD", "-i", bridgeIface, "!", "-o", bridgeIface, "-j", "ACCEPT"}
|
||||||
|
if !iptables.Exists(outgoingArgs...) {
|
||||||
|
if output, err := iptables.Raw(append([]string{"-I"}, outgoingArgs...)...); err != nil {
|
||||||
|
return fmt.Errorf("Unable to allow outgoing packets: %s", err)
|
||||||
|
} else if len(output) != 0 {
|
||||||
|
return &iptables.ChainError{Chain: "FORWARD outgoing", Output: output}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Accept incoming packets for existing connections
|
||||||
|
existingArgs := []string{"FORWARD", "-o", bridgeIface, "-m", "conntrack", "--ctstate", "RELATED,ESTABLISHED", "-j", "ACCEPT"}
|
||||||
|
|
||||||
|
if !iptables.Exists(existingArgs...) {
|
||||||
|
if output, err := iptables.Raw(append([]string{"-I"}, existingArgs...)...); err != nil {
|
||||||
|
return fmt.Errorf("Unable to allow incoming packets: %s", err)
|
||||||
|
} else if len(output) != 0 {
|
||||||
|
return &iptables.ChainError{Chain: "FORWARD incoming", Output: output}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// configureBridge attempts to create and configure a network bridge interface named `bridgeIface` on the host
|
||||||
|
// If bridgeIP is empty, it will try to find a non-conflicting IP from the Docker-specified private ranges
|
||||||
|
// If the bridge `bridgeIface` already exists, it will only perform the IP address association with the existing
|
||||||
|
// bridge (fixes issue #8444)
|
||||||
|
// If an address which doesn't conflict with existing interfaces can't be found, an error is returned.
|
||||||
|
func configureBridge(bridgeIP string, bridgeIPv6 string, enableIPv6 bool) error {
|
||||||
|
nameservers := []string{}
|
||||||
|
resolvConf, _ := resolvconf.Get()
|
||||||
|
// we don't check for an error here, because we don't really care
|
||||||
|
// if we can't read /etc/resolv.conf. So instead we skip the append
|
||||||
|
// if resolvConf is nil. It either doesn't exist, or we can't read it
|
||||||
|
// for some reason.
|
||||||
|
if resolvConf != nil {
|
||||||
|
nameservers = append(nameservers, resolvconf.GetNameserversAsCIDR(resolvConf)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
var ifaceAddr string
|
||||||
|
if len(bridgeIP) != 0 {
|
||||||
|
_, _, err := net.ParseCIDR(bridgeIP)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ifaceAddr = bridgeIP
|
||||||
|
} else {
|
||||||
|
for _, addr := range addrs {
|
||||||
|
_, dockerNetwork, err := net.ParseCIDR(addr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := networkdriver.CheckNameserverOverlaps(nameservers, dockerNetwork); err == nil {
|
||||||
|
if err := networkdriver.CheckRouteOverlaps(dockerNetwork); err == nil {
|
||||||
|
ifaceAddr = addr
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
log.Debugf("%s %s", addr, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ifaceAddr == "" {
|
||||||
|
return fmt.Errorf("Could not find a free IP address range for interface '%s'. Please configure its address manually and run 'docker -b %s'", bridgeIface, bridgeIface)
|
||||||
|
}
|
||||||
|
log.Debugf("Creating bridge %s with network %s", bridgeIface, ifaceAddr)
|
||||||
|
|
||||||
|
if err := createBridgeIface(bridgeIface); err != nil {
|
||||||
|
// the bridge may already exist, therefore we can ignore an "exists" error
|
||||||
|
if !os.IsExist(err) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
iface, err := net.InterfaceByName(bridgeIface)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ipAddr, ipNet, err := net.ParseCIDR(ifaceAddr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := netlink.NetworkLinkAddIp(iface, ipAddr, ipNet); err != nil {
|
||||||
|
return fmt.Errorf("Unable to add private network: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if enableIPv6 {
|
||||||
|
if err := setupIPv6Bridge(bridgeIPv6); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := netlink.NetworkLinkUp(iface); err != nil {
|
||||||
|
return fmt.Errorf("Unable to start network bridge: %s", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupIPv6Bridge(bridgeIPv6 string) error {
|
||||||
|
|
||||||
|
iface, err := net.InterfaceByName(bridgeIface)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Enable IPv6 on the bridge
|
||||||
|
procFile := "/proc/sys/net/ipv6/conf/" + iface.Name + "/disable_ipv6"
|
||||||
|
if err := ioutil.WriteFile(procFile, []byte{'0', '\n'}, 0644); err != nil {
|
||||||
|
return fmt.Errorf("Unable to enable IPv6 addresses on bridge: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ipAddr6, ipNet6, err := net.ParseCIDR(bridgeIPv6)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Unable to parse bridge IPv6 address: %q, error: %v", bridgeIPv6, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := netlink.NetworkLinkAddIp(iface, ipAddr6, ipNet6); err != nil {
|
||||||
|
return fmt.Errorf("Unable to add private IPv6 network: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createBridgeIface(name string) error {
|
||||||
|
kv, err := kernel.GetKernelVersion()
|
||||||
|
// only set the bridge's mac address if the kernel version is > 3.3
|
||||||
|
// before that it was not supported
|
||||||
|
setBridgeMacAddr := err == nil && (kv.Kernel >= 3 && kv.Major >= 3)
|
||||||
|
log.Debugf("setting bridge mac address = %v", setBridgeMacAddr)
|
||||||
|
return netlink.CreateBridge(name, setBridgeMacAddr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate a IEEE802 compliant MAC address from the given IP address.
|
||||||
|
//
|
||||||
|
// The generator is guaranteed to be consistent: the same IP will always yield the same
|
||||||
|
// MAC address. This is to avoid ARP cache issues.
|
||||||
|
func generateMacAddr(ip net.IP) net.HardwareAddr {
|
||||||
|
hw := make(net.HardwareAddr, 6)
|
||||||
|
|
||||||
|
// The first byte of the MAC address has to comply with these rules:
|
||||||
|
// 1. Unicast: Set the least-significant bit to 0.
|
||||||
|
// 2. Address is locally administered: Set the second-least-significant bit (U/L) to 1.
|
||||||
|
// 3. As "small" as possible: The veth address has to be "smaller" than the bridge address.
|
||||||
|
hw[0] = 0x02
|
||||||
|
|
||||||
|
// The first 24 bits of the MAC represent the Organizationally Unique Identifier (OUI).
|
||||||
|
// Since this address is locally administered, we can do whatever we want as long as
|
||||||
|
// it doesn't conflict with other addresses.
|
||||||
|
hw[1] = 0x42
|
||||||
|
|
||||||
|
// Insert the IP address into the last 32 bits of the MAC address.
|
||||||
|
// This is a simple way to guarantee the address will be consistent and unique.
|
||||||
|
copy(hw[2:], ip.To4())
|
||||||
|
|
||||||
|
return hw
|
||||||
|
}
|
||||||
|
|
||||||
|
func linkLocalIPv6FromMac(mac string) (string, error) {
|
||||||
|
hx := strings.Replace(mac, ":", "", -1)
|
||||||
|
hw, err := hex.DecodeString(hx)
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.New("Could not parse MAC address " + mac)
|
||||||
|
}
|
||||||
|
|
||||||
|
hw[0] ^= 0x2
|
||||||
|
|
||||||
|
return fmt.Sprintf("fe80::%x%x:%xff:fe%x:%x%x/64", hw[0], hw[1], hw[2], hw[3], hw[4], hw[5]), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocate a network interface
|
||||||
|
func Allocate(job *engine.Job) engine.Status {
|
||||||
|
var (
|
||||||
|
ip net.IP
|
||||||
|
mac net.HardwareAddr
|
||||||
|
err error
|
||||||
|
id = job.Args[0]
|
||||||
|
requestedIP = net.ParseIP(job.Getenv("RequestedIP"))
|
||||||
|
requestedIPv6 = net.ParseIP(job.Getenv("RequestedIPv6"))
|
||||||
|
globalIPv6 net.IP
|
||||||
|
)
|
||||||
|
|
||||||
|
if requestedIP != nil {
|
||||||
|
ip, err = ipallocator.RequestIP(bridgeIPv4Network, requestedIP)
|
||||||
|
} else {
|
||||||
|
ip, err = ipallocator.RequestIP(bridgeIPv4Network, nil)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return job.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no explicit mac address was given, generate a random one.
|
||||||
|
if mac, err = net.ParseMAC(job.Getenv("RequestedMac")); err != nil {
|
||||||
|
mac = generateMacAddr(ip)
|
||||||
|
}
|
||||||
|
|
||||||
|
if globalIPv6Network != nil {
|
||||||
|
// if globalIPv6Network Size is at least a /80 subnet generate IPv6 address from MAC address
|
||||||
|
netmask_ones, _ := globalIPv6Network.Mask.Size()
|
||||||
|
if requestedIPv6 == nil && netmask_ones <= 80 {
|
||||||
|
requestedIPv6 = globalIPv6Network.IP
|
||||||
|
for i, h := range mac {
|
||||||
|
requestedIPv6[i+10] = h
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
globalIPv6, err = ipallocator.RequestIP(globalIPv6Network, requestedIPv6)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Allocator: RequestIP v6: %s", err.Error())
|
||||||
|
return job.Error(err)
|
||||||
|
}
|
||||||
|
log.Infof("Allocated IPv6 %s", globalIPv6)
|
||||||
|
}
|
||||||
|
|
||||||
|
out := engine.Env{}
|
||||||
|
out.Set("IP", ip.String())
|
||||||
|
out.Set("Mask", bridgeIPv4Network.Mask.String())
|
||||||
|
out.Set("Gateway", bridgeIPv4Network.IP.String())
|
||||||
|
out.Set("MacAddress", mac.String())
|
||||||
|
out.Set("Bridge", bridgeIface)
|
||||||
|
|
||||||
|
size, _ := bridgeIPv4Network.Mask.Size()
|
||||||
|
out.SetInt("IPPrefixLen", size)
|
||||||
|
|
||||||
|
// if linklocal IPv6
|
||||||
|
localIPv6Net, err := linkLocalIPv6FromMac(mac.String())
|
||||||
|
if err != nil {
|
||||||
|
return job.Error(err)
|
||||||
|
}
|
||||||
|
localIPv6, _, _ := net.ParseCIDR(localIPv6Net)
|
||||||
|
out.Set("LinkLocalIPv6", localIPv6.String())
|
||||||
|
out.Set("MacAddress", mac.String())
|
||||||
|
|
||||||
|
if globalIPv6Network != nil {
|
||||||
|
out.Set("GlobalIPv6", globalIPv6.String())
|
||||||
|
sizev6, _ := globalIPv6Network.Mask.Size()
|
||||||
|
out.SetInt("GlobalIPv6PrefixLen", sizev6)
|
||||||
|
out.Set("IPv6Gateway", bridgeIPv6Addr.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
currentInterfaces.Set(id, &networkInterface{
|
||||||
|
IP: ip,
|
||||||
|
IPv6: globalIPv6,
|
||||||
|
})
|
||||||
|
|
||||||
|
out.WriteTo(job.Stdout)
|
||||||
|
|
||||||
|
return engine.StatusOK
|
||||||
|
}
|
||||||
|
|
||||||
|
// release an interface for a select ip
|
||||||
|
func Release(job *engine.Job) engine.Status {
|
||||||
|
var (
|
||||||
|
id = job.Args[0]
|
||||||
|
containerInterface = currentInterfaces.Get(id)
|
||||||
|
)
|
||||||
|
|
||||||
|
if containerInterface == nil {
|
||||||
|
return job.Errorf("No network information to release for %s", id)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, nat := range containerInterface.PortMappings {
|
||||||
|
if err := portmapper.Unmap(nat); err != nil {
|
||||||
|
log.Infof("Unable to unmap port %s: %s", nat, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ipallocator.ReleaseIP(bridgeIPv4Network, containerInterface.IP); err != nil {
|
||||||
|
log.Infof("Unable to release IPv4 %s", err)
|
||||||
|
}
|
||||||
|
if globalIPv6Network != nil {
|
||||||
|
if err := ipallocator.ReleaseIP(globalIPv6Network, containerInterface.IPv6); err != nil {
|
||||||
|
log.Infof("Unable to release IPv6 %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return engine.StatusOK
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocate an external port and map it to the interface
|
||||||
|
func AllocatePort(job *engine.Job) engine.Status {
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
|
||||||
|
ip = defaultBindingIP
|
||||||
|
id = job.Args[0]
|
||||||
|
hostIP = job.Getenv("HostIP")
|
||||||
|
hostPort = job.GetenvInt("HostPort")
|
||||||
|
containerPort = job.GetenvInt("ContainerPort")
|
||||||
|
proto = job.Getenv("Proto")
|
||||||
|
network = currentInterfaces.Get(id)
|
||||||
|
)
|
||||||
|
|
||||||
|
if hostIP != "" {
|
||||||
|
ip = net.ParseIP(hostIP)
|
||||||
|
if ip == nil {
|
||||||
|
return job.Errorf("Bad parameter: invalid host ip %s", hostIP)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// host ip, proto, and host port
|
||||||
|
var container net.Addr
|
||||||
|
switch proto {
|
||||||
|
case "tcp":
|
||||||
|
container = &net.TCPAddr{IP: network.IP, Port: containerPort}
|
||||||
|
case "udp":
|
||||||
|
container = &net.UDPAddr{IP: network.IP, Port: containerPort}
|
||||||
|
default:
|
||||||
|
return job.Errorf("unsupported address type %s", proto)
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Try up to 10 times to get a port that's not already allocated.
|
||||||
|
//
|
||||||
|
// In the event of failure to bind, return the error that portmapper.Map
|
||||||
|
// yields.
|
||||||
|
//
|
||||||
|
|
||||||
|
var host net.Addr
|
||||||
|
for i := 0; i < MaxAllocatedPortAttempts; i++ {
|
||||||
|
if host, err = portmapper.Map(container, ip, hostPort); err == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// There is no point in immediately retrying to map an explicitly
|
||||||
|
// chosen port.
|
||||||
|
if hostPort != 0 {
|
||||||
|
job.Logf("Failed to allocate and map port %d: %s", hostPort, err)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
job.Logf("Failed to allocate and map port: %s, retry: %d", err, i+1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return job.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
network.PortMappings = append(network.PortMappings, host)
|
||||||
|
|
||||||
|
out := engine.Env{}
|
||||||
|
switch netAddr := host.(type) {
|
||||||
|
case *net.TCPAddr:
|
||||||
|
out.Set("HostIP", netAddr.IP.String())
|
||||||
|
out.SetInt("HostPort", netAddr.Port)
|
||||||
|
case *net.UDPAddr:
|
||||||
|
out.Set("HostIP", netAddr.IP.String())
|
||||||
|
out.SetInt("HostPort", netAddr.Port)
|
||||||
|
}
|
||||||
|
if _, err := out.WriteTo(job.Stdout); err != nil {
|
||||||
|
return job.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return engine.StatusOK
|
||||||
|
}
|
||||||
|
|
||||||
|
func LinkContainers(job *engine.Job) engine.Status {
|
||||||
|
var (
|
||||||
|
action = job.Args[0]
|
||||||
|
nfAction iptables.Action
|
||||||
|
childIP = job.Getenv("ChildIP")
|
||||||
|
parentIP = job.Getenv("ParentIP")
|
||||||
|
ignoreErrors = job.GetenvBool("IgnoreErrors")
|
||||||
|
ports = job.GetenvList("Ports")
|
||||||
|
)
|
||||||
|
|
||||||
|
switch action {
|
||||||
|
case "-A":
|
||||||
|
nfAction = iptables.Append
|
||||||
|
case "-I":
|
||||||
|
nfAction = iptables.Insert
|
||||||
|
case "-D":
|
||||||
|
nfAction = iptables.Delete
|
||||||
|
default:
|
||||||
|
return job.Errorf("Invalid action '%s' specified", action)
|
||||||
|
}
|
||||||
|
|
||||||
|
ip1 := net.ParseIP(parentIP)
|
||||||
|
if ip1 == nil {
|
||||||
|
return job.Errorf("parent IP '%s' is invalid", parentIP)
|
||||||
|
}
|
||||||
|
ip2 := net.ParseIP(childIP)
|
||||||
|
if ip2 == nil {
|
||||||
|
return job.Errorf("child IP '%s' is invalid", childIP)
|
||||||
|
}
|
||||||
|
|
||||||
|
chain := iptables.Chain{Name: "DOCKER", Bridge: bridgeIface}
|
||||||
|
for _, p := range ports {
|
||||||
|
port := nat.Port(p)
|
||||||
|
if err := chain.Link(nfAction, ip1, ip2, port.Int(), port.Proto()); !ignoreErrors && err != nil {
|
||||||
|
return job.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return engine.StatusOK
|
||||||
|
}
|
161
libnetwork/Godeps/_workspace/src/github.com/docker/docker/daemon/networkdriver/bridge/driver_test.go
generated
vendored
Normal file
161
libnetwork/Godeps/_workspace/src/github.com/docker/docker/daemon/networkdriver/bridge/driver_test.go
generated
vendored
Normal file
|
@ -0,0 +1,161 @@
|
||||||
|
package bridge
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"strconv"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/docker/docker/daemon/networkdriver/portmapper"
|
||||||
|
"github.com/docker/docker/engine"
|
||||||
|
"github.com/docker/docker/pkg/iptables"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// reset the new proxy command for mocking out the userland proxy in tests
|
||||||
|
portmapper.NewProxy = portmapper.NewMockProxyCommand
|
||||||
|
}
|
||||||
|
|
||||||
|
func findFreePort(t *testing.T) int {
|
||||||
|
l, err := net.Listen("tcp", ":0")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Failed to find a free port")
|
||||||
|
}
|
||||||
|
defer l.Close()
|
||||||
|
|
||||||
|
result, err := net.ResolveTCPAddr("tcp", l.Addr().String())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Failed to resolve address to identify free port")
|
||||||
|
}
|
||||||
|
return result.Port
|
||||||
|
}
|
||||||
|
|
||||||
|
func newPortAllocationJob(eng *engine.Engine, port int) (job *engine.Job) {
|
||||||
|
strPort := strconv.Itoa(port)
|
||||||
|
|
||||||
|
job = eng.Job("allocate_port", "container_id")
|
||||||
|
job.Setenv("HostIP", "127.0.0.1")
|
||||||
|
job.Setenv("HostPort", strPort)
|
||||||
|
job.Setenv("Proto", "tcp")
|
||||||
|
job.Setenv("ContainerPort", strPort)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func newPortAllocationJobWithInvalidHostIP(eng *engine.Engine, port int) (job *engine.Job) {
|
||||||
|
strPort := strconv.Itoa(port)
|
||||||
|
|
||||||
|
job = eng.Job("allocate_port", "container_id")
|
||||||
|
job.Setenv("HostIP", "localhost")
|
||||||
|
job.Setenv("HostPort", strPort)
|
||||||
|
job.Setenv("Proto", "tcp")
|
||||||
|
job.Setenv("ContainerPort", strPort)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAllocatePortDetection(t *testing.T) {
|
||||||
|
eng := engine.New()
|
||||||
|
eng.Logging = false
|
||||||
|
|
||||||
|
freePort := findFreePort(t)
|
||||||
|
|
||||||
|
// Init driver
|
||||||
|
job := eng.Job("initdriver")
|
||||||
|
if res := InitDriver(job); res != engine.StatusOK {
|
||||||
|
t.Fatal("Failed to initialize network driver")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocate interface
|
||||||
|
job = eng.Job("allocate_interface", "container_id")
|
||||||
|
if res := Allocate(job); res != engine.StatusOK {
|
||||||
|
t.Fatal("Failed to allocate network interface")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocate same port twice, expect failure on second call
|
||||||
|
job = newPortAllocationJob(eng, freePort)
|
||||||
|
if res := AllocatePort(job); res != engine.StatusOK {
|
||||||
|
t.Fatal("Failed to find a free port to allocate")
|
||||||
|
}
|
||||||
|
if res := AllocatePort(job); res == engine.StatusOK {
|
||||||
|
t.Fatal("Duplicate port allocation granted by AllocatePort")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHostnameFormatChecking(t *testing.T) {
|
||||||
|
eng := engine.New()
|
||||||
|
eng.Logging = false
|
||||||
|
|
||||||
|
freePort := findFreePort(t)
|
||||||
|
|
||||||
|
// Init driver
|
||||||
|
job := eng.Job("initdriver")
|
||||||
|
if res := InitDriver(job); res != engine.StatusOK {
|
||||||
|
t.Fatal("Failed to initialize network driver")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocate interface
|
||||||
|
job = eng.Job("allocate_interface", "container_id")
|
||||||
|
if res := Allocate(job); res != engine.StatusOK {
|
||||||
|
t.Fatal("Failed to allocate network interface")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocate port with invalid HostIP, expect failure with Bad Request http status
|
||||||
|
job = newPortAllocationJobWithInvalidHostIP(eng, freePort)
|
||||||
|
if res := AllocatePort(job); res == engine.StatusOK {
|
||||||
|
t.Fatal("Failed to check invalid HostIP")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMacAddrGeneration(t *testing.T) {
|
||||||
|
ip := net.ParseIP("192.168.0.1")
|
||||||
|
mac := generateMacAddr(ip).String()
|
||||||
|
|
||||||
|
// Should be consistent.
|
||||||
|
if generateMacAddr(ip).String() != mac {
|
||||||
|
t.Fatal("Inconsistent MAC address")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should be unique.
|
||||||
|
ip2 := net.ParseIP("192.168.0.2")
|
||||||
|
if generateMacAddr(ip2).String() == mac {
|
||||||
|
t.Fatal("Non-unique MAC address")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLinkContainers(t *testing.T) {
|
||||||
|
eng := engine.New()
|
||||||
|
eng.Logging = false
|
||||||
|
|
||||||
|
// Init driver
|
||||||
|
job := eng.Job("initdriver")
|
||||||
|
if res := InitDriver(job); res != engine.StatusOK {
|
||||||
|
t.Fatal("Failed to initialize network driver")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocate interface
|
||||||
|
job = eng.Job("allocate_interface", "container_id")
|
||||||
|
if res := Allocate(job); res != engine.StatusOK {
|
||||||
|
t.Fatal("Failed to allocate network interface")
|
||||||
|
}
|
||||||
|
|
||||||
|
job.Args[0] = "-I"
|
||||||
|
|
||||||
|
job.Setenv("ChildIP", "172.17.0.2")
|
||||||
|
job.Setenv("ParentIP", "172.17.0.1")
|
||||||
|
job.SetenvBool("IgnoreErrors", false)
|
||||||
|
job.SetenvList("Ports", []string{"1234"})
|
||||||
|
|
||||||
|
bridgeIface = "lo"
|
||||||
|
_, err := iptables.NewChain("DOCKER", bridgeIface, iptables.Filter)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if res := LinkContainers(job); res != engine.StatusOK {
|
||||||
|
t.Fatalf("LinkContainers failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
// flush rules
|
||||||
|
if _, err = iptables.Raw([]string{"-F", "DOCKER"}...); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
160
libnetwork/Godeps/_workspace/src/github.com/docker/docker/daemon/networkdriver/ipallocator/allocator.go
generated
vendored
Normal file
160
libnetwork/Godeps/_workspace/src/github.com/docker/docker/daemon/networkdriver/ipallocator/allocator.go
generated
vendored
Normal file
|
@ -0,0 +1,160 @@
|
||||||
|
package ipallocator
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"math/big"
|
||||||
|
"net"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
log "github.com/Sirupsen/logrus"
|
||||||
|
"github.com/docker/docker/daemon/networkdriver"
|
||||||
|
)
|
||||||
|
|
||||||
|
// allocatedMap is thread-unsafe set of allocated IP
|
||||||
|
type allocatedMap struct {
|
||||||
|
p map[string]struct{}
|
||||||
|
last *big.Int
|
||||||
|
begin *big.Int
|
||||||
|
end *big.Int
|
||||||
|
}
|
||||||
|
|
||||||
|
func newAllocatedMap(network *net.IPNet) *allocatedMap {
|
||||||
|
firstIP, lastIP := networkdriver.NetworkRange(network)
|
||||||
|
begin := big.NewInt(0).Add(ipToBigInt(firstIP), big.NewInt(1))
|
||||||
|
end := big.NewInt(0).Sub(ipToBigInt(lastIP), big.NewInt(1))
|
||||||
|
|
||||||
|
return &allocatedMap{
|
||||||
|
p: make(map[string]struct{}),
|
||||||
|
begin: begin,
|
||||||
|
end: end,
|
||||||
|
last: big.NewInt(0).Sub(begin, big.NewInt(1)), // so first allocated will be begin
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type networkSet map[string]*allocatedMap
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrNoAvailableIPs = errors.New("no available ip addresses on network")
|
||||||
|
ErrIPAlreadyAllocated = errors.New("ip already allocated")
|
||||||
|
ErrIPOutOfRange = errors.New("requested ip is out of range")
|
||||||
|
ErrNetworkAlreadyRegistered = errors.New("network already registered")
|
||||||
|
ErrBadSubnet = errors.New("network does not contain specified subnet")
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
lock = sync.Mutex{}
|
||||||
|
allocatedIPs = networkSet{}
|
||||||
|
)
|
||||||
|
|
||||||
|
// RegisterSubnet registers network in global allocator with bounds
|
||||||
|
// defined by subnet. If you want to use network range you must call
|
||||||
|
// this method before first RequestIP, otherwise full network range will be used
|
||||||
|
func RegisterSubnet(network *net.IPNet, subnet *net.IPNet) error {
|
||||||
|
lock.Lock()
|
||||||
|
defer lock.Unlock()
|
||||||
|
key := network.String()
|
||||||
|
if _, ok := allocatedIPs[key]; ok {
|
||||||
|
return ErrNetworkAlreadyRegistered
|
||||||
|
}
|
||||||
|
n := newAllocatedMap(network)
|
||||||
|
beginIP, endIP := networkdriver.NetworkRange(subnet)
|
||||||
|
begin := big.NewInt(0).Add(ipToBigInt(beginIP), big.NewInt(1))
|
||||||
|
end := big.NewInt(0).Sub(ipToBigInt(endIP), big.NewInt(1))
|
||||||
|
|
||||||
|
// Check that subnet is within network
|
||||||
|
if !(begin.Cmp(n.begin) >= 0 && end.Cmp(n.end) <= 0 && begin.Cmp(end) == -1) {
|
||||||
|
return ErrBadSubnet
|
||||||
|
}
|
||||||
|
n.begin.Set(begin)
|
||||||
|
n.end.Set(end)
|
||||||
|
n.last.Sub(begin, big.NewInt(1))
|
||||||
|
allocatedIPs[key] = n
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RequestIP requests an available ip from the given network. It
|
||||||
|
// will return the next available ip if the ip provided is nil. If the
|
||||||
|
// ip provided is not nil it will validate that the provided ip is available
|
||||||
|
// for use or return an error
|
||||||
|
func RequestIP(network *net.IPNet, ip net.IP) (net.IP, error) {
|
||||||
|
lock.Lock()
|
||||||
|
defer lock.Unlock()
|
||||||
|
key := network.String()
|
||||||
|
allocated, ok := allocatedIPs[key]
|
||||||
|
if !ok {
|
||||||
|
allocated = newAllocatedMap(network)
|
||||||
|
allocatedIPs[key] = allocated
|
||||||
|
}
|
||||||
|
|
||||||
|
if ip == nil {
|
||||||
|
return allocated.getNextIP()
|
||||||
|
}
|
||||||
|
return allocated.checkIP(ip)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReleaseIP adds the provided ip back into the pool of
|
||||||
|
// available ips to be returned for use.
|
||||||
|
func ReleaseIP(network *net.IPNet, ip net.IP) error {
|
||||||
|
lock.Lock()
|
||||||
|
defer lock.Unlock()
|
||||||
|
if allocated, exists := allocatedIPs[network.String()]; exists {
|
||||||
|
delete(allocated.p, ip.String())
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (allocated *allocatedMap) checkIP(ip net.IP) (net.IP, error) {
|
||||||
|
if _, ok := allocated.p[ip.String()]; ok {
|
||||||
|
return nil, ErrIPAlreadyAllocated
|
||||||
|
}
|
||||||
|
|
||||||
|
pos := ipToBigInt(ip)
|
||||||
|
// Verify that the IP address is within our network range.
|
||||||
|
if pos.Cmp(allocated.begin) == -1 || pos.Cmp(allocated.end) == 1 {
|
||||||
|
return nil, ErrIPOutOfRange
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register the IP.
|
||||||
|
allocated.p[ip.String()] = struct{}{}
|
||||||
|
|
||||||
|
return ip, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// return an available ip if one is currently available. If not,
|
||||||
|
// return the next available ip for the nextwork
|
||||||
|
func (allocated *allocatedMap) getNextIP() (net.IP, error) {
|
||||||
|
pos := big.NewInt(0).Set(allocated.last)
|
||||||
|
allRange := big.NewInt(0).Sub(allocated.end, allocated.begin)
|
||||||
|
for i := big.NewInt(0); i.Cmp(allRange) <= 0; i.Add(i, big.NewInt(1)) {
|
||||||
|
pos.Add(pos, big.NewInt(1))
|
||||||
|
if pos.Cmp(allocated.end) == 1 {
|
||||||
|
pos.Set(allocated.begin)
|
||||||
|
}
|
||||||
|
if _, ok := allocated.p[bigIntToIP(pos).String()]; ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
allocated.p[bigIntToIP(pos).String()] = struct{}{}
|
||||||
|
allocated.last.Set(pos)
|
||||||
|
return bigIntToIP(pos), nil
|
||||||
|
}
|
||||||
|
return nil, ErrNoAvailableIPs
|
||||||
|
}
|
||||||
|
|
||||||
|
// Converts a 4 bytes IP into a 128 bit integer
|
||||||
|
func ipToBigInt(ip net.IP) *big.Int {
|
||||||
|
x := big.NewInt(0)
|
||||||
|
if ip4 := ip.To4(); ip4 != nil {
|
||||||
|
return x.SetBytes(ip4)
|
||||||
|
}
|
||||||
|
if ip6 := ip.To16(); ip6 != nil {
|
||||||
|
return x.SetBytes(ip6)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Errorf("ipToBigInt: Wrong IP length! %s", ip)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Converts 128 bit integer into a 4 bytes IP address
|
||||||
|
func bigIntToIP(v *big.Int) net.IP {
|
||||||
|
return net.IP(v.Bytes())
|
||||||
|
}
|
681
libnetwork/Godeps/_workspace/src/github.com/docker/docker/daemon/networkdriver/ipallocator/allocator_test.go
generated
vendored
Normal file
681
libnetwork/Godeps/_workspace/src/github.com/docker/docker/daemon/networkdriver/ipallocator/allocator_test.go
generated
vendored
Normal file
|
@ -0,0 +1,681 @@
|
||||||
|
package ipallocator
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
"net"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func reset() {
|
||||||
|
allocatedIPs = networkSet{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConversion(t *testing.T) {
|
||||||
|
ip := net.ParseIP("127.0.0.1")
|
||||||
|
i := ipToBigInt(ip)
|
||||||
|
if i.Cmp(big.NewInt(0x7f000001)) != 0 {
|
||||||
|
t.Fatal("incorrect conversion")
|
||||||
|
}
|
||||||
|
conv := bigIntToIP(i)
|
||||||
|
if !ip.Equal(conv) {
|
||||||
|
t.Error(conv.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConversionIPv6(t *testing.T) {
|
||||||
|
ip := net.ParseIP("2a00:1450::1")
|
||||||
|
ip2 := net.ParseIP("2a00:1450::2")
|
||||||
|
ip3 := net.ParseIP("2a00:1450::1:1")
|
||||||
|
i := ipToBigInt(ip)
|
||||||
|
val, success := big.NewInt(0).SetString("2a001450000000000000000000000001", 16)
|
||||||
|
if !success {
|
||||||
|
t.Fatal("Hex-String to BigInt conversion failed.")
|
||||||
|
}
|
||||||
|
if i.Cmp(val) != 0 {
|
||||||
|
t.Fatal("incorrent conversion")
|
||||||
|
}
|
||||||
|
|
||||||
|
conv := bigIntToIP(i)
|
||||||
|
conv2 := bigIntToIP(big.NewInt(0).Add(i, big.NewInt(1)))
|
||||||
|
conv3 := bigIntToIP(big.NewInt(0).Add(i, big.NewInt(0x10000)))
|
||||||
|
|
||||||
|
if !ip.Equal(conv) {
|
||||||
|
t.Error("2a00:1450::1 should be equal to " + conv.String())
|
||||||
|
}
|
||||||
|
if !ip2.Equal(conv2) {
|
||||||
|
t.Error("2a00:1450::2 should be equal to " + conv2.String())
|
||||||
|
}
|
||||||
|
if !ip3.Equal(conv3) {
|
||||||
|
t.Error("2a00:1450::1:1 should be equal to " + conv3.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRequestNewIps(t *testing.T) {
|
||||||
|
defer reset()
|
||||||
|
network := &net.IPNet{
|
||||||
|
IP: []byte{192, 168, 0, 1},
|
||||||
|
Mask: []byte{255, 255, 255, 0},
|
||||||
|
}
|
||||||
|
|
||||||
|
var ip net.IP
|
||||||
|
var err error
|
||||||
|
|
||||||
|
for i := 1; i < 10; i++ {
|
||||||
|
ip, err = RequestIP(network, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if expected := fmt.Sprintf("192.168.0.%d", i); ip.String() != expected {
|
||||||
|
t.Fatalf("Expected ip %s got %s", expected, ip.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
value := bigIntToIP(big.NewInt(0).Add(ipToBigInt(ip), big.NewInt(1))).String()
|
||||||
|
if err := ReleaseIP(network, ip); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
ip, err = RequestIP(network, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if ip.String() != value {
|
||||||
|
t.Fatalf("Expected to receive the next ip %s got %s", value, ip.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRequestNewIpV6(t *testing.T) {
|
||||||
|
defer reset()
|
||||||
|
network := &net.IPNet{
|
||||||
|
IP: []byte{0x2a, 0x00, 0x14, 0x50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
|
||||||
|
Mask: []byte{255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0}, // /64 netmask
|
||||||
|
}
|
||||||
|
|
||||||
|
var ip net.IP
|
||||||
|
var err error
|
||||||
|
for i := 1; i < 10; i++ {
|
||||||
|
ip, err = RequestIP(network, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if expected := fmt.Sprintf("2a00:1450::%d", i); ip.String() != expected {
|
||||||
|
t.Fatalf("Expected ip %s got %s", expected, ip.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
value := bigIntToIP(big.NewInt(0).Add(ipToBigInt(ip), big.NewInt(1))).String()
|
||||||
|
if err := ReleaseIP(network, ip); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
ip, err = RequestIP(network, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if ip.String() != value {
|
||||||
|
t.Fatalf("Expected to receive the next ip %s got %s", value, ip.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReleaseIp(t *testing.T) {
|
||||||
|
defer reset()
|
||||||
|
network := &net.IPNet{
|
||||||
|
IP: []byte{192, 168, 0, 1},
|
||||||
|
Mask: []byte{255, 255, 255, 0},
|
||||||
|
}
|
||||||
|
|
||||||
|
ip, err := RequestIP(network, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ReleaseIP(network, ip); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReleaseIpV6(t *testing.T) {
|
||||||
|
defer reset()
|
||||||
|
network := &net.IPNet{
|
||||||
|
IP: []byte{0x2a, 0x00, 0x14, 0x50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
|
||||||
|
Mask: []byte{255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0}, // /64 netmask
|
||||||
|
}
|
||||||
|
|
||||||
|
ip, err := RequestIP(network, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ReleaseIP(network, ip); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetReleasedIp(t *testing.T) {
|
||||||
|
defer reset()
|
||||||
|
network := &net.IPNet{
|
||||||
|
IP: []byte{192, 168, 0, 1},
|
||||||
|
Mask: []byte{255, 255, 255, 0},
|
||||||
|
}
|
||||||
|
|
||||||
|
ip, err := RequestIP(network, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
value := ip.String()
|
||||||
|
if err := ReleaseIP(network, ip); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < 253; i++ {
|
||||||
|
_, err = RequestIP(network, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
err = ReleaseIP(network, ip)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ip, err = RequestIP(network, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ip.String() != value {
|
||||||
|
t.Fatalf("Expected to receive same ip %s got %s", value, ip.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetReleasedIpV6(t *testing.T) {
|
||||||
|
defer reset()
|
||||||
|
network := &net.IPNet{
|
||||||
|
IP: []byte{0x2a, 0x00, 0x14, 0x50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
|
||||||
|
Mask: []byte{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0},
|
||||||
|
}
|
||||||
|
|
||||||
|
ip, err := RequestIP(network, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
value := ip.String()
|
||||||
|
if err := ReleaseIP(network, ip); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < 253; i++ {
|
||||||
|
_, err = RequestIP(network, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
err = ReleaseIP(network, ip)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ip, err = RequestIP(network, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ip.String() != value {
|
||||||
|
t.Fatalf("Expected to receive same ip %s got %s", value, ip.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRequestSpecificIp(t *testing.T) {
|
||||||
|
defer reset()
|
||||||
|
network := &net.IPNet{
|
||||||
|
IP: []byte{192, 168, 0, 1},
|
||||||
|
Mask: []byte{255, 255, 255, 224},
|
||||||
|
}
|
||||||
|
|
||||||
|
ip := net.ParseIP("192.168.0.5")
|
||||||
|
|
||||||
|
// Request a "good" IP.
|
||||||
|
if _, err := RequestIP(network, ip); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Request the same IP again.
|
||||||
|
if _, err := RequestIP(network, ip); err != ErrIPAlreadyAllocated {
|
||||||
|
t.Fatalf("Got the same IP twice: %#v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Request an out of range IP.
|
||||||
|
if _, err := RequestIP(network, net.ParseIP("192.168.0.42")); err != ErrIPOutOfRange {
|
||||||
|
t.Fatalf("Got an out of range IP: %#v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRequestSpecificIpV6(t *testing.T) {
|
||||||
|
defer reset()
|
||||||
|
network := &net.IPNet{
|
||||||
|
IP: []byte{0x2a, 0x00, 0x14, 0x50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
|
||||||
|
Mask: []byte{255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0}, // /64 netmask
|
||||||
|
}
|
||||||
|
|
||||||
|
ip := net.ParseIP("2a00:1450::5")
|
||||||
|
|
||||||
|
// Request a "good" IP.
|
||||||
|
if _, err := RequestIP(network, ip); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Request the same IP again.
|
||||||
|
if _, err := RequestIP(network, ip); err != ErrIPAlreadyAllocated {
|
||||||
|
t.Fatalf("Got the same IP twice: %#v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Request an out of range IP.
|
||||||
|
if _, err := RequestIP(network, net.ParseIP("2a00:1500::1")); err != ErrIPOutOfRange {
|
||||||
|
t.Fatalf("Got an out of range IP: %#v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIPAllocator(t *testing.T) {
|
||||||
|
expectedIPs := []net.IP{
|
||||||
|
0: net.IPv4(127, 0, 0, 1),
|
||||||
|
1: net.IPv4(127, 0, 0, 2),
|
||||||
|
2: net.IPv4(127, 0, 0, 3),
|
||||||
|
3: net.IPv4(127, 0, 0, 4),
|
||||||
|
4: net.IPv4(127, 0, 0, 5),
|
||||||
|
5: net.IPv4(127, 0, 0, 6),
|
||||||
|
}
|
||||||
|
|
||||||
|
gwIP, n, _ := net.ParseCIDR("127.0.0.1/29")
|
||||||
|
|
||||||
|
network := &net.IPNet{IP: gwIP, Mask: n.Mask}
|
||||||
|
// Pool after initialisation (f = free, u = used)
|
||||||
|
// 1(f) - 2(f) - 3(f) - 4(f) - 5(f) - 6(f)
|
||||||
|
// ↑
|
||||||
|
|
||||||
|
// Check that we get 6 IPs, from 127.0.0.1–127.0.0.6, in that
|
||||||
|
// order.
|
||||||
|
for i := 0; i < 6; i++ {
|
||||||
|
ip, err := RequestIP(network, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
assertIPEquals(t, expectedIPs[i], ip)
|
||||||
|
}
|
||||||
|
// Before loop begin
|
||||||
|
// 1(f) - 2(f) - 3(f) - 4(f) - 5(f) - 6(f)
|
||||||
|
// ↑
|
||||||
|
|
||||||
|
// After i = 0
|
||||||
|
// 1(u) - 2(f) - 3(f) - 4(f) - 5(f) - 6(f)
|
||||||
|
// ↑
|
||||||
|
|
||||||
|
// After i = 1
|
||||||
|
// 1(u) - 2(u) - 3(f) - 4(f) - 5(f) - 6(f)
|
||||||
|
// ↑
|
||||||
|
|
||||||
|
// After i = 2
|
||||||
|
// 1(u) - 2(u) - 3(u) - 4(f) - 5(f) - 6(f)
|
||||||
|
// ↑
|
||||||
|
|
||||||
|
// After i = 3
|
||||||
|
// 1(u) - 2(u) - 3(u) - 4(u) - 5(f) - 6(f)
|
||||||
|
// ↑
|
||||||
|
|
||||||
|
// After i = 4
|
||||||
|
// 1(u) - 2(u) - 3(u) - 4(u) - 5(u) - 6(f)
|
||||||
|
// ↑
|
||||||
|
|
||||||
|
// After i = 5
|
||||||
|
// 1(u) - 2(u) - 3(u) - 4(u) - 5(u) - 6(u)
|
||||||
|
// ↑
|
||||||
|
|
||||||
|
// Check that there are no more IPs
|
||||||
|
ip, err := RequestIP(network, nil)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("There shouldn't be any IP addresses at this point, got %s\n", ip)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Release some IPs in non-sequential order
|
||||||
|
if err := ReleaseIP(network, expectedIPs[3]); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
// 1(u) - 2(u) - 3(u) - 4(f) - 5(u) - 6(u)
|
||||||
|
// ↑
|
||||||
|
|
||||||
|
if err := ReleaseIP(network, expectedIPs[2]); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
// 1(u) - 2(u) - 3(f) - 4(f) - 5(u) - 6(u)
|
||||||
|
// ↑
|
||||||
|
|
||||||
|
if err := ReleaseIP(network, expectedIPs[4]); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
// 1(u) - 2(u) - 3(f) - 4(f) - 5(f) - 6(u)
|
||||||
|
// ↑
|
||||||
|
|
||||||
|
// Make sure that IPs are reused in sequential order, starting
|
||||||
|
// with the first released IP
|
||||||
|
newIPs := make([]net.IP, 3)
|
||||||
|
for i := 0; i < 3; i++ {
|
||||||
|
ip, err := RequestIP(network, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
newIPs[i] = ip
|
||||||
|
}
|
||||||
|
assertIPEquals(t, expectedIPs[2], newIPs[0])
|
||||||
|
assertIPEquals(t, expectedIPs[3], newIPs[1])
|
||||||
|
assertIPEquals(t, expectedIPs[4], newIPs[2])
|
||||||
|
|
||||||
|
_, err = RequestIP(network, nil)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("There shouldn't be any IP addresses at this point")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAllocateFirstIP(t *testing.T) {
|
||||||
|
defer reset()
|
||||||
|
network := &net.IPNet{
|
||||||
|
IP: []byte{192, 168, 0, 0},
|
||||||
|
Mask: []byte{255, 255, 255, 0},
|
||||||
|
}
|
||||||
|
|
||||||
|
firstIP := network.IP.To4().Mask(network.Mask)
|
||||||
|
first := big.NewInt(0).Add(ipToBigInt(firstIP), big.NewInt(1))
|
||||||
|
|
||||||
|
ip, err := RequestIP(network, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
allocated := ipToBigInt(ip)
|
||||||
|
|
||||||
|
if allocated == first {
|
||||||
|
t.Fatalf("allocated ip should not equal first ip: %d == %d", first, allocated)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAllocateAllIps(t *testing.T) {
|
||||||
|
defer reset()
|
||||||
|
network := &net.IPNet{
|
||||||
|
IP: []byte{192, 168, 0, 1},
|
||||||
|
Mask: []byte{255, 255, 255, 0},
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
current, first net.IP
|
||||||
|
err error
|
||||||
|
isFirst = true
|
||||||
|
)
|
||||||
|
|
||||||
|
for err == nil {
|
||||||
|
current, err = RequestIP(network, nil)
|
||||||
|
if isFirst {
|
||||||
|
first = current
|
||||||
|
isFirst = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != ErrNoAvailableIPs {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := RequestIP(network, nil); err != ErrNoAvailableIPs {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ReleaseIP(network, first); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
again, err := RequestIP(network, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
assertIPEquals(t, first, again)
|
||||||
|
|
||||||
|
// ensure that alloc.last == alloc.begin won't result in dead loop
|
||||||
|
if _, err := RequestIP(network, nil); err != ErrNoAvailableIPs {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test by making alloc.last the only free ip and ensure we get it back
|
||||||
|
// #1. first of the range, (alloc.last == ipToInt(first) already)
|
||||||
|
if err := ReleaseIP(network, first); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ret, err := RequestIP(network, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
assertIPEquals(t, first, ret)
|
||||||
|
|
||||||
|
// #2. last of the range, note that current is the last one
|
||||||
|
last := net.IPv4(192, 168, 0, 254)
|
||||||
|
setLastTo(t, network, last)
|
||||||
|
|
||||||
|
ret, err = RequestIP(network, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
assertIPEquals(t, last, ret)
|
||||||
|
|
||||||
|
// #3. middle of the range
|
||||||
|
mid := net.IPv4(192, 168, 0, 7)
|
||||||
|
setLastTo(t, network, mid)
|
||||||
|
|
||||||
|
ret, err = RequestIP(network, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
assertIPEquals(t, mid, ret)
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure the pool is full when calling setLastTo.
|
||||||
|
// we don't cheat here
|
||||||
|
func setLastTo(t *testing.T, network *net.IPNet, ip net.IP) {
|
||||||
|
if err := ReleaseIP(network, ip); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ret, err := RequestIP(network, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
assertIPEquals(t, ip, ret)
|
||||||
|
|
||||||
|
if err := ReleaseIP(network, ip); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAllocateDifferentSubnets(t *testing.T) {
|
||||||
|
defer reset()
|
||||||
|
network1 := &net.IPNet{
|
||||||
|
IP: []byte{192, 168, 0, 1},
|
||||||
|
Mask: []byte{255, 255, 255, 0},
|
||||||
|
}
|
||||||
|
network2 := &net.IPNet{
|
||||||
|
IP: []byte{127, 0, 0, 1},
|
||||||
|
Mask: []byte{255, 255, 255, 0},
|
||||||
|
}
|
||||||
|
network3 := &net.IPNet{
|
||||||
|
IP: []byte{0x2a, 0x00, 0x14, 0x50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
|
||||||
|
Mask: []byte{255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0}, // /64 netmask
|
||||||
|
}
|
||||||
|
network4 := &net.IPNet{
|
||||||
|
IP: []byte{0x2a, 0x00, 0x16, 0x32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
|
||||||
|
Mask: []byte{255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0}, // /64 netmask
|
||||||
|
}
|
||||||
|
expectedIPs := []net.IP{
|
||||||
|
0: net.IPv4(192, 168, 0, 1),
|
||||||
|
1: net.IPv4(192, 168, 0, 2),
|
||||||
|
2: net.IPv4(127, 0, 0, 1),
|
||||||
|
3: net.IPv4(127, 0, 0, 2),
|
||||||
|
4: net.ParseIP("2a00:1450::1"),
|
||||||
|
5: net.ParseIP("2a00:1450::2"),
|
||||||
|
6: net.ParseIP("2a00:1450::3"),
|
||||||
|
7: net.ParseIP("2a00:1632::1"),
|
||||||
|
8: net.ParseIP("2a00:1632::2"),
|
||||||
|
}
|
||||||
|
|
||||||
|
ip11, err := RequestIP(network1, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
ip12, err := RequestIP(network1, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
ip21, err := RequestIP(network2, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
ip22, err := RequestIP(network2, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
ip31, err := RequestIP(network3, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
ip32, err := RequestIP(network3, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
ip33, err := RequestIP(network3, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
ip41, err := RequestIP(network4, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
ip42, err := RequestIP(network4, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
assertIPEquals(t, expectedIPs[0], ip11)
|
||||||
|
assertIPEquals(t, expectedIPs[1], ip12)
|
||||||
|
assertIPEquals(t, expectedIPs[2], ip21)
|
||||||
|
assertIPEquals(t, expectedIPs[3], ip22)
|
||||||
|
assertIPEquals(t, expectedIPs[4], ip31)
|
||||||
|
assertIPEquals(t, expectedIPs[5], ip32)
|
||||||
|
assertIPEquals(t, expectedIPs[6], ip33)
|
||||||
|
assertIPEquals(t, expectedIPs[7], ip41)
|
||||||
|
assertIPEquals(t, expectedIPs[8], ip42)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRegisterBadTwice(t *testing.T) {
|
||||||
|
defer reset()
|
||||||
|
network := &net.IPNet{
|
||||||
|
IP: []byte{192, 168, 1, 1},
|
||||||
|
Mask: []byte{255, 255, 255, 0},
|
||||||
|
}
|
||||||
|
subnet := &net.IPNet{
|
||||||
|
IP: []byte{192, 168, 1, 8},
|
||||||
|
Mask: []byte{255, 255, 255, 248},
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := RegisterSubnet(network, subnet); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
subnet = &net.IPNet{
|
||||||
|
IP: []byte{192, 168, 1, 16},
|
||||||
|
Mask: []byte{255, 255, 255, 248},
|
||||||
|
}
|
||||||
|
if err := RegisterSubnet(network, subnet); err != ErrNetworkAlreadyRegistered {
|
||||||
|
t.Fatalf("Expecteded ErrNetworkAlreadyRegistered error, got %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRegisterBadRange(t *testing.T) {
|
||||||
|
defer reset()
|
||||||
|
network := &net.IPNet{
|
||||||
|
IP: []byte{192, 168, 1, 1},
|
||||||
|
Mask: []byte{255, 255, 255, 0},
|
||||||
|
}
|
||||||
|
subnet := &net.IPNet{
|
||||||
|
IP: []byte{192, 168, 1, 1},
|
||||||
|
Mask: []byte{255, 255, 0, 0},
|
||||||
|
}
|
||||||
|
if err := RegisterSubnet(network, subnet); err != ErrBadSubnet {
|
||||||
|
t.Fatalf("Expected ErrBadSubnet error, got %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAllocateFromRange(t *testing.T) {
|
||||||
|
defer reset()
|
||||||
|
network := &net.IPNet{
|
||||||
|
IP: []byte{192, 168, 0, 1},
|
||||||
|
Mask: []byte{255, 255, 255, 0},
|
||||||
|
}
|
||||||
|
// 192.168.1.9 - 192.168.1.14
|
||||||
|
subnet := &net.IPNet{
|
||||||
|
IP: []byte{192, 168, 0, 8},
|
||||||
|
Mask: []byte{255, 255, 255, 248},
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := RegisterSubnet(network, subnet); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
expectedIPs := []net.IP{
|
||||||
|
0: net.IPv4(192, 168, 0, 9),
|
||||||
|
1: net.IPv4(192, 168, 0, 10),
|
||||||
|
2: net.IPv4(192, 168, 0, 11),
|
||||||
|
3: net.IPv4(192, 168, 0, 12),
|
||||||
|
4: net.IPv4(192, 168, 0, 13),
|
||||||
|
5: net.IPv4(192, 168, 0, 14),
|
||||||
|
}
|
||||||
|
for _, ip := range expectedIPs {
|
||||||
|
rip, err := RequestIP(network, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
assertIPEquals(t, ip, rip)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := RequestIP(network, nil); err != ErrNoAvailableIPs {
|
||||||
|
t.Fatalf("Expected ErrNoAvailableIPs error, got %v", err)
|
||||||
|
}
|
||||||
|
for _, ip := range expectedIPs {
|
||||||
|
ReleaseIP(network, ip)
|
||||||
|
rip, err := RequestIP(network, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
assertIPEquals(t, ip, rip)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func assertIPEquals(t *testing.T, ip1, ip2 net.IP) {
|
||||||
|
if !ip1.Equal(ip2) {
|
||||||
|
t.Fatalf("Expected IP %s, got %s", ip1, ip2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkRequestIP(b *testing.B) {
|
||||||
|
network := &net.IPNet{
|
||||||
|
IP: []byte{192, 168, 0, 1},
|
||||||
|
Mask: []byte{255, 255, 255, 0},
|
||||||
|
}
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
for j := 0; j < 253; j++ {
|
||||||
|
_, err := RequestIP(network, nil)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reset()
|
||||||
|
}
|
||||||
|
}
|
10
libnetwork/Godeps/_workspace/src/github.com/docker/docker/daemon/networkdriver/network.go
generated
vendored
Normal file
10
libnetwork/Godeps/_workspace/src/github.com/docker/docker/daemon/networkdriver/network.go
generated
vendored
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
package networkdriver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrNetworkOverlapsWithNameservers = errors.New("requested network overlaps with nameserver")
|
||||||
|
ErrNetworkOverlaps = errors.New("requested network overlaps with existing network")
|
||||||
|
)
|
175
libnetwork/Godeps/_workspace/src/github.com/docker/docker/daemon/networkdriver/network_test.go
generated
vendored
Normal file
175
libnetwork/Godeps/_workspace/src/github.com/docker/docker/daemon/networkdriver/network_test.go
generated
vendored
Normal file
|
@ -0,0 +1,175 @@
|
||||||
|
package networkdriver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/docker/libcontainer/netlink"
|
||||||
|
"net"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNonOverlapingNameservers(t *testing.T) {
|
||||||
|
network := &net.IPNet{
|
||||||
|
IP: []byte{192, 168, 0, 1},
|
||||||
|
Mask: []byte{255, 255, 255, 0},
|
||||||
|
}
|
||||||
|
nameservers := []string{
|
||||||
|
"127.0.0.1/32",
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := CheckNameserverOverlaps(nameservers, network); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOverlapingNameservers(t *testing.T) {
|
||||||
|
network := &net.IPNet{
|
||||||
|
IP: []byte{192, 168, 0, 1},
|
||||||
|
Mask: []byte{255, 255, 255, 0},
|
||||||
|
}
|
||||||
|
nameservers := []string{
|
||||||
|
"192.168.0.1/32",
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := CheckNameserverOverlaps(nameservers, network); err == nil {
|
||||||
|
t.Fatalf("Expected error %s got %s", ErrNetworkOverlapsWithNameservers, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCheckRouteOverlaps(t *testing.T) {
|
||||||
|
orig := networkGetRoutesFct
|
||||||
|
defer func() {
|
||||||
|
networkGetRoutesFct = orig
|
||||||
|
}()
|
||||||
|
networkGetRoutesFct = func() ([]netlink.Route, error) {
|
||||||
|
routesData := []string{"10.0.2.0/32", "10.0.3.0/24", "10.0.42.0/24", "172.16.42.0/24", "192.168.142.0/24"}
|
||||||
|
|
||||||
|
routes := []netlink.Route{}
|
||||||
|
for _, addr := range routesData {
|
||||||
|
_, netX, _ := net.ParseCIDR(addr)
|
||||||
|
routes = append(routes, netlink.Route{IPNet: netX})
|
||||||
|
}
|
||||||
|
return routes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
_, netX, _ := net.ParseCIDR("172.16.0.1/24")
|
||||||
|
if err := CheckRouteOverlaps(netX); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, netX, _ = net.ParseCIDR("10.0.2.0/24")
|
||||||
|
if err := CheckRouteOverlaps(netX); err == nil {
|
||||||
|
t.Fatalf("10.0.2.0/24 and 10.0.2.0 should overlap but it doesn't")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCheckNameserverOverlaps(t *testing.T) {
|
||||||
|
nameservers := []string{"10.0.2.3/32", "192.168.102.1/32"}
|
||||||
|
|
||||||
|
_, netX, _ := net.ParseCIDR("10.0.2.3/32")
|
||||||
|
|
||||||
|
if err := CheckNameserverOverlaps(nameservers, netX); err == nil {
|
||||||
|
t.Fatalf("%s should overlap 10.0.2.3/32 but doesn't", netX)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, netX, _ = net.ParseCIDR("192.168.102.2/32")
|
||||||
|
|
||||||
|
if err := CheckNameserverOverlaps(nameservers, netX); err != nil {
|
||||||
|
t.Fatalf("%s should not overlap %v but it does", netX, nameservers)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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/22", 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNetworkRange(t *testing.T) {
|
||||||
|
// Simple class C test
|
||||||
|
_, network, _ := net.ParseCIDR("192.168.0.1/24")
|
||||||
|
first, last := NetworkRange(network)
|
||||||
|
if !first.Equal(net.ParseIP("192.168.0.0")) {
|
||||||
|
t.Error(first.String())
|
||||||
|
}
|
||||||
|
if !last.Equal(net.ParseIP("192.168.0.255")) {
|
||||||
|
t.Error(last.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Class A test
|
||||||
|
_, network, _ = net.ParseCIDR("10.0.0.1/8")
|
||||||
|
first, last = NetworkRange(network)
|
||||||
|
if !first.Equal(net.ParseIP("10.0.0.0")) {
|
||||||
|
t.Error(first.String())
|
||||||
|
}
|
||||||
|
if !last.Equal(net.ParseIP("10.255.255.255")) {
|
||||||
|
t.Error(last.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Class A, random IP address
|
||||||
|
_, network, _ = net.ParseCIDR("10.1.2.3/8")
|
||||||
|
first, last = NetworkRange(network)
|
||||||
|
if !first.Equal(net.ParseIP("10.0.0.0")) {
|
||||||
|
t.Error(first.String())
|
||||||
|
}
|
||||||
|
if !last.Equal(net.ParseIP("10.255.255.255")) {
|
||||||
|
t.Error(last.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
// 32bit mask
|
||||||
|
_, network, _ = net.ParseCIDR("10.1.2.3/32")
|
||||||
|
first, last = NetworkRange(network)
|
||||||
|
if !first.Equal(net.ParseIP("10.1.2.3")) {
|
||||||
|
t.Error(first.String())
|
||||||
|
}
|
||||||
|
if !last.Equal(net.ParseIP("10.1.2.3")) {
|
||||||
|
t.Error(last.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
// 31bit mask
|
||||||
|
_, network, _ = net.ParseCIDR("10.1.2.3/31")
|
||||||
|
first, last = NetworkRange(network)
|
||||||
|
if !first.Equal(net.ParseIP("10.1.2.2")) {
|
||||||
|
t.Error(first.String())
|
||||||
|
}
|
||||||
|
if !last.Equal(net.ParseIP("10.1.2.3")) {
|
||||||
|
t.Error(last.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
// 26bit mask
|
||||||
|
_, network, _ = net.ParseCIDR("10.1.2.3/26")
|
||||||
|
first, last = NetworkRange(network)
|
||||||
|
if !first.Equal(net.ParseIP("10.1.2.0")) {
|
||||||
|
t.Error(first.String())
|
||||||
|
}
|
||||||
|
if !last.Equal(net.ParseIP("10.1.2.63")) {
|
||||||
|
t.Error(last.String())
|
||||||
|
}
|
||||||
|
}
|
153
libnetwork/Godeps/_workspace/src/github.com/docker/docker/daemon/networkdriver/portallocator/portallocator.go
generated
vendored
Normal file
153
libnetwork/Godeps/_workspace/src/github.com/docker/docker/daemon/networkdriver/portallocator/portallocator.go
generated
vendored
Normal file
|
@ -0,0 +1,153 @@
|
||||||
|
package portallocator
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
type portMap struct {
|
||||||
|
p map[int]struct{}
|
||||||
|
last int
|
||||||
|
}
|
||||||
|
|
||||||
|
func newPortMap() *portMap {
|
||||||
|
return &portMap{
|
||||||
|
p: map[int]struct{}{},
|
||||||
|
last: EndPortRange,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type protoMap map[string]*portMap
|
||||||
|
|
||||||
|
func newProtoMap() protoMap {
|
||||||
|
return protoMap{
|
||||||
|
"tcp": newPortMap(),
|
||||||
|
"udp": newPortMap(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type ipMapping map[string]protoMap
|
||||||
|
|
||||||
|
const (
|
||||||
|
BeginPortRange = 49153
|
||||||
|
EndPortRange = 65535
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrAllPortsAllocated = errors.New("all ports are allocated")
|
||||||
|
ErrUnknownProtocol = errors.New("unknown protocol")
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
mutex sync.Mutex
|
||||||
|
|
||||||
|
defaultIP = net.ParseIP("0.0.0.0")
|
||||||
|
globalMap = ipMapping{}
|
||||||
|
)
|
||||||
|
|
||||||
|
type ErrPortAlreadyAllocated struct {
|
||||||
|
ip string
|
||||||
|
port int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewErrPortAlreadyAllocated(ip string, port int) ErrPortAlreadyAllocated {
|
||||||
|
return ErrPortAlreadyAllocated{
|
||||||
|
ip: ip,
|
||||||
|
port: port,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e ErrPortAlreadyAllocated) IP() string {
|
||||||
|
return e.ip
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e ErrPortAlreadyAllocated) Port() int {
|
||||||
|
return e.port
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e ErrPortAlreadyAllocated) IPPort() string {
|
||||||
|
return fmt.Sprintf("%s:%d", e.ip, e.port)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e ErrPortAlreadyAllocated) Error() string {
|
||||||
|
return fmt.Sprintf("Bind for %s:%d failed: port is already allocated", e.ip, e.port)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RequestPort requests new port from global ports pool for specified ip and proto.
|
||||||
|
// If port is 0 it returns first free port. Otherwise it cheks port availability
|
||||||
|
// in pool and return that port or error if port is already busy.
|
||||||
|
func RequestPort(ip net.IP, proto string, port int) (int, error) {
|
||||||
|
mutex.Lock()
|
||||||
|
defer mutex.Unlock()
|
||||||
|
|
||||||
|
if proto != "tcp" && proto != "udp" {
|
||||||
|
return 0, ErrUnknownProtocol
|
||||||
|
}
|
||||||
|
|
||||||
|
if ip == nil {
|
||||||
|
ip = defaultIP
|
||||||
|
}
|
||||||
|
ipstr := ip.String()
|
||||||
|
protomap, ok := globalMap[ipstr]
|
||||||
|
if !ok {
|
||||||
|
protomap = newProtoMap()
|
||||||
|
globalMap[ipstr] = protomap
|
||||||
|
}
|
||||||
|
mapping := protomap[proto]
|
||||||
|
if port > 0 {
|
||||||
|
if _, ok := mapping.p[port]; !ok {
|
||||||
|
mapping.p[port] = struct{}{}
|
||||||
|
return port, nil
|
||||||
|
}
|
||||||
|
return 0, NewErrPortAlreadyAllocated(ipstr, port)
|
||||||
|
}
|
||||||
|
|
||||||
|
port, err := mapping.findPort()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return port, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReleasePort releases port from global ports pool for specified ip and proto.
|
||||||
|
func ReleasePort(ip net.IP, proto string, port int) error {
|
||||||
|
mutex.Lock()
|
||||||
|
defer mutex.Unlock()
|
||||||
|
|
||||||
|
if ip == nil {
|
||||||
|
ip = defaultIP
|
||||||
|
}
|
||||||
|
protomap, ok := globalMap[ip.String()]
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
delete(protomap[proto].p, port)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReleaseAll releases all ports for all ips.
|
||||||
|
func ReleaseAll() error {
|
||||||
|
mutex.Lock()
|
||||||
|
globalMap = ipMapping{}
|
||||||
|
mutex.Unlock()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pm *portMap) findPort() (int, error) {
|
||||||
|
port := pm.last
|
||||||
|
for i := 0; i <= EndPortRange-BeginPortRange; i++ {
|
||||||
|
port++
|
||||||
|
if port > EndPortRange {
|
||||||
|
port = BeginPortRange
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := pm.p[port]; !ok {
|
||||||
|
pm.p[port] = struct{}{}
|
||||||
|
pm.last = port
|
||||||
|
return port, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0, ErrAllPortsAllocated
|
||||||
|
}
|
245
libnetwork/Godeps/_workspace/src/github.com/docker/docker/daemon/networkdriver/portallocator/portallocator_test.go
generated
vendored
Normal file
245
libnetwork/Godeps/_workspace/src/github.com/docker/docker/daemon/networkdriver/portallocator/portallocator_test.go
generated
vendored
Normal file
|
@ -0,0 +1,245 @@
|
||||||
|
package portallocator
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func reset() {
|
||||||
|
ReleaseAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRequestNewPort(t *testing.T) {
|
||||||
|
defer reset()
|
||||||
|
|
||||||
|
port, err := RequestPort(defaultIP, "tcp", 0)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if expected := BeginPortRange; port != expected {
|
||||||
|
t.Fatalf("Expected port %d got %d", expected, port)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRequestSpecificPort(t *testing.T) {
|
||||||
|
defer reset()
|
||||||
|
|
||||||
|
port, err := RequestPort(defaultIP, "tcp", 5000)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if port != 5000 {
|
||||||
|
t.Fatalf("Expected port 5000 got %d", port)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReleasePort(t *testing.T) {
|
||||||
|
defer reset()
|
||||||
|
|
||||||
|
port, err := RequestPort(defaultIP, "tcp", 5000)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if port != 5000 {
|
||||||
|
t.Fatalf("Expected port 5000 got %d", port)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ReleasePort(defaultIP, "tcp", 5000); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReuseReleasedPort(t *testing.T) {
|
||||||
|
defer reset()
|
||||||
|
|
||||||
|
port, err := RequestPort(defaultIP, "tcp", 5000)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if port != 5000 {
|
||||||
|
t.Fatalf("Expected port 5000 got %d", port)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ReleasePort(defaultIP, "tcp", 5000); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
port, err = RequestPort(defaultIP, "tcp", 5000)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReleaseUnreadledPort(t *testing.T) {
|
||||||
|
defer reset()
|
||||||
|
|
||||||
|
port, err := RequestPort(defaultIP, "tcp", 5000)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if port != 5000 {
|
||||||
|
t.Fatalf("Expected port 5000 got %d", port)
|
||||||
|
}
|
||||||
|
|
||||||
|
port, err = RequestPort(defaultIP, "tcp", 5000)
|
||||||
|
|
||||||
|
switch err.(type) {
|
||||||
|
case ErrPortAlreadyAllocated:
|
||||||
|
default:
|
||||||
|
t.Fatalf("Expected port allocation error got %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnknowProtocol(t *testing.T) {
|
||||||
|
defer reset()
|
||||||
|
|
||||||
|
if _, err := RequestPort(defaultIP, "tcpp", 0); err != ErrUnknownProtocol {
|
||||||
|
t.Fatalf("Expected error %s got %s", ErrUnknownProtocol, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAllocateAllPorts(t *testing.T) {
|
||||||
|
defer reset()
|
||||||
|
|
||||||
|
for i := 0; i <= EndPortRange-BeginPortRange; i++ {
|
||||||
|
port, err := RequestPort(defaultIP, "tcp", 0)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if expected := BeginPortRange + i; port != expected {
|
||||||
|
t.Fatalf("Expected port %d got %d", expected, port)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := RequestPort(defaultIP, "tcp", 0); err != ErrAllPortsAllocated {
|
||||||
|
t.Fatalf("Expected error %s got %s", ErrAllPortsAllocated, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := RequestPort(defaultIP, "udp", 0)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// release a port in the middle and ensure we get another tcp port
|
||||||
|
port := BeginPortRange + 5
|
||||||
|
if err := ReleasePort(defaultIP, "tcp", port); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
newPort, err := RequestPort(defaultIP, "tcp", 0)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if newPort != port {
|
||||||
|
t.Fatalf("Expected port %d got %d", port, newPort)
|
||||||
|
}
|
||||||
|
|
||||||
|
// now pm.last == newPort, release it so that it's the only free port of
|
||||||
|
// the range, and ensure we get it back
|
||||||
|
if err := ReleasePort(defaultIP, "tcp", newPort); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
port, err = RequestPort(defaultIP, "tcp", 0)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if newPort != port {
|
||||||
|
t.Fatalf("Expected port %d got %d", newPort, port)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkAllocatePorts(b *testing.B) {
|
||||||
|
defer reset()
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
for i := 0; i <= EndPortRange-BeginPortRange; i++ {
|
||||||
|
port, err := RequestPort(defaultIP, "tcp", 0)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if expected := BeginPortRange + i; port != expected {
|
||||||
|
b.Fatalf("Expected port %d got %d", expected, port)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reset()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPortAllocation(t *testing.T) {
|
||||||
|
defer reset()
|
||||||
|
|
||||||
|
ip := net.ParseIP("192.168.0.1")
|
||||||
|
ip2 := net.ParseIP("192.168.0.2")
|
||||||
|
if port, err := RequestPort(ip, "tcp", 80); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if port != 80 {
|
||||||
|
t.Fatalf("Acquire(80) should return 80, not %d", port)
|
||||||
|
}
|
||||||
|
port, err := RequestPort(ip, "tcp", 0)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if port <= 0 {
|
||||||
|
t.Fatalf("Acquire(0) should return a non-zero port")
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := RequestPort(ip, "tcp", port); err == nil {
|
||||||
|
t.Fatalf("Acquiring a port already in use should return an error")
|
||||||
|
}
|
||||||
|
|
||||||
|
if newPort, err := RequestPort(ip, "tcp", 0); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if newPort == port {
|
||||||
|
t.Fatalf("Acquire(0) allocated the same port twice: %d", port)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := RequestPort(ip, "tcp", 80); err == nil {
|
||||||
|
t.Fatalf("Acquiring a port already in use should return an error")
|
||||||
|
}
|
||||||
|
if _, err := RequestPort(ip2, "tcp", 80); err != nil {
|
||||||
|
t.Fatalf("It should be possible to allocate the same port on a different interface")
|
||||||
|
}
|
||||||
|
if _, err := RequestPort(ip2, "tcp", 80); err == nil {
|
||||||
|
t.Fatalf("Acquiring a port already in use should return an error")
|
||||||
|
}
|
||||||
|
if err := ReleasePort(ip, "tcp", 80); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if _, err := RequestPort(ip, "tcp", 80); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
port, err = RequestPort(ip, "tcp", 0)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
port2, err := RequestPort(ip, "tcp", port+1)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
port3, err := RequestPort(ip, "tcp", 0)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if port3 == port2 {
|
||||||
|
t.Fatal("Requesting a dynamic port should never allocate a used port")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNoDuplicateBPR(t *testing.T) {
|
||||||
|
defer reset()
|
||||||
|
|
||||||
|
if port, err := RequestPort(defaultIP, "tcp", BeginPortRange); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if port != BeginPortRange {
|
||||||
|
t.Fatalf("Expected port %d got %d", BeginPortRange, port)
|
||||||
|
}
|
||||||
|
|
||||||
|
if port, err := RequestPort(defaultIP, "tcp", 0); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if port == BeginPortRange {
|
||||||
|
t.Fatalf("Acquire(0) allocated the same port twice: %d", port)
|
||||||
|
}
|
||||||
|
}
|
176
libnetwork/Godeps/_workspace/src/github.com/docker/docker/daemon/networkdriver/portmapper/mapper.go
generated
vendored
Normal file
176
libnetwork/Godeps/_workspace/src/github.com/docker/docker/daemon/networkdriver/portmapper/mapper.go
generated
vendored
Normal file
|
@ -0,0 +1,176 @@
|
||||||
|
package portmapper
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
log "github.com/Sirupsen/logrus"
|
||||||
|
"github.com/docker/docker/daemon/networkdriver/portallocator"
|
||||||
|
"github.com/docker/docker/pkg/iptables"
|
||||||
|
)
|
||||||
|
|
||||||
|
type mapping struct {
|
||||||
|
proto string
|
||||||
|
userlandProxy UserlandProxy
|
||||||
|
host net.Addr
|
||||||
|
container net.Addr
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
chain *iptables.Chain
|
||||||
|
lock sync.Mutex
|
||||||
|
|
||||||
|
// udp:ip:port
|
||||||
|
currentMappings = make(map[string]*mapping)
|
||||||
|
|
||||||
|
NewProxy = NewProxyCommand
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrUnknownBackendAddressType = errors.New("unknown container address type not supported")
|
||||||
|
ErrPortMappedForIP = errors.New("port is already mapped to ip")
|
||||||
|
ErrPortNotMapped = errors.New("port is not mapped")
|
||||||
|
)
|
||||||
|
|
||||||
|
func SetIptablesChain(c *iptables.Chain) {
|
||||||
|
chain = c
|
||||||
|
}
|
||||||
|
|
||||||
|
func Map(container net.Addr, hostIP net.IP, hostPort int) (host net.Addr, err error) {
|
||||||
|
lock.Lock()
|
||||||
|
defer lock.Unlock()
|
||||||
|
|
||||||
|
var (
|
||||||
|
m *mapping
|
||||||
|
proto string
|
||||||
|
allocatedHostPort int
|
||||||
|
proxy UserlandProxy
|
||||||
|
)
|
||||||
|
|
||||||
|
switch container.(type) {
|
||||||
|
case *net.TCPAddr:
|
||||||
|
proto = "tcp"
|
||||||
|
if allocatedHostPort, err = portallocator.RequestPort(hostIP, proto, hostPort); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
m = &mapping{
|
||||||
|
proto: proto,
|
||||||
|
host: &net.TCPAddr{IP: hostIP, Port: allocatedHostPort},
|
||||||
|
container: container,
|
||||||
|
}
|
||||||
|
|
||||||
|
proxy = NewProxy(proto, hostIP, allocatedHostPort, container.(*net.TCPAddr).IP, container.(*net.TCPAddr).Port)
|
||||||
|
case *net.UDPAddr:
|
||||||
|
proto = "udp"
|
||||||
|
if allocatedHostPort, err = portallocator.RequestPort(hostIP, proto, hostPort); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
m = &mapping{
|
||||||
|
proto: proto,
|
||||||
|
host: &net.UDPAddr{IP: hostIP, Port: allocatedHostPort},
|
||||||
|
container: container,
|
||||||
|
}
|
||||||
|
|
||||||
|
proxy = NewProxy(proto, hostIP, allocatedHostPort, container.(*net.UDPAddr).IP, container.(*net.UDPAddr).Port)
|
||||||
|
default:
|
||||||
|
return nil, ErrUnknownBackendAddressType
|
||||||
|
}
|
||||||
|
|
||||||
|
// release the allocated port on any further error during return.
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
portallocator.ReleasePort(hostIP, proto, allocatedHostPort)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
key := getKey(m.host)
|
||||||
|
if _, exists := currentMappings[key]; exists {
|
||||||
|
return nil, ErrPortMappedForIP
|
||||||
|
}
|
||||||
|
|
||||||
|
containerIP, containerPort := getIPAndPort(m.container)
|
||||||
|
if err := forward(iptables.Append, m.proto, hostIP, allocatedHostPort, containerIP.String(), containerPort); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup := func() error {
|
||||||
|
// need to undo the iptables rules before we return
|
||||||
|
proxy.Stop()
|
||||||
|
forward(iptables.Delete, m.proto, hostIP, allocatedHostPort, containerIP.String(), containerPort)
|
||||||
|
if err := portallocator.ReleasePort(hostIP, m.proto, allocatedHostPort); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := proxy.Start(); err != nil {
|
||||||
|
if err := cleanup(); err != nil {
|
||||||
|
return nil, fmt.Errorf("Error during port allocation cleanup: %v", err)
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
m.userlandProxy = proxy
|
||||||
|
currentMappings[key] = m
|
||||||
|
return m.host, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Unmap(host net.Addr) error {
|
||||||
|
lock.Lock()
|
||||||
|
defer lock.Unlock()
|
||||||
|
|
||||||
|
key := getKey(host)
|
||||||
|
data, exists := currentMappings[key]
|
||||||
|
if !exists {
|
||||||
|
return ErrPortNotMapped
|
||||||
|
}
|
||||||
|
|
||||||
|
data.userlandProxy.Stop()
|
||||||
|
|
||||||
|
delete(currentMappings, key)
|
||||||
|
|
||||||
|
containerIP, containerPort := getIPAndPort(data.container)
|
||||||
|
hostIP, hostPort := getIPAndPort(data.host)
|
||||||
|
if err := forward(iptables.Delete, data.proto, hostIP, hostPort, containerIP.String(), containerPort); err != nil {
|
||||||
|
log.Errorf("Error on iptables delete: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch a := host.(type) {
|
||||||
|
case *net.TCPAddr:
|
||||||
|
return portallocator.ReleasePort(a.IP, "tcp", a.Port)
|
||||||
|
case *net.UDPAddr:
|
||||||
|
return portallocator.ReleasePort(a.IP, "udp", a.Port)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getKey(a net.Addr) string {
|
||||||
|
switch t := a.(type) {
|
||||||
|
case *net.TCPAddr:
|
||||||
|
return fmt.Sprintf("%s:%d/%s", t.IP.String(), t.Port, "tcp")
|
||||||
|
case *net.UDPAddr:
|
||||||
|
return fmt.Sprintf("%s:%d/%s", t.IP.String(), t.Port, "udp")
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func getIPAndPort(a net.Addr) (net.IP, int) {
|
||||||
|
switch t := a.(type) {
|
||||||
|
case *net.TCPAddr:
|
||||||
|
return t.IP, t.Port
|
||||||
|
case *net.UDPAddr:
|
||||||
|
return t.IP, t.Port
|
||||||
|
}
|
||||||
|
return nil, 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func forward(action iptables.Action, proto string, sourceIP net.IP, sourcePort int, containerIP string, containerPort int) error {
|
||||||
|
if chain == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return chain.Forward(action, sourceIP, sourcePort, proto, containerIP, containerPort)
|
||||||
|
}
|
152
libnetwork/Godeps/_workspace/src/github.com/docker/docker/daemon/networkdriver/portmapper/mapper_test.go
generated
vendored
Normal file
152
libnetwork/Godeps/_workspace/src/github.com/docker/docker/daemon/networkdriver/portmapper/mapper_test.go
generated
vendored
Normal file
|
@ -0,0 +1,152 @@
|
||||||
|
package portmapper
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/docker/docker/daemon/networkdriver/portallocator"
|
||||||
|
"github.com/docker/docker/pkg/iptables"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// override this func to mock out the proxy server
|
||||||
|
NewProxy = NewMockProxyCommand
|
||||||
|
}
|
||||||
|
|
||||||
|
func reset() {
|
||||||
|
chain = nil
|
||||||
|
currentMappings = make(map[string]*mapping)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetIptablesChain(t *testing.T) {
|
||||||
|
defer reset()
|
||||||
|
|
||||||
|
c := &iptables.Chain{
|
||||||
|
Name: "TEST",
|
||||||
|
Bridge: "192.168.1.1",
|
||||||
|
}
|
||||||
|
|
||||||
|
if chain != nil {
|
||||||
|
t.Fatal("chain should be nil at init")
|
||||||
|
}
|
||||||
|
|
||||||
|
SetIptablesChain(c)
|
||||||
|
if chain == nil {
|
||||||
|
t.Fatal("chain should not be nil after set")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMapPorts(t *testing.T) {
|
||||||
|
dstIp1 := net.ParseIP("192.168.0.1")
|
||||||
|
dstIp2 := net.ParseIP("192.168.0.2")
|
||||||
|
dstAddr1 := &net.TCPAddr{IP: dstIp1, Port: 80}
|
||||||
|
dstAddr2 := &net.TCPAddr{IP: dstIp2, Port: 80}
|
||||||
|
|
||||||
|
srcAddr1 := &net.TCPAddr{Port: 1080, IP: net.ParseIP("172.16.0.1")}
|
||||||
|
srcAddr2 := &net.TCPAddr{Port: 1080, IP: net.ParseIP("172.16.0.2")}
|
||||||
|
|
||||||
|
addrEqual := func(addr1, addr2 net.Addr) bool {
|
||||||
|
return (addr1.Network() == addr2.Network()) && (addr1.String() == addr2.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
if host, err := Map(srcAddr1, dstIp1, 80); err != nil {
|
||||||
|
t.Fatalf("Failed to allocate port: %s", err)
|
||||||
|
} else if !addrEqual(dstAddr1, host) {
|
||||||
|
t.Fatalf("Incorrect mapping result: expected %s:%s, got %s:%s",
|
||||||
|
dstAddr1.String(), dstAddr1.Network(), host.String(), host.Network())
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := Map(srcAddr1, dstIp1, 80); err == nil {
|
||||||
|
t.Fatalf("Port is in use - mapping should have failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := Map(srcAddr2, dstIp1, 80); err == nil {
|
||||||
|
t.Fatalf("Port is in use - mapping should have failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := Map(srcAddr2, dstIp2, 80); err != nil {
|
||||||
|
t.Fatalf("Failed to allocate port: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if Unmap(dstAddr1) != nil {
|
||||||
|
t.Fatalf("Failed to release port")
|
||||||
|
}
|
||||||
|
|
||||||
|
if Unmap(dstAddr2) != nil {
|
||||||
|
t.Fatalf("Failed to release port")
|
||||||
|
}
|
||||||
|
|
||||||
|
if Unmap(dstAddr2) == nil {
|
||||||
|
t.Fatalf("Port already released, but no error reported")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetUDPKey(t *testing.T) {
|
||||||
|
addr := &net.UDPAddr{IP: net.ParseIP("192.168.1.5"), Port: 53}
|
||||||
|
|
||||||
|
key := getKey(addr)
|
||||||
|
|
||||||
|
if expected := "192.168.1.5:53/udp"; key != expected {
|
||||||
|
t.Fatalf("expected key %s got %s", expected, key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetTCPKey(t *testing.T) {
|
||||||
|
addr := &net.TCPAddr{IP: net.ParseIP("192.168.1.5"), Port: 80}
|
||||||
|
|
||||||
|
key := getKey(addr)
|
||||||
|
|
||||||
|
if expected := "192.168.1.5:80/tcp"; key != expected {
|
||||||
|
t.Fatalf("expected key %s got %s", expected, key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetUDPIPAndPort(t *testing.T) {
|
||||||
|
addr := &net.UDPAddr{IP: net.ParseIP("192.168.1.5"), Port: 53}
|
||||||
|
|
||||||
|
ip, port := getIPAndPort(addr)
|
||||||
|
if expected := "192.168.1.5"; ip.String() != expected {
|
||||||
|
t.Fatalf("expected ip %s got %s", expected, ip)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ep := 53; port != ep {
|
||||||
|
t.Fatalf("expected port %d got %d", ep, port)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMapAllPortsSingleInterface(t *testing.T) {
|
||||||
|
dstIp1 := net.ParseIP("0.0.0.0")
|
||||||
|
srcAddr1 := &net.TCPAddr{Port: 1080, IP: net.ParseIP("172.16.0.1")}
|
||||||
|
|
||||||
|
hosts := []net.Addr{}
|
||||||
|
var host net.Addr
|
||||||
|
var err error
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
for _, val := range hosts {
|
||||||
|
Unmap(val)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
for i := portallocator.BeginPortRange; i < portallocator.EndPortRange; i++ {
|
||||||
|
if host, err = Map(srcAddr1, dstIp1, 0); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
hosts = append(hosts, host)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := Map(srcAddr1, dstIp1, portallocator.BeginPortRange); err == nil {
|
||||||
|
t.Fatalf("Port %d should be bound but is not", portallocator.BeginPortRange)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, val := range hosts {
|
||||||
|
if err := Unmap(val); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hosts = []net.Addr{}
|
||||||
|
}
|
||||||
|
}
|
18
libnetwork/Godeps/_workspace/src/github.com/docker/docker/daemon/networkdriver/portmapper/mock_proxy.go
generated
vendored
Normal file
18
libnetwork/Godeps/_workspace/src/github.com/docker/docker/daemon/networkdriver/portmapper/mock_proxy.go
generated
vendored
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
package portmapper
|
||||||
|
|
||||||
|
import "net"
|
||||||
|
|
||||||
|
func NewMockProxyCommand(proto string, hostIP net.IP, hostPort int, containerIP net.IP, containerPort int) UserlandProxy {
|
||||||
|
return &mockProxyCommand{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type mockProxyCommand struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *mockProxyCommand) Start() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *mockProxyCommand) Stop() error {
|
||||||
|
return nil
|
||||||
|
}
|
161
libnetwork/Godeps/_workspace/src/github.com/docker/docker/daemon/networkdriver/portmapper/proxy.go
generated
vendored
Normal file
161
libnetwork/Godeps/_workspace/src/github.com/docker/docker/daemon/networkdriver/portmapper/proxy.go
generated
vendored
Normal file
|
@ -0,0 +1,161 @@
|
||||||
|
package portmapper
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"os/signal"
|
||||||
|
"strconv"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/docker/docker/pkg/proxy"
|
||||||
|
"github.com/docker/docker/pkg/reexec"
|
||||||
|
)
|
||||||
|
|
||||||
|
const userlandProxyCommandName = "docker-proxy"
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
reexec.Register(userlandProxyCommandName, execProxy)
|
||||||
|
}
|
||||||
|
|
||||||
|
type UserlandProxy interface {
|
||||||
|
Start() error
|
||||||
|
Stop() error
|
||||||
|
}
|
||||||
|
|
||||||
|
// proxyCommand wraps an exec.Cmd to run the userland TCP and UDP
|
||||||
|
// proxies as separate processes.
|
||||||
|
type proxyCommand struct {
|
||||||
|
cmd *exec.Cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
// execProxy is the reexec function that is registered to start the userland proxies
|
||||||
|
func execProxy() {
|
||||||
|
f := os.NewFile(3, "signal-parent")
|
||||||
|
host, container := parseHostContainerAddrs()
|
||||||
|
|
||||||
|
p, err := proxy.NewProxy(host, container)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(f, "1\n%s", err)
|
||||||
|
f.Close()
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
go handleStopSignals(p)
|
||||||
|
fmt.Fprint(f, "0\n")
|
||||||
|
f.Close()
|
||||||
|
|
||||||
|
// Run will block until the proxy stops
|
||||||
|
p.Run()
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseHostContainerAddrs parses the flags passed on reexec to create the TCP or UDP
|
||||||
|
// net.Addrs to map the host and container ports
|
||||||
|
func parseHostContainerAddrs() (host net.Addr, container net.Addr) {
|
||||||
|
var (
|
||||||
|
proto = flag.String("proto", "tcp", "proxy protocol")
|
||||||
|
hostIP = flag.String("host-ip", "", "host ip")
|
||||||
|
hostPort = flag.Int("host-port", -1, "host port")
|
||||||
|
containerIP = flag.String("container-ip", "", "container ip")
|
||||||
|
containerPort = flag.Int("container-port", -1, "container port")
|
||||||
|
)
|
||||||
|
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
switch *proto {
|
||||||
|
case "tcp":
|
||||||
|
host = &net.TCPAddr{IP: net.ParseIP(*hostIP), Port: *hostPort}
|
||||||
|
container = &net.TCPAddr{IP: net.ParseIP(*containerIP), Port: *containerPort}
|
||||||
|
case "udp":
|
||||||
|
host = &net.UDPAddr{IP: net.ParseIP(*hostIP), Port: *hostPort}
|
||||||
|
container = &net.UDPAddr{IP: net.ParseIP(*containerIP), Port: *containerPort}
|
||||||
|
default:
|
||||||
|
log.Fatalf("unsupported protocol %s", *proto)
|
||||||
|
}
|
||||||
|
|
||||||
|
return host, container
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleStopSignals(p proxy.Proxy) {
|
||||||
|
s := make(chan os.Signal, 10)
|
||||||
|
signal.Notify(s, os.Interrupt, syscall.SIGTERM, syscall.SIGSTOP)
|
||||||
|
|
||||||
|
for _ = range s {
|
||||||
|
p.Close()
|
||||||
|
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewProxyCommand(proto string, hostIP net.IP, hostPort int, containerIP net.IP, containerPort int) UserlandProxy {
|
||||||
|
args := []string{
|
||||||
|
userlandProxyCommandName,
|
||||||
|
"-proto", proto,
|
||||||
|
"-host-ip", hostIP.String(),
|
||||||
|
"-host-port", strconv.Itoa(hostPort),
|
||||||
|
"-container-ip", containerIP.String(),
|
||||||
|
"-container-port", strconv.Itoa(containerPort),
|
||||||
|
}
|
||||||
|
|
||||||
|
return &proxyCommand{
|
||||||
|
cmd: &exec.Cmd{
|
||||||
|
Path: reexec.Self(),
|
||||||
|
Args: args,
|
||||||
|
SysProcAttr: &syscall.SysProcAttr{
|
||||||
|
Pdeathsig: syscall.SIGTERM, // send a sigterm to the proxy if the daemon process dies
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *proxyCommand) Start() error {
|
||||||
|
r, w, err := os.Pipe()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("proxy unable to open os.Pipe %s", err)
|
||||||
|
}
|
||||||
|
defer r.Close()
|
||||||
|
p.cmd.ExtraFiles = []*os.File{w}
|
||||||
|
if err := p.cmd.Start(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
w.Close()
|
||||||
|
|
||||||
|
errchan := make(chan error, 1)
|
||||||
|
go func() {
|
||||||
|
buf := make([]byte, 2)
|
||||||
|
r.Read(buf)
|
||||||
|
|
||||||
|
if string(buf) != "0\n" {
|
||||||
|
errStr, err := ioutil.ReadAll(r)
|
||||||
|
if err != nil {
|
||||||
|
errchan <- fmt.Errorf("Error reading exit status from userland proxy: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
errchan <- fmt.Errorf("Error starting userland proxy: %s", errStr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
errchan <- nil
|
||||||
|
}()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case err := <-errchan:
|
||||||
|
return err
|
||||||
|
case <-time.After(16 * time.Second):
|
||||||
|
return fmt.Errorf("Timed out proxy starting the userland proxy")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *proxyCommand) Stop() error {
|
||||||
|
if p.cmd.Process != nil {
|
||||||
|
if err := p.cmd.Process.Signal(os.Interrupt); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return p.cmd.Wait()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
118
libnetwork/Godeps/_workspace/src/github.com/docker/docker/daemon/networkdriver/utils.go
generated
vendored
Normal file
118
libnetwork/Godeps/_workspace/src/github.com/docker/docker/daemon/networkdriver/utils.go
generated
vendored
Normal file
|
@ -0,0 +1,118 @@
|
||||||
|
package networkdriver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"github.com/docker/libcontainer/netlink"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
networkGetRoutesFct = netlink.NetworkGetRoutes
|
||||||
|
ErrNoDefaultRoute = errors.New("no default route")
|
||||||
|
)
|
||||||
|
|
||||||
|
func CheckNameserverOverlaps(nameservers []string, toCheck *net.IPNet) error {
|
||||||
|
if len(nameservers) > 0 {
|
||||||
|
for _, ns := range nameservers {
|
||||||
|
_, nsNetwork, err := net.ParseCIDR(ns)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if NetworkOverlaps(toCheck, nsNetwork) {
|
||||||
|
return ErrNetworkOverlapsWithNameservers
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func CheckRouteOverlaps(toCheck *net.IPNet) error {
|
||||||
|
networks, err := networkGetRoutesFct()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, network := range networks {
|
||||||
|
if network.IPNet != nil && NetworkOverlaps(toCheck, network.IPNet) {
|
||||||
|
return ErrNetworkOverlaps
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detects overlap between one IPNet and another
|
||||||
|
func NetworkOverlaps(netX *net.IPNet, netY *net.IPNet) bool {
|
||||||
|
if len(netX.IP) == len(netY.IP) {
|
||||||
|
if firstIP, _ := NetworkRange(netX); netY.Contains(firstIP) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if firstIP, _ := NetworkRange(netY); netX.Contains(firstIP) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculates the first and last IP addresses in an IPNet
|
||||||
|
func NetworkRange(network *net.IPNet) (net.IP, net.IP) {
|
||||||
|
var netIP net.IP
|
||||||
|
if network.IP.To4() != nil {
|
||||||
|
netIP = network.IP.To4()
|
||||||
|
} else if network.IP.To16() != nil {
|
||||||
|
netIP = network.IP.To16()
|
||||||
|
} else {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
lastIP := make([]byte, len(netIP), len(netIP))
|
||||||
|
|
||||||
|
for i := 0; i < len(netIP); i++ {
|
||||||
|
lastIP[i] = netIP[i] | ^network.Mask[i]
|
||||||
|
}
|
||||||
|
return netIP.Mask(network.Mask), net.IP(lastIP)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the first IPv4 address and slice of IPv6 addresses for the specified network interface
|
||||||
|
func GetIfaceAddr(name string) (net.Addr, []net.Addr, error) {
|
||||||
|
iface, err := net.InterfaceByName(name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
addrs, err := iface.Addrs()
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
var addrs4 []net.Addr
|
||||||
|
var addrs6 []net.Addr
|
||||||
|
for _, addr := range addrs {
|
||||||
|
ip := (addr.(*net.IPNet)).IP
|
||||||
|
if ip4 := ip.To4(); ip4 != nil {
|
||||||
|
addrs4 = append(addrs4, addr)
|
||||||
|
} else if ip6 := ip.To16(); len(ip6) == net.IPv6len {
|
||||||
|
addrs6 = append(addrs6, addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
switch {
|
||||||
|
case len(addrs4) == 0:
|
||||||
|
return nil, nil, fmt.Errorf("Interface %v has no IPv4 addresses", name)
|
||||||
|
case len(addrs4) > 1:
|
||||||
|
fmt.Printf("Interface %v has more than 1 IPv4 address. Defaulting to using %v\n",
|
||||||
|
name, (addrs4[0].(*net.IPNet)).IP)
|
||||||
|
}
|
||||||
|
return addrs4[0], addrs6, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetDefaultRouteIface() (*net.Interface, error) {
|
||||||
|
rs, err := networkGetRoutesFct()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to get routes: %v", err)
|
||||||
|
}
|
||||||
|
for _, r := range rs {
|
||||||
|
if r.Default {
|
||||||
|
return r.Iface, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, ErrNoDefaultRoute
|
||||||
|
}
|
93
libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/kernel/kernel.go
generated
vendored
Normal file
93
libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/kernel/kernel.go
generated
vendored
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
package kernel
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
type KernelVersionInfo struct {
|
||||||
|
Kernel int
|
||||||
|
Major int
|
||||||
|
Minor int
|
||||||
|
Flavor string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *KernelVersionInfo) String() string {
|
||||||
|
return fmt.Sprintf("%d.%d.%d%s", k.Kernel, k.Major, k.Minor, k.Flavor)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare two KernelVersionInfo struct.
|
||||||
|
// Returns -1 if a < b, 0 if a == b, 1 it a > b
|
||||||
|
func CompareKernelVersion(a, b *KernelVersionInfo) int {
|
||||||
|
if a.Kernel < b.Kernel {
|
||||||
|
return -1
|
||||||
|
} else if a.Kernel > b.Kernel {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if a.Major < b.Major {
|
||||||
|
return -1
|
||||||
|
} else if a.Major > b.Major {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if a.Minor < b.Minor {
|
||||||
|
return -1
|
||||||
|
} else if a.Minor > b.Minor {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetKernelVersion() (*KernelVersionInfo, error) {
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
uts, err := uname()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
release := make([]byte, len(uts.Release))
|
||||||
|
|
||||||
|
i := 0
|
||||||
|
for _, c := range uts.Release {
|
||||||
|
release[i] = byte(c)
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the \x00 from the release for Atoi to parse correctly
|
||||||
|
release = release[:bytes.IndexByte(release, 0)]
|
||||||
|
|
||||||
|
return ParseRelease(string(release))
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseRelease(release string) (*KernelVersionInfo, error) {
|
||||||
|
var (
|
||||||
|
kernel, major, minor, parsed int
|
||||||
|
flavor, partial string
|
||||||
|
)
|
||||||
|
|
||||||
|
// Ignore error from Sscanf to allow an empty flavor. Instead, just
|
||||||
|
// make sure we got all the version numbers.
|
||||||
|
parsed, _ = fmt.Sscanf(release, "%d.%d%s", &kernel, &major, &partial)
|
||||||
|
if parsed < 2 {
|
||||||
|
return nil, errors.New("Can't parse kernel version " + release)
|
||||||
|
}
|
||||||
|
|
||||||
|
// sometimes we have 3.12.25-gentoo, but sometimes we just have 3.12-1-amd64
|
||||||
|
parsed, _ = fmt.Sscanf(partial, ".%d%s", &minor, &flavor)
|
||||||
|
if parsed < 1 {
|
||||||
|
flavor = partial
|
||||||
|
}
|
||||||
|
|
||||||
|
return &KernelVersionInfo{
|
||||||
|
Kernel: kernel,
|
||||||
|
Major: major,
|
||||||
|
Minor: minor,
|
||||||
|
Flavor: flavor,
|
||||||
|
}, nil
|
||||||
|
}
|
61
libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/kernel/kernel_test.go
generated
vendored
Normal file
61
libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/kernel/kernel_test.go
generated
vendored
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
package kernel
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func assertParseRelease(t *testing.T, release string, b *KernelVersionInfo, result int) {
|
||||||
|
var (
|
||||||
|
a *KernelVersionInfo
|
||||||
|
)
|
||||||
|
a, _ = ParseRelease(release)
|
||||||
|
|
||||||
|
if r := CompareKernelVersion(a, b); r != result {
|
||||||
|
t.Fatalf("Unexpected kernel version comparison result. Found %d, expected %d", r, result)
|
||||||
|
}
|
||||||
|
if a.Flavor != b.Flavor {
|
||||||
|
t.Fatalf("Unexpected parsed kernel flavor. Found %s, expected %s", a.Flavor, b.Flavor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseRelease(t *testing.T) {
|
||||||
|
assertParseRelease(t, "3.8.0", &KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0}, 0)
|
||||||
|
assertParseRelease(t, "3.4.54.longterm-1", &KernelVersionInfo{Kernel: 3, Major: 4, Minor: 54, Flavor: ".longterm-1"}, 0)
|
||||||
|
assertParseRelease(t, "3.4.54.longterm-1", &KernelVersionInfo{Kernel: 3, Major: 4, Minor: 54, Flavor: ".longterm-1"}, 0)
|
||||||
|
assertParseRelease(t, "3.8.0-19-generic", &KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0, Flavor: "-19-generic"}, 0)
|
||||||
|
assertParseRelease(t, "3.12.8tag", &KernelVersionInfo{Kernel: 3, Major: 12, Minor: 8, Flavor: "tag"}, 0)
|
||||||
|
assertParseRelease(t, "3.12-1-amd64", &KernelVersionInfo{Kernel: 3, Major: 12, Minor: 0, Flavor: "-1-amd64"}, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func assertKernelVersion(t *testing.T, a, b *KernelVersionInfo, result int) {
|
||||||
|
if r := CompareKernelVersion(a, b); r != result {
|
||||||
|
t.Fatalf("Unexpected kernel version comparison result. Found %d, expected %d", r, result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCompareKernelVersion(t *testing.T) {
|
||||||
|
assertKernelVersion(t,
|
||||||
|
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0},
|
||||||
|
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0},
|
||||||
|
0)
|
||||||
|
assertKernelVersion(t,
|
||||||
|
&KernelVersionInfo{Kernel: 2, Major: 6, Minor: 0},
|
||||||
|
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0},
|
||||||
|
-1)
|
||||||
|
assertKernelVersion(t,
|
||||||
|
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0},
|
||||||
|
&KernelVersionInfo{Kernel: 2, Major: 6, Minor: 0},
|
||||||
|
1)
|
||||||
|
assertKernelVersion(t,
|
||||||
|
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0},
|
||||||
|
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0},
|
||||||
|
0)
|
||||||
|
assertKernelVersion(t,
|
||||||
|
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 5},
|
||||||
|
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0},
|
||||||
|
1)
|
||||||
|
assertKernelVersion(t,
|
||||||
|
&KernelVersionInfo{Kernel: 3, Major: 0, Minor: 20},
|
||||||
|
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0},
|
||||||
|
-1)
|
||||||
|
}
|
16
libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/kernel/uname_linux.go
generated
vendored
Normal file
16
libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/kernel/uname_linux.go
generated
vendored
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
package kernel
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Utsname syscall.Utsname
|
||||||
|
|
||||||
|
func uname() (*syscall.Utsname, error) {
|
||||||
|
uts := &syscall.Utsname{}
|
||||||
|
|
||||||
|
if err := syscall.Uname(uts); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return uts, nil
|
||||||
|
}
|
15
libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/kernel/uname_unsupported.go
generated
vendored
Normal file
15
libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/kernel/uname_unsupported.go
generated
vendored
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
// +build !linux
|
||||||
|
|
||||||
|
package kernel
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Utsname struct {
|
||||||
|
Release [65]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func uname() (*Utsname, error) {
|
||||||
|
return nil, errors.New("Kernel version detection is available only on linux")
|
||||||
|
}
|
2
libnetwork/Godeps/_workspace/src/github.com/docker/libcontainer/netlink/MAINTAINERS
generated
vendored
Normal file
2
libnetwork/Godeps/_workspace/src/github.com/docker/libcontainer/netlink/MAINTAINERS
generated
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
Michael Crosby <michael@crosbymichael.com> (@crosbymichael)
|
||||||
|
Guillaume J. Charmes <guillaume@docker.com> (@creack)
|
31
libnetwork/Godeps/_workspace/src/github.com/docker/libcontainer/netlink/netlink.go
generated
vendored
Normal file
31
libnetwork/Godeps/_workspace/src/github.com/docker/libcontainer/netlink/netlink.go
generated
vendored
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
// Packet netlink provide access to low level Netlink sockets and messages.
|
||||||
|
//
|
||||||
|
// Actual implementations are in:
|
||||||
|
// netlink_linux.go
|
||||||
|
// netlink_darwin.go
|
||||||
|
package netlink
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrWrongSockType = errors.New("Wrong socket type")
|
||||||
|
ErrShortResponse = errors.New("Got short response from netlink")
|
||||||
|
ErrInterfaceExists = errors.New("Network interface already exists")
|
||||||
|
)
|
||||||
|
|
||||||
|
// A Route is a subnet associated with the interface to reach it.
|
||||||
|
type Route struct {
|
||||||
|
*net.IPNet
|
||||||
|
Iface *net.Interface
|
||||||
|
Default bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// An IfAddr defines IP network settings for a given network interface
|
||||||
|
type IfAddr struct {
|
||||||
|
Iface *net.Interface
|
||||||
|
IP net.IP
|
||||||
|
IPNet *net.IPNet
|
||||||
|
}
|
1307
libnetwork/Godeps/_workspace/src/github.com/docker/libcontainer/netlink/netlink_linux.go
generated
vendored
Normal file
1307
libnetwork/Godeps/_workspace/src/github.com/docker/libcontainer/netlink/netlink_linux.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
5
libnetwork/Godeps/_workspace/src/github.com/docker/libcontainer/netlink/netlink_linux_arm.go
generated
vendored
Normal file
5
libnetwork/Godeps/_workspace/src/github.com/docker/libcontainer/netlink/netlink_linux_arm.go
generated
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
package netlink
|
||||||
|
|
||||||
|
func ifrDataByte(b byte) uint8 {
|
||||||
|
return uint8(b)
|
||||||
|
}
|
7
libnetwork/Godeps/_workspace/src/github.com/docker/libcontainer/netlink/netlink_linux_notarm.go
generated
vendored
Normal file
7
libnetwork/Godeps/_workspace/src/github.com/docker/libcontainer/netlink/netlink_linux_notarm.go
generated
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
// +build !arm
|
||||||
|
|
||||||
|
package netlink
|
||||||
|
|
||||||
|
func ifrDataByte(b byte) int8 {
|
||||||
|
return int8(b)
|
||||||
|
}
|
408
libnetwork/Godeps/_workspace/src/github.com/docker/libcontainer/netlink/netlink_linux_test.go
generated
vendored
Normal file
408
libnetwork/Godeps/_workspace/src/github.com/docker/libcontainer/netlink/netlink_linux_test.go
generated
vendored
Normal file
|
@ -0,0 +1,408 @@
|
||||||
|
package netlink
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
type testLink struct {
|
||||||
|
name string
|
||||||
|
linkType string
|
||||||
|
}
|
||||||
|
|
||||||
|
func addLink(t *testing.T, name string, linkType string) {
|
||||||
|
if err := NetworkLinkAdd(name, linkType); err != nil {
|
||||||
|
t.Fatalf("Unable to create %s link: %s", name, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func readLink(t *testing.T, name string) *net.Interface {
|
||||||
|
iface, err := net.InterfaceByName(name)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Could not find %s interface: %s", name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return iface
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteLink(t *testing.T, name string) {
|
||||||
|
if err := NetworkLinkDel(name); err != nil {
|
||||||
|
t.Fatalf("Unable to delete %s link: %s", name, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func upLink(t *testing.T, name string) {
|
||||||
|
iface := readLink(t, name)
|
||||||
|
if err := NetworkLinkUp(iface); err != nil {
|
||||||
|
t.Fatalf("Could not bring UP %#v interface: %s", iface, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func downLink(t *testing.T, name string) {
|
||||||
|
iface := readLink(t, name)
|
||||||
|
if err := NetworkLinkDown(iface); err != nil {
|
||||||
|
t.Fatalf("Could not bring DOWN %#v interface: %s", iface, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ipAssigned(iface *net.Interface, ip net.IP) bool {
|
||||||
|
addrs, _ := iface.Addrs()
|
||||||
|
|
||||||
|
for _, addr := range addrs {
|
||||||
|
args := strings.SplitN(addr.String(), "/", 2)
|
||||||
|
if args[0] == ip.String() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNetworkLinkAddDel(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
testLinks := []testLink{
|
||||||
|
{"tstEth", "dummy"},
|
||||||
|
{"tstBr", "bridge"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tl := range testLinks {
|
||||||
|
addLink(t, tl.name, tl.linkType)
|
||||||
|
defer deleteLink(t, tl.name)
|
||||||
|
readLink(t, tl.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNetworkLinkUpDown(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tl := testLink{name: "tstEth", linkType: "dummy"}
|
||||||
|
|
||||||
|
addLink(t, tl.name, tl.linkType)
|
||||||
|
defer deleteLink(t, tl.name)
|
||||||
|
|
||||||
|
upLink(t, tl.name)
|
||||||
|
ifcAfterUp := readLink(t, tl.name)
|
||||||
|
|
||||||
|
if (ifcAfterUp.Flags & syscall.IFF_UP) != syscall.IFF_UP {
|
||||||
|
t.Fatalf("Could not bring UP %#v initerface", tl)
|
||||||
|
}
|
||||||
|
|
||||||
|
downLink(t, tl.name)
|
||||||
|
ifcAfterDown := readLink(t, tl.name)
|
||||||
|
|
||||||
|
if (ifcAfterDown.Flags & syscall.IFF_UP) == syscall.IFF_UP {
|
||||||
|
t.Fatalf("Could not bring DOWN %#v initerface", tl)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNetworkSetMacAddress(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tl := testLink{name: "tstEth", linkType: "dummy"}
|
||||||
|
macaddr := "22:ce:e0:99:63:6f"
|
||||||
|
|
||||||
|
addLink(t, tl.name, tl.linkType)
|
||||||
|
defer deleteLink(t, tl.name)
|
||||||
|
|
||||||
|
ifcBeforeSet := readLink(t, tl.name)
|
||||||
|
|
||||||
|
if err := NetworkSetMacAddress(ifcBeforeSet, macaddr); err != nil {
|
||||||
|
t.Fatalf("Could not set %s MAC address on %#v interface: %s", macaddr, tl, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ifcAfterSet := readLink(t, tl.name)
|
||||||
|
|
||||||
|
if ifcAfterSet.HardwareAddr.String() != macaddr {
|
||||||
|
t.Fatalf("Could not set %s MAC address on %#v interface", macaddr, tl)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNetworkSetMTU(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tl := testLink{name: "tstEth", linkType: "dummy"}
|
||||||
|
mtu := 1400
|
||||||
|
|
||||||
|
addLink(t, tl.name, tl.linkType)
|
||||||
|
defer deleteLink(t, tl.name)
|
||||||
|
|
||||||
|
ifcBeforeSet := readLink(t, tl.name)
|
||||||
|
|
||||||
|
if err := NetworkSetMTU(ifcBeforeSet, mtu); err != nil {
|
||||||
|
t.Fatalf("Could not set %d MTU on %#v interface: %s", mtu, tl, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ifcAfterSet := readLink(t, tl.name)
|
||||||
|
|
||||||
|
if ifcAfterSet.MTU != mtu {
|
||||||
|
t.Fatalf("Could not set %d MTU on %#v interface", mtu, tl)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNetworkSetMasterNoMaster(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
master := testLink{"tstBr", "bridge"}
|
||||||
|
slave := testLink{"tstEth", "dummy"}
|
||||||
|
testLinks := []testLink{master, slave}
|
||||||
|
|
||||||
|
for _, tl := range testLinks {
|
||||||
|
addLink(t, tl.name, tl.linkType)
|
||||||
|
defer deleteLink(t, tl.name)
|
||||||
|
upLink(t, tl.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
masterIfc := readLink(t, master.name)
|
||||||
|
slaveIfc := readLink(t, slave.name)
|
||||||
|
if err := NetworkSetMaster(slaveIfc, masterIfc); err != nil {
|
||||||
|
t.Fatalf("Could not set %#v to be the master of %#v: %s", master, slave, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trying to figure out a way to test which will not break on RHEL6.
|
||||||
|
// We could check for existence of /sys/class/net/tstEth/upper_tstBr
|
||||||
|
// which should point to the ../tstBr which is the UPPER device i.e. network bridge
|
||||||
|
|
||||||
|
if err := NetworkSetNoMaster(slaveIfc); err != nil {
|
||||||
|
t.Fatalf("Could not UNset %#v master of %#v: %s", master, slave, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNetworkChangeName(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tl := testLink{"tstEth", "dummy"}
|
||||||
|
newName := "newTst"
|
||||||
|
|
||||||
|
addLink(t, tl.name, tl.linkType)
|
||||||
|
|
||||||
|
linkIfc := readLink(t, tl.name)
|
||||||
|
if err := NetworkChangeName(linkIfc, newName); err != nil {
|
||||||
|
deleteLink(t, tl.name)
|
||||||
|
t.Fatalf("Could not change %#v interface name to %s: %s", tl, newName, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
readLink(t, newName)
|
||||||
|
deleteLink(t, newName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNetworkLinkAddVlan(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tl := struct {
|
||||||
|
name string
|
||||||
|
id uint16
|
||||||
|
}{
|
||||||
|
name: "tstVlan",
|
||||||
|
id: 32,
|
||||||
|
}
|
||||||
|
masterLink := testLink{"tstEth", "dummy"}
|
||||||
|
|
||||||
|
addLink(t, masterLink.name, masterLink.linkType)
|
||||||
|
defer deleteLink(t, masterLink.name)
|
||||||
|
|
||||||
|
if err := NetworkLinkAddVlan(masterLink.name, tl.name, tl.id); err != nil {
|
||||||
|
t.Fatalf("Unable to create %#v VLAN interface: %s", tl, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
readLink(t, tl.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNetworkLinkAddMacVlan(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tl := struct {
|
||||||
|
name string
|
||||||
|
mode string
|
||||||
|
}{
|
||||||
|
name: "tstVlan",
|
||||||
|
mode: "private",
|
||||||
|
}
|
||||||
|
masterLink := testLink{"tstEth", "dummy"}
|
||||||
|
|
||||||
|
addLink(t, masterLink.name, masterLink.linkType)
|
||||||
|
defer deleteLink(t, masterLink.name)
|
||||||
|
|
||||||
|
if err := NetworkLinkAddMacVlan(masterLink.name, tl.name, tl.mode); err != nil {
|
||||||
|
t.Fatalf("Unable to create %#v MAC VLAN interface: %s", tl, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
readLink(t, tl.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNetworkLinkAddMacVtap(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tl := struct {
|
||||||
|
name string
|
||||||
|
mode string
|
||||||
|
}{
|
||||||
|
name: "tstVtap",
|
||||||
|
mode: "private",
|
||||||
|
}
|
||||||
|
masterLink := testLink{"tstEth", "dummy"}
|
||||||
|
|
||||||
|
addLink(t, masterLink.name, masterLink.linkType)
|
||||||
|
defer deleteLink(t, masterLink.name)
|
||||||
|
|
||||||
|
if err := NetworkLinkAddMacVtap(masterLink.name, tl.name, tl.mode); err != nil {
|
||||||
|
t.Fatalf("Unable to create %#v MAC VTAP interface: %s", tl, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
readLink(t, tl.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAddDelNetworkIp(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ifaceName := "lo"
|
||||||
|
ip := net.ParseIP("127.0.1.1")
|
||||||
|
mask := net.IPv4Mask(255, 255, 255, 255)
|
||||||
|
ipNet := &net.IPNet{IP: ip, Mask: mask}
|
||||||
|
|
||||||
|
iface, err := net.InterfaceByName(ifaceName)
|
||||||
|
if err != nil {
|
||||||
|
t.Skip("No 'lo' interface; skipping tests")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := NetworkLinkAddIp(iface, ip, ipNet); err != nil {
|
||||||
|
t.Fatalf("Could not add IP address %s to interface %#v: %s", ip.String(), iface, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !ipAssigned(iface, ip) {
|
||||||
|
t.Fatalf("Could not locate address '%s' in lo address list.", ip.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := NetworkLinkDelIp(iface, ip, ipNet); err != nil {
|
||||||
|
t.Fatalf("Could not delete IP address %s from interface %#v: %s", ip.String(), iface, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ipAssigned(iface, ip) {
|
||||||
|
t.Fatalf("Located address '%s' in lo address list after removal.", ip.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAddRouteSourceSelection(t *testing.T) {
|
||||||
|
tstIp := "127.1.1.1"
|
||||||
|
tl := testLink{name: "tstEth", linkType: "dummy"}
|
||||||
|
|
||||||
|
addLink(t, tl.name, tl.linkType)
|
||||||
|
defer deleteLink(t, tl.name)
|
||||||
|
|
||||||
|
ip := net.ParseIP(tstIp)
|
||||||
|
mask := net.IPv4Mask(255, 255, 255, 255)
|
||||||
|
ipNet := &net.IPNet{IP: ip, Mask: mask}
|
||||||
|
|
||||||
|
iface, err := net.InterfaceByName(tl.name)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Lost created link %#v", tl)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := NetworkLinkAddIp(iface, ip, ipNet); err != nil {
|
||||||
|
t.Fatalf("Could not add IP address %s to interface %#v: %s", ip.String(), iface, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
upLink(t, tl.name)
|
||||||
|
defer downLink(t, tl.name)
|
||||||
|
|
||||||
|
if err := AddRoute("127.0.0.0/8", tstIp, "", tl.name); err != nil {
|
||||||
|
t.Fatalf("Failed to add route with source address")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCreateVethPair(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
name1 = "veth1"
|
||||||
|
name2 = "veth2"
|
||||||
|
)
|
||||||
|
|
||||||
|
if err := NetworkCreateVethPair(name1, name2, 0); err != nil {
|
||||||
|
t.Fatalf("Could not create veth pair %s %s: %s", name1, name2, err)
|
||||||
|
}
|
||||||
|
defer NetworkLinkDel(name1)
|
||||||
|
|
||||||
|
readLink(t, name1)
|
||||||
|
readLink(t, name2)
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// netlink package tests which do not use RTNETLINK
|
||||||
|
//
|
||||||
|
func TestCreateBridgeWithMac(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
name := "testbridge"
|
||||||
|
|
||||||
|
if err := CreateBridge(name, true); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := net.InterfaceByName(name); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// cleanup and tests
|
||||||
|
|
||||||
|
if err := DeleteBridge(name); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := net.InterfaceByName(name); err == nil {
|
||||||
|
t.Fatalf("expected error getting interface because %s bridge was deleted", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetMacAddress(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
name := "testmac"
|
||||||
|
mac := randMacAddr()
|
||||||
|
|
||||||
|
if err := NetworkLinkAdd(name, "bridge"); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer NetworkLinkDel(name)
|
||||||
|
|
||||||
|
if err := SetMacAddress(name, mac); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
iface, err := net.InterfaceByName(name)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if iface.HardwareAddr.String() != mac {
|
||||||
|
t.Fatalf("mac address %q does not match %q", iface.HardwareAddr, mac)
|
||||||
|
}
|
||||||
|
}
|
88
libnetwork/Godeps/_workspace/src/github.com/docker/libcontainer/netlink/netlink_unsupported.go
generated
vendored
Normal file
88
libnetwork/Godeps/_workspace/src/github.com/docker/libcontainer/netlink/netlink_unsupported.go
generated
vendored
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
// +build !linux
|
||||||
|
|
||||||
|
package netlink
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrNotImplemented = errors.New("not implemented")
|
||||||
|
)
|
||||||
|
|
||||||
|
func NetworkGetRoutes() ([]Route, error) {
|
||||||
|
return nil, ErrNotImplemented
|
||||||
|
}
|
||||||
|
|
||||||
|
func NetworkLinkAdd(name string, linkType string) error {
|
||||||
|
return ErrNotImplemented
|
||||||
|
}
|
||||||
|
|
||||||
|
func NetworkLinkDel(name string) error {
|
||||||
|
return ErrNotImplemented
|
||||||
|
}
|
||||||
|
|
||||||
|
func NetworkLinkUp(iface *net.Interface) error {
|
||||||
|
return ErrNotImplemented
|
||||||
|
}
|
||||||
|
|
||||||
|
func NetworkLinkAddIp(iface *net.Interface, ip net.IP, ipNet *net.IPNet) error {
|
||||||
|
return ErrNotImplemented
|
||||||
|
}
|
||||||
|
|
||||||
|
func NetworkLinkDelIp(iface *net.Interface, ip net.IP, ipNet *net.IPNet) error {
|
||||||
|
return ErrNotImplemented
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddRoute(destination, source, gateway, device string) error {
|
||||||
|
return ErrNotImplemented
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddDefaultGw(ip, device string) error {
|
||||||
|
return ErrNotImplemented
|
||||||
|
}
|
||||||
|
|
||||||
|
func NetworkSetMTU(iface *net.Interface, mtu int) error {
|
||||||
|
return ErrNotImplemented
|
||||||
|
}
|
||||||
|
|
||||||
|
func NetworkSetTxQueueLen(iface *net.Interface, txQueueLen int) error {
|
||||||
|
return ErrNotImplemented
|
||||||
|
}
|
||||||
|
|
||||||
|
func NetworkCreateVethPair(name1, name2 string, txQueueLen int) error {
|
||||||
|
return ErrNotImplemented
|
||||||
|
}
|
||||||
|
|
||||||
|
func NetworkChangeName(iface *net.Interface, newName string) error {
|
||||||
|
return ErrNotImplemented
|
||||||
|
}
|
||||||
|
|
||||||
|
func NetworkSetNsFd(iface *net.Interface, fd int) error {
|
||||||
|
return ErrNotImplemented
|
||||||
|
}
|
||||||
|
|
||||||
|
func NetworkSetNsPid(iface *net.Interface, nspid int) error {
|
||||||
|
return ErrNotImplemented
|
||||||
|
}
|
||||||
|
|
||||||
|
func NetworkSetMaster(iface, master *net.Interface) error {
|
||||||
|
return ErrNotImplemented
|
||||||
|
}
|
||||||
|
|
||||||
|
func NetworkLinkDown(iface *net.Interface) error {
|
||||||
|
return ErrNotImplemented
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateBridge(name string, setMacAddr bool) error {
|
||||||
|
return ErrNotImplemented
|
||||||
|
}
|
||||||
|
|
||||||
|
func DeleteBridge(name string) error {
|
||||||
|
return ErrNotImplemented
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddToBridge(iface, master *net.Interface) error {
|
||||||
|
return ErrNotImplemented
|
||||||
|
}
|
|
@ -1,14 +1,20 @@
|
||||||
machine:
|
machine:
|
||||||
# sudo -E doesn't preserve $PATH, so go isn't found anymore.
|
|
||||||
environment:
|
environment:
|
||||||
GO_BIN: $(which go)
|
BASE_DIR: src/github.com/$CIRCLE_PROJECT_USERNAME/$CIRCLE_PROJECT_REPONAME
|
||||||
GO_PATH: /home/ubuntu/.go_workspace/bin/
|
CHECKOUT: /home/ubuntu/$CIRCLE_PROJECT_REPONAME
|
||||||
|
pre:
|
||||||
|
# sudo -E doesn't preserve $PATH, so go isn't found anymore.
|
||||||
|
- sudo ln -s $(which go) /usr/local/bin
|
||||||
|
|
||||||
|
checkout:
|
||||||
|
post:
|
||||||
|
# We need docker/libnetwork itself in the GOPATH for imports to work.
|
||||||
|
- ln -s $CHECKOUT $(echo $GOPATH | cut -d":" -f1)/$BASE_DIR
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
override:
|
override:
|
||||||
- go get github.com/tools/godep
|
- go get github.com/tools/godep
|
||||||
post:
|
post:
|
||||||
- go get github.com/axw/gocov/gocov
|
|
||||||
- go get github.com/golang/lint/golint
|
- go get github.com/golang/lint/golint
|
||||||
- go get golang.org/x/tools/cmd/goimports
|
- go get golang.org/x/tools/cmd/goimports
|
||||||
|
|
||||||
|
@ -17,5 +23,5 @@ test:
|
||||||
- test -z "$(goimports -l . | grep -v Godeps/_workspace/src/ | tee /dev/stderr)"
|
- test -z "$(goimports -l . | grep -v Godeps/_workspace/src/ | tee /dev/stderr)"
|
||||||
- go vet ./...
|
- go vet ./...
|
||||||
- test -z "$(golint ./... | tee /dev/stderr)"
|
- test -z "$(golint ./... | tee /dev/stderr)"
|
||||||
- sudo -E $GO_BIN test -test.v ./...
|
- sudo -E $(which godep) go test -test.v ./...
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue