Sync libnetwork code to latest docker/master

- Update: portmapper, portallocator, ipallocator
- Remove stale godep dependencies
- Update pkg/iptables and others godep to latest
- Update bridge code and test after above changes
- Merge with latest changes in libnetwork

The code is updated up to docker/master commit SHA 86d66d6273

Signed-off-by: Alessandro Boch <aboch@docker.com>
This commit is contained in:
Alessandro Boch 2015-04-10 11:59:05 -07:00
parent 2216885f11
commit 1f3f37b2f8
43 changed files with 469 additions and 5169 deletions

View file

@ -1,44 +1,31 @@
{
"ImportPath": "github.com/docker/libnetwork",
"GoVersion": "go1.4.2",
"Packages": [
"./..."
],
"GoVersion": "go1.2.1",
"Deps": [
{
"ImportPath": "github.com/Sirupsen/logrus",
"Comment": "v0.6.4-12-g467d9d5",
"Rev": "467d9d55c2d2c17248441a8fc661561161f40d5e"
},
{
"ImportPath": "github.com/docker/docker/daemon/networkdriver",
"Comment": "v1.4.1-1379-g8e107a9",
"Rev": "8e107a93210c54f22ec1354d969c771b1abfbe05"
},
{
"ImportPath": "github.com/docker/docker/pkg/iptables",
"Comment": "v1.4.1-1379-g8e107a9",
"Rev": "8e107a93210c54f22ec1354d969c771b1abfbe05"
"Comment": "v1.4.1-2492-ge690ad9",
"Rev": "e690ad92925a045344bde8d2d59d7a7f602dded6"
},
{
"ImportPath": "github.com/docker/docker/pkg/parsers/kernel",
"Comment": "v1.4.1-1379-g8e107a9",
"Rev": "8e107a93210c54f22ec1354d969c771b1abfbe05"
"Comment": "v1.4.1-2492-ge690ad9",
"Rev": "e690ad92925a045344bde8d2d59d7a7f602dded6"
},
{
"ImportPath": "github.com/docker/docker/pkg/proxy",
"Comment": "v1.4.1-1379-g8e107a9",
"Rev": "8e107a93210c54f22ec1354d969c771b1abfbe05"
"Comment": "v1.4.1-2492-ge690ad9",
"Rev": "e690ad92925a045344bde8d2d59d7a7f602dded6"
},
{
"ImportPath": "github.com/docker/docker/pkg/reexec",
"Comment": "v1.4.1-1379-g8e107a9",
"Rev": "8e107a93210c54f22ec1354d969c771b1abfbe05"
},
{
"ImportPath": "github.com/docker/libcontainer/netlink",
"Comment": "v1.4.0-324-g88989e6",
"Rev": "88989e66d3a1ab960deb37f3dd7f824d85e1b9bc"
"Comment": "v1.4.1-2492-ge690ad9",
"Rev": "e690ad92925a045344bde8d2d59d7a7f602dded6"
},
{
"ImportPath": "github.com/vishvananda/netlink",

View file

@ -1,720 +0,0 @@
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
}

View file

@ -1,161 +0,0 @@
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)
}
}

View file

@ -1,160 +0,0 @@
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())
}

View file

@ -1,681 +0,0 @@
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.1127.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()
}
}

View file

@ -1,10 +0,0 @@
package networkdriver
import (
"errors"
)
var (
ErrNetworkOverlapsWithNameservers = errors.New("requested network overlaps with nameserver")
ErrNetworkOverlaps = errors.New("requested network overlaps with existing network")
)

View file

@ -1,175 +0,0 @@
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())
}
}

View file

@ -1,153 +0,0 @@
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
}

View file

@ -1,245 +0,0 @@
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)
}
}

View file

@ -1,176 +0,0 @@
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)
}

View file

@ -1,152 +0,0 @@
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{}
}
}

View file

@ -1,18 +0,0 @@
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
}

View file

@ -1,161 +0,0 @@
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
}

View file

@ -1,118 +0,0 @@
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
}

View file

@ -1,2 +0,0 @@
Michael Crosby <michael@crosbymichael.com> (@crosbymichael)
Jessie Frazelle <jess@docker.com> (@jfrazelle)

View file

@ -9,7 +9,7 @@ import (
"strconv"
"strings"
log "github.com/Sirupsen/logrus"
"github.com/Sirupsen/logrus"
)
type Action string
@ -21,6 +21,7 @@ const (
Insert Action = "-I"
Nat Table = "nat"
Filter Table = "filter"
Mangle Table = "mangle"
)
var (
@ -82,7 +83,7 @@ func NewChain(name, bridge string, table Table) (*Chain, error) {
preroute := []string{
"-m", "addrtype",
"--dst-type", "LOCAL"}
if !Exists(preroute...) {
if !Exists(Nat, "PREROUTING", preroute...) {
if err := c.Prerouting(Append, preroute...); err != nil {
return nil, fmt.Errorf("Failed to inject docker in PREROUTING chain: %s", err)
}
@ -91,17 +92,17 @@ func NewChain(name, bridge string, table Table) (*Chain, error) {
"-m", "addrtype",
"--dst-type", "LOCAL",
"!", "--dst", "127.0.0.0/8"}
if !Exists(output...) {
if !Exists(Nat, "OUTPUT", output...) {
if err := c.Output(Append, output...); err != nil {
return nil, fmt.Errorf("Failed to inject docker in OUTPUT chain: %s", err)
}
}
case Filter:
link := []string{"FORWARD",
link := []string{
"-o", c.Bridge,
"-j", c.Name}
if !Exists(link...) {
insert := append([]string{string(Insert)}, link...)
if !Exists(Filter, "FORWARD", link...) {
insert := append([]string{string(Insert), "FORWARD"}, link...)
if output, err := Raw(insert...); err != nil {
return nil, err
} else if len(output) != 0 {
@ -242,19 +243,25 @@ func (c *Chain) Remove() error {
}
// Check if a rule exists
func Exists(args ...string) bool {
func Exists(table Table, chain string, rule ...string) bool {
if string(table) == "" {
table = Filter
}
// iptables -C, --check option was added in v.1.4.11
// http://ftp.netfilter.org/pub/iptables/changes-iptables-1.4.11.txt
// try -C
// if exit status is 0 then return true, the rule exists
if _, err := Raw(append([]string{"-C"}, args...)...); err == nil {
if _, err := Raw(append([]string{
"-t", string(table), "-C", chain}, rule...)...); err == nil {
return true
}
// parse iptables-save for the rule
rule := strings.Replace(strings.Join(args, " "), "-t nat ", "", -1)
existingRules, _ := exec.Command("iptables-save").Output()
// parse "iptables -S" for the rule (this checks rules in a specific chain
// in a specific table)
ruleString := strings.Join(rule, " ")
existingRules, _ := exec.Command("iptables", "-t", string(table), "-S", chain).Output()
// regex to replace ips in rule
// because MASQUERADE rule will not be exactly what was passed
@ -262,7 +269,7 @@ func Exists(args ...string) bool {
return strings.Contains(
re.ReplaceAllString(string(existingRules), "?"),
re.ReplaceAllString(rule, "?"),
re.ReplaceAllString(ruleString, "?"),
)
}
@ -276,7 +283,7 @@ func Raw(args ...string) ([]byte, error) {
args = append([]string{"--wait"}, args...)
}
log.Debugf("%s, %v", iptablesPath, args)
logrus.Debugf("%s, %v", iptablesPath, args)
output, err := exec.Command(iptablesPath, args...).CombinedOutput()
if err != nil {

View file

@ -39,8 +39,7 @@ func TestForward(t *testing.T) {
t.Fatal(err)
}
dnatRule := []string{natChain.Name,
"-t", string(natChain.Table),
dnatRule := []string{
"!", "-i", filterChain.Bridge,
"-d", ip.String(),
"-p", proto,
@ -49,12 +48,11 @@ func TestForward(t *testing.T) {
"--to-destination", dstAddr + ":" + strconv.Itoa(dstPort),
}
if !Exists(dnatRule...) {
if !Exists(natChain.Table, natChain.Name, dnatRule...) {
t.Fatalf("DNAT rule does not exist")
}
filterRule := []string{filterChain.Name,
"-t", string(filterChain.Table),
filterRule := []string{
"!", "-i", filterChain.Bridge,
"-o", filterChain.Bridge,
"-d", dstAddr,
@ -63,12 +61,11 @@ func TestForward(t *testing.T) {
"-j", "ACCEPT",
}
if !Exists(filterRule...) {
if !Exists(filterChain.Table, filterChain.Name, filterRule...) {
t.Fatalf("filter rule does not exist")
}
masqRule := []string{"POSTROUTING",
"-t", string(natChain.Table),
masqRule := []string{
"-d", dstAddr,
"-s", dstAddr,
"-p", proto,
@ -76,7 +73,7 @@ func TestForward(t *testing.T) {
"-j", "MASQUERADE",
}
if !Exists(masqRule...) {
if !Exists(natChain.Table, "POSTROUTING", masqRule...) {
t.Fatalf("MASQUERADE rule does not exist")
}
}
@ -94,8 +91,7 @@ func TestLink(t *testing.T) {
t.Fatal(err)
}
rule1 := []string{filterChain.Name,
"-t", string(filterChain.Table),
rule1 := []string{
"-i", filterChain.Bridge,
"-o", filterChain.Bridge,
"-p", proto,
@ -104,12 +100,11 @@ func TestLink(t *testing.T) {
"--dport", strconv.Itoa(port),
"-j", "ACCEPT"}
if !Exists(rule1...) {
if !Exists(filterChain.Table, filterChain.Name, rule1...) {
t.Fatalf("rule1 does not exist")
}
rule2 := []string{filterChain.Name,
"-t", string(filterChain.Table),
rule2 := []string{
"-i", filterChain.Bridge,
"-o", filterChain.Bridge,
"-p", proto,
@ -118,7 +113,7 @@ func TestLink(t *testing.T) {
"--sport", strconv.Itoa(port),
"-j", "ACCEPT"}
if !Exists(rule2...) {
if !Exists(filterChain.Table, filterChain.Name, rule2...) {
t.Fatalf("rule2 does not exist")
}
}
@ -133,17 +128,16 @@ func TestPrerouting(t *testing.T) {
t.Fatal(err)
}
rule := []string{"PREROUTING",
"-t", string(Nat),
rule := []string{
"-j", natChain.Name}
rule = append(rule, args...)
if !Exists(rule...) {
if !Exists(natChain.Table, "PREROUTING", rule...) {
t.Fatalf("rule does not exist")
}
delRule := append([]string{"-D"}, rule...)
delRule := append([]string{"-D", "PREROUTING", "-t", string(Nat)}, rule...)
if _, err = Raw(delRule...); err != nil {
t.Fatal(err)
}
@ -159,17 +153,17 @@ func TestOutput(t *testing.T) {
t.Fatal(err)
}
rule := []string{"OUTPUT",
"-t", string(natChain.Table),
rule := []string{
"-j", natChain.Name}
rule = append(rule, args...)
if !Exists(rule...) {
if !Exists(natChain.Table, "OUTPUT", rule...) {
t.Fatalf("rule does not exist")
}
delRule := append([]string{"-D"}, rule...)
delRule := append([]string{"-D", "OUTPUT", "-t",
string(natChain.Table)}, rule...)
if _, err = Raw(delRule...); err != nil {
t.Fatal(err)
}

View file

@ -1 +0,0 @@
Erik Hollensbe <github@hollensbe.org> (@erikh)

View file

@ -5,7 +5,7 @@ import (
"net"
"syscall"
log "github.com/Sirupsen/logrus"
"github.com/Sirupsen/logrus"
)
type TCPProxy struct {
@ -31,7 +31,7 @@ func NewTCPProxy(frontendAddr, backendAddr *net.TCPAddr) (*TCPProxy, error) {
func (proxy *TCPProxy) clientLoop(client *net.TCPConn, quit chan bool) {
backend, err := net.DialTCP("tcp", nil, proxy.backendAddr)
if err != nil {
log.Printf("Can't forward traffic to backend tcp/%v: %s\n", proxy.backendAddr, err)
logrus.Printf("Can't forward traffic to backend tcp/%v: %s\n", proxy.backendAddr, err)
client.Close()
return
}
@ -78,7 +78,7 @@ func (proxy *TCPProxy) Run() {
for {
client, err := proxy.listener.Accept()
if err != nil {
log.Printf("Stopping proxy on tcp/%v for tcp/%v (%s)", proxy.frontendAddr, proxy.backendAddr, err)
logrus.Printf("Stopping proxy on tcp/%v for tcp/%v (%s)", proxy.frontendAddr, proxy.backendAddr, err)
return
}
go proxy.clientLoop(client.(*net.TCPConn), quit)

View file

@ -8,7 +8,7 @@ import (
"syscall"
"time"
log "github.com/Sirupsen/logrus"
"github.com/Sirupsen/logrus"
)
const (
@ -105,7 +105,7 @@ func (proxy *UDPProxy) Run() {
// ECONNREFUSED like Read do (see comment in
// UDPProxy.replyLoop)
if !isClosedError(err) {
log.Printf("Stopping proxy on udp/%v for udp/%v (%s)", proxy.frontendAddr, proxy.backendAddr, err)
logrus.Printf("Stopping proxy on udp/%v for udp/%v (%s)", proxy.frontendAddr, proxy.backendAddr, err)
}
break
}
@ -116,7 +116,7 @@ func (proxy *UDPProxy) Run() {
if !hit {
proxyConn, err = net.DialUDP("udp", nil, proxy.backendAddr)
if err != nil {
log.Printf("Can't proxy a datagram to udp/%s: %s\n", proxy.backendAddr, err)
logrus.Printf("Can't proxy a datagram to udp/%s: %s\n", proxy.backendAddr, err)
proxy.connTrackLock.Unlock()
continue
}
@ -127,7 +127,7 @@ func (proxy *UDPProxy) Run() {
for i := 0; i != read; {
written, err := proxyConn.Write(readBuf[i:read])
if err != nil {
log.Printf("Can't proxy a datagram to udp/%s: %s\n", proxy.backendAddr, err)
logrus.Printf("Can't proxy a datagram to udp/%s: %s\n", proxy.backendAddr, err)
break
}
i += written

View file

@ -1 +0,0 @@
Michael Crosby <michael@docker.com> (@crosbymichael)

View file

@ -35,8 +35,14 @@ func Self() string {
name := os.Args[0]
if filepath.Base(name) == name {
if lp, err := exec.LookPath(name); err == nil {
name = lp
return lp
}
}
// handle conversion of relative paths to absolute
if absName, err := filepath.Abs(name); err == nil {
return absName
}
// if we coudn't get absolute name, return original
// (NOTE: Go only errors on Abs() if os.Getwd fails)
return name
}

View file

@ -1,2 +0,0 @@
Michael Crosby <michael@crosbymichael.com> (@crosbymichael)
Guillaume J. Charmes <guillaume@docker.com> (@creack)

View file

@ -1,31 +0,0 @@
// 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
}

File diff suppressed because it is too large Load diff

View file

@ -1,5 +0,0 @@
package netlink
func ifrDataByte(b byte) uint8 {
return uint8(b)
}

View file

@ -1,7 +0,0 @@
// +build !arm
package netlink
func ifrDataByte(b byte) int8 {
return int8(b)
}

View file

@ -1,408 +0,0 @@
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)
}
}

View file

@ -1,88 +0,0 @@
// +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
}

View file

@ -2,8 +2,11 @@ package bridge
import (
"net"
"sync"
"github.com/docker/libnetwork"
"github.com/docker/libnetwork/ipallocator"
"github.com/docker/libnetwork/portmapper"
)
const (
@ -11,6 +14,18 @@ const (
vethPrefix = "veth"
)
var (
once sync.Once
ipAllocator *ipallocator.IPAllocator
portMapper *portmapper.PortMapper
)
func initPortMapper() {
once.Do(func() {
portMapper = portmapper.New()
})
}
// Configuration info for the "simplebridge" driver.
type Configuration struct {
BridgeName string
@ -27,6 +42,8 @@ type Configuration struct {
type driver struct{}
func init() {
ipAllocator = ipallocator.New()
initPortMapper()
libnetwork.RegisterNetworkType(networkType, &driver{}, &Configuration{})
}

View file

@ -7,7 +7,6 @@ import (
"github.com/docker/libcontainer/utils"
"github.com/docker/libnetwork"
"github.com/docker/libnetwork/ipallocator"
"github.com/vishvananda/netlink"
)
@ -78,14 +77,14 @@ func (b *bridgeNetwork) Link(name string) ([]*libnetwork.Interface, error) {
return nil, err
}
ip4, err := ipallocator.RequestIP(b.bridge.bridgeIPv4, nil)
ip4, err := ipAllocator.RequestIP(b.bridge.bridgeIPv4, nil)
if err != nil {
return nil, err
}
ipv4Addr := net.IPNet{IP: ip4, Mask: b.bridge.bridgeIPv4.Mask}
if b.bridge.Config.EnableIPv6 {
ip6, err := ipallocator.RequestIP(b.bridge.bridgeIPv6, nil)
ip6, err := ipAllocator.RequestIP(b.bridge.bridgeIPv6, nil)
if err != nil {
return nil, err
}

View file

@ -4,7 +4,6 @@ import (
"fmt"
log "github.com/Sirupsen/logrus"
"github.com/docker/libnetwork/ipallocator"
)
func setupFixedCIDRv4(i *bridgeInterface) error {
@ -14,7 +13,7 @@ func setupFixedCIDRv4(i *bridgeInterface) error {
}
log.Debugf("Using IPv4 subnet: %v", i.Config.FixedCIDR)
if err := ipallocator.RegisterSubnet(addrv4.IPNet, i.Config.FixedCIDR); err != nil {
if err := ipAllocator.RegisterSubnet(addrv4.IPNet, i.Config.FixedCIDR); err != nil {
return fmt.Errorf("Setup FixedCIDRv4 failed for subnet %s in %s: %v", i.Config.FixedCIDR, addrv4.IPNet, err)
}

View file

@ -5,7 +5,6 @@ import (
"testing"
"github.com/docker/libnetwork"
"github.com/docker/libnetwork/ipallocator"
)
func TestSetupFixedCIDRv4(t *testing.T) {
@ -29,7 +28,7 @@ func TestSetupFixedCIDRv4(t *testing.T) {
t.Fatalf("Failed to setup bridge FixedCIDRv4: %v", err)
}
if ip, err := ipallocator.RequestIP(br.Config.FixedCIDR, nil); err != nil {
if ip, err := ipAllocator.RequestIP(br.Config.FixedCIDR, nil); err != nil {
t.Fatalf("Failed to request IP to allocator: %v", err)
} else if expected := "192.168.2.1"; ip.String() != expected {
t.Fatalf("Expected allocated IP %s, got %s", expected, ip)

View file

@ -4,12 +4,11 @@ import (
"fmt"
log "github.com/Sirupsen/logrus"
"github.com/docker/libnetwork/ipallocator"
)
func setupFixedCIDRv6(i *bridgeInterface) error {
log.Debugf("Using IPv6 subnet: %v", i.Config.FixedCIDRv6)
if err := ipallocator.RegisterSubnet(i.Config.FixedCIDRv6, i.Config.FixedCIDRv6); err != nil {
if err := ipAllocator.RegisterSubnet(i.Config.FixedCIDRv6, i.Config.FixedCIDRv6); err != nil {
return fmt.Errorf("Setup FixedCIDRv6 failed for subnet %s in %s: %v", i.Config.FixedCIDRv6, i.Config.FixedCIDRv6, err)
}

View file

@ -5,7 +5,6 @@ import (
"testing"
"github.com/docker/libnetwork"
"github.com/docker/libnetwork/ipallocator"
)
func TestSetupFixedCIDRv6(t *testing.T) {
@ -29,7 +28,7 @@ func TestSetupFixedCIDRv6(t *testing.T) {
t.Fatalf("Failed to setup bridge FixedCIDRv6: %v", err)
}
if ip, err := ipallocator.RequestIP(br.Config.FixedCIDRv6, nil); err != nil {
if ip, err := ipAllocator.RequestIP(br.Config.FixedCIDRv6, nil); err != nil {
t.Fatalf("Failed to request IP to allocator: %v", err)
} else if expected := "2002:db8::1"; ip.String() != expected {
t.Fatalf("Expected allocated IP %s, got %s", expected, ip)

View file

@ -6,7 +6,6 @@ import (
"github.com/docker/docker/pkg/iptables"
"github.com/docker/libnetwork"
"github.com/docker/libnetwork/portmapper"
)
// DockerChain: DOCKER iptable chain name
@ -38,17 +37,25 @@ func setupIPTables(i *bridgeInterface) error {
return fmt.Errorf("Failed to create FILTER chain: %s", err.Error())
}
portmapper.SetIptablesChain(chain)
portMapper.SetIptablesChain(chain)
return nil
}
type iptRule struct {
table iptables.Table
chain string
preArgs []string
args []string
}
func setupIPTablesInternal(bridgeIface string, addr net.Addr, icc, ipmasq, enable bool) error {
var (
address = addr.String()
natRule = []string{"POSTROUTING", "-t", "nat", "-s", address, "!", "-o", bridgeIface, "-j", "MASQUERADE"}
outRule = []string{"FORWARD", "-i", bridgeIface, "!", "-o", bridgeIface, "-j", "ACCEPT"}
inRule = []string{"FORWARD", "-o", bridgeIface, "-m", "conntrack", "--ctstate", "RELATED,ESTABLISHED", "-j", "ACCEPT"}
natRule = iptRule{table: iptables.Nat, chain: "POSTROUTING", preArgs: []string{"-t", "nat"}, args: []string{"-s", address, "!", "-o", bridgeIface, "-j", "MASQUERADE"}}
outRule = iptRule{table: iptables.Filter, chain: "FORWARD", args: []string{"-i", bridgeIface, "!", "-o", bridgeIface, "-j", "ACCEPT"}}
inRule = iptRule{table: iptables.Filter, chain: "FORWARD", args: []string{"-o", bridgeIface, "-m", "conntrack", "--ctstate", "RELATED,ESTABLISHED", "-j", "ACCEPT"}}
)
// Set NAT.
@ -76,28 +83,32 @@ func setupIPTablesInternal(bridgeIface string, addr net.Addr, icc, ipmasq, enabl
return nil
}
func programChainRule(ruleArgs []string, ruleDescr string, insert bool) error {
func programChainRule(rule iptRule, ruleDescr string, insert bool) error {
var (
prefix []string
operation string
condition bool
doesExist = iptables.Exists(rule.table, rule.chain, rule.args...)
)
if insert {
condition = !iptables.Exists(ruleArgs...)
prefix = []string{"-I"}
condition = !doesExist
prefix = []string{"-I", rule.chain}
operation = "enable"
} else {
condition = iptables.Exists(ruleArgs...)
prefix = []string{"-D"}
condition = doesExist
prefix = []string{"-D", rule.chain}
operation = "disable"
}
if rule.preArgs != nil {
prefix = append(rule.preArgs, prefix...)
}
if condition {
if output, err := iptables.Raw(append(prefix, ruleArgs...)...); err != nil {
if output, err := iptables.Raw(append(prefix, rule.args...)...); err != nil {
return fmt.Errorf("Unable to %s %s rule: %s", operation, ruleDescr, err.Error())
} else if len(output) != 0 {
return &iptables.ChainError{Chain: ruleDescr, Output: output}
return &iptables.ChainError{Chain: rule.chain, Output: output}
}
}
@ -106,27 +117,29 @@ func programChainRule(ruleArgs []string, ruleDescr string, insert bool) error {
func setIcc(bridgeIface string, iccEnable, insert bool) error {
var (
args = []string{"FORWARD", "-i", bridgeIface, "-o", bridgeIface, "-j"}
table = iptables.Filter
chain = "FORWARD"
args = []string{"-i", bridgeIface, "-o", bridgeIface, "-j"}
acceptArgs = append(args, "ACCEPT")
dropArgs = append(args, "DROP")
)
if insert {
if !iccEnable {
iptables.Raw(append([]string{"-D"}, acceptArgs...)...)
iptables.Raw(append([]string{"-D", chain}, acceptArgs...)...)
if !iptables.Exists(dropArgs...) {
if output, err := iptables.Raw(append([]string{"-I"}, dropArgs...)...); err != nil {
if !iptables.Exists(table, chain, dropArgs...) {
if output, err := iptables.Raw(append([]string{"-I", chain}, dropArgs...)...); err != nil {
return fmt.Errorf("Unable to prevent intercontainer communication: %s", err.Error())
} else if len(output) != 0 {
return fmt.Errorf("Error disabling intercontainer communication: %s", output)
}
}
} else {
iptables.Raw(append([]string{"-D"}, dropArgs...)...)
iptables.Raw(append([]string{"-D", chain}, dropArgs...)...)
if !iptables.Exists(acceptArgs...) {
if output, err := iptables.Raw(append([]string{"-I"}, acceptArgs...)...); err != nil {
if !iptables.Exists(table, chain, acceptArgs...) {
if output, err := iptables.Raw(append([]string{"-I", chain}, acceptArgs...)...); err != nil {
return fmt.Errorf("Unable to allow intercontainer communication: %s", err.Error())
} else if len(output) != 0 {
return fmt.Errorf("Error enabling intercontainer communication: %s", output)
@ -136,12 +149,12 @@ func setIcc(bridgeIface string, iccEnable, insert bool) error {
} else {
// Remove any ICC rule.
if !iccEnable {
if iptables.Exists(dropArgs...) {
iptables.Raw(append([]string{"-D"}, dropArgs...)...)
if iptables.Exists(table, chain, dropArgs...) {
iptables.Raw(append([]string{"-D", chain}, dropArgs...)...)
}
} else {
if iptables.Exists(acceptArgs...) {
iptables.Raw(append([]string{"-D"}, acceptArgs...)...)
if iptables.Exists(table, chain, acceptArgs...) {
iptables.Raw(append([]string{"-D", chain}, acceptArgs...)...)
}
}
}

View file

@ -19,19 +19,20 @@ func TestProgramIPTable(t *testing.T) {
// Store various iptables chain rules we care for.
rules := []struct {
ruleArgs []string
descr string
}{{[]string{"FORWARD", "-d", "127.1.2.3", "-i", "lo", "-o", "lo", "-j", "DROP"}, "Test Loopback"},
{[]string{"POSTROUTING", "-t", "nat", "-s", iptablesTestBridgeIP, "!", "-o", DefaultBridgeName, "-j", "MASQUERADE"}, "NAT Test"},
{[]string{"FORWARD", "-i", DefaultBridgeName, "!", "-o", DefaultBridgeName, "-j", "ACCEPT"}, "Test ACCEPT NON_ICC OUTGOING"},
{[]string{"FORWARD", "-o", DefaultBridgeName, "-m", "conntrack", "--ctstate", "RELATED,ESTABLISHED", "-j", "ACCEPT"}, "Test ACCEPT INCOMING"},
{[]string{"FORWARD", "-i", DefaultBridgeName, "-o", DefaultBridgeName, "-j", "ACCEPT"}, "Test enable ICC"},
{[]string{"FORWARD", "-i", DefaultBridgeName, "-o", DefaultBridgeName, "-j", "DROP"}, "Test disable ICC"},
rule iptRule
descr string
}{
{iptRule{table: iptables.Filter, chain: "FORWARD", args: []string{"-d", "127.1.2.3", "-i", "lo", "-o", "lo", "-j", "DROP"}}, "Test Loopback"},
{iptRule{table: iptables.Nat, chain: "POSTROUTING", preArgs: []string{"-t", "nat"}, args: []string{"-s", iptablesTestBridgeIP, "!", "-o", DefaultBridgeName, "-j", "MASQUERADE"}}, "NAT Test"},
{iptRule{table: iptables.Filter, chain: "FORWARD", args: []string{"-i", DefaultBridgeName, "!", "-o", DefaultBridgeName, "-j", "ACCEPT"}}, "Test ACCEPT NON_ICC OUTGOING"},
{iptRule{table: iptables.Filter, chain: "FORWARD", args: []string{"-o", DefaultBridgeName, "-m", "conntrack", "--ctstate", "RELATED,ESTABLISHED", "-j", "ACCEPT"}}, "Test ACCEPT INCOMING"},
{iptRule{table: iptables.Filter, chain: "FORWARD", args: []string{"-i", DefaultBridgeName, "-o", DefaultBridgeName, "-j", "ACCEPT"}}, "Test enable ICC"},
{iptRule{table: iptables.Filter, chain: "FORWARD", args: []string{"-i", DefaultBridgeName, "-o", DefaultBridgeName, "-j", "DROP"}}, "Test disable ICC"},
}
// Assert the chain rules' insertion and removal.
for _, c := range rules {
assertIPTableChainProgramming(c.ruleArgs, c.descr, t)
assertIPTableChainProgramming(c.rule, c.descr, t)
}
}
@ -74,20 +75,20 @@ func createTestBridge(br *bridgeInterface, t *testing.T) {
}
// Assert base function which pushes iptables chain rules on insertion and removal.
func assertIPTableChainProgramming(args []string, descr string, t *testing.T) {
func assertIPTableChainProgramming(rule iptRule, descr string, t *testing.T) {
// Add
if err := programChainRule(args, descr, true); err != nil {
if err := programChainRule(rule, descr, true); err != nil {
t.Fatalf("Failed to program iptable rule %s: %s", descr, err.Error())
}
if iptables.Exists(args...) == false {
if iptables.Exists(rule.table, rule.chain, rule.args...) == false {
t.Fatalf("Failed to effectively program iptable rule: %s", descr)
}
// Remove
if err := programChainRule(args, descr, false); err != nil {
if err := programChainRule(rule, descr, false); err != nil {
t.Fatalf("Failed to remove iptable rule %s: %s", descr, err.Error())
}
if iptables.Exists(args...) == true {
if iptables.Exists(rule.table, rule.chain, rule.args...) == true {
t.Fatalf("Failed to effectively remove iptable rule: %s", descr)
}
}

View file

@ -8,7 +8,7 @@ import (
"net"
"sync"
log "github.com/Sirupsen/logrus"
"github.com/Sirupsen/logrus"
"github.com/docker/libnetwork"
)
@ -48,19 +48,26 @@ var (
ErrBadSubnet = errors.New("network does not contain specified subnet")
)
var (
lock = sync.Mutex{}
allocatedIPs = networkSet{}
)
// IPAllocator manages the ipam
type IPAllocator struct {
allocatedIPs networkSet
mutex sync.Mutex
}
// New returns a new instance of IPAllocator
func New() *IPAllocator {
return &IPAllocator{networkSet{}, sync.Mutex{}}
}
// 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()
func (a *IPAllocator) RegisterSubnet(network *net.IPNet, subnet *net.IPNet) error {
a.mutex.Lock()
defer a.mutex.Unlock()
key := network.String()
if _, ok := allocatedIPs[key]; ok {
if _, ok := a.allocatedIPs[key]; ok {
return ErrNetworkAlreadyRegistered
}
n := newAllocatedMap(network)
@ -75,7 +82,7 @@ func RegisterSubnet(network *net.IPNet, subnet *net.IPNet) error {
n.begin.Set(begin)
n.end.Set(end)
n.last.Sub(begin, big.NewInt(1))
allocatedIPs[key] = n
a.allocatedIPs[key] = n
return nil
}
@ -83,14 +90,15 @@ func RegisterSubnet(network *net.IPNet, subnet *net.IPNet) error {
// 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()
func (a *IPAllocator) RequestIP(network *net.IPNet, ip net.IP) (net.IP, error) {
a.mutex.Lock()
defer a.mutex.Unlock()
key := network.String()
allocated, ok := allocatedIPs[key]
allocated, ok := a.allocatedIPs[key]
if !ok {
allocated = newAllocatedMap(network)
allocatedIPs[key] = allocated
a.allocatedIPs[key] = allocated
}
if ip == nil {
@ -101,10 +109,11 @@ func RequestIP(network *net.IPNet, ip net.IP) (net.IP, error) {
// 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 {
func (a *IPAllocator) ReleaseIP(network *net.IPNet, ip net.IP) error {
a.mutex.Lock()
defer a.mutex.Unlock()
if allocated, exists := a.allocatedIPs[network.String()]; exists {
delete(allocated.p, ip.String())
}
return nil
@ -128,7 +137,7 @@ func (allocated *allocatedMap) checkIP(ip net.IP) (net.IP, error) {
}
// return an available ip if one is currently available. If not,
// return the next available ip for the nextwork
// return the next available ip for the network
func (allocated *allocatedMap) getNextIP() (net.IP, error) {
pos := big.NewInt(0).Set(allocated.last)
allRange := big.NewInt(0).Sub(allocated.end, allocated.begin)
@ -157,7 +166,7 @@ func ipToBigInt(ip net.IP) *big.Int {
return x.SetBytes(ip6)
}
log.Errorf("ipToBigInt: Wrong IP length! %s", ip)
logrus.Errorf("ipToBigInt: Wrong IP length! %s", ip)
return nil
}

View file

@ -7,10 +7,6 @@ import (
"testing"
)
func reset() {
allocatedIPs = networkSet{}
}
func TestConversion(t *testing.T) {
ip := net.ParseIP("127.0.0.1")
i := ipToBigInt(ip)
@ -52,7 +48,8 @@ func TestConversionIPv6(t *testing.T) {
}
func TestRequestNewIps(t *testing.T) {
defer reset()
a := New()
network := &net.IPNet{
IP: []byte{192, 168, 0, 1},
Mask: []byte{255, 255, 255, 0},
@ -62,7 +59,7 @@ func TestRequestNewIps(t *testing.T) {
var err error
for i := 1; i < 10; i++ {
ip, err = RequestIP(network, nil)
ip, err = a.RequestIP(network, nil)
if err != nil {
t.Fatal(err)
}
@ -72,10 +69,10 @@ func TestRequestNewIps(t *testing.T) {
}
}
value := bigIntToIP(big.NewInt(0).Add(ipToBigInt(ip), big.NewInt(1))).String()
if err := ReleaseIP(network, ip); err != nil {
if err := a.ReleaseIP(network, ip); err != nil {
t.Fatal(err)
}
ip, err = RequestIP(network, nil)
ip, err = a.RequestIP(network, nil)
if err != nil {
t.Fatal(err)
}
@ -85,7 +82,8 @@ func TestRequestNewIps(t *testing.T) {
}
func TestRequestNewIpV6(t *testing.T) {
defer reset()
a := New()
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
@ -94,7 +92,7 @@ func TestRequestNewIpV6(t *testing.T) {
var ip net.IP
var err error
for i := 1; i < 10; i++ {
ip, err = RequestIP(network, nil)
ip, err = a.RequestIP(network, nil)
if err != nil {
t.Fatal(err)
}
@ -104,10 +102,10 @@ func TestRequestNewIpV6(t *testing.T) {
}
}
value := bigIntToIP(big.NewInt(0).Add(ipToBigInt(ip), big.NewInt(1))).String()
if err := ReleaseIP(network, ip); err != nil {
if err := a.ReleaseIP(network, ip); err != nil {
t.Fatal(err)
}
ip, err = RequestIP(network, nil)
ip, err = a.RequestIP(network, nil)
if err != nil {
t.Fatal(err)
}
@ -117,68 +115,70 @@ func TestRequestNewIpV6(t *testing.T) {
}
func TestReleaseIp(t *testing.T) {
defer reset()
a := New()
network := &net.IPNet{
IP: []byte{192, 168, 0, 1},
Mask: []byte{255, 255, 255, 0},
}
ip, err := RequestIP(network, nil)
ip, err := a.RequestIP(network, nil)
if err != nil {
t.Fatal(err)
}
if err := ReleaseIP(network, ip); err != nil {
if err := a.ReleaseIP(network, ip); err != nil {
t.Fatal(err)
}
}
func TestReleaseIpV6(t *testing.T) {
defer reset()
a := New()
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)
ip, err := a.RequestIP(network, nil)
if err != nil {
t.Fatal(err)
}
if err := ReleaseIP(network, ip); err != nil {
if err := a.ReleaseIP(network, ip); err != nil {
t.Fatal(err)
}
}
func TestGetReleasedIp(t *testing.T) {
defer reset()
a := New()
network := &net.IPNet{
IP: []byte{192, 168, 0, 1},
Mask: []byte{255, 255, 255, 0},
}
ip, err := RequestIP(network, nil)
ip, err := a.RequestIP(network, nil)
if err != nil {
t.Fatal(err)
}
value := ip.String()
if err := ReleaseIP(network, ip); err != nil {
if err := a.ReleaseIP(network, ip); err != nil {
t.Fatal(err)
}
for i := 0; i < 253; i++ {
_, err = RequestIP(network, nil)
_, err = a.RequestIP(network, nil)
if err != nil {
t.Fatal(err)
}
err = ReleaseIP(network, ip)
err = a.ReleaseIP(network, ip)
if err != nil {
t.Fatal(err)
}
}
ip, err = RequestIP(network, nil)
ip, err = a.RequestIP(network, nil)
if err != nil {
t.Fatal(err)
}
@ -189,34 +189,35 @@ func TestGetReleasedIp(t *testing.T) {
}
func TestGetReleasedIpV6(t *testing.T) {
defer reset()
a := New()
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)
ip, err := a.RequestIP(network, nil)
if err != nil {
t.Fatal(err)
}
value := ip.String()
if err := ReleaseIP(network, ip); err != nil {
if err := a.ReleaseIP(network, ip); err != nil {
t.Fatal(err)
}
for i := 0; i < 253; i++ {
_, err = RequestIP(network, nil)
_, err = a.RequestIP(network, nil)
if err != nil {
t.Fatal(err)
}
err = ReleaseIP(network, ip)
err = a.ReleaseIP(network, ip)
if err != nil {
t.Fatal(err)
}
}
ip, err = RequestIP(network, nil)
ip, err = a.RequestIP(network, nil)
if err != nil {
t.Fatal(err)
}
@ -227,7 +228,8 @@ func TestGetReleasedIpV6(t *testing.T) {
}
func TestRequestSpecificIp(t *testing.T) {
defer reset()
a := New()
network := &net.IPNet{
IP: []byte{192, 168, 0, 1},
Mask: []byte{255, 255, 255, 224},
@ -236,23 +238,24 @@ func TestRequestSpecificIp(t *testing.T) {
ip := net.ParseIP("192.168.0.5")
// Request a "good" IP.
if _, err := RequestIP(network, ip); err != nil {
if _, err := a.RequestIP(network, ip); err != nil {
t.Fatal(err)
}
// Request the same IP again.
if _, err := RequestIP(network, ip); err != ErrIPAlreadyAllocated {
if _, err := a.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 {
if _, err := a.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()
a := New()
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
@ -261,22 +264,24 @@ func TestRequestSpecificIpV6(t *testing.T) {
ip := net.ParseIP("2a00:1450::5")
// Request a "good" IP.
if _, err := RequestIP(network, ip); err != nil {
if _, err := a.RequestIP(network, ip); err != nil {
t.Fatal(err)
}
// Request the same IP again.
if _, err := RequestIP(network, ip); err != ErrIPAlreadyAllocated {
if _, err := a.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 {
if _, err := a.RequestIP(network, net.ParseIP("2a00:1500::1")); err != ErrIPOutOfRange {
t.Fatalf("Got an out of range IP: %#v", err)
}
}
func TestIPAllocator(t *testing.T) {
a := New()
expectedIPs := []net.IP{
0: net.IPv4(127, 0, 0, 1),
1: net.IPv4(127, 0, 0, 2),
@ -296,7 +301,7 @@ func TestIPAllocator(t *testing.T) {
// Check that we get 6 IPs, from 127.0.0.1127.0.0.6, in that
// order.
for i := 0; i < 6; i++ {
ip, err := RequestIP(network, nil)
ip, err := a.RequestIP(network, nil)
if err != nil {
t.Fatal(err)
}
@ -332,25 +337,25 @@ func TestIPAllocator(t *testing.T) {
// ↑
// Check that there are no more IPs
ip, err := RequestIP(network, nil)
ip, err := a.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 {
if err := a.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 {
if err := a.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 {
if err := a.ReleaseIP(network, expectedIPs[4]); err != nil {
t.Fatal(err)
}
// 1(u) - 2(u) - 3(f) - 4(f) - 5(f) - 6(u)
@ -360,7 +365,7 @@ func TestIPAllocator(t *testing.T) {
// with the first released IP
newIPs := make([]net.IP, 3)
for i := 0; i < 3; i++ {
ip, err := RequestIP(network, nil)
ip, err := a.RequestIP(network, nil)
if err != nil {
t.Fatal(err)
}
@ -371,14 +376,15 @@ func TestIPAllocator(t *testing.T) {
assertIPEquals(t, expectedIPs[3], newIPs[1])
assertIPEquals(t, expectedIPs[4], newIPs[2])
_, err = RequestIP(network, nil)
_, err = a.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()
a := New()
network := &net.IPNet{
IP: []byte{192, 168, 0, 0},
Mask: []byte{255, 255, 255, 0},
@ -387,7 +393,7 @@ func TestAllocateFirstIP(t *testing.T) {
firstIP := network.IP.To4().Mask(network.Mask)
first := big.NewInt(0).Add(ipToBigInt(firstIP), big.NewInt(1))
ip, err := RequestIP(network, nil)
ip, err := a.RequestIP(network, nil)
if err != nil {
t.Fatal(err)
}
@ -399,7 +405,8 @@ func TestAllocateFirstIP(t *testing.T) {
}
func TestAllocateAllIps(t *testing.T) {
defer reset()
a := New()
network := &net.IPNet{
IP: []byte{192, 168, 0, 1},
Mask: []byte{255, 255, 255, 0},
@ -412,7 +419,7 @@ func TestAllocateAllIps(t *testing.T) {
)
for err == nil {
current, err = RequestIP(network, nil)
current, err = a.RequestIP(network, nil)
if isFirst {
first = current
isFirst = false
@ -423,15 +430,15 @@ func TestAllocateAllIps(t *testing.T) {
t.Fatal(err)
}
if _, err := RequestIP(network, nil); err != ErrNoAvailableIPs {
if _, err := a.RequestIP(network, nil); err != ErrNoAvailableIPs {
t.Fatal(err)
}
if err := ReleaseIP(network, first); err != nil {
if err := a.ReleaseIP(network, first); err != nil {
t.Fatal(err)
}
again, err := RequestIP(network, nil)
again, err := a.RequestIP(network, nil)
if err != nil {
t.Fatal(err)
}
@ -439,17 +446,17 @@ func TestAllocateAllIps(t *testing.T) {
assertIPEquals(t, first, again)
// ensure that alloc.last == alloc.begin won't result in dead loop
if _, err := RequestIP(network, nil); err != ErrNoAvailableIPs {
if _, err := a.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 {
if err := a.ReleaseIP(network, first); err != nil {
t.Fatal(err)
}
ret, err := RequestIP(network, nil)
ret, err := a.RequestIP(network, nil)
if err != nil {
t.Fatal(err)
}
@ -458,9 +465,9 @@ func TestAllocateAllIps(t *testing.T) {
// #2. last of the range, note that current is the last one
last := net.IPv4(192, 168, 0, 254)
setLastTo(t, network, last)
setLastTo(t, a, network, last)
ret, err = RequestIP(network, nil)
ret, err = a.RequestIP(network, nil)
if err != nil {
t.Fatal(err)
}
@ -469,9 +476,9 @@ func TestAllocateAllIps(t *testing.T) {
// #3. middle of the range
mid := net.IPv4(192, 168, 0, 7)
setLastTo(t, network, mid)
setLastTo(t, a, network, mid)
ret, err = RequestIP(network, nil)
ret, err = a.RequestIP(network, nil)
if err != nil {
t.Fatal(err)
}
@ -481,25 +488,25 @@ func TestAllocateAllIps(t *testing.T) {
// 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 {
func setLastTo(t *testing.T, a *IPAllocator, network *net.IPNet, ip net.IP) {
if err := a.ReleaseIP(network, ip); err != nil {
t.Fatal(err)
}
ret, err := RequestIP(network, nil)
ret, err := a.RequestIP(network, nil)
if err != nil {
t.Fatal(err)
}
assertIPEquals(t, ip, ret)
if err := ReleaseIP(network, ip); err != nil {
if err := a.ReleaseIP(network, ip); err != nil {
t.Fatal(err)
}
}
func TestAllocateDifferentSubnets(t *testing.T) {
defer reset()
a := New()
network1 := &net.IPNet{
IP: []byte{192, 168, 0, 1},
Mask: []byte{255, 255, 255, 0},
@ -528,39 +535,39 @@ func TestAllocateDifferentSubnets(t *testing.T) {
8: net.ParseIP("2a00:1632::2"),
}
ip11, err := RequestIP(network1, nil)
ip11, err := a.RequestIP(network1, nil)
if err != nil {
t.Fatal(err)
}
ip12, err := RequestIP(network1, nil)
ip12, err := a.RequestIP(network1, nil)
if err != nil {
t.Fatal(err)
}
ip21, err := RequestIP(network2, nil)
ip21, err := a.RequestIP(network2, nil)
if err != nil {
t.Fatal(err)
}
ip22, err := RequestIP(network2, nil)
ip22, err := a.RequestIP(network2, nil)
if err != nil {
t.Fatal(err)
}
ip31, err := RequestIP(network3, nil)
ip31, err := a.RequestIP(network3, nil)
if err != nil {
t.Fatal(err)
}
ip32, err := RequestIP(network3, nil)
ip32, err := a.RequestIP(network3, nil)
if err != nil {
t.Fatal(err)
}
ip33, err := RequestIP(network3, nil)
ip33, err := a.RequestIP(network3, nil)
if err != nil {
t.Fatal(err)
}
ip41, err := RequestIP(network4, nil)
ip41, err := a.RequestIP(network4, nil)
if err != nil {
t.Fatal(err)
}
ip42, err := RequestIP(network4, nil)
ip42, err := a.RequestIP(network4, nil)
if err != nil {
t.Fatal(err)
}
@ -576,7 +583,7 @@ func TestAllocateDifferentSubnets(t *testing.T) {
}
func TestRegisterBadTwice(t *testing.T) {
defer reset()
a := New()
network := &net.IPNet{
IP: []byte{192, 168, 1, 1},
Mask: []byte{255, 255, 255, 0},
@ -586,20 +593,20 @@ func TestRegisterBadTwice(t *testing.T) {
Mask: []byte{255, 255, 255, 248},
}
if err := RegisterSubnet(network, subnet); err != nil {
if err := a.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 {
if err := a.RegisterSubnet(network, subnet); err != ErrNetworkAlreadyRegistered {
t.Fatalf("Expecteded ErrNetworkAlreadyRegistered error, got %v", err)
}
}
func TestRegisterBadRange(t *testing.T) {
defer reset()
a := New()
network := &net.IPNet{
IP: []byte{192, 168, 1, 1},
Mask: []byte{255, 255, 255, 0},
@ -608,13 +615,13 @@ func TestRegisterBadRange(t *testing.T) {
IP: []byte{192, 168, 1, 1},
Mask: []byte{255, 255, 0, 0},
}
if err := RegisterSubnet(network, subnet); err != ErrBadSubnet {
if err := a.RegisterSubnet(network, subnet); err != ErrBadSubnet {
t.Fatalf("Expected ErrBadSubnet error, got %v", err)
}
}
func TestAllocateFromRange(t *testing.T) {
defer reset()
a := New()
network := &net.IPNet{
IP: []byte{192, 168, 0, 1},
Mask: []byte{255, 255, 255, 0},
@ -625,7 +632,7 @@ func TestAllocateFromRange(t *testing.T) {
Mask: []byte{255, 255, 255, 248},
}
if err := RegisterSubnet(network, subnet); err != nil {
if err := a.RegisterSubnet(network, subnet); err != nil {
t.Fatal(err)
}
expectedIPs := []net.IP{
@ -637,19 +644,19 @@ func TestAllocateFromRange(t *testing.T) {
5: net.IPv4(192, 168, 0, 14),
}
for _, ip := range expectedIPs {
rip, err := RequestIP(network, nil)
rip, err := a.RequestIP(network, nil)
if err != nil {
t.Fatal(err)
}
assertIPEquals(t, ip, rip)
}
if _, err := RequestIP(network, nil); err != ErrNoAvailableIPs {
if _, err := a.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)
a.ReleaseIP(network, ip)
rip, err := a.RequestIP(network, nil)
if err != nil {
t.Fatal(err)
}
@ -669,13 +676,15 @@ func BenchmarkRequestIP(b *testing.B) {
Mask: []byte{255, 255, 255, 0},
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
a := New()
for j := 0; j < 253; j++ {
_, err := RequestIP(network, nil)
_, err := a.RequestIP(network, nil)
if err != nil {
b.Fatal(err)
}
}
reset()
}
}

View file

@ -1,54 +1,31 @@
package portallocator
import (
"bufio"
"errors"
"fmt"
"net"
"os"
"sync"
"github.com/Sirupsen/logrus"
)
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 indicates the first port in port range
BeginPortRange = 49153
// EndPortRange indicates the last port in port range
EndPortRange = 65535
// DefaultPortRangeStart indicates the first port in port range
DefaultPortRangeStart = 49153
// DefaultPortRangeEnd indicates the last port in port range
DefaultPortRangeEnd = 65535
)
type ipMapping map[string]protoMap
var (
// ErrAllPortsAllocated is returned when no more ports are available
ErrAllPortsAllocated = errors.New("all ports are allocated")
// ErrUnknownProtocol is returned when an unknown protocol was specified
ErrUnknownProtocol = errors.New("unknown protocol")
)
var (
mutex sync.Mutex
defaultIP = net.ParseIP("0.0.0.0")
globalMap = ipMapping{}
defaultIP = net.ParseIP("0.0.0.0")
)
// ErrPortAlreadyAllocated is the returned error information when a requested port is already being used
@ -84,12 +61,59 @@ func (e ErrPortAlreadyAllocated) Error() string {
return fmt.Sprintf("Bind for %s:%d failed: port is already allocated", e.ip, e.port)
}
type (
// PortAllocator manages the transport ports database
PortAllocator struct {
mutex sync.Mutex
ipMap ipMapping
Begin int
End int
}
portMap struct {
p map[int]struct{}
begin, end int
last int
}
protoMap map[string]*portMap
)
// New returns a new instance of PortAllocator
func New() *PortAllocator {
start, end, err := getDynamicPortRange()
if err != nil {
logrus.Warn(err)
start, end = DefaultPortRangeStart, DefaultPortRangeEnd
}
return &PortAllocator{
ipMap: ipMapping{},
Begin: start,
End: end,
}
}
func getDynamicPortRange() (start int, end int, err error) {
const portRangeKernelParam = "/proc/sys/net/ipv4/ip_local_port_range"
portRangeFallback := fmt.Sprintf("using fallback port range %d-%d", DefaultPortRangeStart, DefaultPortRangeEnd)
file, err := os.Open(portRangeKernelParam)
if err != nil {
return 0, 0, fmt.Errorf("port allocator - %s due to error: %v", portRangeFallback, err)
}
n, err := fmt.Fscanf(bufio.NewReader(file), "%d\t%d", &start, &end)
if n != 2 || err != nil {
if err == nil {
err = fmt.Errorf("unexpected count of parsed numbers (%d)", n)
}
return 0, 0, fmt.Errorf("port allocator - failed to parse system ephemeral port range from %s - %s: %v", portRangeKernelParam, portRangeFallback, err)
}
return start, end, nil
}
// 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()
func (p *PortAllocator) RequestPort(ip net.IP, proto string, port int) (int, error) {
p.mutex.Lock()
defer p.mutex.Unlock()
if proto != "tcp" && proto != "udp" {
return 0, ErrUnknownProtocol
@ -99,10 +123,14 @@ func RequestPort(ip net.IP, proto string, port int) (int, error) {
ip = defaultIP
}
ipstr := ip.String()
protomap, ok := globalMap[ipstr]
protomap, ok := p.ipMap[ipstr]
if !ok {
protomap = newProtoMap()
globalMap[ipstr] = protomap
protomap = protoMap{
"tcp": p.newPortMap(),
"udp": p.newPortMap(),
}
p.ipMap[ipstr] = protomap
}
mapping := protomap[proto]
if port > 0 {
@ -121,14 +149,14 @@ func RequestPort(ip net.IP, proto string, port int) (int, error) {
}
// 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()
func (p *PortAllocator) ReleasePort(ip net.IP, proto string, port int) error {
p.mutex.Lock()
defer p.mutex.Unlock()
if ip == nil {
ip = defaultIP
}
protomap, ok := globalMap[ip.String()]
protomap, ok := p.ipMap[ip.String()]
if !ok {
return nil
}
@ -136,20 +164,29 @@ func ReleasePort(ip net.IP, proto string, port int) error {
return nil
}
func (p *PortAllocator) newPortMap() *portMap {
return &portMap{
p: map[int]struct{}{},
begin: p.Begin,
end: p.End,
last: p.End,
}
}
// ReleaseAll releases all ports for all ips.
func ReleaseAll() error {
mutex.Lock()
globalMap = ipMapping{}
mutex.Unlock()
func (p *PortAllocator) ReleaseAll() error {
p.mutex.Lock()
p.ipMap = ipMapping{}
p.mutex.Unlock()
return nil
}
func (pm *portMap) findPort() (int, error) {
port := pm.last
for i := 0; i <= EndPortRange-BeginPortRange; i++ {
for i := 0; i <= pm.end-pm.begin; i++ {
port++
if port > EndPortRange {
port = BeginPortRange
if port > pm.end {
port = pm.begin
}
if _, ok := pm.p[port]; !ok {

View file

@ -5,27 +5,23 @@ import (
"testing"
)
func reset() {
ReleaseAll()
}
func TestRequestNewPort(t *testing.T) {
defer reset()
p := New()
port, err := RequestPort(defaultIP, "tcp", 0)
port, err := p.RequestPort(defaultIP, "tcp", 0)
if err != nil {
t.Fatal(err)
}
if expected := BeginPortRange; port != expected {
if expected := p.Begin; port != expected {
t.Fatalf("Expected port %d got %d", expected, port)
}
}
func TestRequestSpecificPort(t *testing.T) {
defer reset()
p := New()
port, err := RequestPort(defaultIP, "tcp", 5000)
port, err := p.RequestPort(defaultIP, "tcp", 5000)
if err != nil {
t.Fatal(err)
}
@ -35,9 +31,9 @@ func TestRequestSpecificPort(t *testing.T) {
}
func TestReleasePort(t *testing.T) {
defer reset()
p := New()
port, err := RequestPort(defaultIP, "tcp", 5000)
port, err := p.RequestPort(defaultIP, "tcp", 5000)
if err != nil {
t.Fatal(err)
}
@ -45,15 +41,15 @@ func TestReleasePort(t *testing.T) {
t.Fatalf("Expected port 5000 got %d", port)
}
if err := ReleasePort(defaultIP, "tcp", 5000); err != nil {
if err := p.ReleasePort(defaultIP, "tcp", 5000); err != nil {
t.Fatal(err)
}
}
func TestReuseReleasedPort(t *testing.T) {
defer reset()
p := New()
port, err := RequestPort(defaultIP, "tcp", 5000)
port, err := p.RequestPort(defaultIP, "tcp", 5000)
if err != nil {
t.Fatal(err)
}
@ -61,20 +57,20 @@ func TestReuseReleasedPort(t *testing.T) {
t.Fatalf("Expected port 5000 got %d", port)
}
if err := ReleasePort(defaultIP, "tcp", 5000); err != nil {
if err := p.ReleasePort(defaultIP, "tcp", 5000); err != nil {
t.Fatal(err)
}
port, err = RequestPort(defaultIP, "tcp", 5000)
port, err = p.RequestPort(defaultIP, "tcp", 5000)
if err != nil {
t.Fatal(err)
}
}
func TestReleaseUnreadledPort(t *testing.T) {
defer reset()
p := New()
port, err := RequestPort(defaultIP, "tcp", 5000)
port, err := p.RequestPort(defaultIP, "tcp", 5000)
if err != nil {
t.Fatal(err)
}
@ -82,7 +78,7 @@ func TestReleaseUnreadledPort(t *testing.T) {
t.Fatalf("Expected port 5000 got %d", port)
}
port, err = RequestPort(defaultIP, "tcp", 5000)
port, err = p.RequestPort(defaultIP, "tcp", 5000)
switch err.(type) {
case ErrPortAlreadyAllocated:
@ -92,42 +88,40 @@ func TestReleaseUnreadledPort(t *testing.T) {
}
func TestUnknowProtocol(t *testing.T) {
defer reset()
if _, err := RequestPort(defaultIP, "tcpp", 0); err != ErrUnknownProtocol {
if _, err := New().RequestPort(defaultIP, "tcpp", 0); err != ErrUnknownProtocol {
t.Fatalf("Expected error %s got %s", ErrUnknownProtocol, err)
}
}
func TestAllocateAllPorts(t *testing.T) {
defer reset()
p := New()
for i := 0; i <= EndPortRange-BeginPortRange; i++ {
port, err := RequestPort(defaultIP, "tcp", 0)
for i := 0; i <= p.End-p.Begin; i++ {
port, err := p.RequestPort(defaultIP, "tcp", 0)
if err != nil {
t.Fatal(err)
}
if expected := BeginPortRange + i; port != expected {
if expected := p.Begin + i; port != expected {
t.Fatalf("Expected port %d got %d", expected, port)
}
}
if _, err := RequestPort(defaultIP, "tcp", 0); err != ErrAllPortsAllocated {
if _, err := p.RequestPort(defaultIP, "tcp", 0); err != ErrAllPortsAllocated {
t.Fatalf("Expected error %s got %s", ErrAllPortsAllocated, err)
}
_, err := RequestPort(defaultIP, "udp", 0)
_, err := p.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 {
port := p.Begin + 5
if err := p.ReleasePort(defaultIP, "tcp", port); err != nil {
t.Fatal(err)
}
newPort, err := RequestPort(defaultIP, "tcp", 0)
newPort, err := p.RequestPort(defaultIP, "tcp", 0)
if err != nil {
t.Fatal(err)
}
@ -137,10 +131,10 @@ func TestAllocateAllPorts(t *testing.T) {
// 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 {
if err := p.ReleasePort(defaultIP, "tcp", newPort); err != nil {
t.Fatal(err)
}
port, err = RequestPort(defaultIP, "tcp", 0)
port, err = p.RequestPort(defaultIP, "tcp", 0)
if err != nil {
t.Fatal(err)
}
@ -150,34 +144,34 @@ func TestAllocateAllPorts(t *testing.T) {
}
func BenchmarkAllocatePorts(b *testing.B) {
defer reset()
p := New()
for i := 0; i < b.N; i++ {
for i := 0; i <= EndPortRange-BeginPortRange; i++ {
port, err := RequestPort(defaultIP, "tcp", 0)
for i := 0; i <= p.End-p.Begin; i++ {
port, err := p.RequestPort(defaultIP, "tcp", 0)
if err != nil {
b.Fatal(err)
}
if expected := BeginPortRange + i; port != expected {
if expected := p.Begin + i; port != expected {
b.Fatalf("Expected port %d got %d", expected, port)
}
}
reset()
p.ReleaseAll()
}
}
func TestPortAllocation(t *testing.T) {
defer reset()
p := New()
ip := net.ParseIP("192.168.0.1")
ip2 := net.ParseIP("192.168.0.2")
if port, err := RequestPort(ip, "tcp", 80); err != nil {
if port, err := p.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)
port, err := p.RequestPort(ip, "tcp", 0)
if err != nil {
t.Fatal(err)
}
@ -185,41 +179,41 @@ func TestPortAllocation(t *testing.T) {
t.Fatalf("Acquire(0) should return a non-zero port")
}
if _, err := RequestPort(ip, "tcp", port); err == nil {
if _, err := p.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 {
if newPort, err := p.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 {
if _, err := p.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 {
if _, err := p.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 {
if _, err := p.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 {
if err := p.ReleasePort(ip, "tcp", 80); err != nil {
t.Fatal(err)
}
if _, err := RequestPort(ip, "tcp", 80); err != nil {
if _, err := p.RequestPort(ip, "tcp", 80); err != nil {
t.Fatal(err)
}
port, err = RequestPort(ip, "tcp", 0)
port, err = p.RequestPort(ip, "tcp", 0)
if err != nil {
t.Fatal(err)
}
port2, err := RequestPort(ip, "tcp", port+1)
port2, err := p.RequestPort(ip, "tcp", port+1)
if err != nil {
t.Fatal(err)
}
port3, err := RequestPort(ip, "tcp", 0)
port3, err := p.RequestPort(ip, "tcp", 0)
if err != nil {
t.Fatal(err)
}
@ -229,17 +223,17 @@ func TestPortAllocation(t *testing.T) {
}
func TestNoDuplicateBPR(t *testing.T) {
defer reset()
p := New()
if port, err := RequestPort(defaultIP, "tcp", BeginPortRange); err != nil {
if port, err := p.RequestPort(defaultIP, "tcp", p.Begin); err != nil {
t.Fatal(err)
} else if port != BeginPortRange {
t.Fatalf("Expected port %d got %d", BeginPortRange, port)
} else if port != p.Begin {
t.Fatalf("Expected port %d got %d", p.Begin, port)
}
if port, err := RequestPort(defaultIP, "tcp", 0); err != nil {
if port, err := p.RequestPort(defaultIP, "tcp", 0); err != nil {
t.Fatal(err)
} else if port == BeginPortRange {
} else if port == p.Begin {
t.Fatalf("Acquire(0) allocated the same port twice: %d", port)
}
}

View file

@ -6,7 +6,7 @@ import (
"net"
"sync"
log "github.com/Sirupsen/logrus"
"github.com/Sirupsen/logrus"
"github.com/docker/docker/pkg/iptables"
"github.com/docker/libnetwork/portallocator"
)
@ -18,15 +18,7 @@ type mapping struct {
container net.Addr
}
var (
chain *iptables.Chain
lock sync.Mutex
// udp:ip:port
currentMappings = make(map[string]*mapping)
newProxy = newProxyCommand
)
var newProxy = newProxyCommand
var (
// ErrUnknownBackendAddressType refers to an unknown container or unsupported address type
@ -37,15 +29,39 @@ var (
ErrPortNotMapped = errors.New("port is not mapped")
)
// PortMapper manages the network address translation
type PortMapper struct {
chain *iptables.Chain
// udp:ip:port
currentMappings map[string]*mapping
lock sync.Mutex
Allocator *portallocator.PortAllocator
}
// New returns a new instance of PortMapper
func New() *PortMapper {
return NewWithPortAllocator(portallocator.New())
}
// NewWithPortAllocator returns a new instance of PortMapper wich will use the specified PortAllocator
func NewWithPortAllocator(allocator *portallocator.PortAllocator) *PortMapper {
return &PortMapper{
currentMappings: make(map[string]*mapping),
Allocator: allocator,
}
}
// SetIptablesChain sets the specified chain into portmapper
func SetIptablesChain(c *iptables.Chain) {
chain = c
func (pm *PortMapper) SetIptablesChain(c *iptables.Chain) {
pm.chain = c
}
// Map maps the specified container transport address to the host's network address and transport port
func Map(container net.Addr, hostIP net.IP, hostPort int) (host net.Addr, err error) {
lock.Lock()
defer lock.Unlock()
func (pm *PortMapper) Map(container net.Addr, hostIP net.IP, hostPort int) (host net.Addr, err error) {
pm.lock.Lock()
defer pm.lock.Unlock()
var (
m *mapping
@ -57,7 +73,7 @@ func Map(container net.Addr, hostIP net.IP, hostPort int) (host net.Addr, err er
switch container.(type) {
case *net.TCPAddr:
proto = "tcp"
if allocatedHostPort, err = portallocator.RequestPort(hostIP, proto, hostPort); err != nil {
if allocatedHostPort, err = pm.Allocator.RequestPort(hostIP, proto, hostPort); err != nil {
return nil, err
}
@ -70,7 +86,7 @@ func Map(container net.Addr, hostIP net.IP, hostPort int) (host net.Addr, err er
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 {
if allocatedHostPort, err = pm.Allocator.RequestPort(hostIP, proto, hostPort); err != nil {
return nil, err
}
@ -88,25 +104,25 @@ func Map(container net.Addr, hostIP net.IP, hostPort int) (host net.Addr, err er
// release the allocated port on any further error during return.
defer func() {
if err != nil {
portallocator.ReleasePort(hostIP, proto, allocatedHostPort)
pm.Allocator.ReleasePort(hostIP, proto, allocatedHostPort)
}
}()
key := getKey(m.host)
if _, exists := currentMappings[key]; exists {
if _, exists := pm.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 {
if err := pm.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 {
pm.forward(iptables.Delete, m.proto, hostIP, allocatedHostPort, containerIP.String(), containerPort)
if err := pm.Allocator.ReleasePort(hostIP, m.proto, allocatedHostPort); err != nil {
return err
}
@ -120,36 +136,36 @@ func Map(container net.Addr, hostIP net.IP, hostPort int) (host net.Addr, err er
return nil, err
}
m.userlandProxy = proxy
currentMappings[key] = m
pm.currentMappings[key] = m
return m.host, nil
}
// Unmap removes stored mapping for the specified host transport address
func Unmap(host net.Addr) error {
lock.Lock()
defer lock.Unlock()
func (pm *PortMapper) Unmap(host net.Addr) error {
pm.lock.Lock()
defer pm.lock.Unlock()
key := getKey(host)
data, exists := currentMappings[key]
data, exists := pm.currentMappings[key]
if !exists {
return ErrPortNotMapped
}
data.userlandProxy.Stop()
delete(currentMappings, key)
delete(pm.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)
if err := pm.forward(iptables.Delete, data.proto, hostIP, hostPort, containerIP.String(), containerPort); err != nil {
logrus.Errorf("Error on iptables delete: %s", err)
}
switch a := host.(type) {
case *net.TCPAddr:
return portallocator.ReleasePort(a.IP, "tcp", a.Port)
return pm.Allocator.ReleasePort(a.IP, "tcp", a.Port)
case *net.UDPAddr:
return portallocator.ReleasePort(a.IP, "udp", a.Port)
return pm.Allocator.ReleasePort(a.IP, "udp", a.Port)
}
return nil
}
@ -174,9 +190,9 @@ func getIPAndPort(a net.Addr) (net.IP, int) {
return nil, 0
}
func forward(action iptables.Action, proto string, sourceIP net.IP, sourcePort int, containerIP string, containerPort int) error {
if chain == nil {
func (pm *PortMapper) forward(action iptables.Action, proto string, sourceIP net.IP, sourcePort int, containerIP string, containerPort int) error {
if pm.chain == nil {
return nil
}
return chain.Forward(action, sourceIP, sourcePort, proto, containerIP, containerPort)
return pm.chain.Forward(action, sourceIP, sourcePort, proto, containerIP, containerPort)
}

View file

@ -5,7 +5,6 @@ import (
"testing"
"github.com/docker/docker/pkg/iptables"
"github.com/docker/libnetwork/portallocator"
)
func init() {
@ -13,30 +12,26 @@ func init() {
newProxy = newMockProxyCommand
}
func reset() {
chain = nil
currentMappings = make(map[string]*mapping)
}
func TestSetIptablesChain(t *testing.T) {
defer reset()
pm := New()
c := &iptables.Chain{
Name: "TEST",
Bridge: "192.168.1.1",
}
if chain != nil {
if pm.chain != nil {
t.Fatal("chain should be nil at init")
}
SetIptablesChain(c)
if chain == nil {
pm.SetIptablesChain(c)
if pm.chain == nil {
t.Fatal("chain should not be nil after set")
}
}
func TestMapPorts(t *testing.T) {
pm := New()
dstIP1 := net.ParseIP("192.168.0.1")
dstIP2 := net.ParseIP("192.168.0.2")
dstAddr1 := &net.TCPAddr{IP: dstIP1, Port: 80}
@ -49,34 +44,34 @@ func TestMapPorts(t *testing.T) {
return (addr1.Network() == addr2.Network()) && (addr1.String() == addr2.String())
}
if host, err := Map(srcAddr1, dstIP1, 80); err != nil {
if host, err := pm.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 {
if _, err := pm.Map(srcAddr1, dstIP1, 80); err == nil {
t.Fatalf("Port is in use - mapping should have failed")
}
if _, err := Map(srcAddr2, dstIP1, 80); err == nil {
if _, err := pm.Map(srcAddr2, dstIP1, 80); err == nil {
t.Fatalf("Port is in use - mapping should have failed")
}
if _, err := Map(srcAddr2, dstIP2, 80); err != nil {
if _, err := pm.Map(srcAddr2, dstIP2, 80); err != nil {
t.Fatalf("Failed to allocate port: %s", err)
}
if Unmap(dstAddr1) != nil {
if pm.Unmap(dstAddr1) != nil {
t.Fatalf("Failed to release port")
}
if Unmap(dstAddr2) != nil {
if pm.Unmap(dstAddr2) != nil {
t.Fatalf("Failed to release port")
}
if Unmap(dstAddr2) == nil {
if pm.Unmap(dstAddr2) == nil {
t.Fatalf("Port already released, but no error reported")
}
}
@ -115,6 +110,7 @@ func TestGetUDPIPAndPort(t *testing.T) {
}
func TestMapAllPortsSingleInterface(t *testing.T) {
pm := New()
dstIP1 := net.ParseIP("0.0.0.0")
srcAddr1 := &net.TCPAddr{Port: 1080, IP: net.ParseIP("172.16.0.1")}
@ -124,25 +120,26 @@ func TestMapAllPortsSingleInterface(t *testing.T) {
defer func() {
for _, val := range hosts {
Unmap(val)
pm.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 {
start, end := pm.Allocator.Begin, pm.Allocator.End
for i := start; i < end; i++ {
if host, err = pm.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)
if _, err := pm.Map(srcAddr1, dstIP1, start); err == nil {
t.Fatalf("Port %d should be bound but is not", start)
}
for _, val := range hosts {
if err := Unmap(val); err != nil {
if err := pm.Unmap(val); err != nil {
t.Fatal(err)
}
}