diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 0000000000000..9ba08aea9e305 --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,10 @@ +pipeline { + agent { label 'freebsd' } + stages { + stage('Build') { + steps { + sh 'AUTO_GOPATH=1 ./hack/make.sh binary' + } + } + } +} diff --git a/container/container_notlinux.go b/container/container_notlinux.go index f65653e992d7d..c9774fd5b9e14 100644 --- a/container/container_notlinux.go +++ b/container/container_notlinux.go @@ -11,13 +11,3 @@ func detachMounted(path string) error { // Therefore there are separate definitions for this. return unix.Unmount(path, 0) } - -// SecretMount returns the mount for the secret path -func (container *Container) SecretMount() *Mount { - return nil -} - -// UnmountSecrets unmounts the fs for secrets -func (container *Container) UnmountSecrets() error { - return nil -} diff --git a/daemon/container_freebsd.go b/daemon/container_freebsd.go new file mode 100644 index 0000000000000..6db130ab05f3f --- /dev/null +++ b/daemon/container_freebsd.go @@ -0,0 +1,9 @@ +package daemon + +import ( + "github.com/docker/docker/container" +) + +func (daemon *Daemon) saveApparmorConfig(container *container.Container) error { + return nil +} diff --git a/daemon/container_operations_freebsd.go b/daemon/container_operations_freebsd.go new file mode 100644 index 0000000000000..c9bc1c6c79578 --- /dev/null +++ b/daemon/container_operations_freebsd.go @@ -0,0 +1,44 @@ +package daemon + +import ( + "github.com/docker/docker/container" + "github.com/docker/docker/runconfig" + "github.com/docker/libnetwork" +) + +func (daemon *Daemon) setupLinkedContainers(container *container.Container) ([]string, error) { + return nil, nil +} + +func (daemon *Daemon) setupIpcDirs(container *container.Container) error { + return nil +} + +func killProcessDirectly(container *container.Container) error { + return nil +} + +func detachMounted(path string) error { + return nil +} + +func isLinkable(child *container.Container) bool { + // A container is linkable only if it belongs to the default network + _, ok := child.NetworkSettings.Networks[runconfig.DefaultDaemonNetworkMode().NetworkName()] + return ok +} + +func enableIPOnPredefinedNetwork() bool { + return false +} + +func (daemon *Daemon) isNetworkHotPluggable() bool { + return false +} + +func setupPathsAndSandboxOptions(container *container.Container, sboxOptions *[]libnetwork.SandboxOption) error { + return nil +} + +func initializeNetworkingPaths(container *container.Container, nc *container.Container) { +} diff --git a/daemon/container_operations_unix.go b/daemon/container_operations_linux.go similarity index 99% rename from daemon/container_operations_unix.go rename to daemon/container_operations_linux.go index 17d5a061dd5a6..33bc2417274e7 100644 --- a/daemon/container_operations_unix.go +++ b/daemon/container_operations_linux.go @@ -1,4 +1,4 @@ -// +build linux freebsd +// +build linux package daemon diff --git a/daemon/container_operations_solaris.go b/daemon/container_operations_solaris.go index 1653948de1547..c9bc1c6c79578 100644 --- a/daemon/container_operations_solaris.go +++ b/daemon/container_operations_solaris.go @@ -1,5 +1,3 @@ -// +build solaris - package daemon import ( diff --git a/daemon/daemon_freebsd.go b/daemon/daemon_freebsd.go new file mode 100644 index 0000000000000..7b7d7f3046d5c --- /dev/null +++ b/daemon/daemon_freebsd.go @@ -0,0 +1,37 @@ +package daemon + +import ( + "github.com/docker/libnetwork" + "github.com/docker/docker/api/types" + "github.com/docker/docker/container" + "github.com/docker/docker/daemon/config" +) +func getPluginExecRoot(root string) string { + return "/run/docker/plugins" +} + +func (daemon *Daemon) cleanupMountsByID(id string) error { + return nil +} + +// cleanupMounts umounts shm/mqueue mounts for old containers +func (daemon *Daemon) cleanupMounts() error { + return nil +} + +func initBridgeDriver(controller libnetwork.NetworkController, config *config.Config) error { + // TODO + return nil +} + +func removeDefaultBridgeInterface() { +} + +func (daemon *Daemon) stats(c *container.Container) (*types.StatsJSON, error) { + s := &types.StatsJSON{} + return s, nil +} + +func (daemon *Daemon) initCgroupsPath(path string) error { + return nil +} diff --git a/daemon/daemon_linux.go b/daemon/daemon_linux.go index 5faf533fdeffd..37fd5a8e974ed 100644 --- a/daemon/daemon_linux.go +++ b/daemon/daemon_linux.go @@ -10,6 +10,11 @@ import ( "github.com/Sirupsen/logrus" "github.com/docker/docker/pkg/mount" + "github.com/docker/libnetwork/drivers/bridge" + "github.com/docker/docker/api/types" + "github.com/golang/protobuf/ptypes" + "github.com/opencontainers/runc/libcontainer/cgroups" + "github.com/vishvananda/netlink" ) // On Linux, plugins use a static path for storing execution state, @@ -86,3 +91,238 @@ func getCleanPatterns(id string) (regexps []*regexp.Regexp) { } return } + +func initBridgeDriver(controller libnetwork.NetworkController, config *config.Config) error { + bridgeName := bridge.DefaultBridgeName + if config.BridgeConfig.Iface != "" { + bridgeName = config.BridgeConfig.Iface + } + netOption := map[string]string{ + bridge.BridgeName: bridgeName, + bridge.DefaultBridge: strconv.FormatBool(true), + netlabel.DriverMTU: strconv.Itoa(config.Mtu), + bridge.EnableIPMasquerade: strconv.FormatBool(config.BridgeConfig.EnableIPMasq), + bridge.EnableICC: strconv.FormatBool(config.BridgeConfig.InterContainerCommunication), + } + + // --ip processing + if config.BridgeConfig.DefaultIP != nil { + netOption[bridge.DefaultBindingIP] = config.BridgeConfig.DefaultIP.String() + } + + var ( + ipamV4Conf *libnetwork.IpamConf + ipamV6Conf *libnetwork.IpamConf + ) + + ipamV4Conf = &libnetwork.IpamConf{AuxAddresses: make(map[string]string)} + + nwList, nw6List, err := netutils.ElectInterfaceAddresses(bridgeName) + if err != nil { + return errors.Wrap(err, "list bridge addresses failed") + } + + nw := nwList[0] + if len(nwList) > 1 && config.BridgeConfig.FixedCIDR != "" { + _, fCIDR, err := net.ParseCIDR(config.BridgeConfig.FixedCIDR) + if err != nil { + return errors.Wrap(err, "parse CIDR failed") + } + // Iterate through in case there are multiple addresses for the bridge + for _, entry := range nwList { + if fCIDR.Contains(entry.IP) { + nw = entry + break + } + } + } + + ipamV4Conf.PreferredPool = lntypes.GetIPNetCanonical(nw).String() + hip, _ := lntypes.GetHostPartIP(nw.IP, nw.Mask) + if hip.IsGlobalUnicast() { + ipamV4Conf.Gateway = nw.IP.String() + } + + if config.BridgeConfig.IP != "" { + ipamV4Conf.PreferredPool = config.BridgeConfig.IP + ip, _, err := net.ParseCIDR(config.BridgeConfig.IP) + if err != nil { + return err + } + ipamV4Conf.Gateway = ip.String() + } else if bridgeName == bridge.DefaultBridgeName && ipamV4Conf.PreferredPool != "" { + logrus.Infof("Default bridge (%s) is assigned with an IP address %s. Daemon option --bip can be used to set a preferred IP address", bridgeName, ipamV4Conf.PreferredPool) + } + + if config.BridgeConfig.FixedCIDR != "" { + _, fCIDR, err := net.ParseCIDR(config.BridgeConfig.FixedCIDR) + if err != nil { + return err + } + + ipamV4Conf.SubPool = fCIDR.String() + } + + if config.BridgeConfig.DefaultGatewayIPv4 != nil { + ipamV4Conf.AuxAddresses["DefaultGatewayIPv4"] = config.BridgeConfig.DefaultGatewayIPv4.String() + } + + var deferIPv6Alloc bool + if config.BridgeConfig.FixedCIDRv6 != "" { + _, fCIDRv6, err := net.ParseCIDR(config.BridgeConfig.FixedCIDRv6) + if err != nil { + return err + } + + // In case user has specified the daemon flag --fixed-cidr-v6 and the passed network has + // at least 48 host bits, we need to guarantee the current behavior where the containers' + // IPv6 addresses will be constructed based on the containers' interface MAC address. + // We do so by telling libnetwork to defer the IPv6 address allocation for the endpoints + // on this network until after the driver has created the endpoint and returned the + // constructed address. Libnetwork will then reserve this address with the ipam driver. + ones, _ := fCIDRv6.Mask.Size() + deferIPv6Alloc = ones <= 80 + + if ipamV6Conf == nil { + ipamV6Conf = &libnetwork.IpamConf{AuxAddresses: make(map[string]string)} + } + ipamV6Conf.PreferredPool = fCIDRv6.String() + + // In case the --fixed-cidr-v6 is specified and the current docker0 bridge IPv6 + // address belongs to the same network, we need to inform libnetwork about it, so + // that it can be reserved with IPAM and it will not be given away to somebody else + for _, nw6 := range nw6List { + if fCIDRv6.Contains(nw6.IP) { + ipamV6Conf.Gateway = nw6.IP.String() + break + } + } + } + + if config.BridgeConfig.DefaultGatewayIPv6 != nil { + if ipamV6Conf == nil { + ipamV6Conf = &libnetwork.IpamConf{AuxAddresses: make(map[string]string)} + } + ipamV6Conf.AuxAddresses["DefaultGatewayIPv6"] = config.BridgeConfig.DefaultGatewayIPv6.String() + } + + v4Conf := []*libnetwork.IpamConf{ipamV4Conf} + v6Conf := []*libnetwork.IpamConf{} + if ipamV6Conf != nil { + v6Conf = append(v6Conf, ipamV6Conf) + } + // Initialize default network on "bridge" with the same name + _, err = controller.NewNetwork("bridge", "bridge", "", + libnetwork.NetworkOptionEnableIPv6(config.BridgeConfig.EnableIPv6), + libnetwork.NetworkOptionDriverOpts(netOption), + libnetwork.NetworkOptionIpam("default", "", v4Conf, v6Conf, nil), + libnetwork.NetworkOptionDeferIPv6Alloc(deferIPv6Alloc)) + if err != nil { + return fmt.Errorf("Error creating default \"bridge\" network: %v", err) + } + return nil +} + + +// Remove default bridge interface if present (--bridge=none use case) +func removeDefaultBridgeInterface() { + if lnk, err := netlink.LinkByName(bridge.DefaultBridgeName); err == nil { + if err := netlink.LinkDel(lnk); err != nil { + logrus.Warnf("Failed to remove bridge interface (%s): %v", bridge.DefaultBridgeName, err) + } + } +} + +func (daemon *Daemon) stats(c *container.Container) (*types.StatsJSON, error) { + if !c.IsRunning() { + return nil, errNotRunning{c.ID} + } + stats, err := daemon.containerd.Stats(c.ID) + if err != nil { + return nil, err + } + s := &types.StatsJSON{} + cgs := stats.CgroupStats + if cgs != nil { + s.BlkioStats = types.BlkioStats{ + IoServiceBytesRecursive: copyBlkioEntry(cgs.BlkioStats.IoServiceBytesRecursive), + IoServicedRecursive: copyBlkioEntry(cgs.BlkioStats.IoServicedRecursive), + IoQueuedRecursive: copyBlkioEntry(cgs.BlkioStats.IoQueuedRecursive), + IoServiceTimeRecursive: copyBlkioEntry(cgs.BlkioStats.IoServiceTimeRecursive), + IoWaitTimeRecursive: copyBlkioEntry(cgs.BlkioStats.IoWaitTimeRecursive), + IoMergedRecursive: copyBlkioEntry(cgs.BlkioStats.IoMergedRecursive), + IoTimeRecursive: copyBlkioEntry(cgs.BlkioStats.IoTimeRecursive), + SectorsRecursive: copyBlkioEntry(cgs.BlkioStats.SectorsRecursive), + } + cpu := cgs.CpuStats + s.CPUStats = types.CPUStats{ + CPUUsage: types.CPUUsage{ + TotalUsage: cpu.CpuUsage.TotalUsage, + PercpuUsage: cpu.CpuUsage.PercpuUsage, + UsageInKernelmode: cpu.CpuUsage.UsageInKernelmode, + UsageInUsermode: cpu.CpuUsage.UsageInUsermode, + }, + ThrottlingData: types.ThrottlingData{ + Periods: cpu.ThrottlingData.Periods, + ThrottledPeriods: cpu.ThrottlingData.ThrottledPeriods, + ThrottledTime: cpu.ThrottlingData.ThrottledTime, + }, + } + mem := cgs.MemoryStats.Usage + s.MemoryStats = types.MemoryStats{ + Usage: mem.Usage, + MaxUsage: mem.MaxUsage, + Stats: cgs.MemoryStats.Stats, + Failcnt: mem.Failcnt, + Limit: mem.Limit, + } + // if the container does not set memory limit, use the machineMemory + if mem.Limit > daemon.machineMemory && daemon.machineMemory > 0 { + s.MemoryStats.Limit = daemon.machineMemory + } + if cgs.PidsStats != nil { + s.PidsStats = types.PidsStats{ + Current: cgs.PidsStats.Current, + } + } + } + s.Read, err = ptypes.Timestamp(stats.Timestamp) + if err != nil { + return nil, err + } + return s, nil +} + +func (daemon *Daemon) initCgroupsPath(path string) error { + if path == "/" || path == "." { + return nil + } + + if daemon.configStore.CPURealtimePeriod == 0 && daemon.configStore.CPURealtimeRuntime == 0 { + return nil + } + + // Recursively create cgroup to ensure that the system and all parent cgroups have values set + // for the period and runtime as this limits what the children can be set to. + daemon.initCgroupsPath(filepath.Dir(path)) + + mnt, root, err := cgroups.FindCgroupMountpointAndRoot("cpu") + if err != nil { + return err + } + // When docker is run inside docker, the root is based of the host cgroup. + // Should this be handled in runc/libcontainer/cgroups ? + if strings.HasPrefix(root, "/docker/") { + root = "/" + } + + path = filepath.Join(mnt, root, path) + sysinfo := sysinfo.New(true) + if err := maybeCreateCPURealTimeFile(sysinfo.CPURealtimePeriod, daemon.configStore.CPURealtimePeriod, "cpu.rt_period_us", path); err != nil { + return err + } + if err := maybeCreateCPURealTimeFile(sysinfo.CPURealtimeRuntime, daemon.configStore.CPURealtimeRuntime, "cpu.rt_runtime_us", path); err != nil { + return err + } + return nil +} diff --git a/daemon/daemon_unix.go b/daemon/daemon_unix.go index 0f342bcfdab7d..7f686d5d82159 100644 --- a/daemon/daemon_unix.go +++ b/daemon/daemon_unix.go @@ -7,7 +7,6 @@ import ( "bytes" "fmt" "io/ioutil" - "net" "os" "path/filepath" "runtime" @@ -34,18 +33,11 @@ import ( "github.com/docker/docker/volume" "github.com/docker/libnetwork" nwconfig "github.com/docker/libnetwork/config" - "github.com/docker/libnetwork/drivers/bridge" "github.com/docker/libnetwork/netlabel" - "github.com/docker/libnetwork/netutils" "github.com/docker/libnetwork/options" - lntypes "github.com/docker/libnetwork/types" - "github.com/golang/protobuf/ptypes" - "github.com/opencontainers/runc/libcontainer/cgroups" "github.com/opencontainers/runc/libcontainer/label" rsystem "github.com/opencontainers/runc/libcontainer/system" specs "github.com/opencontainers/runtime-spec/specs-go" - "github.com/pkg/errors" - "github.com/vishvananda/netlink" ) const ( @@ -786,146 +778,6 @@ func driverOptions(config *config.Config) []nwconfig.Option { return dOptions } -func initBridgeDriver(controller libnetwork.NetworkController, config *config.Config) error { - bridgeName := bridge.DefaultBridgeName - if config.BridgeConfig.Iface != "" { - bridgeName = config.BridgeConfig.Iface - } - netOption := map[string]string{ - bridge.BridgeName: bridgeName, - bridge.DefaultBridge: strconv.FormatBool(true), - netlabel.DriverMTU: strconv.Itoa(config.Mtu), - bridge.EnableIPMasquerade: strconv.FormatBool(config.BridgeConfig.EnableIPMasq), - bridge.EnableICC: strconv.FormatBool(config.BridgeConfig.InterContainerCommunication), - } - - // --ip processing - if config.BridgeConfig.DefaultIP != nil { - netOption[bridge.DefaultBindingIP] = config.BridgeConfig.DefaultIP.String() - } - - var ( - ipamV4Conf *libnetwork.IpamConf - ipamV6Conf *libnetwork.IpamConf - ) - - ipamV4Conf = &libnetwork.IpamConf{AuxAddresses: make(map[string]string)} - - nwList, nw6List, err := netutils.ElectInterfaceAddresses(bridgeName) - if err != nil { - return errors.Wrap(err, "list bridge addresses failed") - } - - nw := nwList[0] - if len(nwList) > 1 && config.BridgeConfig.FixedCIDR != "" { - _, fCIDR, err := net.ParseCIDR(config.BridgeConfig.FixedCIDR) - if err != nil { - return errors.Wrap(err, "parse CIDR failed") - } - // Iterate through in case there are multiple addresses for the bridge - for _, entry := range nwList { - if fCIDR.Contains(entry.IP) { - nw = entry - break - } - } - } - - ipamV4Conf.PreferredPool = lntypes.GetIPNetCanonical(nw).String() - hip, _ := lntypes.GetHostPartIP(nw.IP, nw.Mask) - if hip.IsGlobalUnicast() { - ipamV4Conf.Gateway = nw.IP.String() - } - - if config.BridgeConfig.IP != "" { - ipamV4Conf.PreferredPool = config.BridgeConfig.IP - ip, _, err := net.ParseCIDR(config.BridgeConfig.IP) - if err != nil { - return err - } - ipamV4Conf.Gateway = ip.String() - } else if bridgeName == bridge.DefaultBridgeName && ipamV4Conf.PreferredPool != "" { - logrus.Infof("Default bridge (%s) is assigned with an IP address %s. Daemon option --bip can be used to set a preferred IP address", bridgeName, ipamV4Conf.PreferredPool) - } - - if config.BridgeConfig.FixedCIDR != "" { - _, fCIDR, err := net.ParseCIDR(config.BridgeConfig.FixedCIDR) - if err != nil { - return err - } - - ipamV4Conf.SubPool = fCIDR.String() - } - - if config.BridgeConfig.DefaultGatewayIPv4 != nil { - ipamV4Conf.AuxAddresses["DefaultGatewayIPv4"] = config.BridgeConfig.DefaultGatewayIPv4.String() - } - - var deferIPv6Alloc bool - if config.BridgeConfig.FixedCIDRv6 != "" { - _, fCIDRv6, err := net.ParseCIDR(config.BridgeConfig.FixedCIDRv6) - if err != nil { - return err - } - - // In case user has specified the daemon flag --fixed-cidr-v6 and the passed network has - // at least 48 host bits, we need to guarantee the current behavior where the containers' - // IPv6 addresses will be constructed based on the containers' interface MAC address. - // We do so by telling libnetwork to defer the IPv6 address allocation for the endpoints - // on this network until after the driver has created the endpoint and returned the - // constructed address. Libnetwork will then reserve this address with the ipam driver. - ones, _ := fCIDRv6.Mask.Size() - deferIPv6Alloc = ones <= 80 - - if ipamV6Conf == nil { - ipamV6Conf = &libnetwork.IpamConf{AuxAddresses: make(map[string]string)} - } - ipamV6Conf.PreferredPool = fCIDRv6.String() - - // In case the --fixed-cidr-v6 is specified and the current docker0 bridge IPv6 - // address belongs to the same network, we need to inform libnetwork about it, so - // that it can be reserved with IPAM and it will not be given away to somebody else - for _, nw6 := range nw6List { - if fCIDRv6.Contains(nw6.IP) { - ipamV6Conf.Gateway = nw6.IP.String() - break - } - } - } - - if config.BridgeConfig.DefaultGatewayIPv6 != nil { - if ipamV6Conf == nil { - ipamV6Conf = &libnetwork.IpamConf{AuxAddresses: make(map[string]string)} - } - ipamV6Conf.AuxAddresses["DefaultGatewayIPv6"] = config.BridgeConfig.DefaultGatewayIPv6.String() - } - - v4Conf := []*libnetwork.IpamConf{ipamV4Conf} - v6Conf := []*libnetwork.IpamConf{} - if ipamV6Conf != nil { - v6Conf = append(v6Conf, ipamV6Conf) - } - // Initialize default network on "bridge" with the same name - _, err = controller.NewNetwork("bridge", "bridge", "", - libnetwork.NetworkOptionEnableIPv6(config.BridgeConfig.EnableIPv6), - libnetwork.NetworkOptionDriverOpts(netOption), - libnetwork.NetworkOptionIpam("default", "", v4Conf, v6Conf, nil), - libnetwork.NetworkOptionDeferIPv6Alloc(deferIPv6Alloc)) - if err != nil { - return fmt.Errorf("Error creating default \"bridge\" network: %v", err) - } - return nil -} - -// Remove default bridge interface if present (--bridge=none use case) -func removeDefaultBridgeInterface() { - if lnk, err := netlink.LinkByName(bridge.DefaultBridgeName); err == nil { - if err := netlink.LinkDel(lnk); err != nil { - logrus.Warnf("Failed to remove bridge interface (%s): %v", bridge.DefaultBridgeName, err) - } - } -} - func (daemon *Daemon) getLayerInit() func(string) error { return daemon.setupInitLayer } @@ -1152,66 +1004,6 @@ func (daemon *Daemon) conditionalUnmountOnCleanup(container *container.Container return daemon.Unmount(container) } -func (daemon *Daemon) stats(c *container.Container) (*types.StatsJSON, error) { - if !c.IsRunning() { - return nil, errNotRunning{c.ID} - } - stats, err := daemon.containerd.Stats(c.ID) - if err != nil { - return nil, err - } - s := &types.StatsJSON{} - cgs := stats.CgroupStats - if cgs != nil { - s.BlkioStats = types.BlkioStats{ - IoServiceBytesRecursive: copyBlkioEntry(cgs.BlkioStats.IoServiceBytesRecursive), - IoServicedRecursive: copyBlkioEntry(cgs.BlkioStats.IoServicedRecursive), - IoQueuedRecursive: copyBlkioEntry(cgs.BlkioStats.IoQueuedRecursive), - IoServiceTimeRecursive: copyBlkioEntry(cgs.BlkioStats.IoServiceTimeRecursive), - IoWaitTimeRecursive: copyBlkioEntry(cgs.BlkioStats.IoWaitTimeRecursive), - IoMergedRecursive: copyBlkioEntry(cgs.BlkioStats.IoMergedRecursive), - IoTimeRecursive: copyBlkioEntry(cgs.BlkioStats.IoTimeRecursive), - SectorsRecursive: copyBlkioEntry(cgs.BlkioStats.SectorsRecursive), - } - cpu := cgs.CpuStats - s.CPUStats = types.CPUStats{ - CPUUsage: types.CPUUsage{ - TotalUsage: cpu.CpuUsage.TotalUsage, - PercpuUsage: cpu.CpuUsage.PercpuUsage, - UsageInKernelmode: cpu.CpuUsage.UsageInKernelmode, - UsageInUsermode: cpu.CpuUsage.UsageInUsermode, - }, - ThrottlingData: types.ThrottlingData{ - Periods: cpu.ThrottlingData.Periods, - ThrottledPeriods: cpu.ThrottlingData.ThrottledPeriods, - ThrottledTime: cpu.ThrottlingData.ThrottledTime, - }, - } - mem := cgs.MemoryStats.Usage - s.MemoryStats = types.MemoryStats{ - Usage: mem.Usage, - MaxUsage: mem.MaxUsage, - Stats: cgs.MemoryStats.Stats, - Failcnt: mem.Failcnt, - Limit: mem.Limit, - } - // if the container does not set memory limit, use the machineMemory - if mem.Limit > daemon.machineMemory && daemon.machineMemory > 0 { - s.MemoryStats.Limit = daemon.machineMemory - } - if cgs.PidsStats != nil { - s.PidsStats = types.PidsStats{ - Current: cgs.PidsStats.Current, - } - } - } - s.Read, err = ptypes.Timestamp(stats.Timestamp) - if err != nil { - return nil, err - } - return s, nil -} - // setDefaultIsolation determines the default isolation mode for the // daemon to run in. This is only applicable on Windows func (daemon *Daemon) setDefaultIsolation() error { @@ -1256,40 +1048,6 @@ func setupOOMScoreAdj(score int) error { return err } -func (daemon *Daemon) initCgroupsPath(path string) error { - if path == "/" || path == "." { - return nil - } - - if daemon.configStore.CPURealtimePeriod == 0 && daemon.configStore.CPURealtimeRuntime == 0 { - return nil - } - - // Recursively create cgroup to ensure that the system and all parent cgroups have values set - // for the period and runtime as this limits what the children can be set to. - daemon.initCgroupsPath(filepath.Dir(path)) - - mnt, root, err := cgroups.FindCgroupMountpointAndRoot("cpu") - if err != nil { - return err - } - // When docker is run inside docker, the root is based of the host cgroup. - // Should this be handled in runc/libcontainer/cgroups ? - if strings.HasPrefix(root, "/docker/") { - root = "/" - } - - path = filepath.Join(mnt, root, path) - sysinfo := sysinfo.New(true) - if err := maybeCreateCPURealTimeFile(sysinfo.CPURealtimePeriod, daemon.configStore.CPURealtimePeriod, "cpu.rt_period_us", path); err != nil { - return err - } - if err := maybeCreateCPURealTimeFile(sysinfo.CPURealtimeRuntime, daemon.configStore.CPURealtimeRuntime, "cpu.rt_runtime_us", path); err != nil { - return err - } - return nil -} - func maybeCreateCPURealTimeFile(sysinfoPresent bool, configValue int64, file string, path string) error { if sysinfoPresent && configValue != 0 { if err := os.MkdirAll(path, 0755); err != nil && !os.IsExist(err) { diff --git a/daemon/exec_freebsd.go b/daemon/exec_freebsd.go new file mode 100644 index 0000000000000..7003355d9118a --- /dev/null +++ b/daemon/exec_freebsd.go @@ -0,0 +1,11 @@ +package daemon + +import ( + "github.com/docker/docker/container" + "github.com/docker/docker/daemon/exec" + "github.com/docker/docker/libcontainerd" +) + +func execSetPlatformOpt(c *container.Container, ec *exec.Config, p *libcontainerd.Process) error { + return nil +} diff --git a/daemon/monitor_freebsd.go b/daemon/monitor_freebsd.go new file mode 100644 index 0000000000000..5ccfada76ac3c --- /dev/null +++ b/daemon/monitor_freebsd.go @@ -0,0 +1,18 @@ +package daemon + +import ( + "github.com/docker/docker/container" + "github.com/docker/docker/libcontainerd" +) + +// platformConstructExitStatus returns a platform specific exit status structure +func platformConstructExitStatus(e libcontainerd.StateInfo) *container.ExitStatus { + return &container.ExitStatus{ + ExitCode: int(e.ExitCode), + } +} + +// postRunProcessing perfoms any processing needed on the container after it has stopped. +func (daemon *Daemon) postRunProcessing(container *container.Container, e libcontainerd.StateInfo) error { + return nil +} diff --git a/daemon/oci_freebsd.go b/daemon/oci_freebsd.go new file mode 100644 index 0000000000000..0e8ddbdc128c4 --- /dev/null +++ b/daemon/oci_freebsd.go @@ -0,0 +1,74 @@ +package daemon + +import ( + "fmt" + "sort" + + containertypes "github.com/docker/docker/api/types/container" + "github.com/docker/docker/container" + "github.com/docker/docker/oci" + "github.com/opencontainers/runtime-spec/specs-go" +) + +func setResources(s *specs.Spec, r containertypes.Resources) error { + return nil +} + +func setUser(s *specs.Spec, c *container.Container) error { + return nil +} + +func getUser(c *container.Container, username string) (uint32, uint32, []uint32, error) { + return 0, 0, nil, nil +} + +// mergeUlimits merge the Ulimits from HostConfig with daemon defaults, and update HostConfig +// It will do nothing on non-Linux platform +func (daemon *Daemon) mergeUlimits(c *containertypes.HostConfig) { + return +} + +func (daemon *Daemon) createSpec(c *container.Container) (*specs.Spec, error) { + s := oci.DefaultSpec() + if err := daemon.populateCommonSpec(&s, c); err != nil { + return nil, err + } + + if err := setResources(&s, c.HostConfig.Resources); err != nil { + return nil, fmt.Errorf("runtime spec resources: %v", err) + } + + if err := setUser(&s, c); err != nil { + return nil, fmt.Errorf("spec user: %v", err) + } + + if err := daemon.setNetworkInterface(&s, c); err != nil { + return nil, err + } + + if err := daemon.setupIpcDirs(c); err != nil { + return nil, err + } + + ms, err := daemon.setupMounts(c) + if err != nil { + return nil, err + } + ms = append(ms, c.IpcMounts()...) + tmpfsMounts, err := c.TmpfsMounts() + if err != nil { + return nil, err + } + ms = append(ms, tmpfsMounts...) + sort.Sort(mounts(ms)) + + return (*specs.Spec)(&s), nil +} + +func (daemon *Daemon) setNetworkInterface(s *specs.Spec, c *container.Container) error { + return nil +} + +func (daemon *Daemon) populateCommonSpec(s *specs.Spec, c *container.Container) error { + return nil +} diff --git a/daemon/update_freebsd.go b/daemon/update_freebsd.go new file mode 100644 index 0000000000000..f3b545c5f01ef --- /dev/null +++ b/daemon/update_freebsd.go @@ -0,0 +1,11 @@ +package daemon + +import ( + "github.com/docker/docker/api/types/container" + "github.com/docker/docker/libcontainerd" +) + +func toContainerdResources(resources container.Resources) libcontainerd.Resources { + var r libcontainerd.Resources + return r +} diff --git a/libcontainerd/client_freebsd.go b/libcontainerd/client_freebsd.go new file mode 100644 index 0000000000000..b9206ab511226 --- /dev/null +++ b/libcontainerd/client_freebsd.go @@ -0,0 +1,104 @@ +package libcontainerd + +import ( + "golang.org/x/net/context" + containerd "github.com/docker/containerd/api/grpc/types" +) + +type client struct { + clientCommon + + // Platform specific properties below here. + remote *remote + q queue + exitNotifiers map[string]*exitNotifier + liveRestore bool +} + +// GetServerVersion returns the connected server version information +func (clnt *client) GetServerVersion(ctx context.Context) (*ServerVersion, error) { + resp, err := clnt.remote.apiClient.GetServerVersion(ctx, &containerd.GetServerVersionRequest{}) + if err != nil { + return nil, err + } + + sv := &ServerVersion{ + GetServerVersionResponse: *resp, + } + + return sv, nil +} + +func (clnt *client) AddProcess(ctx context.Context, containerID, processFriendlyName string, specp Process, attachStdio StdioCallback) (int, error) { + return -1, nil +} + +func (clnt *client) SignalProcess(containerID string, pid string, sig int) error { + return nil +} + +func (clnt *client) Resize(containerID, processFriendlyName string, width, height int) error { + return nil +} + +func (clnt *client) Pause(containerID string) error { + return nil +} + +func (clnt *client) Resume(containerID string) error { + return nil +} + +func (clnt *client) Stats(containerID string) (*Stats, error) { + return nil, nil +} + +func (clnt *client) getExitNotifier(containerID string) *exitNotifier { + clnt.mapMutex.RLock() + defer clnt.mapMutex.RUnlock() + return clnt.exitNotifiers[containerID] +} + +func (clnt *client) getOrCreateExitNotifier(containerID string) *exitNotifier { + clnt.mapMutex.Lock() + defer clnt.mapMutex.Unlock() + w, ok := clnt.exitNotifiers[containerID] + if !ok { + w = &exitNotifier{c: make(chan struct{}), client: clnt} + clnt.exitNotifiers[containerID] = w + } + return w +} + +// Restore is the handler for restoring a container +func (clnt *client) Restore(containerID string, attachStdio StdioCallback, options ...CreateOption) error { + return nil +} + +func (clnt *client) GetPidsForContainer(containerID string) ([]int, error) { + return nil, nil +} + +// Summary returns a summary of the processes running in a container. +func (clnt *client) Summary(containerID string) ([]Summary, error) { + return nil, nil +} + +// UpdateResources updates resources for a running container. +func (clnt *client) UpdateResources(containerID string, resources Resources) error { + // Updating resource isn't supported on Solaris + // but we should return nil for enabling updating container + return nil +} + +func (clnt *client) CreateCheckpoint(containerID string, checkpointID string, checkpointDir string, exit bool) error { + return nil +} + +func (clnt *client) DeleteCheckpoint(containerID string, checkpointID string, checkpointDir string) error { + return nil +} + +func (clnt *client) ListCheckpoints(containerID string, checkpointDir string) (*Checkpoints, error) { + return nil, nil +} diff --git a/libcontainerd/client_unix.go b/libcontainerd/client_unix.go index 21e8fea666bb6..9aedd44f47a24 100644 --- a/libcontainerd/client_unix.go +++ b/libcontainerd/client_unix.go @@ -1,4 +1,4 @@ -// +build linux solaris +// +build linux solaris freebsd package libcontainerd diff --git a/libcontainerd/container_unix.go b/libcontainerd/container_unix.go index 61bab145f2a29..c9e1d0b8dc9de 100644 --- a/libcontainerd/container_unix.go +++ b/libcontainerd/container_unix.go @@ -1,4 +1,4 @@ -// +build linux solaris +// +build linux solaris freebsd package libcontainerd diff --git a/libcontainerd/oom_solaris.go b/libcontainerd/oom_unix.go similarity index 65% rename from libcontainerd/oom_solaris.go rename to libcontainerd/oom_unix.go index 2ebe5e87cf23f..6b66a53cc043d 100644 --- a/libcontainerd/oom_solaris.go +++ b/libcontainerd/oom_unix.go @@ -1,3 +1,5 @@ +// +build solaris,freebsd +build !linux + package libcontainerd func setOOMScore(pid, score int) error { diff --git a/libcontainerd/process_unix.go b/libcontainerd/process_unix.go index 506fca6e11c74..f45da6f5614e9 100644 --- a/libcontainerd/process_unix.go +++ b/libcontainerd/process_unix.go @@ -1,4 +1,4 @@ -// +build linux solaris +// +build linux solaris freebsd package libcontainerd diff --git a/libcontainerd/queue_unix.go b/libcontainerd/queue_unix.go index b848b9872b9e5..dae16897cb66a 100644 --- a/libcontainerd/queue_unix.go +++ b/libcontainerd/queue_unix.go @@ -1,4 +1,4 @@ -// +build linux solaris +// +build linux solaris freebsd package libcontainerd diff --git a/libcontainerd/remote_unix.go b/libcontainerd/remote_unix.go index eebbc886c69c1..a5b93715038b8 100644 --- a/libcontainerd/remote_unix.go +++ b/libcontainerd/remote_unix.go @@ -1,4 +1,4 @@ -// +build linux solaris +// +build linux solaris freebsd package libcontainerd diff --git a/libcontainerd/types_freebsd.go b/libcontainerd/types_freebsd.go new file mode 100644 index 0000000000000..dbafef669fe03 --- /dev/null +++ b/libcontainerd/types_freebsd.go @@ -0,0 +1,43 @@ +package libcontainerd + +import ( + containerd "github.com/docker/containerd/api/grpc/types" + "github.com/opencontainers/runtime-spec/specs-go" +) + +// Process contains information to start a specific application inside the container. +type Process struct { + // Terminal creates an interactive terminal for the container. + Terminal bool `json:"terminal"` + // User specifies user information for the process. + User *specs.User `json:"user"` + // Args specifies the binary and arguments for the application to execute. + Args []string `json:"args"` + // Env populates the process environment for the process. + Env []string `json:"env,omitempty"` + // Cwd is the current working directory for the process and must be + // relative to the container's root. + Cwd *string `json:"cwd"` + // Capabilities are linux capabilities that are kept for the container. + Capabilities []string `json:"capabilities,omitempty"` +} + +// Stats contains a stats properties from containerd. +type Stats struct{} + +// Summary contains a container summary from containerd +type Summary struct{} + +// StateInfo contains description about the new state container has entered. +type StateInfo struct { + CommonStateInfo + + // Platform specific StateInfo + OOMKilled bool +} + +// Resources defines updatable container resource values. +type Resources struct{} + +// Checkpoints contains the details of a checkpoint +type Checkpoints containerd.ListCheckpointResponse diff --git a/libcontainerd/utils_solaris.go b/libcontainerd/utils_unix.go similarity index 92% rename from libcontainerd/utils_solaris.go rename to libcontainerd/utils_unix.go index 49632b45e544f..153c5e393f71d 100644 --- a/libcontainerd/utils_solaris.go +++ b/libcontainerd/utils_unix.go @@ -1,3 +1,5 @@ +// +build solaris,freebsd +build !linux + package libcontainerd import ( diff --git a/oci/defaults_freebsd.go b/oci/defaults_freebsd.go new file mode 100644 index 0000000000000..85c8b68e16e9c --- /dev/null +++ b/oci/defaults_freebsd.go @@ -0,0 +1,20 @@ +package oci + +import ( + "runtime" + + "github.com/opencontainers/runtime-spec/specs-go" +) + +// DefaultSpec returns default oci spec used by docker. +func DefaultSpec() specs.Spec { + s := specs.Spec{ + Version: "0.6.0", + Platform: specs.Platform{ + OS: "SunOS", + Arch: runtime.GOARCH, + }, + } + s.Solaris = &specs.Solaris{} + return s +} diff --git a/pkg/mount/sharedsubtree_freebsd.go b/pkg/mount/sharedsubtree_freebsd.go new file mode 100644 index 0000000000000..f3c13e5a16ce8 --- /dev/null +++ b/pkg/mount/sharedsubtree_freebsd.go @@ -0,0 +1,67 @@ +package mount + +// MakeShared ensures a mounted filesystem has the SHARED mount option enabled. +// See the supported options in flags.go for further reference. +func MakeShared(mountPoint string) error { + return ensureMountedAs(mountPoint, "shared") +} + +// MakeRShared ensures a mounted filesystem has the RSHARED mount option enabled. +// See the supported options in flags.go for further reference. +func MakeRShared(mountPoint string) error { + return ensureMountedAs(mountPoint, "rshared") +} + +// MakePrivate ensures a mounted filesystem has the PRIVATE mount option enabled. +// See the supported options in flags.go for further reference. +func MakePrivate(mountPoint string) error { + return ensureMountedAs(mountPoint, "private") +} + +// MakeRPrivate ensures a mounted filesystem has the RPRIVATE mount option +// enabled. See the supported options in flags.go for further reference. +func MakeRPrivate(mountPoint string) error { + return ensureMountedAs(mountPoint, "rprivate") +} + +// MakeSlave ensures a mounted filesystem has the SLAVE mount option enabled. +// See the supported options in flags.go for further reference. +func MakeSlave(mountPoint string) error { + return ensureMountedAs(mountPoint, "slave") +} + +// MakeRSlave ensures a mounted filesystem has the RSLAVE mount option enabled. +// See the supported options in flags.go for further reference. +func MakeRSlave(mountPoint string) error { + return ensureMountedAs(mountPoint, "rslave") +} + +// MakeUnbindable ensures a mounted filesystem has the UNBINDABLE mount option +// enabled. See the supported options in flags.go for further reference. +func MakeUnbindable(mountPoint string) error { + return ensureMountedAs(mountPoint, "unbindable") +} + +// MakeRUnbindable ensures a mounted filesystem has the RUNBINDABLE mount +// option enabled. See the supported options in flags.go for further reference. +func MakeRUnbindable(mountPoint string) error { + return ensureMountedAs(mountPoint, "runbindable") +} + +func ensureMountedAs(mountPoint, options string) error { + mounted, err := Mounted(mountPoint) + if err != nil { + return err + } + + if !mounted { + if err := Mount(mountPoint, mountPoint, "none", "bind,rw"); err != nil { + return err + } + } + if _, err = Mounted(mountPoint); err != nil { + return err + } + + return ForceMount("", mountPoint, "none", options) +} diff --git a/plugin/manager_solaris.go b/plugin/manager_unix.go similarity index 93% rename from plugin/manager_solaris.go rename to plugin/manager_unix.go index 72ccae72d3208..455b68d62f411 100644 --- a/plugin/manager_solaris.go +++ b/plugin/manager_unix.go @@ -1,3 +1,5 @@ +// +build solaris,freebsd +build !linux + package plugin import ( diff --git a/vendor/github.com/docker/libnetwork/drivers/bridge/bridge.go b/vendor/github.com/docker/libnetwork/drivers/bridge/bridge.go index e681b8f7c4668..423ceb710fe60 100644 --- a/vendor/github.com/docker/libnetwork/drivers/bridge/bridge.go +++ b/vendor/github.com/docker/libnetwork/drivers/bridge/bridge.go @@ -1,3 +1,5 @@ +// +build linux + package bridge import ( diff --git a/vendor/github.com/docker/libnetwork/drivers/bridge/bridge_store.go b/vendor/github.com/docker/libnetwork/drivers/bridge/bridge_store.go index c7c83d8369873..f488b97a31ed8 100644 --- a/vendor/github.com/docker/libnetwork/drivers/bridge/bridge_store.go +++ b/vendor/github.com/docker/libnetwork/drivers/bridge/bridge_store.go @@ -1,3 +1,5 @@ +// +build linux + package bridge import ( diff --git a/vendor/github.com/docker/libnetwork/drivers/bridge/interface.go b/vendor/github.com/docker/libnetwork/drivers/bridge/interface.go index 9b20900416dc8..5ab6a38fb7ab8 100644 --- a/vendor/github.com/docker/libnetwork/drivers/bridge/interface.go +++ b/vendor/github.com/docker/libnetwork/drivers/bridge/interface.go @@ -1,3 +1,5 @@ +// +build linux + package bridge import ( diff --git a/vendor/github.com/docker/libnetwork/drivers/bridge/link.go b/vendor/github.com/docker/libnetwork/drivers/bridge/link.go index 53e9eeef990e6..734fbbb048229 100644 --- a/vendor/github.com/docker/libnetwork/drivers/bridge/link.go +++ b/vendor/github.com/docker/libnetwork/drivers/bridge/link.go @@ -1,3 +1,5 @@ +// +build linux + package bridge import ( diff --git a/vendor/github.com/docker/libnetwork/drivers/bridge/port_mapping.go b/vendor/github.com/docker/libnetwork/drivers/bridge/port_mapping.go index 965cc9a039cce..b69a630e85a46 100644 --- a/vendor/github.com/docker/libnetwork/drivers/bridge/port_mapping.go +++ b/vendor/github.com/docker/libnetwork/drivers/bridge/port_mapping.go @@ -1,3 +1,5 @@ +// +build linux + package bridge import ( diff --git a/vendor/github.com/docker/libnetwork/drivers/bridge/setup.go b/vendor/github.com/docker/libnetwork/drivers/bridge/setup.go index eeb3611b78e56..dd781cf1d003a 100644 --- a/vendor/github.com/docker/libnetwork/drivers/bridge/setup.go +++ b/vendor/github.com/docker/libnetwork/drivers/bridge/setup.go @@ -1,3 +1,5 @@ +// +build linux + package bridge type setupStep func(*networkConfiguration, *bridgeInterface) error diff --git a/vendor/github.com/docker/libnetwork/drivers/bridge/setup_bridgenetfiltering.go b/vendor/github.com/docker/libnetwork/drivers/bridge/setup_bridgenetfiltering.go index 884c7115eccec..a094d19ede9eb 100644 --- a/vendor/github.com/docker/libnetwork/drivers/bridge/setup_bridgenetfiltering.go +++ b/vendor/github.com/docker/libnetwork/drivers/bridge/setup_bridgenetfiltering.go @@ -1,3 +1,5 @@ +// +build linux + package bridge import ( diff --git a/vendor/github.com/docker/libnetwork/drivers/bridge/setup_device.go b/vendor/github.com/docker/libnetwork/drivers/bridge/setup_device.go index 0961bea55d89d..db9845a0d1484 100644 --- a/vendor/github.com/docker/libnetwork/drivers/bridge/setup_device.go +++ b/vendor/github.com/docker/libnetwork/drivers/bridge/setup_device.go @@ -1,3 +1,5 @@ +// +build linux + package bridge import ( diff --git a/vendor/github.com/docker/libnetwork/drivers/bridge/setup_firewalld.go b/vendor/github.com/docker/libnetwork/drivers/bridge/setup_firewalld.go index fc45a7e983a26..4bac1dde213eb 100644 --- a/vendor/github.com/docker/libnetwork/drivers/bridge/setup_firewalld.go +++ b/vendor/github.com/docker/libnetwork/drivers/bridge/setup_firewalld.go @@ -1,3 +1,5 @@ +// +build linux + package bridge import "github.com/docker/libnetwork/iptables" diff --git a/vendor/github.com/docker/libnetwork/drivers/bridge/setup_ip_tables.go b/vendor/github.com/docker/libnetwork/drivers/bridge/setup_ip_tables.go index 839e16f8ffef4..862d419b4405e 100644 --- a/vendor/github.com/docker/libnetwork/drivers/bridge/setup_ip_tables.go +++ b/vendor/github.com/docker/libnetwork/drivers/bridge/setup_ip_tables.go @@ -1,3 +1,5 @@ +// +build linux + package bridge import ( diff --git a/vendor/github.com/docker/libnetwork/drivers/bridge/setup_ipv4.go b/vendor/github.com/docker/libnetwork/drivers/bridge/setup_ipv4.go index 7f8707266d5fb..02eeea60ce0f2 100644 --- a/vendor/github.com/docker/libnetwork/drivers/bridge/setup_ipv4.go +++ b/vendor/github.com/docker/libnetwork/drivers/bridge/setup_ipv4.go @@ -1,3 +1,5 @@ +// +build linux + package bridge import ( diff --git a/vendor/github.com/docker/libnetwork/drivers/bridge/setup_ipv6.go b/vendor/github.com/docker/libnetwork/drivers/bridge/setup_ipv6.go index ee3d753ac11ed..8e0be446d7318 100644 --- a/vendor/github.com/docker/libnetwork/drivers/bridge/setup_ipv6.go +++ b/vendor/github.com/docker/libnetwork/drivers/bridge/setup_ipv6.go @@ -1,3 +1,5 @@ +// +build linux + package bridge import ( diff --git a/vendor/github.com/docker/libnetwork/drivers/bridge/setup_verify.go b/vendor/github.com/docker/libnetwork/drivers/bridge/setup_verify.go index 330a5b4560c41..01e57e6a348d9 100644 --- a/vendor/github.com/docker/libnetwork/drivers/bridge/setup_verify.go +++ b/vendor/github.com/docker/libnetwork/drivers/bridge/setup_verify.go @@ -1,3 +1,5 @@ +// +build linux + package bridge import ( diff --git a/vendor/github.com/docker/libnetwork/drivers/freebsd/bridge/bridge.go b/vendor/github.com/docker/libnetwork/drivers/freebsd/bridge/bridge.go new file mode 100644 index 0000000000000..0c435c96e2d50 --- /dev/null +++ b/vendor/github.com/docker/libnetwork/drivers/freebsd/bridge/bridge.go @@ -0,0 +1,1094 @@ +// +build freebsd + +package bridge + +import ( + //"bufio" + "errors" + "fmt" + "net" + //"os" + "os/exec" + "strconv" + //"strings" + "sync" + + "github.com/Sirupsen/logrus" + "github.com/docker/libnetwork/datastore" + "github.com/docker/libnetwork/discoverapi" + "github.com/docker/libnetwork/driverapi" + "github.com/docker/libnetwork/netlabel" + "github.com/docker/libnetwork/netutils" + "github.com/docker/libnetwork/options" + //"github.com/docker/libnetwork/portmapper" // ==> iptables ==> netlink + "github.com/docker/libnetwork/types" +) + +const ( + networkType = "bridge" + + // DefaultBridgeName is the default name for the bridge interface managed + // by the driver when unspecified by the caller. + DefaultBridgeName = "bridge0" + + // BridgeName label for bridge driver + BridgeName = "com.docker.network.bridge.name" + + // EnableIPMasquerade label for bridge driver + EnableIPMasquerade = "com.docker.network.bridge.enable_ip_masquerade" + + // EnableICC label + EnableICC = "com.docker.network.bridge.enable_icc" + + // DefaultBindingIP label + DefaultBindingIP = "com.docker.network.bridge.host_binding_ipv4" + + // DefaultBridge label + DefaultBridge = "com.docker.network.bridge.default_bridge" + + // DefaultGatewayV4AuxKey represents the default-gateway configured by the user + DefaultGatewayV4AuxKey = "DefaultGatewayIPv4" + + // DefaultGatewayV6AuxKey represents the ipv6 default-gateway configured by the user + DefaultGatewayV6AuxKey = "DefaultGatewayIPv6" +) + +// configuration info for the "bridge" driver. +type configuration struct { + EnableIPForwarding bool + EnableIPTables bool + EnableUserlandProxy bool +} + +// networkConfiguration for network specific configuration +type networkConfiguration struct { + ID string + BridgeName string + BridgeNameInternal string + EnableIPv6 bool + EnableIPMasquerade bool + EnableICC bool + Mtu int + DefaultBindingIntf string + DefaultBindingIP net.IP + DefaultBridge bool + // Internal fields set after ipam data parsing + AddressIPv4 *net.IPNet + AddressIPv6 *net.IPNet + DefaultGatewayIPv4 net.IP + DefaultGatewayIPv6 net.IP + dbIndex uint64 + dbExists bool + Internal bool +} + +// endpointConfiguration represents the user specified configuration for the sandbox endpoint +type endpointConfiguration struct { + MacAddress net.HardwareAddr + PortBindings []types.PortBinding + ExposedPorts []types.TransportPort +} + +// containerConfiguration represents the user specified configuration for a container +type containerConfiguration struct { + ParentEndpoints []string + ChildEndpoints []string +} + +// cnnectivityConfiguration represents the user specified configuration regarding the external connectivity +type connectivityConfiguration struct { + PortBindings []types.PortBinding + ExposedPorts []types.TransportPort +} + +type bridgeEndpoint struct { + id string + nid string + srcName string + addr *net.IPNet + addrv6 *net.IPNet + macAddress net.HardwareAddr + config *endpointConfiguration // User specified parameters + containerConfig *containerConfiguration + extConnConfig *connectivityConfiguration + portMapping []types.PortBinding // Operation port bindings + dbIndex uint64 + dbExists bool +} + +type bridgeInterface struct { + bridgeIPv4 *net.IPNet + bridgeIPv6 *net.IPNet + gatewayIPv4 net.IP + gatewayIPv6 net.IP +} + +type bridgeNetwork struct { + id string + bridge *bridgeInterface + config *networkConfiguration + endpoints map[string]*bridgeEndpoint // key: endpoint id + //portMapper *portmapper.PortMapper + driver *driver // The network's driver + sync.Mutex +} + +type driver struct { + config *configuration + network *bridgeNetwork + //natChain *iptables.ChainInfo + //filterChain *iptables.ChainInfo + //isolationChain *iptables.ChainInfo + networks map[string]*bridgeNetwork + store datastore.DataStore + sync.Mutex + defrouteIP net.IP +} + +// New constructs a new bridge driver +func newDriver() *driver { + return &driver{networks: map[string]*bridgeNetwork{}} +} + +// Init registers a new instance of bridge driver +func Init(dc driverapi.DriverCallback, config map[string]interface{}) error { + d := newDriver() + if err := d.configure(config); err != nil { + return err + } + + c := driverapi.Capability{ + DataScope: datastore.LocalScope, + } + return dc.RegisterDriver(networkType, d, c) +} + +func (d *driver) NetworkAllocate(id string, option map[string]string, ipV4Data, ipV6Data []driverapi.IPAMData) (map[string]string, error) { + return nil, types.NotImplementedErrorf("not implemented") +} + +func (d *driver) NetworkFree(id string) error { + return types.NotImplementedErrorf("not implemented") +} + +func (d *driver) EventNotify(etype driverapi.EventType, nid, tableName, key string, value []byte) { +} + +func (d *driver) DecodeTableEntry(tablename string, key string, value []byte) (string, map[string]string) { + return "", nil +} + +func (d *driver) CreateNetwork(id string, option map[string]interface{}, nInfo driverapi.NetworkInfo, ipV4Data, ipV6Data []driverapi.IPAMData) error { + if len(ipV4Data) == 0 || ipV4Data[0].Pool.String() == "0.0.0.0/0" { + return types.BadRequestErrorf("ipv4 pool is empty") + } + // Sanity checks + d.Lock() + if _, ok := d.networks[id]; ok { + d.Unlock() + return types.ForbiddenErrorf("network %s exists", id) + } + d.Unlock() + + // Parse and validate the config. It should not conflict with existing networks' config + config, err := parseNetworkOptions(d, id, option) + if err != nil { + return err + } + + err = config.processIPAM(id, ipV4Data, ipV6Data) + if err != nil { + return err + } + + if err = d.createNetwork(config); err != nil { + return err + } + + return d.storeUpdate(config) +} + +func newInterface(config *networkConfiguration) *bridgeInterface { + i := &bridgeInterface{} + + i.bridgeIPv4 = config.AddressIPv4 + i.gatewayIPv4 = config.AddressIPv4.IP + if config.BridgeName == "" { + config.BridgeName = DefaultBridgeName + } + return i +} + +// This function prunes the pf.conf for the firewall +// that enable the service successfully. +func fixPFConf() error { + return nil +} + +func (d *driver) initFirewall() error { + return nil +} + +func (d *driver) initRouting() error { + return nil +} + +func (d *driver) configure(option map[string]interface{}) error { + var err error + + if err = d.initFirewall(); err != nil { + return fmt.Errorf("failed to configure firewall: %v", err) + } + if err = d.initRouting(); err != nil { + return fmt.Errorf("failed to configure routing: %v", err) + } + if err = d.initStore(option); err != nil { + return fmt.Errorf("failed to initialize datastore: %v", err) + } + + return nil +} + +func (d *driver) getNetwork(id string) (*bridgeNetwork, error) { + d.Lock() + defer d.Unlock() + + if id == "" { + return nil, types.BadRequestErrorf("invalid network id: %s", id) + } + + if nw, ok := d.networks[id]; ok { + return nw, nil + } + + return nil, types.NotFoundErrorf("network not found: %s", id) +} + +// Return a slice of networks over which caller can iterate safely +func (d *driver) getNetworks() []*bridgeNetwork { + d.Lock() + defer d.Unlock() + + ls := make([]*bridgeNetwork, 0, len(d.networks)) + for _, nw := range d.networks { + ls = append(ls, nw) + } + return ls +} + +func bridgeSetup(config *networkConfiguration) error { + return nil +} + +func bridgeCleanup(config *networkConfiguration, logErr bool) { + var err error + + bridgeName := config.BridgeName + tableName := "bridge_nw_subnets" + gwName := fmt.Sprintf("%s_gw0", bridgeName) + gwIP := config.AddressIPv4.String() + pfAnchor := fmt.Sprintf("_auto/docker/%s", bridgeName) + tableAnchor := fmt.Sprintf("_auto/docker/%s", tableName) + + err = exec.Command("/usr/sbin/pfctl", "-a", pfAnchor, "-F", "all").Run() + if err != nil && logErr { + logrus.Warn("cannot flush firewall rules") + } + err = exec.Command("/usr/sbin/ifconfig", gwName, "unplumb").Run() + if err != nil && logErr { + logrus.Warn("cannot remove gateway interface") + } + err = exec.Command("/usr/sbin/dladm", "delete-vnic", + "-t", gwName).Run() + if err != nil && logErr { + logrus.Warn("cannot delete vnic") + } + err = exec.Command("/usr/sbin/dladm", "delete-etherstub", + "-t", config.BridgeNameInternal).Run() + if err != nil && logErr { + logrus.Warn("cannot delete etherstub") + } + err = exec.Command("/usr/sbin/pfctl", "-a", tableAnchor, "-t", tableName, "-T", "delete", gwIP).Run() + if err != nil && logErr { + logrus.Warnf("cannot remove bridge network '%s' from PF table", bridgeName) + } +} + +func (d *driver) createNetwork(config *networkConfiguration) error { + var err error + + logrus.Infof("Creating bridge network: %s %s %s", config.ID, + config.BridgeName, config.AddressIPv4) + + networkList := d.getNetworks() + for i, nw := range networkList { + nw.Lock() + nwConfig := nw.config + nw.Unlock() + if err := nwConfig.Conflicts(config); err != nil { + if config.DefaultBridge { + // We encountered and identified a stale default network + // We must delete it as libnetwork is the source of thruth + // The default network being created must be the only one + // This can happen only from docker 1.12 on ward + logrus.Infof("Removing stale default bridge network %s (%s)", nwConfig.ID, nwConfig.BridgeName) + if err := d.DeleteNetwork(nwConfig.ID); err != nil { + logrus.Warnf("Failed to remove stale default network: %s (%s): %v. Will remove from store.", nwConfig.ID, nwConfig.BridgeName, err) + d.storeDelete(nwConfig) + } + networkList = append(networkList[:i], networkList[i+1:]...) + } else { + return types.ForbiddenErrorf( + "cannot create network %s (%s): "+ + "conflicts with network %s (%s): %s", + nwConfig.BridgeName, config.ID, nw.id, + nw.config.BridgeName, err.Error()) + } + } + } + if config.DefaultBindingIP == nil || + config.DefaultBindingIP.IsUnspecified() { + config.DefaultBindingIP = d.defrouteIP + } + + // Create and set network handler in driver + network := &bridgeNetwork{ + id: config.ID, + endpoints: make(map[string]*bridgeEndpoint), + config: config, + //portMapper: portmapper.New(""), + driver: d, + } + + d.Lock() + d.networks[config.ID] = network + d.Unlock() + + // On failure make sure to reset driver network handler to nil + defer func() { + if err != nil { + d.Lock() + delete(d.networks, config.ID) + d.Unlock() + } + }() + + // Create or retrieve the bridge L3 interface + bridgeIface := newInterface(config) + network.bridge = bridgeIface + + // Verify the network configuration does not conflict with previously installed + // networks. This step is needed now because driver might have now set the bridge + // name on this config struct. And because we need to check for possible address + // conflicts, so we need to check against operational networks. + if err = config.conflictsWithNetworks(config.ID, networkList); err != nil { + return err + } + + // We only attempt to create the bridge when the requested device name is + // the default one. + if config.BridgeName != DefaultBridgeName && config.DefaultBridge { + return NonDefaultBridgeExistError(config.BridgeName) + } + + bridgeCleanup(config, false) + err = bridgeSetup(config) + if err != nil { + return err + } + return nil +} + +func (d *driver) DeleteNetwork(nid string) error { + var err error + // Get network handler and remove it from driver + d.Lock() + n, ok := d.networks[nid] + d.Unlock() + + if !ok { + return types.InternalMaskableErrorf("network %s does not exist", nid) + } + d.Lock() + delete(d.networks, nid) + d.Unlock() + + // On failure set network handler back in driver, but + // only if is not already taken over by some other thread + defer func() { + if err != nil { + d.Lock() + if _, ok := d.networks[nid]; !ok { + d.networks[nid] = n + } + d.Unlock() + } + }() + + // Sanity check + if n == nil { + err = driverapi.ErrNoNetwork(nid) + return err + } + + // Cannot remove network if endpoints are still present + if len(n.endpoints) != 0 { + err = ActiveEndpointsError(n.id) + return err + } + bridgeCleanup(n.config, true) + logrus.Infof("Deleting bridge network: %s", nid[:12]) + return d.storeDelete(n.config) +} + +func (d *driver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo, epOptions map[string]interface{}) error { + if ifInfo == nil { + return errors.New("invalid interface passed") + } + + // Get the network handler and make sure it exists + d.Lock() + n, ok := d.networks[nid] + d.Unlock() + + if !ok { + return types.NotFoundErrorf("network %s does not exist", nid) + } + if n == nil { + return driverapi.ErrNoNetwork(nid) + } + + // Sanity check + n.Lock() + if n.id != nid { + n.Unlock() + return InvalidNetworkIDError(nid) + } + n.Unlock() + + // Check if endpoint id is good and retrieve correspondent endpoint + ep, err := n.getEndpoint(eid) + if err != nil { + return err + } + + // Endpoint with that id exists either on desired or other sandbox + if ep != nil { + return driverapi.ErrEndpointExists(eid) + } + + // Try to convert the options to endpoint configuration + epConfig, err := parseEndpointOptions(epOptions) + if err != nil { + return err + } + + // Create and add the endpoint + n.Lock() + endpoint := &bridgeEndpoint{id: eid, config: epConfig} + n.endpoints[eid] = endpoint + n.Unlock() + + // On failure make sure to remove the endpoint + defer func() { + if err != nil { + n.Lock() + delete(n.endpoints, eid) + n.Unlock() + } + }() + + // Create the sandbox side pipe interface + if ifInfo.MacAddress() == nil { + // No MAC address assigned to interface. Generate a random MAC to assign + endpoint.macAddress = netutils.GenerateRandomMAC() + if err := ifInfo.SetMacAddress(endpoint.macAddress); err != nil { + logrus.Warnf("Unable to set mac address: %s to endpoint: %s", + endpoint.macAddress.String(), endpoint.id) + return err + } + } else { + endpoint.macAddress = ifInfo.MacAddress() + } + endpoint.addr = ifInfo.Address() + endpoint.addrv6 = ifInfo.AddressIPv6() + c := n.config + + // Program any required port mapping and store them in the endpoint + endpoint.portMapping, err = n.allocatePorts(endpoint, c.DefaultBindingIntf, c.DefaultBindingIP, true) + if err != nil { + return err + } + + return nil +} + +func (d *driver) DeleteEndpoint(nid, eid string) error { + var err error + + // Get the network handler and make sure it exists + d.Lock() + n, ok := d.networks[nid] + d.Unlock() + + if !ok { + return types.InternalMaskableErrorf("network %s does not exist", nid) + } + if n == nil { + return driverapi.ErrNoNetwork(nid) + } + + // Sanity Check + n.Lock() + if n.id != nid { + n.Unlock() + return InvalidNetworkIDError(nid) + } + n.Unlock() + + // Check endpoint id and if an endpoint is actually there + ep, err := n.getEndpoint(eid) + if err != nil { + return err + } + if ep == nil { + return EndpointNotFoundError(eid) + } + + // Remove it + n.Lock() + delete(n.endpoints, eid) + n.Unlock() + + // On failure make sure to set back ep in n.endpoints, but only + // if it hasn't been taken over already by some other thread. + defer func() { + if err != nil { + n.Lock() + if _, ok := n.endpoints[eid]; !ok { + n.endpoints[eid] = ep + } + n.Unlock() + } + }() + + err = n.releasePorts(ep) + if err != nil { + logrus.Warn(err) + } + + return nil +} + +func (d *driver) EndpointOperInfo(nid, eid string) (map[string]interface{}, error) { + // Get the network handler and make sure it exists + d.Lock() + n, ok := d.networks[nid] + d.Unlock() + if !ok { + return nil, types.NotFoundErrorf("network %s does not exist", nid) + } + if n == nil { + return nil, driverapi.ErrNoNetwork(nid) + } + + // Sanity check + n.Lock() + if n.id != nid { + n.Unlock() + return nil, InvalidNetworkIDError(nid) + } + n.Unlock() + + // Check if endpoint id is good and retrieve correspondent endpoint + ep, err := n.getEndpoint(eid) + if err != nil { + return nil, err + } + if ep == nil { + return nil, driverapi.ErrNoEndpoint(eid) + } + + m := make(map[string]interface{}) + + if ep.extConnConfig != nil && ep.extConnConfig.ExposedPorts != nil { + // Return a copy of the config data + epc := make([]types.TransportPort, 0, len(ep.extConnConfig.ExposedPorts)) + for _, tp := range ep.extConnConfig.ExposedPorts { + epc = append(epc, tp.GetCopy()) + } + m[netlabel.ExposedPorts] = epc + } + + if ep.portMapping != nil { + // Return a copy of the operational data + pmc := make([]types.PortBinding, 0, len(ep.portMapping)) + for _, pm := range ep.portMapping { + pmc = append(pmc, pm.GetCopy()) + } + m[netlabel.PortMap] = pmc + } + + if len(ep.macAddress) != 0 { + m[netlabel.MacAddress] = ep.macAddress + } + return m, nil +} + +// Join method is invoked when a Sandbox is attached to an endpoint. +func (d *driver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo, options map[string]interface{}) error { + network, err := d.getNetwork(nid) + if err != nil { + return err + } + + endpoint, err := network.getEndpoint(eid) + if err != nil { + return err + } + + if endpoint == nil { + return EndpointNotFoundError(eid) + } + + endpoint.containerConfig, err = parseContainerOptions(options) + if err != nil { + return err + } + + err = jinfo.SetGateway(network.bridge.gatewayIPv4) + if err != nil { + return err + } + + err = jinfo.SetGatewayIPv6(network.bridge.gatewayIPv6) + if err != nil { + return err + } + + return nil +} + +func (d *driver) link(network *bridgeNetwork, endpoint *bridgeEndpoint, enable bool) error { + return nil +} + +// Leave method is invoked when a Sandbox detaches from an endpoint. +func (d *driver) Leave(nid, eid string) error { + network, err := d.getNetwork(nid) + if err != nil { + return types.InternalMaskableErrorf("%s", err) + } + + endpoint, err := network.getEndpoint(eid) + if err != nil { + return err + } + + if endpoint == nil { + return EndpointNotFoundError(eid) + } + + return nil +} + +func (d *driver) ProgramExternalConnectivity(nid, eid string, options map[string]interface{}) error { + network, err := d.getNetwork(nid) + if err != nil { + return err + } + + endpoint, err := network.getEndpoint(eid) + if err != nil { + return err + } + + if endpoint == nil { + return EndpointNotFoundError(eid) + } + + endpoint.extConnConfig, err = parseConnectivityOptions(options) + if err != nil { + return err + } + + // Program any required port mapping and store them in the endpoint + endpoint.portMapping, err = network.allocatePorts(endpoint, network.config.DefaultBindingIntf, network.config.DefaultBindingIP, true) + if err != nil { + return err + } + + if !network.config.EnableICC { + return d.link(network, endpoint, true) + } + + return nil +} + +func (d *driver) RevokeExternalConnectivity(nid, eid string) error { + network, err := d.getNetwork(nid) + if err != nil { + return err + } + + endpoint, err := network.getEndpoint(eid) + if err != nil { + return err + } + + if endpoint == nil { + return EndpointNotFoundError(eid) + } + + err = network.releasePorts(endpoint) + if err != nil { + logrus.Warn(err) + } + + return nil +} + +func (d *driver) Type() string { + return networkType +} + +func (d *driver) IsBuiltIn() bool { + return true +} + +// DiscoverNew is a notification for a new discovery event, such as a new node joining a cluster +func (d *driver) DiscoverNew(dType discoverapi.DiscoveryType, data interface{}) error { + return nil +} + +// DiscoverDelete is a notification for a discovery delete event, such as a node leaving a cluster +func (d *driver) DiscoverDelete(dType discoverapi.DiscoveryType, data interface{}) error { + return nil +} + +// Validate performs a static validation on the network configuration parameters. +// Whatever can be assessed a priori before attempting any programming. +func (c *networkConfiguration) Validate() error { + if c.Mtu < 0 { + return ErrInvalidMtu(c.Mtu) + } + + // If bridge v4 subnet is specified + if c.AddressIPv4 != nil { + // If default gw is specified, it must be part of bridge subnet + if c.DefaultGatewayIPv4 != nil { + if !c.AddressIPv4.Contains(c.DefaultGatewayIPv4) { + return &ErrInvalidGateway{} + } + } + } + + // If default v6 gw is specified, AddressIPv6 must be specified and gw must belong to AddressIPv6 subnet + if c.EnableIPv6 && c.DefaultGatewayIPv6 != nil { + if c.AddressIPv6 == nil || !c.AddressIPv6.Contains(c.DefaultGatewayIPv6) { + return &ErrInvalidGateway{} + } + } + return nil +} + +// Checks whether this network's configuration for the network with this id conflicts with any of the passed networks +func (c *networkConfiguration) conflictsWithNetworks(id string, others []*bridgeNetwork) error { + for _, nw := range others { + + nw.Lock() + nwID := nw.id + nwConfig := nw.config + nwBridge := nw.bridge + nw.Unlock() + + if nwID == id { + continue + } + // Verify the name (which may have been set by newInterface()) does not conflict with + // existing bridge interfaces. Ironically the system chosen name gets stored in the config... + // Basically we are checking if the two original configs were both empty. + if nwConfig.BridgeName == c.BridgeName { + return types.ForbiddenErrorf("conflicts with network %s (%s) by bridge name", nwID, nwConfig.BridgeName) + } + // If this network config specifies the AddressIPv4, we need + // to make sure it does not conflict with any previously allocated + // bridges. This could not be completely caught by the config conflict + // check, because networks which config does not specify the AddressIPv4 + // get their address and subnet selected by the driver (see electBridgeIPv4()) + if c.AddressIPv4 != nil { + if nwBridge.bridgeIPv4.Contains(c.AddressIPv4.IP) || + c.AddressIPv4.Contains(nwBridge.bridgeIPv4.IP) { + return types.ForbiddenErrorf("conflicts with network %s (%s) by ip network", nwID, nwConfig.BridgeName) + } + } + } + + return nil +} + +// Conflicts check if two NetworkConfiguration objects overlap +func (c *networkConfiguration) Conflicts(o *networkConfiguration) error { + if o == nil { + return fmt.Errorf("same configuration") + } + + // Also empty, because only one network with empty name is allowed + if c.BridgeName == o.BridgeName { + return fmt.Errorf("networks have same bridge name") + } + + // They must be in different subnets + if (c.AddressIPv4 != nil && o.AddressIPv4 != nil) && + (c.AddressIPv4.Contains(o.AddressIPv4.IP) || o.AddressIPv4.Contains(c.AddressIPv4.IP)) { + return fmt.Errorf("networks have overlapping IPv4") + } + + // They must be in different v6 subnets + if (c.AddressIPv6 != nil && o.AddressIPv6 != nil) && + (c.AddressIPv6.Contains(o.AddressIPv6.IP) || o.AddressIPv6.Contains(c.AddressIPv6.IP)) { + return fmt.Errorf("networks have overlapping IPv6") + } + + return nil +} + +func (c *networkConfiguration) fromLabels(labels map[string]string) error { + var err error + for label, value := range labels { + switch label { + case BridgeName: + c.BridgeName = value + case netlabel.DriverMTU: + if c.Mtu, err = strconv.Atoi(value); err != nil { + return parseErr(label, value, err.Error()) + } + case netlabel.EnableIPv6: + if c.EnableIPv6, err = strconv.ParseBool(value); err != nil { + return parseErr(label, value, err.Error()) + } + case EnableIPMasquerade: + if c.EnableIPMasquerade, err = strconv.ParseBool(value); err != nil { + return parseErr(label, value, err.Error()) + } + case EnableICC: + if c.EnableICC, err = strconv.ParseBool(value); err != nil { + return parseErr(label, value, err.Error()) + } + case DefaultBridge: + if c.DefaultBridge, err = strconv.ParseBool(value); err != nil { + return parseErr(label, value, err.Error()) + } + case DefaultBindingIP: + if c.DefaultBindingIP = net.ParseIP(value); c.DefaultBindingIP == nil { + return parseErr(label, value, "nil ip") + } + } + } + + return nil +} + +func parseErr(label, value, errString string) error { + return types.BadRequestErrorf("failed to parse %s value: %v (%s)", label, value, errString) +} + +func parseNetworkGenericOptions(data interface{}) (*networkConfiguration, error) { + var ( + err error + config *networkConfiguration + ) + + switch opt := data.(type) { + case *networkConfiguration: + config = opt + case map[string]string: + config = &networkConfiguration{ + EnableICC: true, + EnableIPMasquerade: true, + } + err = config.fromLabels(opt) + case options.Generic: + var opaqueConfig interface{} + if opaqueConfig, err = options.GenerateFromModel(opt, config); err == nil { + config = opaqueConfig.(*networkConfiguration) + } + default: + err = types.BadRequestErrorf("do not recognize network configuration format: %T", opt) + } + + return config, err +} + +func parseNetworkOptions(d *driver, id string, option options.Generic) (*networkConfiguration, error) { + var ( + err error + config = &networkConfiguration{} + ) + + // Parse generic label first, config will be re-assigned + if genData, ok := option[netlabel.GenericData]; ok && genData != nil { + if config, err = parseNetworkGenericOptions(genData); err != nil { + return nil, err + } + } + + // Process well-known labels next + if val, ok := option[netlabel.EnableIPv6]; ok { + config.EnableIPv6 = val.(bool) + } + + if val, ok := option[netlabel.Internal]; ok { + if internal, ok := val.(bool); ok && internal { + config.Internal = true + } + } + + // Finally validate the configuration + if err = config.Validate(); err != nil { + return nil, err + } + + if config.BridgeName == "" && config.DefaultBridge == false { + config.BridgeName = "br_" + id[:12] + "_0" + } + + lastChar := config.BridgeName[len(config.BridgeName)-1:] + if _, err = strconv.Atoi(lastChar); err != nil { + config.BridgeNameInternal = config.BridgeName + "_0" + } else { + config.BridgeNameInternal = config.BridgeName + } + + config.ID = id + return config, nil +} + +func (c *networkConfiguration) processIPAM(id string, ipamV4Data, ipamV6Data []driverapi.IPAMData) error { + if len(ipamV4Data) > 1 || len(ipamV6Data) > 1 { + return types.ForbiddenErrorf("bridge driver doesnt support multiple subnets") + } + + if len(ipamV4Data) == 0 { + return types.BadRequestErrorf("bridge network %s requires ipv4 configuration", id) + } + + if ipamV4Data[0].Gateway != nil { + c.AddressIPv4 = types.GetIPNetCopy(ipamV4Data[0].Gateway) + } + + if gw, ok := ipamV4Data[0].AuxAddresses[DefaultGatewayV4AuxKey]; ok { + c.DefaultGatewayIPv4 = gw.IP + } + + if len(ipamV6Data) > 0 { + c.AddressIPv6 = ipamV6Data[0].Pool + + if ipamV6Data[0].Gateway != nil { + c.AddressIPv6 = types.GetIPNetCopy(ipamV6Data[0].Gateway) + } + + if gw, ok := ipamV6Data[0].AuxAddresses[DefaultGatewayV6AuxKey]; ok { + c.DefaultGatewayIPv6 = gw.IP + } + } + + return nil +} + +func (n *bridgeNetwork) getEndpoint(eid string) (*bridgeEndpoint, error) { + n.Lock() + defer n.Unlock() + + if eid == "" { + return nil, InvalidEndpointIDError(eid) + } + + if ep, ok := n.endpoints[eid]; ok { + return ep, nil + } + + return nil, nil +} + +func parseEndpointOptions(epOptions map[string]interface{}) (*endpointConfiguration, error) { + if epOptions == nil { + return nil, nil + } + + ec := &endpointConfiguration{} + + if opt, ok := epOptions[netlabel.MacAddress]; ok { + if mac, ok := opt.(net.HardwareAddr); ok { + ec.MacAddress = mac + } else { + return nil, &ErrInvalidEndpointConfig{} + } + } + + if opt, ok := epOptions[netlabel.PortMap]; ok { + if bs, ok := opt.([]types.PortBinding); ok { + ec.PortBindings = bs + } else { + return nil, &ErrInvalidEndpointConfig{} + } + } + + if opt, ok := epOptions[netlabel.ExposedPorts]; ok { + if ports, ok := opt.([]types.TransportPort); ok { + ec.ExposedPorts = ports + } else { + return nil, &ErrInvalidEndpointConfig{} + } + } + + return ec, nil +} + +func parseContainerOptions(cOptions map[string]interface{}) (*containerConfiguration, error) { + if cOptions == nil { + return nil, nil + } + genericData := cOptions[netlabel.GenericData] + if genericData == nil { + return nil, nil + } + switch opt := genericData.(type) { + case options.Generic: + opaqueConfig, err := options.GenerateFromModel(opt, &containerConfiguration{}) + if err != nil { + return nil, err + } + return opaqueConfig.(*containerConfiguration), nil + case *containerConfiguration: + return opt, nil + default: + return nil, nil + } +} + +func parseConnectivityOptions(cOptions map[string]interface{}) (*connectivityConfiguration, error) { + if cOptions == nil { + return nil, nil + } + + cc := &connectivityConfiguration{} + + if opt, ok := cOptions[netlabel.PortMap]; ok { + if pb, ok := opt.([]types.PortBinding); ok { + cc.PortBindings = pb + } else { + return nil, types.BadRequestErrorf("Invalid port mapping data in connectivity configuration: %v", opt) + } + } + + if opt, ok := cOptions[netlabel.ExposedPorts]; ok { + if ports, ok := opt.([]types.TransportPort); ok { + cc.ExposedPorts = ports + } else { + return nil, types.BadRequestErrorf("Invalid exposed ports data in connectivity configuration: %v", opt) + } + } + + return cc, nil +} diff --git a/vendor/github.com/docker/libnetwork/drivers/freebsd/bridge/bridge_store.go b/vendor/github.com/docker/libnetwork/drivers/freebsd/bridge/bridge_store.go new file mode 100644 index 0000000000000..5beaf232b573c --- /dev/null +++ b/vendor/github.com/docker/libnetwork/drivers/freebsd/bridge/bridge_store.go @@ -0,0 +1,384 @@ +// +build freebsd + +package bridge + +import ( + "encoding/json" + "fmt" + "net" + + "github.com/Sirupsen/logrus" + "github.com/docker/libnetwork/datastore" + "github.com/docker/libnetwork/discoverapi" + "github.com/docker/libnetwork/netlabel" + "github.com/docker/libnetwork/types" +) + +const ( + // network config prefix was not specific enough. + // To be backward compatible, need custom endpoint + // prefix with different root + bridgePrefix = "bridge" + bridgeEndpointPrefix = "bridge-endpoint" +) + +func (d *driver) initStore(option map[string]interface{}) error { + if data, ok := option[netlabel.LocalKVClient]; ok { + var err error + dsc, ok := data.(discoverapi.DatastoreConfigData) + if !ok { + return types.InternalErrorf("incorrect data in datastore configuration: %v", data) + } + d.store, err = datastore.NewDataStoreFromConfig(dsc) + if err != nil { + return types.InternalErrorf("bridge driver failed to initialize data store: %v", err) + } + + err = d.populateNetworks() + if err != nil { + return err + } + + err = d.populateEndpoints() + if err != nil { + return err + } + } + + return nil +} + +func (d *driver) populateNetworks() error { + kvol, err := d.store.List(datastore.Key(bridgePrefix), &networkConfiguration{}) + if err != nil && err != datastore.ErrKeyNotFound { + return fmt.Errorf("failed to get bridge network configurations from store: %v", err) + } + + // It's normal for network configuration state to be empty. Just return. + if err == datastore.ErrKeyNotFound { + return nil + } + + for _, kvo := range kvol { + ncfg := kvo.(*networkConfiguration) + if err = d.createNetwork(ncfg); err != nil { + logrus.Warnf("could not create bridge network for id %s bridge name %s while booting up from persistent state: %v", ncfg.ID, ncfg.BridgeName, err) + } + logrus.Debugf("Network (%s) restored", ncfg.ID[0:7]) + } + + return nil +} + +func (d *driver) populateEndpoints() error { + kvol, err := d.store.List(datastore.Key(bridgeEndpointPrefix), &bridgeEndpoint{}) + if err != nil && err != datastore.ErrKeyNotFound { + return fmt.Errorf("failed to get bridge endpoints from store: %v", err) + } + + if err == datastore.ErrKeyNotFound { + return nil + } + + for _, kvo := range kvol { + ep := kvo.(*bridgeEndpoint) + n, ok := d.networks[ep.nid] + if !ok { + logrus.Debugf("Network (%s) not found for restored bridge endpoint (%s)", ep.nid[0:7], ep.id[0:7]) + logrus.Debugf("Deleting stale bridge endpoint (%s) from store", ep.nid[0:7]) + if err := d.storeDelete(ep); err != nil { + logrus.Debugf("Failed to delete stale bridge endpoint (%s) from store", ep.nid[0:7]) + } + continue + } + n.endpoints[ep.id] = ep + n.restorePortAllocations(ep) + logrus.Debugf("Endpoint (%s) restored to network (%s)", ep.id[0:7], ep.nid[0:7]) + } + + return nil +} + +func (d *driver) storeUpdate(kvObject datastore.KVObject) error { + if d.store == nil { + logrus.Warnf("bridge store not initialized. kv object %s is not added to the store", datastore.Key(kvObject.Key()...)) + return nil + } + + if err := d.store.PutObjectAtomic(kvObject); err != nil { + return fmt.Errorf("failed to update bridge store for object type %T: %v", kvObject, err) + } + + return nil +} + +func (d *driver) storeDelete(kvObject datastore.KVObject) error { + if d.store == nil { + logrus.Debugf("bridge store not initialized. kv object %s is not deleted from store", datastore.Key(kvObject.Key()...)) + return nil + } + +retry: + if err := d.store.DeleteObjectAtomic(kvObject); err != nil { + if err == datastore.ErrKeyModified { + if err := d.store.GetObject(datastore.Key(kvObject.Key()...), kvObject); err != nil { + return fmt.Errorf("could not update the kvobject to latest when trying to delete: %v", err) + } + goto retry + } + return err + } + + return nil +} + +func (ncfg *networkConfiguration) MarshalJSON() ([]byte, error) { + nMap := make(map[string]interface{}) + nMap["ID"] = ncfg.ID + nMap["BridgeName"] = ncfg.BridgeName + nMap["BridgeNameInternal"] = ncfg.BridgeNameInternal + nMap["EnableIPv6"] = ncfg.EnableIPv6 + nMap["EnableIPMasquerade"] = ncfg.EnableIPMasquerade + nMap["EnableICC"] = ncfg.EnableICC + nMap["Mtu"] = ncfg.Mtu + nMap["Internal"] = ncfg.Internal + nMap["DefaultBridge"] = ncfg.DefaultBridge + nMap["DefaultBindingIP"] = ncfg.DefaultBindingIP.String() + nMap["DefaultBindingIntf"] = ncfg.DefaultBindingIntf + nMap["DefaultGatewayIPv4"] = ncfg.DefaultGatewayIPv4.String() + nMap["DefaultGatewayIPv6"] = ncfg.DefaultGatewayIPv6.String() + + if ncfg.AddressIPv4 != nil { + nMap["AddressIPv4"] = ncfg.AddressIPv4.String() + } + + if ncfg.AddressIPv6 != nil { + nMap["AddressIPv6"] = ncfg.AddressIPv6.String() + } + + return json.Marshal(nMap) +} + +func (ncfg *networkConfiguration) UnmarshalJSON(b []byte) error { + var ( + err error + nMap map[string]interface{} + ) + + if err = json.Unmarshal(b, &nMap); err != nil { + return err + } + + if v, ok := nMap["AddressIPv4"]; ok { + if ncfg.AddressIPv4, err = types.ParseCIDR(v.(string)); err != nil { + return types.InternalErrorf("failed to decode bridge network address IPv4 after json unmarshal: %s", v.(string)) + } + } + + if v, ok := nMap["AddressIPv6"]; ok { + if ncfg.AddressIPv6, err = types.ParseCIDR(v.(string)); err != nil { + return types.InternalErrorf("failed to decode bridge network address IPv6 after json unmarshal: %s", v.(string)) + } + } + + ncfg.DefaultBridge = nMap["DefaultBridge"].(bool) + ncfg.DefaultBindingIP = net.ParseIP(nMap["DefaultBindingIP"].(string)) + ncfg.DefaultBindingIntf = nMap["DefaultBindingIntf"].(string) + ncfg.DefaultGatewayIPv4 = net.ParseIP(nMap["DefaultGatewayIPv4"].(string)) + ncfg.DefaultGatewayIPv6 = net.ParseIP(nMap["DefaultGatewayIPv6"].(string)) + ncfg.ID = nMap["ID"].(string) + ncfg.BridgeName = nMap["BridgeName"].(string) + ncfg.BridgeNameInternal = nMap["BridgeNameInternal"].(string) + ncfg.EnableIPv6 = nMap["EnableIPv6"].(bool) + ncfg.EnableIPMasquerade = nMap["EnableIPMasquerade"].(bool) + ncfg.EnableICC = nMap["EnableICC"].(bool) + ncfg.Mtu = int(nMap["Mtu"].(float64)) + if v, ok := nMap["Internal"]; ok { + ncfg.Internal = v.(bool) + } + + return nil +} + +func (ncfg *networkConfiguration) Key() []string { + return []string{bridgePrefix, ncfg.ID} +} + +func (ncfg *networkConfiguration) KeyPrefix() []string { + return []string{bridgePrefix} +} + +func (ncfg *networkConfiguration) Value() []byte { + b, err := json.Marshal(ncfg) + if err != nil { + return nil + } + return b +} + +func (ncfg *networkConfiguration) SetValue(value []byte) error { + return json.Unmarshal(value, ncfg) +} + +func (ncfg *networkConfiguration) Index() uint64 { + return ncfg.dbIndex +} + +func (ncfg *networkConfiguration) SetIndex(index uint64) { + ncfg.dbIndex = index + ncfg.dbExists = true +} + +func (ncfg *networkConfiguration) Exists() bool { + return ncfg.dbExists +} + +func (ncfg *networkConfiguration) Skip() bool { + return false +} + +func (ncfg *networkConfiguration) New() datastore.KVObject { + return &networkConfiguration{} +} + +func (ncfg *networkConfiguration) CopyTo(o datastore.KVObject) error { + dstNcfg := o.(*networkConfiguration) + *dstNcfg = *ncfg + return nil +} + +func (ncfg *networkConfiguration) DataScope() string { + return datastore.LocalScope +} + +func (ep *bridgeEndpoint) MarshalJSON() ([]byte, error) { + epMap := make(map[string]interface{}) + epMap["id"] = ep.id + epMap["nid"] = ep.nid + epMap["SrcName"] = ep.srcName + epMap["MacAddress"] = ep.macAddress.String() + epMap["Addr"] = ep.addr.String() + if ep.addrv6 != nil { + epMap["Addrv6"] = ep.addrv6.String() + } + epMap["Config"] = ep.config + epMap["ContainerConfig"] = ep.containerConfig + epMap["ExternalConnConfig"] = ep.extConnConfig + epMap["PortMapping"] = ep.portMapping + + return json.Marshal(epMap) +} + +func (ep *bridgeEndpoint) UnmarshalJSON(b []byte) error { + var ( + err error + epMap map[string]interface{} + ) + + if err = json.Unmarshal(b, &epMap); err != nil { + return fmt.Errorf("Failed to unmarshal to bridge endpoint: %v", err) + } + + if v, ok := epMap["MacAddress"]; ok { + if ep.macAddress, err = net.ParseMAC(v.(string)); err != nil { + return types.InternalErrorf("failed to decode bridge endpoint MAC address (%s) after json unmarshal: %v", v.(string), err) + } + } + if v, ok := epMap["Addr"]; ok { + if ep.addr, err = types.ParseCIDR(v.(string)); err != nil { + return types.InternalErrorf("failed to decode bridge endpoint IPv4 address (%s) after json unmarshal: %v", v.(string), err) + } + } + if v, ok := epMap["Addrv6"]; ok { + if ep.addrv6, err = types.ParseCIDR(v.(string)); err != nil { + return types.InternalErrorf("failed to decode bridge endpoint IPv6 address (%s) after json unmarshal: %v", v.(string), err) + } + } + ep.id = epMap["id"].(string) + ep.nid = epMap["nid"].(string) + ep.srcName = epMap["SrcName"].(string) + d, _ := json.Marshal(epMap["Config"]) + if err := json.Unmarshal(d, &ep.config); err != nil { + logrus.Warnf("Failed to decode endpoint config %v", err) + } + d, _ = json.Marshal(epMap["ContainerConfig"]) + if err := json.Unmarshal(d, &ep.containerConfig); err != nil { + logrus.Warnf("Failed to decode endpoint container config %v", err) + } + d, _ = json.Marshal(epMap["ExternalConnConfig"]) + if err := json.Unmarshal(d, &ep.extConnConfig); err != nil { + logrus.Warnf("Failed to decode endpoint external connectivity configuration %v", err) + } + d, _ = json.Marshal(epMap["PortMapping"]) + if err := json.Unmarshal(d, &ep.portMapping); err != nil { + logrus.Warnf("Failed to decode endpoint port mapping %v", err) + } + + return nil +} + +func (ep *bridgeEndpoint) Key() []string { + return []string{bridgeEndpointPrefix, ep.id} +} + +func (ep *bridgeEndpoint) KeyPrefix() []string { + return []string{bridgeEndpointPrefix} +} + +func (ep *bridgeEndpoint) Value() []byte { + b, err := json.Marshal(ep) + if err != nil { + return nil + } + return b +} + +func (ep *bridgeEndpoint) SetValue(value []byte) error { + return json.Unmarshal(value, ep) +} + +func (ep *bridgeEndpoint) Index() uint64 { + return ep.dbIndex +} + +func (ep *bridgeEndpoint) SetIndex(index uint64) { + ep.dbIndex = index + ep.dbExists = true +} + +func (ep *bridgeEndpoint) Exists() bool { + return ep.dbExists +} + +func (ep *bridgeEndpoint) Skip() bool { + return false +} + +func (ep *bridgeEndpoint) New() datastore.KVObject { + return &bridgeEndpoint{} +} + +func (ep *bridgeEndpoint) CopyTo(o datastore.KVObject) error { + dstEp := o.(*bridgeEndpoint) + *dstEp = *ep + return nil +} + +func (ep *bridgeEndpoint) DataScope() string { + return datastore.LocalScope +} + +func (n *bridgeNetwork) restorePortAllocations(ep *bridgeEndpoint) { + if ep.extConnConfig == nil || + ep.extConnConfig.ExposedPorts == nil || + ep.extConnConfig.PortBindings == nil { + return + } + tmp := ep.extConnConfig.PortBindings + ep.extConnConfig.PortBindings = ep.portMapping + _, err := n.allocatePorts(ep, n.config.DefaultBindingIntf, n.config.DefaultBindingIP, n.driver.config.EnableUserlandProxy) + if err != nil { + logrus.Warnf("Failed to reserve existing port mapping for endpoint %s:%v", ep.id[0:7], err) + } + ep.extConnConfig.PortBindings = tmp +} diff --git a/vendor/github.com/docker/libnetwork/drivers/freebsd/bridge/errors.go b/vendor/github.com/docker/libnetwork/drivers/freebsd/bridge/errors.go new file mode 100644 index 0000000000000..f72cd9b7a1610 --- /dev/null +++ b/vendor/github.com/docker/libnetwork/drivers/freebsd/bridge/errors.go @@ -0,0 +1,120 @@ +// +build freebsd +package bridge + +import "fmt" + +// ErrInvalidEndpointConfig error is returned when an endpoint create is attempted with an invalid endpoint configuration. +type ErrInvalidEndpointConfig struct{} + +func (eiec *ErrInvalidEndpointConfig) Error() string { + return "trying to create an endpoint with an invalid endpoint configuration" +} + +// BadRequest denotes the type of this error +func (eiec *ErrInvalidEndpointConfig) BadRequest() {} + +// ErrNoIPAddr error is returned when bridge has no IPv4 address configured. +type ErrNoIPAddr struct{} + +func (enip *ErrNoIPAddr) Error() string { + return "bridge has no IPv4 address configured" +} + +// InternalError denotes the type of this error +func (enip *ErrNoIPAddr) InternalError() {} + +// ErrInvalidGateway is returned when the user provided default gateway (v4/v6) is not not valid. +type ErrInvalidGateway struct{} + +func (eig *ErrInvalidGateway) Error() string { + return "default gateway ip must be part of the network" +} + +// BadRequest denotes the type of this error +func (eig *ErrInvalidGateway) BadRequest() {} + +// ErrInvalidMtu is returned when the user provided MTU is not valid. +type ErrInvalidMtu int + +func (eim ErrInvalidMtu) Error() string { + return fmt.Sprintf("invalid MTU number: %d", int(eim)) +} + +// BadRequest denotes the type of this error +func (eim ErrInvalidMtu) BadRequest() {} + +// ErrUnsupportedAddressType is returned when the specified address type is not supported. +type ErrUnsupportedAddressType string + +func (uat ErrUnsupportedAddressType) Error() string { + return fmt.Sprintf("unsupported address type: %s", string(uat)) +} + +// BadRequest denotes the type of this error +func (uat ErrUnsupportedAddressType) BadRequest() {} + +// ActiveEndpointsError is returned when there are +// still active endpoints in the network being deleted. +type ActiveEndpointsError string + +func (aee ActiveEndpointsError) Error() string { + return fmt.Sprintf("network %s has active endpoint", string(aee)) +} + +// Forbidden denotes the type of this error +func (aee ActiveEndpointsError) Forbidden() {} + +// InvalidNetworkIDError is returned when the passed +// network id for an existing network is not a known id. +type InvalidNetworkIDError string + +func (inie InvalidNetworkIDError) Error() string { + return fmt.Sprintf("invalid network id %s", string(inie)) +} + +// NotFound denotes the type of this error +func (inie InvalidNetworkIDError) NotFound() {} + +// InvalidEndpointIDError is returned when the passed +// endpoint id is not valid. +type InvalidEndpointIDError string + +func (ieie InvalidEndpointIDError) Error() string { + return fmt.Sprintf("invalid endpoint id: %s", string(ieie)) +} + +// BadRequest denotes the type of this error +func (ieie InvalidEndpointIDError) BadRequest() {} + +// EndpointNotFoundError is returned when the no endpoint +// with the passed endpoint id is found. +type EndpointNotFoundError string + +func (enfe EndpointNotFoundError) Error() string { + return fmt.Sprintf("endpoint not found: %s", string(enfe)) +} + +// NotFound denotes the type of this error +func (enfe EndpointNotFoundError) NotFound() {} + +// NonDefaultBridgeExistError is returned when a non-default +// bridge config is passed but it does not already exist. +type NonDefaultBridgeExistError string + +func (ndbee NonDefaultBridgeExistError) Error() string { + return fmt.Sprintf("bridge device with non default name %s must be created manually", string(ndbee)) +} + +// Forbidden denotes the type of this error +func (ndbee NonDefaultBridgeExistError) Forbidden() {} + +// NonDefaultBridgeNeedsIPError is returned when a non-default +// bridge config is passed but it has no ip configured +type NonDefaultBridgeNeedsIPError string + +func (ndbee NonDefaultBridgeNeedsIPError) Error() string { + return fmt.Sprintf("bridge device with non default name %s must have a valid IP address", string(ndbee)) +} + +// Forbidden denotes the type of this error +func (ndbee NonDefaultBridgeNeedsIPError) Forbidden() {} diff --git a/vendor/github.com/docker/libnetwork/drivers/freebsd/bridge/port_mapping.go b/vendor/github.com/docker/libnetwork/drivers/freebsd/bridge/port_mapping.go new file mode 100644 index 0000000000000..6736c0736f449 --- /dev/null +++ b/vendor/github.com/docker/libnetwork/drivers/freebsd/bridge/port_mapping.go @@ -0,0 +1,235 @@ +// +build freebsd + +package bridge + +import ( + "bytes" + "errors" + "fmt" + "net" + //"os" + "os/exec" + + "github.com/Sirupsen/logrus" + "github.com/docker/libnetwork/types" +) + +var ( + defaultBindingIP = net.IPv4(0, 0, 0, 0) +) + +const ( + maxAllocatePortAttempts = 10 +) + +func addPFRules(epid, bindIntf string, bs []types.PortBinding) { + /* + var id string + + if len(epid) > 12 { + id = epid[:12] + } else { + id = epid + } + + fname := "/var/lib/docker/network/files/pf." + id + + f, err := os.OpenFile(fname, + os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600) + if err != nil { + logrus.Warn("cannot open temp pf file") + return + } + for _, b := range bs { + r := fmt.Sprintf( + "pass in on %s proto %s from any to (%s) "+ + "port %d rdr-to %s port %d\n", bindIntf, + b.Proto.String(), bindIntf, b.HostPort, + b.IP.String(), b.Port) + _, err = f.WriteString(r) + if err != nil { + logrus.Warnf("cannot write firewall rules to %s: %v", fname, err) + } + } + f.Close() + + anchor := fmt.Sprintf("_auto/docker/ep%s", id) + err = exec.Command("/usr/sbin/pfctl", "-a", anchor, "-f", fname).Run() + if err != nil { + logrus.Warnf("failed to add firewall rules: %v", err) + } + os.Remove(fname) + */ +} + +func removePFRules(epid string) { + var id string + + if len(epid) > 12 { + id = epid[:12] + } else { + id = epid + } + + anchor := fmt.Sprintf("_auto/docker/ep%s", id) + err := exec.Command("/usr/sbin/pfctl", "-a", anchor, "-F", "all").Run() + if err != nil { + logrus.Warnf("failed to remove firewall rules: %v", err) + } +} + +func (n *bridgeNetwork) allocatePorts(ep *bridgeEndpoint, bindIntf string, reqDefBindIP net.IP, ulPxyEnabled bool) ([]types.PortBinding, error) { + if ep.extConnConfig == nil || ep.extConnConfig.PortBindings == nil { + return nil, nil + } + + defHostIP := defaultBindingIP + if reqDefBindIP != nil { + defHostIP = reqDefBindIP + } + + bs, err := n.allocatePortsInternal(ep.extConnConfig.PortBindings, bindIntf, ep.addr.IP, defHostIP, ulPxyEnabled) + if err != nil { + return nil, err + } + + // Add PF rules for port bindings, if any + if len(bs) > 0 { + addPFRules(ep.id, bindIntf, bs) + } + + return bs, err +} + +func (n *bridgeNetwork) allocatePortsInternal(bindings []types.PortBinding, bindIntf string, containerIP, defHostIP net.IP, ulPxyEnabled bool) ([]types.PortBinding, error) { + bs := make([]types.PortBinding, 0, len(bindings)) + for _, c := range bindings { + b := c.GetCopy() + if err := n.allocatePort(&b, containerIP, defHostIP); err != nil { + // On allocation failure,release previously + // allocated ports. On cleanup error, just log + // a warning message + if cuErr := n.releasePortsInternal(bs); cuErr != nil { + logrus.Warnf("Upon allocation failure "+ + "for %v, failed to clear previously "+ + "allocated port bindings: %v", b, cuErr) + } + return nil, err + } + bs = append(bs, b) + } + return bs, nil +} + +func (n *bridgeNetwork) allocatePort(bnd *types.PortBinding, containerIP, defHostIP net.IP) error { + var ( + host net.Addr + err error + ) + + // Store the container interface address in the operational binding + bnd.IP = containerIP + + // Adjust the host address in the operational binding + if len(bnd.HostIP) == 0 { + bnd.HostIP = defHostIP + } + + // Adjust HostPortEnd if this is not a range. + if bnd.HostPortEnd == 0 { + bnd.HostPortEnd = bnd.HostPort + } + + /* TODO + // Construct the container side transport address + container, err := bnd.ContainerAddr() + if err != nil { + return err + } + */ + // Try up to maxAllocatePortAttempts times to get a port that's + // not already allocated. + for i := 0; i < maxAllocatePortAttempts; i++ { + /* + TODO + if host, err = n.portMapper.MapRange(container, bnd.HostIP, + int(bnd.HostPort), int(bnd.HostPortEnd), false); err == nil { + break + } + */ + // There is no point in immediately retrying to map an + // explicitly chosen port. + if bnd.HostPort != 0 { + logrus.Warnf( + "Failed to allocate and map port %d-%d: %s", + bnd.HostPort, bnd.HostPortEnd, err) + break + } + logrus.Warnf("Failed to allocate and map port: %s, retry: %d", + err, i+1) + } + if err != nil { + return err + } + + // Save the host port (regardless it was or not specified in the + // binding) + switch netAddr := host.(type) { + case *net.TCPAddr: + bnd.HostPort = uint16(host.(*net.TCPAddr).Port) + return nil + case *net.UDPAddr: + bnd.HostPort = uint16(host.(*net.UDPAddr).Port) + return nil + default: + // For completeness + return ErrUnsupportedAddressType(fmt.Sprintf("%T", netAddr)) + } +} + +func (n *bridgeNetwork) releasePorts(ep *bridgeEndpoint) error { + err := n.releasePortsInternal(ep.portMapping) + if err != nil { + return nil + } + + // remove rules if there are any port mappings + if len(ep.portMapping) > 0 { + removePFRules(ep.id) + } + + return nil + +} + +func (n *bridgeNetwork) releasePortsInternal(bindings []types.PortBinding) error { + var errorBuf bytes.Buffer + + // Attempt to release all port bindings, do not stop on failure + for _, m := range bindings { + if err := n.releasePort(m); err != nil { + errorBuf.WriteString( + fmt.Sprintf( + "\ncould not release %v because of %v", + m, err)) + } + } + + if errorBuf.Len() != 0 { + return errors.New(errorBuf.String()) + } + return nil +} + +func (n *bridgeNetwork) releasePort(bnd types.PortBinding) error { + /* + // Construct the host side transport address + host, err := bnd.HostAddr() + if err != nil { + return err + } + // TODO + return n.portMapper.Unmap(host) + */ + return nil +} diff --git a/vendor/github.com/docker/libnetwork/drivers_freebsd.go b/vendor/github.com/docker/libnetwork/drivers_freebsd.go index d117c25780c23..8dca2c3418225 100644 --- a/vendor/github.com/docker/libnetwork/drivers_freebsd.go +++ b/vendor/github.com/docker/libnetwork/drivers_freebsd.go @@ -1,12 +1,14 @@ package libnetwork import ( + "github.com/docker/libnetwork/drivers/freebsd/bridge" "github.com/docker/libnetwork/drivers/null" "github.com/docker/libnetwork/drivers/remote" ) func getInitializers(experimental bool) []initializer { return []initializer{ + {bridge.Init, "bridge"}, {null.Init, "null"}, {remote.Init, "remote"}, } diff --git a/vendor/github.com/docker/libnetwork/iptables/conntrack.go b/vendor/github.com/docker/libnetwork/iptables/conntrack.go index 5731c53c0403e..5784fcb18f453 100644 --- a/vendor/github.com/docker/libnetwork/iptables/conntrack.go +++ b/vendor/github.com/docker/libnetwork/iptables/conntrack.go @@ -1,3 +1,5 @@ +// +build !freebsd + package iptables import ( diff --git a/vendor/github.com/docker/libnetwork/netutils/utils_freebsd.go b/vendor/github.com/docker/libnetwork/netutils/utils_freebsd.go index 02bcd32aa8e23..393c67c889163 100644 --- a/vendor/github.com/docker/libnetwork/netutils/utils_freebsd.go +++ b/vendor/github.com/docker/libnetwork/netutils/utils_freebsd.go @@ -1,7 +1,10 @@ package netutils import ( + "fmt" "net" + "os/exec" + "strings" "github.com/docker/libnetwork/types" ) @@ -19,5 +22,32 @@ func ElectInterfaceAddresses(name string) ([]*net.IPNet, []*net.IPNet, error) { // FindAvailableNetwork returns a network from the passed list which does not // overlap with existing interfaces in the system func FindAvailableNetwork(list []*net.IPNet) (*net.IPNet, error) { - return nil, types.NotImplementedErrorf("not supported on freebsd") + for _, avail := range list { + cidr := strings.Split(avail.String(), "/") + ipitems := strings.Split(cidr[0], ".") + ip := ipitems[0] + "." + + ipitems[1] + "." + + ipitems[2] + "." + "1" + + out, err := exec.Command("/sbin/route", "get", ip).Output() + if err != nil { + fmt.Println("failed to run route get command") + return nil, err + } + lines := strings.Split(string(out), "\n") + for _, l := range lines { + s := strings.Split(string(l), ":") + if len(s) == 2 { + k, v := s[0], strings.TrimSpace(s[1]) + if k == "destination" { + if v == "default" { + return avail, nil + } + break + } + } + } + } + return nil, fmt.Errorf("no available network") + //types.NotImplementedErrorf("not supported on freebsd") } diff --git a/vendor/github.com/docker/libnetwork/ns/init_unspecified.go b/vendor/github.com/docker/libnetwork/ns/init_unspecified.go new file mode 100644 index 0000000000000..216d969761945 --- /dev/null +++ b/vendor/github.com/docker/libnetwork/ns/init_unspecified.go @@ -0,0 +1 @@ +package ns diff --git a/vendor/github.com/docker/libnetwork/portallocator/portallocator_freebsd.go b/vendor/github.com/docker/libnetwork/portallocator/portallocator_freebsd.go new file mode 100644 index 0000000000000..97d7fbb49d8c3 --- /dev/null +++ b/vendor/github.com/docker/libnetwork/portallocator/portallocator_freebsd.go @@ -0,0 +1,42 @@ +package portallocator + +import ( + "bytes" + "fmt" + "os/exec" +) + +func getDynamicPortRange() (start int, end int, err error) { + portRangeKernelSysctl := []string{"net.inet.ip.portrange.hifirst", "net.ip.portrange.hilast"} + portRangeFallback := fmt.Sprintf("using fallback port range %d-%d", DefaultPortRangeStart, DefaultPortRangeEnd) + portRangeLowCmd := exec.Command("/sbin/sysctl", portRangeKernelSysctl[0]) + var portRangeLowOut bytes.Buffer + portRangeLowCmd.Stdout = &portRangeLowOut + cmdErr := portRangeLowCmd.Run() + if cmdErr != nil { + return 0, 0, fmt.Errorf("port allocator - sysctl net.inet.ip.portrange.hifirst failed - %s: %v", portRangeFallback, err) + } + n, err := fmt.Sscanf(portRangeLowOut.String(), "%d", &start) + if n != 1 || 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 start from %s - %s: %v", portRangeLowOut.String(), portRangeFallback, err) + } + + portRangeHighCmd := exec.Command("/sbin/sysctl", portRangeKernelSysctl[1]) + var portRangeHighOut bytes.Buffer + portRangeHighCmd.Stdout = &portRangeHighOut + cmdErr = portRangeHighCmd.Run() + if cmdErr != nil { + return 0, 0, fmt.Errorf("port allocator - sysctl net.inet.ip.portrange.hilast failed - %s: %v", portRangeFallback, err) + } + n, err = fmt.Sscanf(portRangeHighOut.String(), "%d", &end) + if n != 1 || 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 end from %s - %s: %v", portRangeHighOut.String(), portRangeFallback, err) + } + return start, end, nil +} diff --git a/vendor/github.com/docker/libnetwork/portmapper/proxy_solaris.go b/vendor/github.com/docker/libnetwork/portmapper/proxy_unix.go similarity index 94% rename from vendor/github.com/docker/libnetwork/portmapper/proxy_solaris.go rename to vendor/github.com/docker/libnetwork/portmapper/proxy_unix.go index dc70b5edcebc0..5e7a51fcda92a 100644 --- a/vendor/github.com/docker/libnetwork/portmapper/proxy_solaris.go +++ b/vendor/github.com/docker/libnetwork/portmapper/proxy_unix.go @@ -1,3 +1,5 @@ +// +build solaris,freebsd +build !linux + package portmapper import ( diff --git a/vendor/github.com/docker/libnetwork/resolver_freebsd.go b/vendor/github.com/docker/libnetwork/resolver_freebsd.go new file mode 100644 index 0000000000000..b9c663f984e92 --- /dev/null +++ b/vendor/github.com/docker/libnetwork/resolver_freebsd.go @@ -0,0 +1,6 @@ +package libnetwork + + +func (r *resolver) setupIPTable() error { + return nil +} diff --git a/vendor/github.com/docker/libnetwork/resolver_unix.go b/vendor/github.com/docker/libnetwork/resolver_unix.go index 5fcc6b9fa9cf3..53cdc0bf7f62e 100644 --- a/vendor/github.com/docker/libnetwork/resolver_unix.go +++ b/vendor/github.com/docker/libnetwork/resolver_unix.go @@ -1,4 +1,4 @@ -// +build !windows +// +build !windows,!freebsd package libnetwork diff --git a/vendor/github.com/docker/libnetwork/service_unsupported.go b/vendor/github.com/docker/libnetwork/service_unsupported.go index 37b9828191808..5f0d38d2cb52c 100644 --- a/vendor/github.com/docker/libnetwork/service_unsupported.go +++ b/vendor/github.com/docker/libnetwork/service_unsupported.go @@ -18,6 +18,26 @@ func (c *controller) rmServiceBinding(name, sid, nid, eid string, vip net.IP, in return fmt.Errorf("not supported") } +func (c *controller) getLBIndex(sid, nid string, ingressPorts []*PortConfig) int { + skey := serviceKey{ + id: sid, + ports: portConfigs(ingressPorts).String(), + } + c.Lock() + s, ok := c.serviceBindings[skey] + c.Unlock() + + if !ok { + return 0 + } + + s.Lock() + lb := s.loadBalancers[nid] + s.Unlock() + + return int(lb.fwMark) +} + func (sb *sandbox) populateLoadbalancers(ep *endpoint) { } diff --git a/vendor/github.com/godbus/dbus/transport_unix.go b/vendor/github.com/godbus/dbus/transport_unix.go index a1d00cbc1247f..064582a860481 100644 --- a/vendor/github.com/godbus/dbus/transport_unix.go +++ b/vendor/github.com/godbus/dbus/transport_unix.go @@ -194,3 +194,8 @@ func (t *unixTransport) SendMessage(msg *Message) error { func (t *unixTransport) SupportsUnixFDs() bool { return true } + +func (t *unixTransport) SendNullByte() error { + _, err := t.Write([]byte{0}) + return err +} diff --git a/vendor/github.com/tonistiigi/fifo/handle_nolinux.go b/vendor/github.com/tonistiigi/fifo/handle_nolinux.go index d9648d8bfa26d..5dbc716be18e9 100644 --- a/vendor/github.com/tonistiigi/fifo/handle_nolinux.go +++ b/vendor/github.com/tonistiigi/fifo/handle_nolinux.go @@ -11,7 +11,7 @@ import ( type handle struct { fn string dev uint64 - ino uint64 + ino uint32 } func getHandle(fn string) (*handle, error) { diff --git a/vendor/github.com/vishvananda/netlink/filter.go b/vendor/github.com/vishvananda/netlink/filter.go index 938b28b0b03bd..a6b7b637c77be 100644 --- a/vendor/github.com/vishvananda/netlink/filter.go +++ b/vendor/github.com/vishvananda/netlink/filter.go @@ -1,3 +1,5 @@ +// +build linux + package netlink import ( diff --git a/vendor/github.com/vishvananda/netlink/handle_unspecified.go b/vendor/github.com/vishvananda/netlink/handle_unspecified.go index 32cf022732de4..2d33aabb3adb9 100644 --- a/vendor/github.com/vishvananda/netlink/handle_unspecified.go +++ b/vendor/github.com/vishvananda/netlink/handle_unspecified.go @@ -181,18 +181,6 @@ func (h *Handle) ClassList(link Link, parent uint32) ([]Class, error) { return nil, ErrNotImplemented } -func (h *Handle) FilterDel(filter Filter) error { - return ErrNotImplemented -} - -func (h *Handle) FilterAdd(filter Filter) error { - return ErrNotImplemented -} - -func (h *Handle) FilterList(link Link, parent uint32) ([]Filter, error) { - return nil, ErrNotImplemented -} - func (h *Handle) NeighAdd(neigh *Neigh) error { return ErrNotImplemented }