|
@@ -7,14 +7,15 @@ import (
|
|
|
"io/ioutil"
|
|
|
"net"
|
|
|
"os"
|
|
|
+ "strconv"
|
|
|
"strings"
|
|
|
"sync"
|
|
|
|
|
|
"github.com/Sirupsen/logrus"
|
|
|
+ "github.com/docker/docker/daemon/network"
|
|
|
"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/parsers/kernel"
|
|
@@ -91,29 +92,34 @@ func initPortMapper() {
|
|
|
})
|
|
|
}
|
|
|
|
|
|
-func InitDriver(job *engine.Job) error {
|
|
|
+type Config struct {
|
|
|
+ EnableIPv6 bool
|
|
|
+ EnableIptables bool
|
|
|
+ EnableIpForward bool
|
|
|
+ EnableIpMasq bool
|
|
|
+ DefaultIp net.IP
|
|
|
+ Iface string
|
|
|
+ IP string
|
|
|
+ FixedCIDR string
|
|
|
+ FixedCIDRv6 string
|
|
|
+ InterContainerCommunication bool
|
|
|
+}
|
|
|
+
|
|
|
+func InitDriver(config *Config) error {
|
|
|
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")
|
|
|
+ networkv4 *net.IPNet
|
|
|
+ networkv6 *net.IPNet
|
|
|
+ addrv4 net.Addr
|
|
|
+ addrsv6 []net.Addr
|
|
|
+ bridgeIPv6 = "fe80::1/64"
|
|
|
)
|
|
|
initPortMapper()
|
|
|
|
|
|
- if defaultIP := job.Getenv("DefaultBindingIP"); defaultIP != "" {
|
|
|
- defaultBindingIP = net.ParseIP(defaultIP)
|
|
|
+ if config.DefaultIp != nil {
|
|
|
+ defaultBindingIP = config.DefaultIp
|
|
|
}
|
|
|
|
|
|
- bridgeIface = job.Getenv("BridgeIface")
|
|
|
+ bridgeIface = config.Iface
|
|
|
usingDefaultBridge := false
|
|
|
if bridgeIface == "" {
|
|
|
usingDefaultBridge = true
|
|
@@ -130,7 +136,7 @@ func InitDriver(job *engine.Job) error {
|
|
|
}
|
|
|
|
|
|
// If the iface is not found, try to create it
|
|
|
- if err := configureBridge(bridgeIP, bridgeIPv6, enableIPv6); err != nil {
|
|
|
+ if err := configureBridge(config.IP, bridgeIPv6, config.EnableIPv6); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
|
|
@@ -139,19 +145,19 @@ func InitDriver(job *engine.Job) error {
|
|
|
return err
|
|
|
}
|
|
|
|
|
|
- if fixedCIDRv6 != "" {
|
|
|
+ if config.FixedCIDRv6 != "" {
|
|
|
// Setting route to global IPv6 subnet
|
|
|
- logrus.Infof("Adding route to IPv6 network %q via device %q", fixedCIDRv6, bridgeIface)
|
|
|
- if err := netlink.AddRoute(fixedCIDRv6, "", "", bridgeIface); err != nil {
|
|
|
- logrus.Fatalf("Could not add route to IPv6 network %q via device %q", fixedCIDRv6, bridgeIface)
|
|
|
+ logrus.Infof("Adding route to IPv6 network %q via device %q", config.FixedCIDRv6, bridgeIface)
|
|
|
+ if err := netlink.AddRoute(config.FixedCIDRv6, "", "", bridgeIface); err != nil {
|
|
|
+ logrus.Fatalf("Could not add route to IPv6 network %q via device %q", config.FixedCIDRv6, bridgeIface)
|
|
|
}
|
|
|
}
|
|
|
} else {
|
|
|
// Bridge exists already, getting info...
|
|
|
// Validate that the bridge ip matches the ip specified by BridgeIP
|
|
|
- if bridgeIP != "" {
|
|
|
+ if config.IP != "" {
|
|
|
networkv4 = addrv4.(*net.IPNet)
|
|
|
- bip, _, err := net.ParseCIDR(bridgeIP)
|
|
|
+ bip, _, err := net.ParseCIDR(config.IP)
|
|
|
if err != nil {
|
|
|
return err
|
|
|
}
|
|
@@ -164,7 +170,7 @@ func InitDriver(job *engine.Job) error {
|
|
|
// (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 len(addrsv6) == 0 && config.EnableIPv6 {
|
|
|
if err := setupIPv6Bridge(bridgeIPv6); err != nil {
|
|
|
return err
|
|
|
}
|
|
@@ -175,10 +181,10 @@ func InitDriver(job *engine.Job) error {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // TODO: Check if route to fixedCIDRv6 is set
|
|
|
+ // TODO: Check if route to config.FixedCIDRv6 is set
|
|
|
}
|
|
|
|
|
|
- if enableIPv6 {
|
|
|
+ if config.EnableIPv6 {
|
|
|
bip6, _, err := net.ParseCIDR(bridgeIPv6)
|
|
|
if err != nil {
|
|
|
return err
|
|
@@ -198,7 +204,7 @@ func InitDriver(job *engine.Job) error {
|
|
|
|
|
|
networkv4 = addrv4.(*net.IPNet)
|
|
|
|
|
|
- if enableIPv6 {
|
|
|
+ if config.EnableIPv6 {
|
|
|
if len(addrsv6) == 0 {
|
|
|
return errors.New("IPv6 enabled but no IPv6 detected")
|
|
|
}
|
|
@@ -206,20 +212,20 @@ func InitDriver(job *engine.Job) error {
|
|
|
}
|
|
|
|
|
|
// Configure iptables for link support
|
|
|
- if enableIPTables {
|
|
|
- if err := setupIPTables(addrv4, icc, ipMasq); err != nil {
|
|
|
+ if config.EnableIptables {
|
|
|
+ if err := setupIPTables(addrv4, config.InterContainerCommunication, config.EnableIpMasq); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
- if ipForward {
|
|
|
+ if config.EnableIpForward {
|
|
|
// Enable IPv4 forwarding
|
|
|
if err := ioutil.WriteFile("/proc/sys/net/ipv4/ip_forward", []byte{'1', '\n'}, 0644); err != nil {
|
|
|
logrus.Warnf("WARNING: unable to enable IPv4 forwarding: %s\n", err)
|
|
|
}
|
|
|
|
|
|
- if fixedCIDRv6 != "" {
|
|
|
+ if config.FixedCIDRv6 != "" {
|
|
|
// Enable IPv6 forwarding
|
|
|
if err := ioutil.WriteFile("/proc/sys/net/ipv6/conf/default/forwarding", []byte{'1', '\n'}, 0644); err != nil {
|
|
|
logrus.Warnf("WARNING: unable to enable IPv6 default forwarding: %s\n", err)
|
|
@@ -235,7 +241,7 @@ func InitDriver(job *engine.Job) error {
|
|
|
return err
|
|
|
}
|
|
|
|
|
|
- if enableIPTables {
|
|
|
+ if config.EnableIptables {
|
|
|
_, err := iptables.NewChain("DOCKER", bridgeIface, iptables.Nat)
|
|
|
if err != nil {
|
|
|
return err
|
|
@@ -248,8 +254,8 @@ func InitDriver(job *engine.Job) error {
|
|
|
}
|
|
|
|
|
|
bridgeIPv4Network = networkv4
|
|
|
- if fixedCIDR != "" {
|
|
|
- _, subnet, err := net.ParseCIDR(fixedCIDR)
|
|
|
+ if config.FixedCIDR != "" {
|
|
|
+ _, subnet, err := net.ParseCIDR(config.FixedCIDR)
|
|
|
if err != nil {
|
|
|
return err
|
|
|
}
|
|
@@ -259,8 +265,8 @@ func InitDriver(job *engine.Job) error {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- if fixedCIDRv6 != "" {
|
|
|
- _, subnet, err := net.ParseCIDR(fixedCIDRv6)
|
|
|
+ if config.FixedCIDRv6 != "" {
|
|
|
+ _, subnet, err := net.ParseCIDR(config.FixedCIDRv6)
|
|
|
if err != nil {
|
|
|
return err
|
|
|
}
|
|
@@ -274,19 +280,6 @@ func InitDriver(job *engine.Job) error {
|
|
|
// Block BridgeIP in IP allocator
|
|
|
ipAllocator.RequestIP(bridgeIPv4Network, bridgeIPv4Network.IP)
|
|
|
|
|
|
- // https://github.com/docker/docker/issues/2768
|
|
|
- job.Eng.HackSetGlobalVar("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 err
|
|
|
- }
|
|
|
- }
|
|
|
return nil
|
|
|
}
|
|
|
|
|
@@ -513,70 +506,67 @@ func linkLocalIPv6FromMac(mac string) (string, error) {
|
|
|
}
|
|
|
|
|
|
// Allocate a network interface
|
|
|
-func Allocate(job *engine.Job) error {
|
|
|
+func Allocate(id, requestedMac, requestedIP, requestedIPv6 string) (*network.Settings, error) {
|
|
|
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
|
|
|
+ ip net.IP
|
|
|
+ mac net.HardwareAddr
|
|
|
+ err error
|
|
|
+ globalIPv6 net.IP
|
|
|
)
|
|
|
|
|
|
- ip, err = ipAllocator.RequestIP(bridgeIPv4Network, requestedIP)
|
|
|
+ ip, err = ipAllocator.RequestIP(bridgeIPv4Network, net.ParseIP(requestedIP))
|
|
|
if err != nil {
|
|
|
- return err
|
|
|
+ return nil, err
|
|
|
}
|
|
|
|
|
|
// If no explicit mac address was given, generate a random one.
|
|
|
- if mac, err = net.ParseMAC(job.Getenv("RequestedMac")); err != nil {
|
|
|
+ if mac, err = net.ParseMAC(requestedMac); err != nil {
|
|
|
mac = generateMacAddr(ip)
|
|
|
}
|
|
|
|
|
|
if globalIPv6Network != nil {
|
|
|
// If globalIPv6Network Size is at least a /80 subnet generate IPv6 address from MAC address
|
|
|
netmaskOnes, _ := globalIPv6Network.Mask.Size()
|
|
|
- if requestedIPv6 == nil && netmaskOnes <= 80 {
|
|
|
- requestedIPv6 = make(net.IP, len(globalIPv6Network.IP))
|
|
|
- copy(requestedIPv6, globalIPv6Network.IP)
|
|
|
+ ipv6 := net.ParseIP(requestedIPv6)
|
|
|
+ if ipv6 == nil && netmaskOnes <= 80 {
|
|
|
+ ipv6 = make(net.IP, len(globalIPv6Network.IP))
|
|
|
+ copy(ipv6, globalIPv6Network.IP)
|
|
|
for i, h := range mac {
|
|
|
- requestedIPv6[i+10] = h
|
|
|
+ ipv6[i+10] = h
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- globalIPv6, err = ipAllocator.RequestIP(globalIPv6Network, requestedIPv6)
|
|
|
+ globalIPv6, err = ipAllocator.RequestIP(globalIPv6Network, ipv6)
|
|
|
if err != nil {
|
|
|
logrus.Errorf("Allocator: RequestIP v6: %v", err)
|
|
|
- return err
|
|
|
+ return nil, err
|
|
|
}
|
|
|
logrus.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)
|
|
|
+ maskSize, _ := bridgeIPv4Network.Mask.Size()
|
|
|
|
|
|
// If linklocal IPv6
|
|
|
localIPv6Net, err := linkLocalIPv6FromMac(mac.String())
|
|
|
if err != nil {
|
|
|
- return err
|
|
|
+ return nil, err
|
|
|
}
|
|
|
localIPv6, _, _ := net.ParseCIDR(localIPv6Net)
|
|
|
- out.Set("LinkLocalIPv6", localIPv6.String())
|
|
|
- out.Set("MacAddress", mac.String())
|
|
|
+
|
|
|
+ networkSettings := &network.Settings{
|
|
|
+ IPAddress: ip.String(),
|
|
|
+ Gateway: bridgeIPv4Network.IP.String(),
|
|
|
+ MacAddress: mac.String(),
|
|
|
+ Bridge: bridgeIface,
|
|
|
+ IPPrefixLen: maskSize,
|
|
|
+ LinkLocalIPv6Address: localIPv6.String(),
|
|
|
+ }
|
|
|
|
|
|
if globalIPv6Network != nil {
|
|
|
- out.Set("GlobalIPv6", globalIPv6.String())
|
|
|
- sizev6, _ := globalIPv6Network.Mask.Size()
|
|
|
- out.SetInt("GlobalIPv6PrefixLen", sizev6)
|
|
|
- out.Set("IPv6Gateway", bridgeIPv6Addr.String())
|
|
|
+ networkSettings.GlobalIPv6Address = globalIPv6.String()
|
|
|
+ maskV6Size, _ := globalIPv6Network.Mask.Size()
|
|
|
+ networkSettings.GlobalIPv6PrefixLen = maskV6Size
|
|
|
+ networkSettings.IPv6Gateway = bridgeIPv6Addr.String()
|
|
|
}
|
|
|
|
|
|
currentInterfaces.Set(id, &networkInterface{
|
|
@@ -584,20 +574,15 @@ func Allocate(job *engine.Job) error {
|
|
|
IPv6: globalIPv6,
|
|
|
})
|
|
|
|
|
|
- out.WriteTo(job.Stdout)
|
|
|
-
|
|
|
- return nil
|
|
|
+ return networkSettings, nil
|
|
|
}
|
|
|
|
|
|
// Release an interface for a select ip
|
|
|
-func Release(job *engine.Job) error {
|
|
|
- var (
|
|
|
- id = job.Args[0]
|
|
|
- containerInterface = currentInterfaces.Get(id)
|
|
|
- )
|
|
|
+func Release(id string) {
|
|
|
+ var containerInterface = currentInterfaces.Get(id)
|
|
|
|
|
|
if containerInterface == nil {
|
|
|
- return fmt.Errorf("No network information to release for %s", id)
|
|
|
+ logrus.Warnf("No network information to release for %s", id)
|
|
|
}
|
|
|
|
|
|
for _, nat := range containerInterface.PortMappings {
|
|
@@ -614,27 +599,21 @@ func Release(job *engine.Job) error {
|
|
|
logrus.Infof("Unable to release IPv6 %s", err)
|
|
|
}
|
|
|
}
|
|
|
- return nil
|
|
|
}
|
|
|
|
|
|
// Allocate an external port and map it to the interface
|
|
|
-func AllocatePort(job *engine.Job) error {
|
|
|
+func AllocatePort(id string, port nat.Port, binding nat.PortBinding) (nat.PortBinding, error) {
|
|
|
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")
|
|
|
+ proto = port.Proto()
|
|
|
+ containerPort = port.Int()
|
|
|
network = currentInterfaces.Get(id)
|
|
|
)
|
|
|
|
|
|
- if hostIP != "" {
|
|
|
- ip = net.ParseIP(hostIP)
|
|
|
+ if binding.HostIp != "" {
|
|
|
+ ip = net.ParseIP(binding.HostIp)
|
|
|
if ip == nil {
|
|
|
- return fmt.Errorf("Bad parameter: invalid host ip %s", hostIP)
|
|
|
+ return nat.PortBinding{}, fmt.Errorf("Bad parameter: invalid host ip %s", binding.HostIp)
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -646,7 +625,7 @@ func AllocatePort(job *engine.Job) error {
|
|
|
case "udp":
|
|
|
container = &net.UDPAddr{IP: network.IP, Port: containerPort}
|
|
|
default:
|
|
|
- return fmt.Errorf("unsupported address type %s", proto)
|
|
|
+ return nat.PortBinding{}, fmt.Errorf("unsupported address type %s", proto)
|
|
|
}
|
|
|
|
|
|
//
|
|
@@ -656,7 +635,14 @@ func AllocatePort(job *engine.Job) error {
|
|
|
// yields.
|
|
|
//
|
|
|
|
|
|
- var host net.Addr
|
|
|
+ var (
|
|
|
+ host net.Addr
|
|
|
+ err error
|
|
|
+ )
|
|
|
+ hostPort, err := nat.ParsePort(binding.HostPort)
|
|
|
+ if err != nil {
|
|
|
+ return nat.PortBinding{}, err
|
|
|
+ }
|
|
|
for i := 0; i < MaxAllocatedPortAttempts; i++ {
|
|
|
if host, err = portMapper.Map(container, ip, hostPort); err == nil {
|
|
|
break
|
|
@@ -671,36 +657,24 @@ func AllocatePort(job *engine.Job) error {
|
|
|
}
|
|
|
|
|
|
if err != nil {
|
|
|
- return err
|
|
|
+ return nat.PortBinding{}, 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)
|
|
|
+ return nat.PortBinding{HostIp: netAddr.IP.String(), HostPort: strconv.Itoa(netAddr.Port)}, nil
|
|
|
case *net.UDPAddr:
|
|
|
- out.Set("HostIP", netAddr.IP.String())
|
|
|
- out.SetInt("HostPort", netAddr.Port)
|
|
|
- }
|
|
|
- if _, err := out.WriteTo(job.Stdout); err != nil {
|
|
|
- return err
|
|
|
+ return nat.PortBinding{HostIp: netAddr.IP.String(), HostPort: strconv.Itoa(netAddr.Port)}, nil
|
|
|
+ default:
|
|
|
+ return nat.PortBinding{}, fmt.Errorf("unsupported address type %T", netAddr)
|
|
|
}
|
|
|
-
|
|
|
- return nil
|
|
|
}
|
|
|
|
|
|
-func LinkContainers(job *engine.Job) error {
|
|
|
- var (
|
|
|
- action = job.Args[0]
|
|
|
- nfAction iptables.Action
|
|
|
- childIP = job.Getenv("ChildIP")
|
|
|
- parentIP = job.Getenv("ParentIP")
|
|
|
- ignoreErrors = job.GetenvBool("IgnoreErrors")
|
|
|
- ports = job.GetenvList("Ports")
|
|
|
- )
|
|
|
+//TODO: should it return something more than just an error?
|
|
|
+func LinkContainers(action, parentIP, childIP string, ports []nat.Port, ignoreErrors bool) error {
|
|
|
+ var nfAction iptables.Action
|
|
|
|
|
|
switch action {
|
|
|
case "-A":
|
|
@@ -723,8 +697,7 @@ func LinkContainers(job *engine.Job) error {
|
|
|
}
|
|
|
|
|
|
chain := iptables.Chain{Name: "DOCKER", Bridge: bridgeIface}
|
|
|
- for _, p := range ports {
|
|
|
- port := nat.Port(p)
|
|
|
+ for _, port := range ports {
|
|
|
if err := chain.Link(nfAction, ip1, ip2, port.Int(), port.Proto()); !ignoreErrors && err != nil {
|
|
|
return err
|
|
|
}
|