Index: sysutils/docker-freebsd/Makefile =================================================================== --- sysutils/docker-freebsd/Makefile +++ sysutils/docker-freebsd/Makefile @@ -1,41 +1,28 @@ -# Created by: kmoore@FreeBSD.org # $FreeBSD$ PORTNAME= docker-freebsd -PORTVERSION= 20150625 -PORTREVISION= 2 +DISTVERSIONPREFIX= v +DISTVERSION= 19.03.13 CATEGORIES= sysutils MAINTAINER= joneum@FreeBSD.org -COMMENT= Docker containment system +COMMENT= Docker Engine based on moby LICENSE= APACHE20 LICENSE_FILE= ${WRKSRC}/LICENSE -BROKEN= fails to build -DEPRECATED= Broken for more than 9 months -EXPIRATION_DATE= 2020-01-10 +BUILD_DEPENDS= bash:shells/bash -BUILD_DEPENDS= bash:shells/bash \ - sqlite3:databases/sqlite3 -RUN_DEPENDS= bash:shells/bash \ - sqlite3:databases/sqlite3 +USES= go -USES= go:run - USE_GITHUB= yes -GH_ACCOUNT= kvasdopil -GH_PROJECT= docker -GH_TAGNAME= 582db78 +GH_ACCOUNT= moby +GH_PROJECT= moby +GH_SUBDIR= src/github.com/docker/docker -PLIST_FILES= bin/docker -USE_RC_SUBR= docker +GO_TARGET= ./cmd/dockerd do-build: - @cd ${WRKSRC} && ${SETENV} ${MAKE_ENV} AUTO_GOPATH=1 DOCKER_GITCOMMIT=${GH_TAGNAME} ./hack/make.sh binary - -do-install: - @${MKDIR} ${STAGEDIR}${PREFIX}/bin - ${INSTALL_PROGRAM} ${WRKSRC}/bundles/latest/binary/docker ${STAGEDIR}${PREFIX}/bin/ + @cd ${GO_WRKSRC} && export DOCKER_GITCOMMIT=${GH_TAGNAME} && ${SETENV} ${GO_ENV} ./hack/make.sh binary .include Index: sysutils/docker-freebsd/distinfo =================================================================== --- sysutils/docker-freebsd/distinfo +++ sysutils/docker-freebsd/distinfo @@ -1,2 +1,3 @@ -SHA256 (kvasdopil-docker-20150625-582db78_GH0.tar.gz) = a750d344af4af3d30b1a3373f382ab597a2a7aa4a0bb5c22d650d0c5cc9ac506 -SIZE (kvasdopil-docker-20150625-582db78_GH0.tar.gz) = 7292884 +TIMESTAMP = 1602853279 +SHA256 (moby-moby-v19.03.13_GH0.tar.gz) = f43331fef1d24e31f43392fc1fed72b48fc17fd432d341d6eb1f68ca11383406 +SIZE (moby-moby-v19.03.13_GH0.tar.gz) = 10406691 Index: sysutils/docker-freebsd/files/patch-FreeBSD.adoc =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-FreeBSD.adoc @@ -0,0 +1,268 @@ +--- FreeBSD.adoc.orig 2020-09-04 14:57:27 UTC ++++ FreeBSD.adoc +@@ -0,0 +1,265 @@ ++= Docker on FreeBSD ++ ++The FreeBSD port of Docker requires ZFS and FreeBSD 11.1-RELEASE or greater. ++ ++[NOTE] ++==== ++The current `freebsd-compat` branch is based off of the `v17.05.0-ce` tag from ++upstream. ++==== ++ ++== Participate ++ ++ ++Chat for this effort can be found in the `#freebsd-docker` chanenl on ++link:http://freenode.net[Freenode]. ++ ++== Running ++ ++[[prereqs]] ++== Prerequisites ++ ++Please ensure the following packages are installed in order to build from ++source: ++ ++* `go` ++* `git` ++* `bash` ++* `ca_root_nss` ++* `libepoll-shim` ++ ++[source,bash] ++---- ++sudo pkg install ca_root_nss bash git go libepoll-shim ++---- ++ ++ ++[[zfs]] ++=== Setting up ZFS ++ ++In order to provide storage for containers running on FreeBSD, Docker relies on ++ZFS underneath the hood. This means the FreeBSD system must have ZFS loaded, ++and active. ++ ++==== Systems without ZFS-based disks ++ ++[source,bash] ++---- ++kldload zfs && \ ++ dd if=/dev/zero of=/usr/local/dockerfs bs=1024K count=4000 && \ ++ zpool create -f zroot /usr/local/dockerfs && \ ++ zfs create -o mountpoint=/usr/docker zroot/docker ++---- ++ ++==== Systems with ZFS-based disks ++ ++[source,bash] ++---- ++zfs create -o mountpoint=/usr/docker zroot/docker ++---- ++ ++ ++ ++[[networking]] ++=== Setting up networking ++ ++ ++[[pf]] ++==== Setting up Packet Filer ++ ++In order to provide networking for containers, Docker must have access to ++Packet Filter (`pf`). ++ ++* `sudo kldload pf` ++ ++ ++The example below provides a bridged network for Docker. If you installed ++FreeBSD/Docker via the `sysutils/docker-freebsd` port, this will already be ++configured for you. ++ ++[source,bash] ++---- ++echo "nat on {yout-external-interface} from 172.17.0.0/16 to any -> ({your-external-interface})" > /etc/pf.conf ++pfctl -f /etc/pf.conf ++pfctl -e ++---- ++ ++ ++=== Progress ++ ++.Features ++|=== ++| Feature | Status ++ ++| Image loading ++| :white_check_mark: ++ ++| Container creationg ++| :white_check_mark: ++ ++| Container start/stop ++| :white_check_mark: ++ ++| Shared Networking ++| partial support ++ ++| Port forwarding ++| :white_check_mark: ++ ++| Volumes ++| :x: ++ ++| Links ++| :x: ++ ++| Virtual networking ++| :x: ++ ++| Limits ++| :x: ++ ++|=== ++ ++.Commands ++|=== ++| Command | Status ++ ++| attach ++| :white_check_mark: ++ ++| build ++| ++ ++| commit ++| :white_check_mark: ++ ++| cp ++| :white_check_mark: ++ ++| create ++| :white_check_mark: ++ ++| diff ++| :white_check_mark: ++ ++| events ++| :white_check_mark: ++ ++| exec ++| :white_check_mark: ++ ++| export ++| :white_check_mark: ++ ++| history ++| :white_check_mark: ++ ++| images ++| :white_check_mark: ++ ++| import ++| :white_check_mark: ++ ++| info ++| :bug: ++ ++| inspect ++| :white_check_mark: ++ ++| kill ++| :white_check_mark: ++ ++| load ++| :bug: ++ ++| login ++| :white_check_mark: ++ ++| logout ++| :white_check_mark: ++ ++| logs ++| :white_check_mark: ++ ++| pause ++| :x: ++ ++| port ++| :white_check_mark: ++ ++| ps ++| :white_check_mark: ++ ++| pull ++| :white_check_mark: ++ ++| push ++| :white_check_mark: ++ ++| rename ++| :white_check_mark: ++ ++| restart ++| :white_check_mark: ++ ++| rm ++| :white_check_mark: ++ ++| rmi ++| :white_check_mark: ++ ++| run ++| :white_check_mark: ++ ++| save ++| :white_check_mark: ++ ++| search ++| :white_check_mark: ++ ++| start ++| :white_check_mark: ++ ++| stats ++| :bug: ++ ++| stop ++| :white_check_mark: ++ ++| tag ++| :white_check_mark: ++ ++| top ++| :white_check_mark: ++ ++| unpause ++| :x: ++ ++| version ++| :white_check_mark: ++ ++| wait ++| :white_check_mark: ++ ++|=== ++ ++== Hacking ++ ++To build on 11.1-RELEASE, assuming the <> have been installed: ++ ++[source,bash] ++---- ++gmake -f Makefile.freebsd ++---- ++ ++This should create the `docker` and `dockerd` executables in ++`./bundles/latest/`. Please ensure that <> and <> are set up properly. ++ ++=== References ++ ++Below are a list of useful references for understanding both Docker and ++Docker/FreeBSD. ++ ++* link:https://blog.docker.com/2017/08/what-is-containerd-runtime/[What is containerd]. ++* link:https://docs.docker.com/engine/userguide/storagedriver/zfs-driver/[Using the ZFS storage driver]. Index: sysutils/docker-freebsd/files/patch-Makefile.freebsd =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-Makefile.freebsd @@ -0,0 +1,71 @@ +--- Makefile.freebsd.orig 2020-09-04 14:57:27 UTC ++++ Makefile.freebsd +@@ -0,0 +1,68 @@ ++# This file exists to support the non-Docker-based build requirements for ++# FreeBSD/Docker ++# ++# Hacking GOPATH to take the first directory in the list and use that to clone ++# our dependencies ++export GO_PATH=$(firstword $(subst :, ,$(GOPATH))) ++export AUTO_GO_PATH=1 ++export DEST_DIR=$(PWD)/bundles/bin ++export RUNC_PATH="${GO_PATH}/src/github.com/opencontainers/runc" ++export CONTAINERD_PATH="${GO_PATH}/src/github.com/containerd/containerd" ++export CONTAINERD_REFSPEC=freebsd-compat-0.2 ++export LIBNETWORK_PATH="${GO_PATH}/src/github.com/docker/libnetwork" ++export TINI_PATH="${GO_PATH}/src/tini" ++ ++all: binary ++ ++binary: $(DEST_DIR)/docker-containerd $(DEST_DIR)/docker-proxy ++ ./hack/make.sh binary ++ # Copy into bundles/bin for packaging ++ for f in bundles/latest/*/*; do \ ++ [ -L "$$f" ] || continue; \ ++ cp -f "$$(readlink -f $$f)" "$(DEST_DIR)/$${f##*/}"; \ ++ done ++ ++$(DEST_DIR)/docker-containerd: prepare ++ if [ ! -d $(CONTAINERD_PATH) ]; then \ ++ git clone https://github.com/freebsd-docker/containerd.git $(CONTAINERD_PATH) && \ ++ cd $(CONTAINERD_PATH) && \ ++ git checkout $(CONTAINERD_REFSPEC); \ ++ fi; ++ cd $(CONTAINERD_PATH) && \ ++ $(MAKE) && \ ++ cp bin/containerd $(DEST_DIR)/docker-containerd && \ ++ cp bin/containerd-shim $(DEST_DIR)/docker-containerd-shim && \ ++ cp bin/ctr $(DEST_DIR)/docker-containerd-ctr ++ ++$(DEST_DIR)/docker-proxy: prepare ++ if [ ! -d $(LIBNETWORK_PATH) ]; then \ ++ git clone https://github.com/freebsd-docker/libnetwork.git $(LIBNETWORK_PATH); \ ++ fi; ++ cd $(LIBNETWORK_PATH) && \ ++ go build -o $(DEST_DIR)/docker-proxy github.com/docker/libnetwork/cmd/proxy ++ ++ ++runc: ++ if [ ! -d $(RUNC_PATH) ]; then \ ++ git clone https://github.com/freebsd-docker/runc.git $(RUNC_PATH); \ ++ fi; ++ cd $(RUNC_PATH) && \ ++ $(MAKE) ++ ++tini: check-depends ++ if [ ! -d $(TINI_PATH) ]; then \ ++ git clone https://github.com/krallin/tini.git $(TINI_PATH); \ ++ fi; ++ cd $(TINI_PATH) && \ ++ cmake . && \ ++ $(MAKE) tini-static ++ ++check-depends: ++ echo ">> Verify that you have CMake installed" ++ ++prepare: bundles/bin ++ ++bundles/bin: ++ mkdir -p bundles/bin ++ ++.PHONY: check-depends prepare all binary Index: sysutils/docker-freebsd/files/patch-builder_dockerfile_internals__freebsd.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-builder_dockerfile_internals__freebsd.go @@ -0,0 +1,95 @@ +Fix build on FreeBSD by copying linux implementation: + +builder/dockerfile/internals.go:193:19: undefined: parseChownFlag + +--- builder/dockerfile/internals_freebsd.go.orig 2019-03-08 14:02:51 UTC ++++ builder/dockerfile/internals_freebsd.go +@@ -0,0 +1,88 @@ ++package dockerfile // import "github.com/docker/docker/builder/dockerfile" ++ ++import ( ++ "path/filepath" ++ "strconv" ++ "strings" ++ ++ "github.com/docker/docker/pkg/idtools" ++ "github.com/docker/docker/pkg/symlink" ++ lcUser "github.com/opencontainers/runc/libcontainer/user" ++ "github.com/pkg/errors" ++) ++ ++func parseChownFlag(builder *Builder, state *dispatchState, chown, ctrRootPath string, identityMapping *idtools.IdentityMapping) (idtools.Identity, error) { ++ var userStr, grpStr string ++ parts := strings.Split(chown, ":") ++ if len(parts) > 2 { ++ return idtools.Identity{}, errors.New("invalid chown string format: " + chown) ++ } ++ if len(parts) == 1 { ++ // if no group specified, use the user spec as group as well ++ userStr, grpStr = parts[0], parts[0] ++ } else { ++ userStr, grpStr = parts[0], parts[1] ++ } ++ ++ passwdPath, err := symlink.FollowSymlinkInScope(filepath.Join(ctrRootPath, "etc", "passwd"), ctrRootPath) ++ if err != nil { ++ return idtools.Identity{}, errors.Wrapf(err, "can't resolve /etc/passwd path in container rootfs") ++ } ++ groupPath, err := symlink.FollowSymlinkInScope(filepath.Join(ctrRootPath, "etc", "group"), ctrRootPath) ++ if err != nil { ++ return idtools.Identity{}, errors.Wrapf(err, "can't resolve /etc/group path in container rootfs") ++ } ++ uid, err := lookupUser(userStr, passwdPath) ++ if err != nil { ++ return idtools.Identity{}, errors.Wrapf(err, "can't find uid for user "+userStr) ++ } ++ gid, err := lookupGroup(grpStr, groupPath) ++ if err != nil { ++ return idtools.Identity{}, errors.Wrapf(err, "can't find gid for group "+grpStr) ++ } ++ ++ // convert as necessary because of user namespaces ++ chownPair, err := identityMapping.ToHost(idtools.Identity{UID: uid, GID: gid}) ++ if err != nil { ++ return idtools.Identity{}, errors.Wrapf(err, "unable to convert uid/gid to host mapping") ++ } ++ return chownPair, nil ++} ++ ++func lookupUser(userStr, filepath string) (int, error) { ++ // if the string is actually a uid integer, parse to int and return ++ // as we don't need to translate with the help of files ++ uid, err := strconv.Atoi(userStr) ++ if err == nil { ++ return uid, nil ++ } ++ users, err := lcUser.ParsePasswdFileFilter(filepath, func(u lcUser.User) bool { ++ return u.Name == userStr ++ }) ++ if err != nil { ++ return 0, err ++ } ++ if len(users) == 0 { ++ return 0, errors.New("no such user: " + userStr) ++ } ++ return users[0].Uid, nil ++} ++ ++func lookupGroup(groupStr, filepath string) (int, error) { ++ // if the string is actually a gid integer, parse to int and return ++ // as we don't need to translate with the help of files ++ gid, err := strconv.Atoi(groupStr) ++ if err == nil { ++ return gid, nil ++ } ++ groups, err := lcUser.ParseGroupFileFilter(filepath, func(g lcUser.Group) bool { ++ return g.Name == groupStr ++ }) ++ if err != nil { ++ return 0, err ++ } ++ if len(groups) == 0 { ++ return 0, errors.New("no such group: " + groupStr) ++ } ++ return groups[0].Gid, nil ++} Index: sysutils/docker-freebsd/files/patch-daemon_container__freebsd.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-daemon_container__freebsd.go @@ -0,0 +1,12 @@ +--- daemon/container_freebsd.go.orig 2020-09-18 09:01:00 UTC ++++ 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 ++} Index: sysutils/docker-freebsd/files/patch-daemon_container__operations.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-daemon_container__operations.go @@ -0,0 +1,33 @@ +--- daemon/container_operations.go.orig 2020-09-04 14:54:50 UTC ++++ daemon/container_operations.go +@@ -68,7 +68,7 @@ func (daemon *Daemon) buildSandboxOptions(container *c + sboxOptions = append(sboxOptions, libnetwork.OptionUseExternalKey()) + } + +- if err = daemon.setupPathsAndSandboxOptions(container, &sboxOptions); err != nil { ++ if err = setupPathsAndSandboxOptions(container, &sboxOptions); err != nil { + return nil, err + } + +@@ -618,9 +618,9 @@ func validateNetworkingConfig(n libnetwork.Network, ep + if hasUserDefinedIPAddress(epConfig.IPAMConfig) && !enableIPOnPredefinedNetwork() { + return runconfig.ErrUnsupportedNetworkAndIP + } +- if len(epConfig.Aliases) > 0 && !serviceDiscoveryOnDefaultNetwork() { +- return runconfig.ErrUnsupportedNetworkAndAlias +- } ++ // if len(epConfig.Aliases) > 0 && !serviceDiscoveryOnDefaultNetwork() { ++ // return runconfig.ErrUnsupportedNetworkAndAlias ++ // } + } + if !hasUserDefinedIPAddress(epConfig.IPAMConfig) { + return nil +@@ -935,7 +935,7 @@ func (daemon *Daemon) initializeNetworking(container * + return err + } + +- err = daemon.initializeNetworkingPaths(container, nc) ++ err = initializeNetworkingPaths(container, nc) + if err != nil { + return err + } Index: sysutils/docker-freebsd/files/patch-daemon_container__operations__freebsd.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-daemon_container__operations__freebsd.go @@ -0,0 +1,48 @@ +--- daemon/container_operations_freebsd.go.orig 2020-09-04 14:57:27 UTC ++++ daemon/container_operations_freebsd.go +@@ -0,0 +1,45 @@ ++package daemon // import "github.com/docker/docker/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) error { ++ return nil ++} Index: sysutils/docker-freebsd/files/patch-daemon_container__operations__linux.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-daemon_container__operations__linux.go @@ -0,0 +1,418 @@ +--- daemon/container_operations_linux.go.orig 2020-09-04 14:57:27 UTC ++++ daemon/container_operations_linux.go +@@ -0,0 +1,415 @@ ++// +build linux ++ ++package daemon // import "github.com/docker/docker/daemon" ++ ++import ( ++ "context" ++ "fmt" ++ "io/ioutil" ++ "os" ++ "path/filepath" ++ "strconv" ++ "time" ++ ++ "github.com/docker/docker/container" ++ "github.com/docker/docker/daemon/links" ++ "github.com/docker/docker/errdefs" ++ "github.com/docker/docker/pkg/idtools" ++ "github.com/docker/docker/pkg/mount" ++ "github.com/docker/docker/pkg/stringid" ++ "github.com/docker/docker/runconfig" ++ "github.com/docker/libnetwork" ++ "github.com/opencontainers/selinux/go-selinux/label" ++ "github.com/pkg/errors" ++ "github.com/sirupsen/logrus" ++ "golang.org/x/sys/unix" ++) ++ ++func (daemon *Daemon) setupLinkedContainers(container *container.Container) ([]string, error) { ++ var env []string ++ children := daemon.children(container) ++ ++ bridgeSettings := container.NetworkSettings.Networks[runconfig.DefaultDaemonNetworkMode().NetworkName()] ++ if bridgeSettings == nil || bridgeSettings.EndpointSettings == nil { ++ return nil, nil ++ } ++ ++ for linkAlias, child := range children { ++ if !child.IsRunning() { ++ return nil, fmt.Errorf("Cannot link to a non running container: %s AS %s", child.Name, linkAlias) ++ } ++ ++ childBridgeSettings := child.NetworkSettings.Networks[runconfig.DefaultDaemonNetworkMode().NetworkName()] ++ if childBridgeSettings == nil || childBridgeSettings.EndpointSettings == nil { ++ return nil, fmt.Errorf("container %s not attached to default bridge network", child.ID) ++ } ++ ++ link := links.NewLink( ++ bridgeSettings.IPAddress, ++ childBridgeSettings.IPAddress, ++ linkAlias, ++ child.Config.Env, ++ child.Config.ExposedPorts, ++ ) ++ ++ env = append(env, link.ToEnv()...) ++ } ++ ++ return env, nil ++} ++ ++func (daemon *Daemon) getIpcContainer(id string) (*container.Container, error) { ++ errMsg := "can't join IPC of container " + id ++ // Check the container exists ++ container, err := daemon.GetContainer(id) ++ if err != nil { ++ return nil, errors.Wrap(err, errMsg) ++ } ++ // Check the container is running and not restarting ++ if err := daemon.checkContainer(container, containerIsRunning, containerIsNotRestarting); err != nil { ++ return nil, errors.Wrap(err, errMsg) ++ } ++ // Check the container ipc is shareable ++ if st, err := os.Stat(container.ShmPath); err != nil || !st.IsDir() { ++ if err == nil || os.IsNotExist(err) { ++ return nil, errors.New(errMsg + ": non-shareable IPC (hint: use IpcMode:shareable for the donor container)") ++ } ++ // stat() failed? ++ return nil, errors.Wrap(err, errMsg+": unexpected error from stat "+container.ShmPath) ++ } ++ ++ return container, nil ++} ++ ++func (daemon *Daemon) getPidContainer(container *container.Container) (*container.Container, error) { ++ containerID := container.HostConfig.PidMode.Container() ++ container, err := daemon.GetContainer(containerID) ++ if err != nil { ++ return nil, errors.Wrapf(err, "cannot join PID of a non running container: %s", containerID) ++ } ++ return container, daemon.checkContainer(container, containerIsRunning, containerIsNotRestarting) ++} ++ ++func containerIsRunning(c *container.Container) error { ++ if !c.IsRunning() { ++ return errdefs.Conflict(errors.Errorf("container %s is not running", c.ID)) ++ } ++ return nil ++} ++ ++func containerIsNotRestarting(c *container.Container) error { ++ if c.IsRestarting() { ++ return errContainerIsRestarting(c.ID) ++ } ++ return nil ++} ++ ++func (daemon *Daemon) setupIpcDirs(c *container.Container) error { ++ ipcMode := c.HostConfig.IpcMode ++ ++ switch { ++ case ipcMode.IsContainer(): ++ ic, err := daemon.getIpcContainer(ipcMode.Container()) ++ if err != nil { ++ return err ++ } ++ c.ShmPath = ic.ShmPath ++ ++ case ipcMode.IsHost(): ++ if _, err := os.Stat("/dev/shm"); err != nil { ++ return fmt.Errorf("/dev/shm is not mounted, but must be for --ipc=host") ++ } ++ c.ShmPath = "/dev/shm" ++ ++ case ipcMode.IsPrivate(), ipcMode.IsNone(): ++ // c.ShmPath will/should not be used, so make it empty. ++ // Container's /dev/shm mount comes from OCI spec. ++ c.ShmPath = "" ++ ++ case ipcMode.IsEmpty(): ++ // A container was created by an older version of the daemon. ++ // The default behavior used to be what is now called "shareable". ++ fallthrough ++ ++ case ipcMode.IsShareable(): ++ rootIDs := daemon.idMapping.RootPair() ++ if !c.HasMountFor("/dev/shm") { ++ shmPath, err := c.ShmResourcePath() ++ if err != nil { ++ return err ++ } ++ ++ if err := idtools.MkdirAllAndChown(shmPath, 0700, rootIDs); err != nil { ++ return err ++ } ++ ++ shmproperty := "mode=1777,size=" + strconv.FormatInt(c.HostConfig.ShmSize, 10) ++ if err := unix.Mount("shm", shmPath, "tmpfs", uintptr(unix.MS_NOEXEC|unix.MS_NOSUID|unix.MS_NODEV), label.FormatMountLabel(shmproperty, c.GetMountLabel())); err != nil { ++ return fmt.Errorf("mounting shm tmpfs: %s", err) ++ } ++ if err := os.Chown(shmPath, rootIDs.UID, rootIDs.GID); err != nil { ++ return err ++ } ++ c.ShmPath = shmPath ++ } ++ ++ default: ++ return fmt.Errorf("invalid IPC mode: %v", ipcMode) ++ } ++ ++ return nil ++} ++ ++func (daemon *Daemon) setupSecretDir(c *container.Container) (setupErr error) { ++ if len(c.SecretReferences) == 0 && len(c.ConfigReferences) == 0 { ++ return nil ++ } ++ ++ if err := daemon.createSecretsDir(c); err != nil { ++ return err ++ } ++ defer func() { ++ if setupErr != nil { ++ daemon.cleanupSecretDir(c) ++ } ++ }() ++ ++ if c.DependencyStore == nil { ++ return fmt.Errorf("secret store is not initialized") ++ } ++ ++ // retrieve possible remapped range start for root UID, GID ++ rootIDs := daemon.idMapping.RootPair() ++ ++ for _, s := range c.SecretReferences { ++ // TODO (ehazlett): use type switch when more are supported ++ if s.File == nil { ++ logrus.Error("secret target type is not a file target") ++ continue ++ } ++ ++ // secrets are created in the SecretMountPath on the host, at a ++ // single level ++ fPath, err := c.SecretFilePath(*s) ++ if err != nil { ++ return errors.Wrap(err, "error getting secret file path") ++ } ++ if err := idtools.MkdirAllAndChown(filepath.Dir(fPath), 0700, rootIDs); err != nil { ++ return errors.Wrap(err, "error creating secret mount path") ++ } ++ ++ logrus.WithFields(logrus.Fields{ ++ "name": s.File.Name, ++ "path": fPath, ++ }).Debug("injecting secret") ++ secret, err := c.DependencyStore.Secrets().Get(s.SecretID) ++ if err != nil { ++ return errors.Wrap(err, "unable to get secret from secret store") ++ } ++ if err := ioutil.WriteFile(fPath, secret.Spec.Data, s.File.Mode); err != nil { ++ return errors.Wrap(err, "error injecting secret") ++ } ++ ++ uid, err := strconv.Atoi(s.File.UID) ++ if err != nil { ++ return err ++ } ++ gid, err := strconv.Atoi(s.File.GID) ++ if err != nil { ++ return err ++ } ++ ++ if err := os.Chown(fPath, rootIDs.UID+uid, rootIDs.GID+gid); err != nil { ++ return errors.Wrap(err, "error setting ownership for secret") ++ } ++ if err := os.Chmod(fPath, s.File.Mode); err != nil { ++ return errors.Wrap(err, "error setting file mode for secret") ++ } ++ } ++ ++ for _, ref := range c.ConfigReferences { ++ // TODO (ehazlett): use type switch when more are supported ++ if ref.File == nil { ++ // Runtime configs are not mounted into the container, but they're ++ // a valid type of config so we should not error when we encounter ++ // one. ++ if ref.Runtime == nil { ++ logrus.Error("config target type is not a file or runtime target") ++ } ++ // However, in any case, this isn't a file config, so we have no ++ // further work to do ++ continue ++ } ++ ++ fPath, err := c.ConfigFilePath(*ref) ++ if err != nil { ++ return errors.Wrap(err, "error getting config file path for container") ++ } ++ if err := idtools.MkdirAllAndChown(filepath.Dir(fPath), 0700, rootIDs); err != nil { ++ return errors.Wrap(err, "error creating config mount path") ++ } ++ ++ logrus.WithFields(logrus.Fields{ ++ "name": ref.File.Name, ++ "path": fPath, ++ }).Debug("injecting config") ++ config, err := c.DependencyStore.Configs().Get(ref.ConfigID) ++ if err != nil { ++ return errors.Wrap(err, "unable to get config from config store") ++ } ++ if err := ioutil.WriteFile(fPath, config.Spec.Data, ref.File.Mode); err != nil { ++ return errors.Wrap(err, "error injecting config") ++ } ++ ++ uid, err := strconv.Atoi(ref.File.UID) ++ if err != nil { ++ return err ++ } ++ gid, err := strconv.Atoi(ref.File.GID) ++ if err != nil { ++ return err ++ } ++ ++ if err := os.Chown(fPath, rootIDs.UID+uid, rootIDs.GID+gid); err != nil { ++ return errors.Wrap(err, "error setting ownership for config") ++ } ++ if err := os.Chmod(fPath, ref.File.Mode); err != nil { ++ return errors.Wrap(err, "error setting file mode for config") ++ } ++ } ++ ++ return daemon.remountSecretDir(c) ++} ++ ++// createSecretsDir is used to create a dir suitable for storing container secrets. ++// In practice this is using a tmpfs mount and is used for both "configs" and "secrets" ++func (daemon *Daemon) createSecretsDir(c *container.Container) error { ++ // retrieve possible remapped range start for root UID, GID ++ rootIDs := daemon.idMapping.RootPair() ++ dir, err := c.SecretMountPath() ++ if err != nil { ++ return errors.Wrap(err, "error getting container secrets dir") ++ } ++ ++ // create tmpfs ++ if err := idtools.MkdirAllAndChown(dir, 0700, rootIDs); err != nil { ++ return errors.Wrap(err, "error creating secret local mount path") ++ } ++ ++ tmpfsOwnership := fmt.Sprintf("uid=%d,gid=%d", rootIDs.UID, rootIDs.GID) ++ if err := mount.Mount("tmpfs", dir, "tmpfs", "nodev,nosuid,noexec,"+tmpfsOwnership); err != nil { ++ return errors.Wrap(err, "unable to setup secret mount") ++ } ++ return nil ++} ++ ++func (daemon *Daemon) remountSecretDir(c *container.Container) error { ++ dir, err := c.SecretMountPath() ++ if err != nil { ++ return errors.Wrap(err, "error getting container secrets path") ++ } ++ if err := label.Relabel(dir, c.MountLabel, false); err != nil { ++ logrus.WithError(err).WithField("dir", dir).Warn("Error while attempting to set selinux label") ++ } ++ rootIDs := daemon.idMapping.RootPair() ++ tmpfsOwnership := fmt.Sprintf("uid=%d,gid=%d", rootIDs.UID, rootIDs.GID) ++ ++ // remount secrets ro ++ if err := mount.Mount("tmpfs", dir, "tmpfs", "remount,ro,"+tmpfsOwnership); err != nil { ++ return errors.Wrap(err, "unable to remount dir as readonly") ++ } ++ ++ return nil ++} ++ ++func (daemon *Daemon) cleanupSecretDir(c *container.Container) { ++ dir, err := c.SecretMountPath() ++ if err != nil { ++ logrus.WithError(err).WithField("container", c.ID).Warn("error getting secrets mount path for container") ++ } ++ if err := mount.RecursiveUnmount(dir); err != nil { ++ logrus.WithField("dir", dir).WithError(err).Warn("Error while attempting to unmount dir, this may prevent removal of container.") ++ } ++ if err := os.RemoveAll(dir); err != nil && !os.IsNotExist(err) { ++ logrus.WithField("dir", dir).WithError(err).Error("Error removing dir.") ++ } ++} ++ ++func killProcessDirectly(cntr *container.Container) error { ++ ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) ++ defer cancel() ++ ++ // Block until the container to stops or timeout. ++ status := <-cntr.Wait(ctx, container.WaitConditionNotRunning) ++ if status.Err() != nil { ++ // Ensure that we don't kill ourselves ++ if pid := cntr.GetPID(); pid != 0 { ++ logrus.Infof("Container %s failed to exit within 10 seconds of kill - trying direct SIGKILL", stringid.TruncateID(cntr.ID)) ++ if err := unix.Kill(pid, 9); err != nil { ++ if err != unix.ESRCH { ++ return err ++ } ++ e := errNoSuchProcess{pid, 9} ++ logrus.Debug(e) ++ return e ++ } ++ } ++ } ++ 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 ++} ++ ++// serviceDiscoveryOnDefaultNetwork indicates if service discovery is supported on the default network ++func serviceDiscoveryOnDefaultNetwork() bool { ++ return false ++} ++ ++func (daemon *Daemon) setupPathsAndSandboxOptions(container *container.Container, sboxOptions *[]libnetwork.SandboxOption) error { ++ var err error ++ ++ if container.HostConfig.NetworkMode.IsHost() { ++ // Point to the host files, so that will be copied into the container running in host mode ++ *sboxOptions = append(*sboxOptions, libnetwork.OptionOriginHostsPath("/etc/hosts")) ++ } ++ ++ // Copy the host's resolv.conf for the container (/etc/resolv.conf or /run/systemd/resolve/resolv.conf) ++ *sboxOptions = append(*sboxOptions, libnetwork.OptionOriginResolvConfPath(daemon.configStore.GetResolvConf())) ++ ++ container.HostsPath, err = container.GetRootResourcePath("hosts") ++ if err != nil { ++ return err ++ } ++ *sboxOptions = append(*sboxOptions, libnetwork.OptionHostsPath(container.HostsPath)) ++ ++ container.ResolvConfPath, err = container.GetRootResourcePath("resolv.conf") ++ if err != nil { ++ return err ++ } ++ *sboxOptions = append(*sboxOptions, libnetwork.OptionResolvConfPath(container.ResolvConfPath)) ++ return nil ++} ++ ++func (daemon *Daemon) initializeNetworkingPaths(container *container.Container, nc *container.Container) error { ++ container.HostnamePath = nc.HostnamePath ++ container.HostsPath = nc.HostsPath ++ container.ResolvConfPath = nc.ResolvConfPath ++ return nil ++} ++ ++func (daemon *Daemon) setupContainerMountsRoot(c *container.Container) error { ++ // get the root mount path so we can make it unbindable ++ p, err := c.MountsResourcePath("") ++ if err != nil { ++ return err ++ } ++ return idtools.MkdirAllAndChown(p, 0700, daemon.idMapping.RootPair()) ++} Index: sysutils/docker-freebsd/files/patch-daemon_container__operations__unix.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-daemon_container__operations__unix.go @@ -0,0 +1,418 @@ +--- daemon/container_operations_unix.go.orig 2020-09-04 14:54:50 UTC ++++ daemon/container_operations_unix.go +@@ -1,415 +0,0 @@ +-// +build linux freebsd +- +-package daemon // import "github.com/docker/docker/daemon" +- +-import ( +- "context" +- "fmt" +- "io/ioutil" +- "os" +- "path/filepath" +- "strconv" +- "time" +- +- "github.com/docker/docker/container" +- "github.com/docker/docker/daemon/links" +- "github.com/docker/docker/errdefs" +- "github.com/docker/docker/pkg/idtools" +- "github.com/docker/docker/pkg/mount" +- "github.com/docker/docker/pkg/stringid" +- "github.com/docker/docker/runconfig" +- "github.com/docker/libnetwork" +- "github.com/opencontainers/selinux/go-selinux/label" +- "github.com/pkg/errors" +- "github.com/sirupsen/logrus" +- "golang.org/x/sys/unix" +-) +- +-func (daemon *Daemon) setupLinkedContainers(container *container.Container) ([]string, error) { +- var env []string +- children := daemon.children(container) +- +- bridgeSettings := container.NetworkSettings.Networks[runconfig.DefaultDaemonNetworkMode().NetworkName()] +- if bridgeSettings == nil || bridgeSettings.EndpointSettings == nil { +- return nil, nil +- } +- +- for linkAlias, child := range children { +- if !child.IsRunning() { +- return nil, fmt.Errorf("Cannot link to a non running container: %s AS %s", child.Name, linkAlias) +- } +- +- childBridgeSettings := child.NetworkSettings.Networks[runconfig.DefaultDaemonNetworkMode().NetworkName()] +- if childBridgeSettings == nil || childBridgeSettings.EndpointSettings == nil { +- return nil, fmt.Errorf("container %s not attached to default bridge network", child.ID) +- } +- +- link := links.NewLink( +- bridgeSettings.IPAddress, +- childBridgeSettings.IPAddress, +- linkAlias, +- child.Config.Env, +- child.Config.ExposedPorts, +- ) +- +- env = append(env, link.ToEnv()...) +- } +- +- return env, nil +-} +- +-func (daemon *Daemon) getIpcContainer(id string) (*container.Container, error) { +- errMsg := "can't join IPC of container " + id +- // Check the container exists +- container, err := daemon.GetContainer(id) +- if err != nil { +- return nil, errors.Wrap(err, errMsg) +- } +- // Check the container is running and not restarting +- if err := daemon.checkContainer(container, containerIsRunning, containerIsNotRestarting); err != nil { +- return nil, errors.Wrap(err, errMsg) +- } +- // Check the container ipc is shareable +- if st, err := os.Stat(container.ShmPath); err != nil || !st.IsDir() { +- if err == nil || os.IsNotExist(err) { +- return nil, errors.New(errMsg + ": non-shareable IPC (hint: use IpcMode:shareable for the donor container)") +- } +- // stat() failed? +- return nil, errors.Wrap(err, errMsg+": unexpected error from stat "+container.ShmPath) +- } +- +- return container, nil +-} +- +-func (daemon *Daemon) getPidContainer(container *container.Container) (*container.Container, error) { +- containerID := container.HostConfig.PidMode.Container() +- container, err := daemon.GetContainer(containerID) +- if err != nil { +- return nil, errors.Wrapf(err, "cannot join PID of a non running container: %s", containerID) +- } +- return container, daemon.checkContainer(container, containerIsRunning, containerIsNotRestarting) +-} +- +-func containerIsRunning(c *container.Container) error { +- if !c.IsRunning() { +- return errdefs.Conflict(errors.Errorf("container %s is not running", c.ID)) +- } +- return nil +-} +- +-func containerIsNotRestarting(c *container.Container) error { +- if c.IsRestarting() { +- return errContainerIsRestarting(c.ID) +- } +- return nil +-} +- +-func (daemon *Daemon) setupIpcDirs(c *container.Container) error { +- ipcMode := c.HostConfig.IpcMode +- +- switch { +- case ipcMode.IsContainer(): +- ic, err := daemon.getIpcContainer(ipcMode.Container()) +- if err != nil { +- return err +- } +- c.ShmPath = ic.ShmPath +- +- case ipcMode.IsHost(): +- if _, err := os.Stat("/dev/shm"); err != nil { +- return fmt.Errorf("/dev/shm is not mounted, but must be for --ipc=host") +- } +- c.ShmPath = "/dev/shm" +- +- case ipcMode.IsPrivate(), ipcMode.IsNone(): +- // c.ShmPath will/should not be used, so make it empty. +- // Container's /dev/shm mount comes from OCI spec. +- c.ShmPath = "" +- +- case ipcMode.IsEmpty(): +- // A container was created by an older version of the daemon. +- // The default behavior used to be what is now called "shareable". +- fallthrough +- +- case ipcMode.IsShareable(): +- rootIDs := daemon.idMapping.RootPair() +- if !c.HasMountFor("/dev/shm") { +- shmPath, err := c.ShmResourcePath() +- if err != nil { +- return err +- } +- +- if err := idtools.MkdirAllAndChown(shmPath, 0700, rootIDs); err != nil { +- return err +- } +- +- shmproperty := "mode=1777,size=" + strconv.FormatInt(c.HostConfig.ShmSize, 10) +- if err := unix.Mount("shm", shmPath, "tmpfs", uintptr(unix.MS_NOEXEC|unix.MS_NOSUID|unix.MS_NODEV), label.FormatMountLabel(shmproperty, c.GetMountLabel())); err != nil { +- return fmt.Errorf("mounting shm tmpfs: %s", err) +- } +- if err := os.Chown(shmPath, rootIDs.UID, rootIDs.GID); err != nil { +- return err +- } +- c.ShmPath = shmPath +- } +- +- default: +- return fmt.Errorf("invalid IPC mode: %v", ipcMode) +- } +- +- return nil +-} +- +-func (daemon *Daemon) setupSecretDir(c *container.Container) (setupErr error) { +- if len(c.SecretReferences) == 0 && len(c.ConfigReferences) == 0 { +- return nil +- } +- +- if err := daemon.createSecretsDir(c); err != nil { +- return err +- } +- defer func() { +- if setupErr != nil { +- daemon.cleanupSecretDir(c) +- } +- }() +- +- if c.DependencyStore == nil { +- return fmt.Errorf("secret store is not initialized") +- } +- +- // retrieve possible remapped range start for root UID, GID +- rootIDs := daemon.idMapping.RootPair() +- +- for _, s := range c.SecretReferences { +- // TODO (ehazlett): use type switch when more are supported +- if s.File == nil { +- logrus.Error("secret target type is not a file target") +- continue +- } +- +- // secrets are created in the SecretMountPath on the host, at a +- // single level +- fPath, err := c.SecretFilePath(*s) +- if err != nil { +- return errors.Wrap(err, "error getting secret file path") +- } +- if err := idtools.MkdirAllAndChown(filepath.Dir(fPath), 0700, rootIDs); err != nil { +- return errors.Wrap(err, "error creating secret mount path") +- } +- +- logrus.WithFields(logrus.Fields{ +- "name": s.File.Name, +- "path": fPath, +- }).Debug("injecting secret") +- secret, err := c.DependencyStore.Secrets().Get(s.SecretID) +- if err != nil { +- return errors.Wrap(err, "unable to get secret from secret store") +- } +- if err := ioutil.WriteFile(fPath, secret.Spec.Data, s.File.Mode); err != nil { +- return errors.Wrap(err, "error injecting secret") +- } +- +- uid, err := strconv.Atoi(s.File.UID) +- if err != nil { +- return err +- } +- gid, err := strconv.Atoi(s.File.GID) +- if err != nil { +- return err +- } +- +- if err := os.Chown(fPath, rootIDs.UID+uid, rootIDs.GID+gid); err != nil { +- return errors.Wrap(err, "error setting ownership for secret") +- } +- if err := os.Chmod(fPath, s.File.Mode); err != nil { +- return errors.Wrap(err, "error setting file mode for secret") +- } +- } +- +- for _, ref := range c.ConfigReferences { +- // TODO (ehazlett): use type switch when more are supported +- if ref.File == nil { +- // Runtime configs are not mounted into the container, but they're +- // a valid type of config so we should not error when we encounter +- // one. +- if ref.Runtime == nil { +- logrus.Error("config target type is not a file or runtime target") +- } +- // However, in any case, this isn't a file config, so we have no +- // further work to do +- continue +- } +- +- fPath, err := c.ConfigFilePath(*ref) +- if err != nil { +- return errors.Wrap(err, "error getting config file path for container") +- } +- if err := idtools.MkdirAllAndChown(filepath.Dir(fPath), 0700, rootIDs); err != nil { +- return errors.Wrap(err, "error creating config mount path") +- } +- +- logrus.WithFields(logrus.Fields{ +- "name": ref.File.Name, +- "path": fPath, +- }).Debug("injecting config") +- config, err := c.DependencyStore.Configs().Get(ref.ConfigID) +- if err != nil { +- return errors.Wrap(err, "unable to get config from config store") +- } +- if err := ioutil.WriteFile(fPath, config.Spec.Data, ref.File.Mode); err != nil { +- return errors.Wrap(err, "error injecting config") +- } +- +- uid, err := strconv.Atoi(ref.File.UID) +- if err != nil { +- return err +- } +- gid, err := strconv.Atoi(ref.File.GID) +- if err != nil { +- return err +- } +- +- if err := os.Chown(fPath, rootIDs.UID+uid, rootIDs.GID+gid); err != nil { +- return errors.Wrap(err, "error setting ownership for config") +- } +- if err := os.Chmod(fPath, ref.File.Mode); err != nil { +- return errors.Wrap(err, "error setting file mode for config") +- } +- } +- +- return daemon.remountSecretDir(c) +-} +- +-// createSecretsDir is used to create a dir suitable for storing container secrets. +-// In practice this is using a tmpfs mount and is used for both "configs" and "secrets" +-func (daemon *Daemon) createSecretsDir(c *container.Container) error { +- // retrieve possible remapped range start for root UID, GID +- rootIDs := daemon.idMapping.RootPair() +- dir, err := c.SecretMountPath() +- if err != nil { +- return errors.Wrap(err, "error getting container secrets dir") +- } +- +- // create tmpfs +- if err := idtools.MkdirAllAndChown(dir, 0700, rootIDs); err != nil { +- return errors.Wrap(err, "error creating secret local mount path") +- } +- +- tmpfsOwnership := fmt.Sprintf("uid=%d,gid=%d", rootIDs.UID, rootIDs.GID) +- if err := mount.Mount("tmpfs", dir, "tmpfs", "nodev,nosuid,noexec,"+tmpfsOwnership); err != nil { +- return errors.Wrap(err, "unable to setup secret mount") +- } +- return nil +-} +- +-func (daemon *Daemon) remountSecretDir(c *container.Container) error { +- dir, err := c.SecretMountPath() +- if err != nil { +- return errors.Wrap(err, "error getting container secrets path") +- } +- if err := label.Relabel(dir, c.MountLabel, false); err != nil { +- logrus.WithError(err).WithField("dir", dir).Warn("Error while attempting to set selinux label") +- } +- rootIDs := daemon.idMapping.RootPair() +- tmpfsOwnership := fmt.Sprintf("uid=%d,gid=%d", rootIDs.UID, rootIDs.GID) +- +- // remount secrets ro +- if err := mount.Mount("tmpfs", dir, "tmpfs", "remount,ro,"+tmpfsOwnership); err != nil { +- return errors.Wrap(err, "unable to remount dir as readonly") +- } +- +- return nil +-} +- +-func (daemon *Daemon) cleanupSecretDir(c *container.Container) { +- dir, err := c.SecretMountPath() +- if err != nil { +- logrus.WithError(err).WithField("container", c.ID).Warn("error getting secrets mount path for container") +- } +- if err := mount.RecursiveUnmount(dir); err != nil { +- logrus.WithField("dir", dir).WithError(err).Warn("Error while attempting to unmount dir, this may prevent removal of container.") +- } +- if err := os.RemoveAll(dir); err != nil && !os.IsNotExist(err) { +- logrus.WithField("dir", dir).WithError(err).Error("Error removing dir.") +- } +-} +- +-func killProcessDirectly(cntr *container.Container) error { +- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) +- defer cancel() +- +- // Block until the container to stops or timeout. +- status := <-cntr.Wait(ctx, container.WaitConditionNotRunning) +- if status.Err() != nil { +- // Ensure that we don't kill ourselves +- if pid := cntr.GetPID(); pid != 0 { +- logrus.Infof("Container %s failed to exit within 10 seconds of kill - trying direct SIGKILL", stringid.TruncateID(cntr.ID)) +- if err := unix.Kill(pid, 9); err != nil { +- if err != unix.ESRCH { +- return err +- } +- e := errNoSuchProcess{pid, 9} +- logrus.Debug(e) +- return e +- } +- } +- } +- 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 +-} +- +-// serviceDiscoveryOnDefaultNetwork indicates if service discovery is supported on the default network +-func serviceDiscoveryOnDefaultNetwork() bool { +- return false +-} +- +-func (daemon *Daemon) setupPathsAndSandboxOptions(container *container.Container, sboxOptions *[]libnetwork.SandboxOption) error { +- var err error +- +- if container.HostConfig.NetworkMode.IsHost() { +- // Point to the host files, so that will be copied into the container running in host mode +- *sboxOptions = append(*sboxOptions, libnetwork.OptionOriginHostsPath("/etc/hosts")) +- } +- +- // Copy the host's resolv.conf for the container (/etc/resolv.conf or /run/systemd/resolve/resolv.conf) +- *sboxOptions = append(*sboxOptions, libnetwork.OptionOriginResolvConfPath(daemon.configStore.GetResolvConf())) +- +- container.HostsPath, err = container.GetRootResourcePath("hosts") +- if err != nil { +- return err +- } +- *sboxOptions = append(*sboxOptions, libnetwork.OptionHostsPath(container.HostsPath)) +- +- container.ResolvConfPath, err = container.GetRootResourcePath("resolv.conf") +- if err != nil { +- return err +- } +- *sboxOptions = append(*sboxOptions, libnetwork.OptionResolvConfPath(container.ResolvConfPath)) +- return nil +-} +- +-func (daemon *Daemon) initializeNetworkingPaths(container *container.Container, nc *container.Container) error { +- container.HostnamePath = nc.HostnamePath +- container.HostsPath = nc.HostsPath +- container.ResolvConfPath = nc.ResolvConfPath +- return nil +-} +- +-func (daemon *Daemon) setupContainerMountsRoot(c *container.Container) error { +- // get the root mount path so we can make it unbindable +- p, err := c.MountsResourcePath("") +- if err != nil { +- return err +- } +- return idtools.MkdirAllAndChown(p, 0700, daemon.idMapping.RootPair()) +-} Index: sysutils/docker-freebsd/files/patch-daemon_daemon__freebsd.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-daemon_daemon__freebsd.go @@ -0,0 +1,142 @@ +--- daemon/daemon_freebsd.go.orig 2020-10-16 13:57:41 UTC ++++ daemon/daemon_freebsd.go +@@ -0,0 +1,139 @@ ++package daemon // import "github.com/docker/docker/daemon" ++ ++// based on daemon_linux ++ ++import ( ++ "bufio" ++ "fmt" ++ "io" ++ "os" ++ "path/filepath" ++ "regexp" ++ "strings" ++ ++ "github.com/docker/docker/daemon/config" ++ "github.com/docker/docker/pkg/fileutils" ++ "github.com/docker/docker/pkg/mount" ++ "github.com/docker/libnetwork/resolvconf" ++ "github.com/pkg/errors" ++ "github.com/sirupsen/logrus" ++) ++ ++func getPluginExecRoot(root string) string { ++ return filepath.Join(root, "plugins") ++} ++ ++func (daemon *Daemon) cleanupMountsByID(id string) error { ++ return nil ++} ++ ++func (daemon *Daemon) cleanupMountsFromReaderByID(reader io.Reader, id string, unmount func(target string) error) error { ++ if daemon.root == "" { ++ return nil ++ } ++ var errors []string ++ ++ regexps := getCleanPatterns(id) ++ sc := bufio.NewScanner(reader) ++ for sc.Scan() { ++ if fields := strings.Fields(sc.Text()); len(fields) >= 4 { ++ if mnt := fields[4]; strings.HasPrefix(mnt, daemon.root) { ++ for _, p := range regexps { ++ if p.MatchString(mnt) { ++ if err := unmount(mnt); err != nil { ++ logrus.Error(err) ++ errors = append(errors, err.Error()) ++ } ++ } ++ } ++ } ++ } ++ } ++ ++ if err := sc.Err(); err != nil { ++ return err ++ } ++ ++ if len(errors) > 0 { ++ return fmt.Errorf("Error cleaning up mounts:\n%v", strings.Join(errors, "\n")) ++ } ++ ++ logrus.Debugf("Cleaning up old mountid %v: done.", id) ++ return nil ++} ++ ++// cleanupMounts umounts used by container resources and the daemon root mount ++func (daemon *Daemon) cleanupMounts() error { ++ if err := daemon.cleanupMountsByID(""); err != nil { ++ return err ++ } ++ ++ info, err := mount.GetMounts(mount.SingleEntryFilter(daemon.root)) ++ if err != nil { ++ return errors.Wrap(err, "error reading mount table for cleanup") ++ } ++ ++ if len(info) < 1 { ++ // no mount found, we're done here ++ return nil ++ } ++ ++ // `info.Root` here is the root mountpoint of the passed in path (`daemon.root`). ++ // The ony cases that need to be cleaned up is when the daemon has performed a ++ // `mount --bind /daemon/root /daemon/root && mount --make-shared /daemon/root` ++ // This is only done when the daemon is started up and `/daemon/root` is not ++ // already on a shared mountpoint. ++ //if !shouldUnmountRoot(daemon.root, info[0]) { ++ // return nil ++ //} ++ ++ unmountFile := getUnmountOnShutdownPath(daemon.configStore) ++ if _, err := os.Stat(unmountFile); err != nil { ++ return nil ++ } ++ ++ logrus.WithField("mountpoint", daemon.root).Debug("unmounting daemon root") ++ if err := mount.Unmount(daemon.root); err != nil { ++ return err ++ } ++ return os.Remove(unmountFile) ++} ++ ++func getCleanPatterns(id string) (regexps []*regexp.Regexp) { ++ var patterns []string ++ if id == "" { ++ id = "[0-9a-f]{64}" ++ patterns = append(patterns, "containers/"+id+"/shm") ++ } ++ patterns = append(patterns, "aufs/mnt/"+id+"$", "overlay/"+id+"/merged$", "zfs/graph/"+id+"$") ++ for _, p := range patterns { ++ r, err := regexp.Compile(p) ++ if err == nil { ++ regexps = append(regexps, r) ++ } ++ } ++ return ++} ++ ++func getRealPath(path string) (string, error) { ++ return fileutils.ReadSymlinkedDirectory(path) ++} ++ ++//func shouldUnmountRoot(root string, info *mount.Info) bool { ++// if !strings.HasSuffix(root, info.Root) { ++// return false ++// } ++// return hasMountinfoOption(info.Optional, sharedPropagationOption) ++//} ++ ++// setupResolvConf sets the appropriate resolv.conf file if not specified ++// When systemd-resolved is running the default /etc/resolv.conf points to ++// localhost. In this case fetch the alternative config file that is in a ++// different path so that containers can use it ++// In all the other cases fallback to the default one ++func setupResolvConf(config *config.Config) { ++ if config.ResolvConf != "" { ++ return ++ } ++ config.ResolvConf = resolvconf.Path() ++} Index: sysutils/docker-freebsd/files/patch-daemon_daemon__linux.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-daemon_daemon__linux.go @@ -0,0 +1,46 @@ +--- daemon/daemon_linux.go.orig 2020-09-04 14:54:51 UTC ++++ daemon/daemon_linux.go +@@ -145,3 +145,43 @@ func setupResolvConf(config *config.Config) { + } + config.ResolvConf = resolvconf.Path() + } ++ ++// setupDaemonProcess sets various settings for the daemon's process ++func setupDaemonProcess(config *config.Config) error { ++ // setup the daemons oom_score_adj ++ return setupOOMScoreAdj(config.OOMScoreAdjust) ++} ++ ++func setupOOMScoreAdj(score int) error { ++ f, err := os.OpenFile("/proc/self/oom_score_adj", os.O_WRONLY, 0) ++ if err != nil { ++ return err ++ } ++ defer f.Close() ++ stringScore := strconv.Itoa(score) ++ _, err = f.WriteString(stringScore) ++ if os.IsPermission(err) { ++ // Setting oom_score_adj does not work in an ++ // unprivileged container. Ignore the error, but log ++ // it if we appear not to be in that situation. ++ if !rsystem.RunningInUserNS() { ++ logrus.Debugf("Permission denied writing %q to /proc/self/oom_score_adj", stringScore) ++ } ++ return nil ++ } ++ ++ return err ++} ++ ++func (daemon *Daemon) setupSeccompProfile() error { ++ if daemon.configStore.SeccompProfile != "" { ++ daemon.seccompProfilePath = daemon.configStore.SeccompProfile ++ b, err := ioutil.ReadFile(daemon.configStore.SeccompProfile) ++ if err != nil { ++ return fmt.Errorf("opening seccomp profile (%s) failed: %v", daemon.configStore.SeccompProfile, err) ++ } ++ daemon.seccompProfile = b ++ } ++ return nil ++} ++ Index: sysutils/docker-freebsd/files/patch-daemon_daemon__unix.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-daemon_daemon__unix.go @@ -0,0 +1,382 @@ +--- daemon/daemon_unix.go.orig 2020-10-23 18:37:16 UTC ++++ daemon/daemon_unix.go +@@ -29,7 +29,8 @@ import ( + "github.com/docker/docker/pkg/containerfs" + "github.com/docker/docker/pkg/idtools" + "github.com/docker/docker/pkg/ioutils" +- "github.com/docker/docker/pkg/mount" ++ ++ //"github.com/docker/docker/pkg/mount" + "github.com/docker/docker/pkg/parsers" + "github.com/docker/docker/pkg/parsers/kernel" + "github.com/docker/docker/pkg/sysinfo" +@@ -37,18 +38,18 @@ import ( + volumemounts "github.com/docker/docker/volume/mounts" + "github.com/docker/libnetwork" + nwconfig "github.com/docker/libnetwork/config" +- "github.com/docker/libnetwork/drivers/bridge" ++ "github.com/docker/libnetwork/drivers/freebsd/bridge" + "github.com/docker/libnetwork/netlabel" + "github.com/docker/libnetwork/netutils" + "github.com/docker/libnetwork/options" + lntypes "github.com/docker/libnetwork/types" +- "github.com/opencontainers/runc/libcontainer/cgroups" ++ ++ // "github.com/opencontainers/runc/libcontainer/cgroups" + rsystem "github.com/opencontainers/runc/libcontainer/system" + "github.com/opencontainers/runtime-spec/specs-go" + "github.com/opencontainers/selinux/go-selinux/label" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +- "github.com/vishvananda/netlink" + "golang.org/x/sys/unix" + ) + +@@ -874,11 +875,11 @@ func (daemon *Daemon) initNetworkController(config *co + } + + // Initialize default network on "host" +- if n, _ := controller.NetworkByName("host"); n == nil { +- if _, err := controller.NewNetwork("host", "host", "", libnetwork.NetworkOptionPersist(true)); err != nil { +- return nil, fmt.Errorf("Error creating default \"host\" network: %v", err) +- } +- } ++ // if n, _ := controller.NetworkByName("host"); n == nil { ++ // if _, err := controller.NewNetwork("host", "host", "", libnetwork.NetworkOptionPersist(true)); err != nil { ++ // return nil, fmt.Errorf("Error creating default \"host\" network: %v", err) ++ // } ++ // } + + // Clear stale bridge network + if n, err := controller.NetworkByName("bridge"); err == nil { +@@ -1043,16 +1044,13 @@ func initBridgeDriver(controller libnetwork.NetworkCon + 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 removeDefaultBridgeInterface() error { ++ return fmt.Errorf("Bridge network driver not supported on FreeBSD (yet)") + } + + func setupInitLayer(idMapping *idtools.IdentityMapping) func(containerfs.ContainerFS) error { +@@ -1260,45 +1258,45 @@ func setupDaemonRoot(config *config.Config, rootDir st + } + + func setupDaemonRootPropagation(cfg *config.Config) error { +- rootParentMount, options, err := getSourceMount(cfg.Root) +- if err != nil { +- return errors.Wrap(err, "error getting daemon root's parent mount") +- } ++ // rootParentMount, options, err := getSourceMount(cfg.Root) ++ // if err != nil { ++ // return errors.Wrap(err, "error getting daemon root's parent mount") ++ // } + +- var cleanupOldFile bool +- cleanupFile := getUnmountOnShutdownPath(cfg) +- defer func() { +- if !cleanupOldFile { +- return +- } +- if err := os.Remove(cleanupFile); err != nil && !os.IsNotExist(err) { +- logrus.WithError(err).WithField("file", cleanupFile).Warn("could not clean up old root propagation unmount file") +- } +- }() ++ // var cleanupOldFile bool ++ // cleanupFile := getUnmountOnShutdownPath(cfg) ++ // defer func() { ++ // if !cleanupOldFile { ++ // return ++ // } ++ // if err := os.Remove(cleanupFile); err != nil && !os.IsNotExist(err) { ++ // logrus.WithError(err).WithField("file", cleanupFile).Warn("could not clean up old root propagation unmount file") ++ // } ++ // }() + +- if hasMountinfoOption(options, sharedPropagationOption, slavePropagationOption) { +- cleanupOldFile = true +- return nil +- } ++ // if hasMountinfoOption(options, sharedPropagationOption, slavePropagationOption) { ++ // cleanupOldFile = true ++ // return nil ++ // } + +- if err := mount.MakeShared(cfg.Root); err != nil { +- return errors.Wrap(err, "could not setup daemon root propagation to shared") +- } ++ // if err := mount.MakeShared(cfg.Root); err != nil { ++ // return errors.Wrap(err, "could not setup daemon root propagation to shared") ++ // } + +- // check the case where this may have already been a mount to itself. +- // If so then the daemon only performed a remount and should not try to unmount this later. +- if rootParentMount == cfg.Root { +- cleanupOldFile = true +- return nil +- } ++ // // check the case where this may have already been a mount to itself. ++ // // If so then the daemon only performed a remount and should not try to unmount this later. ++ // if rootParentMount == cfg.Root { ++ // cleanupOldFile = true ++ // return nil ++ // } + +- if err := os.MkdirAll(filepath.Dir(cleanupFile), 0700); err != nil { +- return errors.Wrap(err, "error creating dir to store mount cleanup file") +- } ++ // if err := os.MkdirAll(filepath.Dir(cleanupFile), 0700); err != nil { ++ // return errors.Wrap(err, "error creating dir to store mount cleanup file") ++ // } + +- if err := ioutil.WriteFile(cleanupFile, nil, 0600); err != nil { +- return errors.Wrap(err, "error writing file to signal mount cleanup on shutdown") +- } ++ // if err := ioutil.WriteFile(cleanupFile, nil, 0600); err != nil { ++ // return errors.Wrap(err, "error writing file to signal mount cleanup on shutdown") ++ // } + return nil + } + +@@ -1387,7 +1385,7 @@ func (daemon *Daemon) stats(c *container.Container) (* + if !c.IsRunning() { + return nil, errNotRunning(c.ID) + } +- cs, err := daemon.containerd.Stats(context.Background(), c.ID) ++ _, err := daemon.containerd.Stats(context.Background(), c.ID) + if err != nil { + if strings.Contains(err.Error(), "container not found") { + return nil, containerNotFound(c.ID) +@@ -1395,97 +1393,97 @@ func (daemon *Daemon) stats(c *container.Container) (* + return nil, err + } + s := &types.StatsJSON{} +- s.Read = cs.Read +- stats := cs.Metrics +- if stats.Blkio != nil { +- s.BlkioStats = types.BlkioStats{ +- IoServiceBytesRecursive: copyBlkioEntry(stats.Blkio.IoServiceBytesRecursive), +- IoServicedRecursive: copyBlkioEntry(stats.Blkio.IoServicedRecursive), +- IoQueuedRecursive: copyBlkioEntry(stats.Blkio.IoQueuedRecursive), +- IoServiceTimeRecursive: copyBlkioEntry(stats.Blkio.IoServiceTimeRecursive), +- IoWaitTimeRecursive: copyBlkioEntry(stats.Blkio.IoWaitTimeRecursive), +- IoMergedRecursive: copyBlkioEntry(stats.Blkio.IoMergedRecursive), +- IoTimeRecursive: copyBlkioEntry(stats.Blkio.IoTimeRecursive), +- SectorsRecursive: copyBlkioEntry(stats.Blkio.SectorsRecursive), +- } +- } +- if stats.CPU != nil { +- s.CPUStats = types.CPUStats{ +- CPUUsage: types.CPUUsage{ +- TotalUsage: stats.CPU.Usage.Total, +- PercpuUsage: stats.CPU.Usage.PerCPU, +- UsageInKernelmode: stats.CPU.Usage.Kernel, +- UsageInUsermode: stats.CPU.Usage.User, +- }, +- ThrottlingData: types.ThrottlingData{ +- Periods: stats.CPU.Throttling.Periods, +- ThrottledPeriods: stats.CPU.Throttling.ThrottledPeriods, +- ThrottledTime: stats.CPU.Throttling.ThrottledTime, +- }, +- } +- } ++ // s.Read = cs.Read ++ // stats := cs.Metrics ++ // if stats.Blkio != nil { ++ // s.BlkioStats = types.BlkioStats{ ++ // IoServiceBytesRecursive: copyBlkioEntry(stats.Blkio.IoServiceBytesRecursive), ++ // IoServicedRecursive: copyBlkioEntry(stats.Blkio.IoServicedRecursive), ++ // IoQueuedRecursive: copyBlkioEntry(stats.Blkio.IoQueuedRecursive), ++ // IoServiceTimeRecursive: copyBlkioEntry(stats.Blkio.IoServiceTimeRecursive), ++ // IoWaitTimeRecursive: copyBlkioEntry(stats.Blkio.IoWaitTimeRecursive), ++ // IoMergedRecursive: copyBlkioEntry(stats.Blkio.IoMergedRecursive), ++ // IoTimeRecursive: copyBlkioEntry(stats.Blkio.IoTimeRecursive), ++ // SectorsRecursive: copyBlkioEntry(stats.Blkio.SectorsRecursive), ++ // } ++ // } ++ // if stats.CPU != nil { ++ // s.CPUStats = types.CPUStats{ ++ // CPUUsage: types.CPUUsage{ ++ // TotalUsage: stats.CPU.Usage.Total, ++ // PercpuUsage: stats.CPU.Usage.PerCPU, ++ // UsageInKernelmode: stats.CPU.Usage.Kernel, ++ // UsageInUsermode: stats.CPU.Usage.User, ++ // }, ++ // ThrottlingData: types.ThrottlingData{ ++ // Periods: stats.CPU.Throttling.Periods, ++ // ThrottledPeriods: stats.CPU.Throttling.ThrottledPeriods, ++ // ThrottledTime: stats.CPU.Throttling.ThrottledTime, ++ // }, ++ // } ++ // } + +- if stats.Memory != nil { +- raw := make(map[string]uint64) +- raw["cache"] = stats.Memory.Cache +- raw["rss"] = stats.Memory.RSS +- raw["rss_huge"] = stats.Memory.RSSHuge +- raw["mapped_file"] = stats.Memory.MappedFile +- raw["dirty"] = stats.Memory.Dirty +- raw["writeback"] = stats.Memory.Writeback +- raw["pgpgin"] = stats.Memory.PgPgIn +- raw["pgpgout"] = stats.Memory.PgPgOut +- raw["pgfault"] = stats.Memory.PgFault +- raw["pgmajfault"] = stats.Memory.PgMajFault +- raw["inactive_anon"] = stats.Memory.InactiveAnon +- raw["active_anon"] = stats.Memory.ActiveAnon +- raw["inactive_file"] = stats.Memory.InactiveFile +- raw["active_file"] = stats.Memory.ActiveFile +- raw["unevictable"] = stats.Memory.Unevictable +- raw["hierarchical_memory_limit"] = stats.Memory.HierarchicalMemoryLimit +- raw["hierarchical_memsw_limit"] = stats.Memory.HierarchicalSwapLimit +- raw["total_cache"] = stats.Memory.TotalCache +- raw["total_rss"] = stats.Memory.TotalRSS +- raw["total_rss_huge"] = stats.Memory.TotalRSSHuge +- raw["total_mapped_file"] = stats.Memory.TotalMappedFile +- raw["total_dirty"] = stats.Memory.TotalDirty +- raw["total_writeback"] = stats.Memory.TotalWriteback +- raw["total_pgpgin"] = stats.Memory.TotalPgPgIn +- raw["total_pgpgout"] = stats.Memory.TotalPgPgOut +- raw["total_pgfault"] = stats.Memory.TotalPgFault +- raw["total_pgmajfault"] = stats.Memory.TotalPgMajFault +- raw["total_inactive_anon"] = stats.Memory.TotalInactiveAnon +- raw["total_active_anon"] = stats.Memory.TotalActiveAnon +- raw["total_inactive_file"] = stats.Memory.TotalInactiveFile +- raw["total_active_file"] = stats.Memory.TotalActiveFile +- raw["total_unevictable"] = stats.Memory.TotalUnevictable ++ // if stats.Memory != nil { ++ // raw := make(map[string]uint64) ++ // raw["cache"] = stats.Memory.Cache ++ // raw["rss"] = stats.Memory.RSS ++ // raw["rss_huge"] = stats.Memory.RSSHuge ++ // raw["mapped_file"] = stats.Memory.MappedFile ++ // raw["dirty"] = stats.Memory.Dirty ++ // raw["writeback"] = stats.Memory.Writeback ++ // raw["pgpgin"] = stats.Memory.PgPgIn ++ // raw["pgpgout"] = stats.Memory.PgPgOut ++ // raw["pgfault"] = stats.Memory.PgFault ++ // raw["pgmajfault"] = stats.Memory.PgMajFault ++ // raw["inactive_anon"] = stats.Memory.InactiveAnon ++ // raw["active_anon"] = stats.Memory.ActiveAnon ++ // raw["inactive_file"] = stats.Memory.InactiveFile ++ // raw["active_file"] = stats.Memory.ActiveFile ++ // raw["unevictable"] = stats.Memory.Unevictable ++ // raw["hierarchical_memory_limit"] = stats.Memory.HierarchicalMemoryLimit ++ // raw["hierarchical_memsw_limit"] = stats.Memory.HierarchicalSwapLimit ++ // raw["total_cache"] = stats.Memory.TotalCache ++ // raw["total_rss"] = stats.Memory.TotalRSS ++ // raw["total_rss_huge"] = stats.Memory.TotalRSSHuge ++ // raw["total_mapped_file"] = stats.Memory.TotalMappedFile ++ // raw["total_dirty"] = stats.Memory.TotalDirty ++ // raw["total_writeback"] = stats.Memory.TotalWriteback ++ // raw["total_pgpgin"] = stats.Memory.TotalPgPgIn ++ // raw["total_pgpgout"] = stats.Memory.TotalPgPgOut ++ // raw["total_pgfault"] = stats.Memory.TotalPgFault ++ // raw["total_pgmajfault"] = stats.Memory.TotalPgMajFault ++ // raw["total_inactive_anon"] = stats.Memory.TotalInactiveAnon ++ // raw["total_active_anon"] = stats.Memory.TotalActiveAnon ++ // raw["total_inactive_file"] = stats.Memory.TotalInactiveFile ++ // raw["total_active_file"] = stats.Memory.TotalActiveFile ++ // raw["total_unevictable"] = stats.Memory.TotalUnevictable + +- if stats.Memory.Usage != nil { +- s.MemoryStats = types.MemoryStats{ +- Stats: raw, +- Usage: stats.Memory.Usage.Usage, +- MaxUsage: stats.Memory.Usage.Max, +- Limit: stats.Memory.Usage.Limit, +- Failcnt: stats.Memory.Usage.Failcnt, +- } +- } else { +- s.MemoryStats = types.MemoryStats{ +- Stats: raw, +- } +- } ++ // if stats.Memory.Usage != nil { ++ // s.MemoryStats = types.MemoryStats{ ++ // Stats: raw, ++ // Usage: stats.Memory.Usage.Usage, ++ // MaxUsage: stats.Memory.Usage.Max, ++ // Limit: stats.Memory.Usage.Limit, ++ // Failcnt: stats.Memory.Usage.Failcnt, ++ // } ++ // } else { ++ // s.MemoryStats = types.MemoryStats{ ++ // Stats: raw, ++ // } ++ // } + +- // if the container does not set memory limit, use the machineMemory +- if s.MemoryStats.Limit > daemon.machineMemory && daemon.machineMemory > 0 { +- s.MemoryStats.Limit = daemon.machineMemory +- } +- } ++ // // if the container does not set memory limit, use the machineMemory ++ // if s.MemoryStats.Limit > daemon.machineMemory && daemon.machineMemory > 0 { ++ // s.MemoryStats.Limit = daemon.machineMemory ++ // } ++ // } + +- if stats.Pids != nil { +- s.PidsStats = types.PidsStats{ +- Current: stats.Pids.Current, +- Limit: stats.Pids.Limit, +- } +- } ++ // if stats.Pids != nil { ++ // s.PidsStats = types.PidsStats{ ++ // Current: stats.Pids.Current, ++ // Limit: stats.Pids.Limit, ++ // } ++ // } + + return s, nil + } +@@ -1538,24 +1536,7 @@ func setMayDetachMounts() error { + } + + func setupOOMScoreAdj(score int) error { +- f, err := os.OpenFile("/proc/self/oom_score_adj", os.O_WRONLY, 0) +- if err != nil { +- return err +- } +- defer f.Close() +- stringScore := strconv.Itoa(score) +- _, err = f.WriteString(stringScore) +- if os.IsPermission(err) { +- // Setting oom_score_adj does not work in an +- // unprivileged container. Ignore the error, but log +- // it if we appear not to be in that situation. +- if !rsystem.RunningInUserNS() { +- logrus.Debugf("Permission denied writing %q to /proc/self/oom_score_adj", stringScore) +- } +- return nil +- } +- +- return err ++ return nil + } + + func (daemon *Daemon) initCgroupsPath(path string) error { +@@ -1571,7 +1552,10 @@ func (daemon *Daemon) initCgroupsPath(path string) err + // 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") ++ mnt := "" ++ root := "" ++ var err error = nil ++ //mnt, root, err := cgroups.FindCgroupMountpointAndRoot("", "cpu") + if err != nil { + return err + } Index: sysutils/docker-freebsd/files/patch-daemon_exec__freebsd.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-daemon_exec__freebsd.go @@ -0,0 +1,14 @@ +--- daemon/exec_freebsd.go.orig 2020-09-18 09:01:00 UTC ++++ daemon/exec_freebsd.go +@@ -0,0 +1,11 @@ ++package daemon ++ ++import ( ++ "github.com/docker/docker/container" ++ "github.com/docker/docker/daemon/exec" ++ specs "github.com/opencontainers/runtime-spec/specs-go" ++) ++ ++func (daemon *Daemon) execSetPlatformOpt(c *container.Container, ec *exec.Config, p *specs.Process) error { ++ return nil ++} Index: sysutils/docker-freebsd/files/patch-daemon_graphdriver_driver__freebsd.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-daemon_graphdriver_driver__freebsd.go @@ -0,0 +1,67 @@ +Fix build error on FreeBSD: + +daemon/graphdriver/driver_freebsd.go:17:38: cannot use &buf (type *unix.Statfs_t) as type *syscall.Statfs_t in argument to syscall.Statfs + +--- daemon/graphdriver/driver_freebsd.go.orig 2019-02-26 00:29:56 UTC ++++ daemon/graphdriver/driver_freebsd.go +@@ -1,8 +1,7 @@ + package graphdriver // import "github.com/docker/docker/daemon/graphdriver" + + import ( +- "syscall" +- ++ "github.com/docker/docker/pkg/mount" + "golang.org/x/sys/unix" + ) + +@@ -11,10 +10,49 @@ var ( + priority = "zfs" + ) + ++// GetFSMagic returns the filesystem id given the path. ++func GetFSMagic(rootpath string) (FsMagic, error) { ++ var buf unix.Statfs_t ++ if err := unix.Statfs(rootpath, &buf); err != nil { ++ return 0, err ++ } ++ return FsMagic(buf.Type), nil ++} ++ ++// NewFsChecker returns a checker configured for the provided FsMagic ++func NewFsChecker(t FsMagic) Checker { ++ return &fsChecker{ ++ t: t, ++ } ++} ++ ++type fsChecker struct { ++ t FsMagic ++} ++ ++func (c *fsChecker) IsMounted(path string) bool { ++ m, _ := Mounted(c.t, path) ++ return m ++} ++ ++// NewDefaultChecker returns a check that parses /proc/mountinfo to check ++// if the specified path is mounted. ++func NewDefaultChecker() Checker { ++ return &defaultChecker{} ++} ++ ++type defaultChecker struct { ++} ++ ++func (c *defaultChecker) IsMounted(path string) bool { ++ m, _ := mount.Mounted(path) ++ return m ++} ++ + // Mounted checks if the given path is mounted as the fs type + func Mounted(fsType FsMagic, mountPath string) (bool, error) { + var buf unix.Statfs_t +- if err := syscall.Statfs(mountPath, &buf); err != nil { ++ if err := unix.Statfs(mountPath, &buf); err != nil { + return false, err + } + return FsMagic(buf.Type) == fsType, nil Index: sysutils/docker-freebsd/files/patch-daemon_graphdriver_zfs_zfs.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-daemon_graphdriver_zfs_zfs.go @@ -0,0 +1,11 @@ +--- daemon/graphdriver/zfs/zfs.go.orig 2019-10-07 21:12:15 UTC ++++ daemon/graphdriver/zfs/zfs.go +@@ -414,7 +414,7 @@ func (d *Driver) Put(id string) error { + + logger.Debugf(`unmount("%s")`, mountpoint) + +- if err := unix.Unmount(mountpoint, unix.MNT_DETACH); err != nil { ++ if err := unix.Unmount(mountpoint, 0); err != nil { + logger.Warnf("Failed to unmount %s mount %s: %v", id, mountpoint, err) + } + if err := unix.Rmdir(mountpoint); err != nil && !os.IsNotExist(err) { Index: sysutils/docker-freebsd/files/patch-daemon_inspect.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-daemon_inspect.go @@ -0,0 +1,20 @@ +--- daemon/inspect.go.orig 2019-10-07 21:12:15 UTC ++++ daemon/inspect.go +@@ -22,7 +22,7 @@ import ( + func (daemon *Daemon) ContainerInspect(name string, size bool, version string) (interface{}, error) { + switch { + case versions.LessThan(version, "1.20"): +- return daemon.containerInspectPre120(name) ++ return nil, errors.New("Port pre-1.20 not supported on freeBSD") + case versions.Equal(version, "1.20"): + return daemon.containerInspect120(name) + } +@@ -135,7 +135,7 @@ func (daemon *Daemon) getInspectData(container *contai + } + + // We merge the Ulimits from hostConfig with daemon default +- daemon.mergeUlimits(&hostConfig) ++ // daemon.mergeUlimits(&hostConfig) + + var containerHealth *types.Health + if container.State.Health != nil { Index: sysutils/docker-freebsd/files/patch-daemon_inspect__freebsd.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-daemon_inspect__freebsd.go @@ -0,0 +1,76 @@ +--- daemon/inspect_freebsd.go.orig 2020-09-04 09:13:42 UTC ++++ daemon/inspect_freebsd.go +@@ -0,0 +1,73 @@ ++package daemon // import "github.com/docker/docker/daemon" ++ ++import ( ++ "github.com/docker/docker/api/types" ++ "github.com/docker/docker/api/types/backend" ++ "github.com/docker/docker/api/types/versions/v1p19" ++ "github.com/docker/docker/container" ++ "github.com/docker/docker/daemon/exec" ++) ++ ++// This sets platform-specific fields ++func setPlatformSpecificContainerFields(container *container.Container, contJSONBase *types.ContainerJSONBase) *types.ContainerJSONBase { ++ contJSONBase.AppArmorProfile = container.AppArmorProfile ++ contJSONBase.ResolvConfPath = container.ResolvConfPath ++ contJSONBase.HostnamePath = container.HostnamePath ++ contJSONBase.HostsPath = container.HostsPath ++ ++ return contJSONBase ++} ++ ++// containerInspectPre120 gets containers for pre 1.20 APIs. ++func (daemon *Daemon) containerInspectPre120(name string) (*v1p19.ContainerJSON, error) { ++ container, err := daemon.GetContainer(name) ++ if err != nil { ++ return nil, err ++ } ++ ++ container.Lock() ++ defer container.Unlock() ++ ++ base, err := daemon.getInspectData(container) ++ if err != nil { ++ return nil, err ++ } ++ ++ volumes := make(map[string]string) ++ volumesRW := make(map[string]bool) ++ for _, m := range container.MountPoints { ++ volumes[m.Destination] = m.Path() ++ volumesRW[m.Destination] = m.RW ++ } ++ ++ config := &v1p19.ContainerConfig{ ++ Config: container.Config, ++ MacAddress: container.Config.MacAddress, ++ NetworkDisabled: container.Config.NetworkDisabled, ++ ExposedPorts: container.Config.ExposedPorts, ++ VolumeDriver: container.HostConfig.VolumeDriver, ++ Memory: container.HostConfig.Memory, ++ MemorySwap: container.HostConfig.MemorySwap, ++ CPUShares: container.HostConfig.CPUShares, ++ CPUSet: container.HostConfig.CpusetCpus, ++ } ++ networkSettings := daemon.getBackwardsCompatibleNetworkSettings(container.NetworkSettings) ++ ++ return &v1p19.ContainerJSON{ ++ ContainerJSONBase: base, ++ Volumes: volumes, ++ VolumesRW: volumesRW, ++ Config: config, ++ NetworkSettings: networkSettings, ++ }, nil ++} ++ ++func inspectExecProcessConfig(e *exec.Config) *backend.ExecProcessConfig { ++ return &backend.ExecProcessConfig{ ++ Tty: e.Tty, ++ Entrypoint: e.Entrypoint, ++ Arguments: e.Args, ++ Privileged: &e.Privileged, ++ User: e.User, ++ } ++} Index: sysutils/docker-freebsd/files/patch-daemon_listeners_listeners__freebsd.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-daemon_listeners_listeners__freebsd.go @@ -0,0 +1,110 @@ +--- daemon/listeners/listeners_freebsd.go.orig 2020-09-04 09:13:42 UTC ++++ daemon/listeners/listeners_freebsd.go +@@ -0,0 +1,107 @@ ++package listeners // import "github.com/docker/docker/daemon/listeners" ++ ++import ( ++ "crypto/tls" ++ "fmt" ++ "net" ++ "os" ++ "strconv" ++ ++ "github.com/coreos/go-systemd/activation" ++ "github.com/docker/docker/pkg/homedir" ++ "github.com/docker/go-connections/sockets" ++ "github.com/sirupsen/logrus" ++) ++ ++// Init creates new listeners for the server. ++// TODO: Clean up the fact that socketGroup and tlsConfig aren't always used. ++func Init(proto, addr, socketGroup string, tlsConfig *tls.Config) ([]net.Listener, error) { ++ ls := []net.Listener{} ++ ++ switch proto { ++ case "fd": ++ fds, err := listenFD(addr, tlsConfig) ++ if err != nil { ++ return nil, err ++ } ++ ls = append(ls, fds...) ++ case "tcp": ++ l, err := sockets.NewTCPSocket(addr, tlsConfig) ++ if err != nil { ++ return nil, err ++ } ++ ls = append(ls, l) ++ case "unix": ++ gid, err := lookupGID(socketGroup) ++ if err != nil { ++ if socketGroup != "" { ++ if socketGroup != defaultSocketGroup { ++ return nil, err ++ } ++ logrus.Warnf("could not change group %s to %s: %v", addr, defaultSocketGroup, err) ++ } ++ gid = os.Getgid() ++ } ++ l, err := sockets.NewUnixSocket(addr, gid) ++ if err != nil { ++ return nil, fmt.Errorf("can't create unix socket %s: %v", addr, err) ++ } ++ if _, err := homedir.StickRuntimeDirContents([]string{addr}); err != nil { ++ // StickRuntimeDirContents returns nil error if XDG_RUNTIME_DIR is just unset ++ logrus.WithError(err).Warnf("cannot set sticky bit on socket %s under XDG_RUNTIME_DIR", addr) ++ } ++ ls = append(ls, l) ++ default: ++ return nil, fmt.Errorf("invalid protocol format: %q", proto) ++ } ++ ++ return ls, nil ++} ++ ++// listenFD returns the specified socket activated files as a slice of ++// net.Listeners or all of the activated files if "*" is given. ++func listenFD(addr string, tlsConfig *tls.Config) ([]net.Listener, error) { ++ var ( ++ err error ++ listeners []net.Listener ++ ) ++ // socket activation ++ if tlsConfig != nil { ++ listeners, err = activation.TLSListeners(tlsConfig) ++ } else { ++ listeners, err = activation.Listeners() ++ } ++ if err != nil { ++ return nil, err ++ } ++ ++ if len(listeners) == 0 { ++ return nil, fmt.Errorf("no sockets found via socket activation: make sure the service was started by systemd") ++ } ++ ++ // default to all fds just like unix:// and tcp:// ++ if addr == "" || addr == "*" { ++ return listeners, nil ++ } ++ ++ fdNum, err := strconv.Atoi(addr) ++ if err != nil { ++ return nil, fmt.Errorf("failed to parse systemd fd address: should be a number: %v", addr) ++ } ++ fdOffset := fdNum - 3 ++ if len(listeners) < fdOffset+1 { ++ return nil, fmt.Errorf("too few socket activated files passed in by systemd") ++ } ++ if listeners[fdOffset] == nil { ++ return nil, fmt.Errorf("failed to listen on systemd activated file: fd %d", fdOffset+3) ++ } ++ for i, ls := range listeners { ++ if i == fdOffset || ls == nil { ++ continue ++ } ++ if err := ls.Close(); err != nil { ++ return nil, fmt.Errorf("failed to close systemd activated file: fd %d: %v", fdOffset+3, err) ++ } ++ } ++ return []net.Listener{listeners[fdOffset]}, nil ++} Index: sysutils/docker-freebsd/files/patch-daemon_monitor__freebsd.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-daemon_monitor__freebsd.go @@ -0,0 +1,21 @@ +--- daemon/monitor_freebsd.go.orig 2020-09-04 14:57:27 UTC ++++ daemon/monitor_freebsd.go +@@ -0,0 +1,18 @@ ++package daemon ++ ++import ( ++ "github.com/docker/docker/container" ++ "github.com/docker/docker/libcontainerd/types" ++) ++ ++// platformConstructExitStatus returns a platform specific exit status structure ++func platformConstructExitStatus(e types.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 types.StateInfo) error { ++ return nil ++} Index: sysutils/docker-freebsd/files/patch-daemon_network.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-daemon_network.go @@ -0,0 +1,17 @@ +--- daemon/network.go.orig 2020-09-04 14:54:50 UTC ++++ daemon/network.go +@@ -796,10 +796,10 @@ func buildCreateEndpointOptions(c *container.Container + + defaultNetName := runconfig.DefaultDaemonNetworkMode().NetworkName() + +- if (!serviceDiscoveryOnDefaultNetwork() && n.Name() == defaultNetName) || +- c.NetworkSettings.IsAnonymousEndpoint { +- createOptions = append(createOptions, libnetwork.CreateOptionAnonymous()) +- } ++ // if (!serviceDiscoveryOnDefaultNetwork() && n.Name() == defaultNetName) || ++ // c.NetworkSettings.IsAnonymousEndpoint { ++ // createOptions = append(createOptions, libnetwork.CreateOptionAnonymous()) ++ // } + + if epConfig != nil { + ipam := epConfig.IPAMConfig Index: sysutils/docker-freebsd/files/patch-daemon_oci__freebsd.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-daemon_oci__freebsd.go @@ -0,0 +1,77 @@ +--- daemon/oci_freebsd.go.orig 2020-09-18 09:01:00 UTC ++++ 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 ++} Index: sysutils/docker-freebsd/files/patch-daemon_stats_collector__unix.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-daemon_stats_collector__unix.go @@ -0,0 +1,30 @@ +--- daemon/stats/collector_unix.go.orig 2019-10-07 21:12:15 UTC ++++ daemon/stats/collector_unix.go +@@ -7,15 +7,11 @@ import ( + "os" + "strconv" + "strings" +- +- "github.com/opencontainers/runc/libcontainer/system" +- "golang.org/x/sys/unix" + ) + + // platformNewStatsCollector performs platform specific initialisation of the + // Collector structure. + func platformNewStatsCollector(s *Collector) { +- s.clockTicksPerSecond = uint64(system.GetClockTicks()) + } + + const nanoSecondsPerSecond = 1e9 +@@ -66,10 +62,5 @@ func (s *Collector) getSystemCPUUsage() (uint64, error + } + + func (s *Collector) getNumberOnlineCPUs() (uint32, error) { +- var cpuset unix.CPUSet +- err := unix.SchedGetaffinity(0, &cpuset) +- if err != nil { +- return 0, err +- } +- return uint32(cpuset.Count()), nil ++ return 0, nil + } Index: sysutils/docker-freebsd/files/patch-daemon_update__freebsd.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-daemon_update__freebsd.go @@ -0,0 +1,14 @@ +--- daemon/update_freebsd.go.orig 2020-09-18 09:01:00 UTC ++++ daemon/update_freebsd.go +@@ -0,0 +1,11 @@ ++package daemon ++ ++import ( ++ "github.com/docker/docker/api/types/container" ++ libcontainerdtypes "github.com/docker/docker/libcontainerd/types" ++) ++ ++func toContainerdResources(resources container.Resources) *libcontainerdtypes.Resources { ++ var r *libcontainerdtypes.Resources ++ return r ++} Index: sysutils/docker-freebsd/files/patch-daemon_volumes__freebsd.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-daemon_volumes__freebsd.go @@ -0,0 +1,12 @@ +--- daemon/volumes_freebsd.go.orig 2020-09-04 09:13:42 UTC ++++ daemon/volumes_freebsd.go +@@ -0,0 +1,9 @@ ++package daemon // import "github.com/docker/docker/daemon" ++ ++import ( ++ "github.com/docker/docker/api/types/mount" ++) ++ ++func (daemon *Daemon) validateBindDaemonRoot(m mount.Mount) (bool, error) { ++ return false, nil ++} Index: sysutils/docker-freebsd/files/patch-hack_make.sh =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-hack_make.sh @@ -0,0 +1,19 @@ +--- hack/make.sh.orig 2020-09-18 09:00:53 UTC ++++ hack/make.sh +@@ -120,7 +120,6 @@ BUILDFLAGS=( ${BUILDFLAGS} "${ORIG_BUILDFLAGS[@]}" ) + + LDFLAGS_STATIC_DOCKER=" + $LDFLAGS_STATIC +- -extldflags \"$EXTLDFLAGS_STATIC\" + " + + if [ "$(uname -s)" = 'FreeBSD' ]; then +@@ -130,7 +129,7 @@ if [ "$(uname -s)" = 'FreeBSD' ]; then + + # "-extld clang" is a workaround for + # https://code.google.com/p/go/issues/detail?id=6845 +- LDFLAGS="$LDFLAGS -extld clang" ++ LDFLAGS="$LDFLAGS -extld clang -extldflags -Wl,-z,notext" + fi + + bundle() { Index: sysutils/docker-freebsd/files/patch-libcontainerd_libcontainerd__freebsd.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-libcontainerd_libcontainerd__freebsd.go @@ -0,0 +1,19 @@ +This is a copy from libcontainerd_linux.go + +--- libcontainerd/libcontainerd_freebsd.go.orig 2019-06-24 09:47:19 UTC ++++ libcontainerd/libcontainerd_freebsd.go +@@ -0,0 +1,14 @@ ++package libcontainerd // import "github.com/docker/docker/libcontainerd" ++ ++import ( ++ "context" ++ ++ "github.com/containerd/containerd" ++ "github.com/docker/docker/libcontainerd/remote" ++ libcontainerdtypes "github.com/docker/docker/libcontainerd/types" ++) ++ ++// NewClient creates a new libcontainerd client from a containerd client ++func NewClient(ctx context.Context, cli *containerd.Client, stateDir, ns string, b libcontainerdtypes.Backend) (libcontainerdtypes.Client, error) { ++ return remote.NewClient(ctx, cli, stateDir, ns, b) ++} Index: sysutils/docker-freebsd/files/patch-libcontainerd_oom__unix.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-libcontainerd_oom__unix.go @@ -0,0 +1,10 @@ +--- libcontainerd/oom_unix.go.orig 2020-09-04 14:57:27 UTC ++++ libcontainerd/oom_unix.go +@@ -0,0 +1,7 @@ ++// +build solaris,freebsd +build !linux ++ ++package libcontainerd ++ ++func setOOMScore(pid, score int) error { ++ return nil ++} Index: sysutils/docker-freebsd/files/patch-libcontainerd_remote_client__freebsd.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-libcontainerd_remote_client__freebsd.go @@ -0,0 +1,129 @@ +--- libcontainerd/remote/client_freebsd.go.orig 2019-06-24 18:09:27 UTC ++++ libcontainerd/remote/client_freebsd.go +@@ -0,0 +1,126 @@ ++package remote // import "github.com/docker/docker/libcontainerd/remote" ++ ++import ( ++ "context" ++ "fmt" ++ "os" ++ "path/filepath" ++ "strings" ++ ++ "github.com/containerd/containerd" ++ "github.com/containerd/containerd/cio" ++ "github.com/containerd/containerd/containers" ++ libcontainerdtypes "github.com/docker/docker/libcontainerd/types" ++ "github.com/docker/docker/pkg/idtools" ++ "github.com/opencontainers/runtime-spec/specs-go" ++ "github.com/sirupsen/logrus" ++) ++ ++const runtimeName = "io.containerd.runtime.v1.linux" ++ ++func summaryFromInterface(i interface{}) (*libcontainerdtypes.Summary, error) { ++ return &libcontainerdtypes.Summary{}, nil ++} ++ ++func (c *client) UpdateResources(ctx context.Context, containerID string, resources *libcontainerdtypes.Resources) error { ++ p, err := c.getProcess(ctx, containerID, libcontainerdtypes.InitProcessName) ++ if err != nil { ++ return err ++ } ++ ++ // go doesn't like the alias in 1.8, this means this need to be ++ // platform specific ++ return p.(containerd.Task).Update(ctx, containerd.WithResources((*specs.LinuxResources)(resources))) ++} ++ ++func hostIDFromMap(id uint32, mp []specs.LinuxIDMapping) int { ++ for _, m := range mp { ++ if id >= m.ContainerID && id <= m.ContainerID+m.Size-1 { ++ return int(m.HostID + id - m.ContainerID) ++ } ++ } ++ return 0 ++} ++ ++func getSpecUser(ociSpec *specs.Spec) (int, int) { ++ var ( ++ uid int ++ gid int ++ ) ++ ++ for _, ns := range ociSpec.Linux.Namespaces { ++ if ns.Type == specs.UserNamespace { ++ uid = hostIDFromMap(0, ociSpec.Linux.UIDMappings) ++ gid = hostIDFromMap(0, ociSpec.Linux.GIDMappings) ++ break ++ } ++ } ++ ++ return uid, gid ++} ++ ++// WithBundle creates the bundle for the container ++func WithBundle(bundleDir string, ociSpec *specs.Spec) containerd.NewContainerOpts { ++ return func(ctx context.Context, client *containerd.Client, c *containers.Container) error { ++ if c.Labels == nil { ++ c.Labels = make(map[string]string) ++ } ++ uid, gid := getSpecUser(ociSpec) ++ if uid == 0 && gid == 0 { ++ c.Labels[DockerContainerBundlePath] = bundleDir ++ return idtools.MkdirAllAndChownNew(bundleDir, 0755, idtools.Identity{UID: 0, GID: 0}) ++ } ++ ++ p := string(filepath.Separator) ++ components := strings.Split(bundleDir, string(filepath.Separator)) ++ for _, d := range components[1:] { ++ p = filepath.Join(p, d) ++ fi, err := os.Stat(p) ++ if err != nil && !os.IsNotExist(err) { ++ return err ++ } ++ if os.IsNotExist(err) || fi.Mode()&1 == 0 { ++ p = fmt.Sprintf("%s.%d.%d", p, uid, gid) ++ if err := idtools.MkdirAndChown(p, 0700, idtools.Identity{UID: uid, GID: gid}); err != nil && !os.IsExist(err) { ++ return err ++ } ++ } ++ } ++ if c.Labels == nil { ++ c.Labels = make(map[string]string) ++ } ++ c.Labels[DockerContainerBundlePath] = p ++ return nil ++ } ++} ++ ++func newFIFOSet(bundleDir, processID string, withStdin, withTerminal bool) *cio.FIFOSet { ++ config := cio.Config{ ++ Terminal: withTerminal, ++ Stdout: filepath.Join(bundleDir, processID+"-stdout"), ++ } ++ paths := []string{config.Stdout} ++ ++ if withStdin { ++ config.Stdin = filepath.Join(bundleDir, processID+"-stdin") ++ paths = append(paths, config.Stdin) ++ } ++ if !withTerminal { ++ config.Stderr = filepath.Join(bundleDir, processID+"-stderr") ++ paths = append(paths, config.Stderr) ++ } ++ closer := func() error { ++ for _, path := range paths { ++ if err := os.RemoveAll(path); err != nil { ++ logrus.Warnf("libcontainerd: failed to remove fifo %v: %v", path, err) ++ } ++ } ++ return nil ++ } ++ ++ return cio.NewFIFOSet(config, closer) ++} ++ ++func (c *client) newDirectIO(ctx context.Context, fifos *cio.FIFOSet) (*cio.DirectIO, error) { ++ return cio.NewDirectIO(ctx, fifos) ++} Index: sysutils/docker-freebsd/files/patch-libcontainerd_supervisor_remote__daemon__freebsd.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-libcontainerd_supervisor_remote__daemon__freebsd.go @@ -0,0 +1,72 @@ +--- libcontainerd/supervisor/remote_daemon_freebsd.go.orig 2019-06-24 18:36:48 UTC ++++ libcontainerd/supervisor/remote_daemon_freebsd.go +@@ -0,0 +1,69 @@ ++package supervisor // import "github.com/docker/docker/libcontainerd/supervisor" ++ ++import ( ++ "os" ++ "path/filepath" ++ "syscall" ++ "time" ++ ++ "github.com/containerd/containerd/defaults" ++ "github.com/docker/docker/pkg/system" ++) ++ ++const ( ++ sockFile = "containerd.sock" ++ debugSockFile = "containerd-debug.sock" ++) ++ ++func (r *remote) setDefaults() { ++ if r.GRPC.Address == "" { ++ r.GRPC.Address = filepath.Join(r.stateDir, sockFile) ++ } ++ if r.GRPC.MaxRecvMsgSize == 0 { ++ r.GRPC.MaxRecvMsgSize = defaults.DefaultMaxRecvMsgSize ++ } ++ if r.GRPC.MaxSendMsgSize == 0 { ++ r.GRPC.MaxSendMsgSize = defaults.DefaultMaxSendMsgSize ++ } ++ if r.Debug.Address == "" { ++ r.Debug.Address = filepath.Join(r.stateDir, debugSockFile) ++ } ++ if r.OOMScore == 0 { ++ r.OOMScore = -999 ++ } ++ ++ for key, conf := range r.pluginConfs.Plugins { ++ if conf == nil { ++ r.DisabledPlugins = append(r.DisabledPlugins, key) ++ delete(r.pluginConfs.Plugins, key) ++ } ++ } ++} ++ ++func (r *remote) stopDaemon() { ++ // Ask the daemon to quit ++ syscall.Kill(r.daemonPid, syscall.SIGTERM) ++ // Wait up to 15secs for it to stop ++ for i := time.Duration(0); i < shutdownTimeout; i += time.Second { ++ if !system.IsProcessAlive(r.daemonPid) { ++ break ++ } ++ time.Sleep(time.Second) ++ } ++ ++ if system.IsProcessAlive(r.daemonPid) { ++ r.logger.WithField("pid", r.daemonPid).Warn("daemon didn't stop within 15 secs, killing it") ++ syscall.Kill(r.daemonPid, syscall.SIGKILL) ++ } ++} ++ ++func (r *remote) killDaemon() { ++ // Try to get a stack trace ++ syscall.Kill(r.daemonPid, syscall.SIGUSR1) ++ <-time.After(100 * time.Millisecond) ++ system.KillProcess(r.daemonPid) ++} ++ ++func (r *remote) platformCleanup() { ++ os.Remove(filepath.Join(r.stateDir, sockFile)) ++} Index: sysutils/docker-freebsd/files/patch-libcontainerd_supervisor_remote__daemon__options__freebsd.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-libcontainerd_supervisor_remote__daemon__options__freebsd.go @@ -0,0 +1,12 @@ +--- libcontainerd/supervisor/remote_daemon_options_freebsd.go.orig 2020-09-04 09:13:43 UTC ++++ libcontainerd/supervisor/remote_daemon_options_freebsd.go +@@ -0,0 +1,9 @@ ++package supervisor // import "github.com/docker/docker/libcontainerd/supervisor" ++ ++// WithOOMScore defines the oom_score_adj to set for the containerd process. ++func WithOOMScore(score int) DaemonOpt { ++ return func(r *remote) error { ++ r.OOMScore = score ++ return nil ++ } ++} Index: sysutils/docker-freebsd/files/patch-libcontainerd_supervisor_utils__freebsd.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-libcontainerd_supervisor_utils__freebsd.go @@ -0,0 +1,14 @@ +--- libcontainerd/supervisor/utils_freebsd.go.orig 2019-06-24 18:38:41 UTC ++++ libcontainerd/supervisor/utils_freebsd.go +@@ -0,0 +1,11 @@ ++package supervisor // import "github.com/docker/docker/libcontainerd/supervisor" ++ ++import "syscall" ++ ++// containerdSysProcAttr returns the SysProcAttr to use when exec'ing ++// containerd ++func containerdSysProcAttr() *syscall.SysProcAttr { ++ return &syscall.SysProcAttr{ ++ Setsid: true, ++ } ++} Index: sysutils/docker-freebsd/files/patch-libcontainerd_types_types__freebsd.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-libcontainerd_types_types__freebsd.go @@ -0,0 +1,36 @@ +--- libcontainerd/types/types_freebsd.go.orig 2020-09-04 14:57:27 UTC ++++ libcontainerd/types/types_freebsd.go +@@ -0,0 +1,33 @@ ++package types ++ ++import ( ++ "time" ++ ++ "github.com/opencontainers/runtime-spec/specs-go" ++) ++ ++// Summary is not used on FreeBSD ++type Summary struct{} ++ ++// Stats holds metrics properties as returned by containerd ++type Stats struct {} ++ ++// InterfaceToStats returns a stats object from the platform-specific interface. ++func InterfaceToStats(read time.Time, v interface{}) *Stats { ++ return &Stats{} ++} ++ ++// StateInfo contains description about the new state container has entered. ++type StateInfo struct { ++ //CommonStateInfo ++ ++ // Platform specific StateInfo ++ OOMKilled bool ++ ExitCode int ++} ++ ++// Resources defines updatable container resource values. TODO: it must match containerd upcoming API ++type Resources specs.LinuxResources ++ ++// Checkpoints contains the details of a checkpoint ++type Checkpoints struct{} Index: sysutils/docker-freebsd/files/patch-libcontainerd_utils__unix.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-libcontainerd_utils__unix.go @@ -0,0 +1,21 @@ +--- libcontainerd/utils_unix.go.orig 2020-09-04 14:57:27 UTC ++++ libcontainerd/utils_unix.go +@@ -0,0 +1,18 @@ ++// +build solaris,freebsd +build !linux ++ ++package libcontainerd ++ ++import ( ++ "syscall" ++ ++ "github.com/opencontainers/runtime-spec/specs-go" ++) ++ ++func getRootIDs(s specs.Spec) (int, int, error) { ++ return 0, 0, nil ++} ++ ++// setPDeathSig sets the parent death signal to SIGKILL ++func setSysProcAttr(sid bool) *syscall.SysProcAttr { ++ return nil ++} Index: sysutils/docker-freebsd/files/patch-pkg_archive_archive__unix.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-pkg_archive_archive__unix.go @@ -0,0 +1,11 @@ +--- pkg/archive/archive_unix.go.orig 2019-06-24 10:21:29 UTC ++++ pkg/archive/archive_unix.go +@@ -96,7 +96,7 @@ func handleTarTypeBlockCharFifo(hdr *tar.Header, path + mode |= unix.S_IFIFO + } + +- return system.Mknod(path, mode, int(system.Mkdev(hdr.Devmajor, hdr.Devminor))) ++ return system.Mknod(path, mode, uint64(system.Mkdev(hdr.Devmajor, hdr.Devminor))) + } + + func handleLChmod(hdr *tar.Header, path string, hdrInfo os.FileInfo) error { Index: sysutils/docker-freebsd/files/patch-pkg_archive_changes__unix.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-pkg_archive_changes__unix.go @@ -0,0 +1,11 @@ +--- pkg/archive/changes_unix.go.orig 2019-06-18 21:30:11 UTC ++++ pkg/archive/changes_unix.go +@@ -35,7 +35,7 @@ func (info *FileInfo) isDir() bool { + } + + func getIno(fi os.FileInfo) uint64 { +- return fi.Sys().(*syscall.Stat_t).Ino ++ return uint64(fi.Sys().(*syscall.Stat_t).Ino) + } + + func hasHardlinks(fi os.FileInfo) bool { Index: sysutils/docker-freebsd/files/patch-pkg_mount_mountinfo__freebsd.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-pkg_mount_mountinfo__freebsd.go @@ -0,0 +1,11 @@ +--- pkg/mount/mountinfo_freebsd.go.orig 2019-02-06 23:39:49 UTC ++++ pkg/mount/mountinfo_freebsd.go +@@ -37,7 +37,7 @@ func parseMountTable(filter FilterFunc) ([]*Info, erro + + if filter != nil { + // filter out entries we're not interested in +- skip, stop = filter(p) ++ skip, stop = filter(&mountinfo) + if skip { + continue + } Index: sysutils/docker-freebsd/files/patch-pkg_mount_sharedsubtree__freebsd.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-pkg_mount_sharedsubtree__freebsd.go @@ -0,0 +1,70 @@ +--- pkg/mount/sharedsubtree_freebsd.go.orig 2020-09-04 14:57:27 UTC ++++ 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) ++} Index: sysutils/docker-freebsd/files/patch-pkg_parsers_operatingsystem_operatingsystem__freebsd.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-pkg_parsers_operatingsystem_operatingsystem__freebsd.go @@ -0,0 +1,34 @@ +--- pkg/parsers/operatingsystem/operatingsystem_freebsd.go.orig 2020-09-04 14:57:27 UTC ++++ pkg/parsers/operatingsystem/operatingsystem_freebsd.go +@@ -0,0 +1,31 @@ ++// +build freebsd ++ ++package operatingsystem ++ ++import ( ++ "errors" ++ "os/exec" ++ "syscall" ++) ++ ++// GetOperatingSystem gets the name of the current operating system. ++func GetOperatingSystem() (string, error) { ++ cmd := exec.Command("uname", "-s") ++ osName, err := cmd.Output() ++ if err != nil { ++ return "", err ++ } ++ return string(osName), nil ++} ++ ++// IsContainerized returns true if we are running inside a container. ++func IsContainerized() (bool, error) { ++ jailed, err := syscall.Sysctl("security.jail.jailed") ++ if err != nil { ++ return false, errors.New("Cannot detect if we are in a jail") ++ } ++ if jailed[0] == 1 { ++ return true, nil ++ } ++ return false, nil ++} Index: sysutils/docker-freebsd/files/patch-pkg_parsers_operatingsystem_operatingsystem__unix.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-pkg_parsers_operatingsystem_operatingsystem__unix.go @@ -0,0 +1,15 @@ +--- pkg/parsers/operatingsystem/operatingsystem_unix.go.orig 2020-09-04 14:54:52 UTC ++++ pkg/parsers/operatingsystem/operatingsystem_unix.go +@@ -1,4 +1,4 @@ +-// +build freebsd darwin ++// +build darwin + + package operatingsystem // import "github.com/docker/docker/pkg/parsers/operatingsystem" + +@@ -20,6 +20,5 @@ func GetOperatingSystem() (string, error) { + // IsContainerized returns true if we are running inside a container. + // No-op on FreeBSD and Darwin, always returns false. + func IsContainerized() (bool, error) { +- // TODO: Implement jail detection for freeBSD + return false, errors.New("Cannot detect if we are in container") + } Index: sysutils/docker-freebsd/files/patch-pkg_system_mknod.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-pkg_system_mknod.go @@ -0,0 +1,11 @@ +--- pkg/system/mknod.go.orig 2019-06-18 21:30:11 UTC ++++ pkg/system/mknod.go +@@ -8,7 +8,7 @@ import ( + + // Mknod creates a filesystem node (file, device special file or named pipe) named path + // with attributes specified by mode and dev. +-func Mknod(path string, mode uint32, dev int) error { ++func Mknod(path string, mode uint32, dev uint64) error { + return unix.Mknod(path, mode, dev) + } + Index: sysutils/docker-freebsd/files/patch-plugin_manager__freebsd.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-plugin_manager__freebsd.go @@ -0,0 +1,33 @@ +Fix build on FreeBSD by copying Windows stub + +--- plugin/manager_freebsd.go.orig 2019-03-08 09:00:07 UTC ++++ plugin/manager_freebsd.go +@@ -0,0 +1,28 @@ ++package plugin // import "github.com/docker/docker/plugin" ++ ++import ( ++ "fmt" ++ ++ "github.com/docker/docker/plugin/v2" ++ specs "github.com/opencontainers/runtime-spec/specs-go" ++) ++ ++func (pm *Manager) enable(p *v2.Plugin, c *controller, force bool) error { ++ return fmt.Errorf("Not implemented") ++} ++ ++func (pm *Manager) initSpec(p *v2.Plugin) (*specs.Spec, error) { ++ return nil, fmt.Errorf("Not implemented") ++} ++ ++func (pm *Manager) disable(p *v2.Plugin, c *controller) error { ++ return fmt.Errorf("Not implemented") ++} ++ ++func (pm *Manager) restore(p *v2.Plugin, c *controller) error { ++ return fmt.Errorf("Not implemented") ++} ++ ++// Shutdown plugins ++func (pm *Manager) Shutdown() { ++} Index: sysutils/docker-freebsd/files/patch-vendor_github.com_containerd_cgroups_memory.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-vendor_github.com_containerd_cgroups_memory.go @@ -0,0 +1,11 @@ +--- vendor/github.com/containerd/cgroups/memory.go.orig 2020-09-18 09:00:55 UTC ++++ vendor/github.com/containerd/cgroups/memory.go +@@ -208,7 +208,7 @@ func (m *memoryController) OOMEventFD(path string) (ui + return 0, err + } + defer f.Close() +- fd, _, serr := unix.RawSyscall(unix.SYS_EVENTFD2, 0, unix.EFD_CLOEXEC, 0) ++ fd, _, serr := unix.RawSyscall(0, 0, 0, 0)//unix.RawSyscall(unix.SYS_EVENTFD2, 0, unix.EFD_CLOEXEC, 0) + if serr != 0 { + return 0, serr + } Index: sysutils/docker-freebsd/files/patch-vendor_github.com_containerd_containerd_archive_tar__unix.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-vendor_github.com_containerd_containerd_archive_tar__unix.go @@ -0,0 +1,11 @@ +--- vendor/github.com/containerd/containerd/archive/tar_unix.go.orig 2019-06-18 21:30:11 UTC ++++ vendor/github.com/containerd/containerd/archive/tar_unix.go +@@ -122,7 +122,7 @@ func handleTarTypeBlockCharFifo(hdr *tar.Header, path + mode |= unix.S_IFIFO + } + +- return unix.Mknod(path, mode, int(unix.Mkdev(uint32(hdr.Devmajor), uint32(hdr.Devminor)))) ++ return unix.Mknod(path, mode, unix.Mkdev(uint32(hdr.Devmajor), uint32(hdr.Devminor))) + } + + func handleLChmod(hdr *tar.Header, path string, hdrInfo os.FileInfo) error { Index: sysutils/docker-freebsd/files/patch-vendor_github.com_containerd_containerd_runtime_v1_linux_bundle.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-vendor_github.com_containerd_containerd_runtime_v1_linux_bundle.go @@ -0,0 +1,8 @@ +--- vendor/github.com/containerd/containerd/runtime/v1/linux/bundle.go.orig 2019-02-26 21:02:47 UTC ++++ vendor/github.com/containerd/containerd/runtime/v1/linux/bundle.go +@@ -1,4 +1,4 @@ +-// +build linux ++// +build linux freebsd + + /* + Copyright The containerd Authors. Index: sysutils/docker-freebsd/files/patch-vendor_github.com_containerd_containerd_runtime_v1_linux_process.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-vendor_github.com_containerd_containerd_runtime_v1_linux_process.go @@ -0,0 +1,8 @@ +--- vendor/github.com/containerd/containerd/runtime/v1/linux/process.go.orig 2019-02-06 23:39:49 UTC ++++ vendor/github.com/containerd/containerd/runtime/v1/linux/process.go +@@ -1,4 +1,4 @@ +-// +build linux ++// +build linux freebsd + + /* + Copyright The containerd Authors. Index: sysutils/docker-freebsd/files/patch-vendor_github.com_containerd_containerd_runtime_v1_linux_runtime.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-vendor_github.com_containerd_containerd_runtime_v1_linux_runtime.go @@ -0,0 +1,8 @@ +--- vendor/github.com/containerd/containerd/runtime/v1/linux/runtime.go.orig 2019-02-06 23:39:49 UTC ++++ vendor/github.com/containerd/containerd/runtime/v1/linux/runtime.go +@@ -1,4 +1,4 @@ +-// +build linux ++// +build linux freebsd + + /* + Copyright The containerd Authors. Index: sysutils/docker-freebsd/files/patch-vendor_github.com_containerd_containerd_runtime_v1_linux_task.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-vendor_github.com_containerd_containerd_runtime_v1_linux_task.go @@ -0,0 +1,8 @@ +--- vendor/github.com/containerd/containerd/runtime/v1/linux/task.go.orig 2019-02-06 23:39:49 UTC ++++ vendor/github.com/containerd/containerd/runtime/v1/linux/task.go +@@ -1,4 +1,4 @@ +-// +build linux ++// +build linux freebsd + + /* + Copyright The containerd Authors. Index: sysutils/docker-freebsd/files/patch-vendor_github.com_containerd_continuity_devices_devices__unix.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-vendor_github.com_containerd_continuity_devices_devices__unix.go @@ -0,0 +1,11 @@ +--- vendor/github.com/containerd/continuity/devices/devices_unix.go.orig 2019-06-18 21:30:11 UTC ++++ vendor/github.com/containerd/continuity/devices/devices_unix.go +@@ -55,7 +55,7 @@ func Mknod(p string, mode os.FileMode, maj, min int) e + m |= unix.S_IFIFO + } + +- return unix.Mknod(p, m, int(dev)) ++ return unix.Mknod(p, m, dev) + } + + // syscallMode returns the syscall-specific mode bits from Go's portable mode bits. Index: sysutils/docker-freebsd/files/patch-vendor_github.com_containerd_continuity_fs_copy__unix.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-vendor_github.com_containerd_continuity_fs_copy__unix.go @@ -0,0 +1,9 @@ +--- vendor/github.com/containerd/continuity/fs/copy_unix.go.orig 2019-06-18 21:30:11 UTC ++++ vendor/github.com/containerd/continuity/fs/copy_unix.go +@@ -108,5 +108,5 @@ func copyDevice(dst string, fi os.FileInfo) error { + if !ok { + return errors.New("unsupported stat type") + } +- return unix.Mknod(dst, uint32(fi.Mode()), int(st.Rdev)) ++ return unix.Mknod(dst, uint32(fi.Mode()), st.Rdev) + } Index: sysutils/docker-freebsd/files/patch-vendor_github.com_docker_libnetwork_drivers__freebsd.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-vendor_github.com_docker_libnetwork_drivers__freebsd.go @@ -0,0 +1,17 @@ +--- vendor/github.com/docker/libnetwork/drivers_freebsd.go.orig 2020-09-04 14:54:57 UTC ++++ 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"}, + } Index: sysutils/docker-freebsd/files/patch-vendor_github.com_docker_libnetwork_drivers_bridge_bridge.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-vendor_github.com_docker_libnetwork_drivers_bridge_bridge.go @@ -0,0 +1,8 @@ +--- vendor/github.com/docker/libnetwork/drivers/bridge/bridge.go.orig 2020-09-04 14:54:57 UTC ++++ vendor/github.com/docker/libnetwork/drivers/bridge/bridge.go +@@ -1,3 +1,5 @@ ++// +build linux ++ + package bridge + + import ( Index: sysutils/docker-freebsd/files/patch-vendor_github.com_docker_libnetwork_drivers_bridge_bridge__store.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-vendor_github.com_docker_libnetwork_drivers_bridge_bridge__store.go @@ -0,0 +1,8 @@ +--- vendor/github.com/docker/libnetwork/drivers/bridge/bridge_store.go.orig 2020-09-04 14:54:57 UTC ++++ vendor/github.com/docker/libnetwork/drivers/bridge/bridge_store.go +@@ -1,3 +1,5 @@ ++// +build linux ++ + package bridge + + import ( Index: sysutils/docker-freebsd/files/patch-vendor_github.com_docker_libnetwork_drivers_bridge_interface.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-vendor_github.com_docker_libnetwork_drivers_bridge_interface.go @@ -0,0 +1,8 @@ +--- vendor/github.com/docker/libnetwork/drivers/bridge/interface.go.orig 2020-09-04 14:54:57 UTC ++++ vendor/github.com/docker/libnetwork/drivers/bridge/interface.go +@@ -1,3 +1,5 @@ ++// +build linux ++ + package bridge + + import ( Index: sysutils/docker-freebsd/files/patch-vendor_github.com_docker_libnetwork_drivers_bridge_link.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-vendor_github.com_docker_libnetwork_drivers_bridge_link.go @@ -0,0 +1,8 @@ +--- vendor/github.com/docker/libnetwork/drivers/bridge/link.go.orig 2020-09-04 14:54:57 UTC ++++ vendor/github.com/docker/libnetwork/drivers/bridge/link.go +@@ -1,3 +1,5 @@ ++// +build linux ++ + package bridge + + import ( Index: sysutils/docker-freebsd/files/patch-vendor_github.com_docker_libnetwork_drivers_bridge_port__mapping.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-vendor_github.com_docker_libnetwork_drivers_bridge_port__mapping.go @@ -0,0 +1,8 @@ +--- vendor/github.com/docker/libnetwork/drivers/bridge/port_mapping.go.orig 2020-09-04 14:54:57 UTC ++++ vendor/github.com/docker/libnetwork/drivers/bridge/port_mapping.go +@@ -1,3 +1,5 @@ ++// +build linux ++ + package bridge + + import ( Index: sysutils/docker-freebsd/files/patch-vendor_github.com_docker_libnetwork_drivers_bridge_setup.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-vendor_github.com_docker_libnetwork_drivers_bridge_setup.go @@ -0,0 +1,8 @@ +--- vendor/github.com/docker/libnetwork/drivers/bridge/setup.go.orig 2020-09-04 14:54:57 UTC ++++ vendor/github.com/docker/libnetwork/drivers/bridge/setup.go +@@ -1,3 +1,5 @@ ++// +build linux ++ + package bridge + + type setupStep func(*networkConfiguration, *bridgeInterface) error Index: sysutils/docker-freebsd/files/patch-vendor_github.com_docker_libnetwork_drivers_bridge_setup__bridgenetfiltering.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-vendor_github.com_docker_libnetwork_drivers_bridge_setup__bridgenetfiltering.go @@ -0,0 +1,8 @@ +--- vendor/github.com/docker/libnetwork/drivers/bridge/setup_bridgenetfiltering.go.orig 2020-09-04 14:54:57 UTC ++++ vendor/github.com/docker/libnetwork/drivers/bridge/setup_bridgenetfiltering.go +@@ -1,3 +1,5 @@ ++// +build linux ++ + package bridge + + import ( Index: sysutils/docker-freebsd/files/patch-vendor_github.com_docker_libnetwork_drivers_bridge_setup__device.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-vendor_github.com_docker_libnetwork_drivers_bridge_setup__device.go @@ -0,0 +1,8 @@ +--- vendor/github.com/docker/libnetwork/drivers/bridge/setup_device.go.orig 2020-09-04 14:54:57 UTC ++++ vendor/github.com/docker/libnetwork/drivers/bridge/setup_device.go +@@ -1,3 +1,5 @@ ++// +build linux ++ + package bridge + + import ( Index: sysutils/docker-freebsd/files/patch-vendor_github.com_docker_libnetwork_drivers_bridge_setup__firewalld.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-vendor_github.com_docker_libnetwork_drivers_bridge_setup__firewalld.go @@ -0,0 +1,8 @@ +--- vendor/github.com/docker/libnetwork/drivers/bridge/setup_firewalld.go.orig 2020-09-04 14:54:57 UTC ++++ vendor/github.com/docker/libnetwork/drivers/bridge/setup_firewalld.go +@@ -1,3 +1,5 @@ ++// +build linux ++ + package bridge + + import "github.com/docker/libnetwork/iptables" Index: sysutils/docker-freebsd/files/patch-vendor_github.com_docker_libnetwork_drivers_bridge_setup__ip__tables.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-vendor_github.com_docker_libnetwork_drivers_bridge_setup__ip__tables.go @@ -0,0 +1,8 @@ +--- vendor/github.com/docker/libnetwork/drivers/bridge/setup_ip_tables.go.orig 2020-09-04 14:54:57 UTC ++++ vendor/github.com/docker/libnetwork/drivers/bridge/setup_ip_tables.go +@@ -1,3 +1,5 @@ ++// +build linux ++ + package bridge + + import ( Index: sysutils/docker-freebsd/files/patch-vendor_github.com_docker_libnetwork_drivers_bridge_setup__ipv4.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-vendor_github.com_docker_libnetwork_drivers_bridge_setup__ipv4.go @@ -0,0 +1,8 @@ +--- vendor/github.com/docker/libnetwork/drivers/bridge/setup_ipv4.go.orig 2020-09-04 14:54:57 UTC ++++ vendor/github.com/docker/libnetwork/drivers/bridge/setup_ipv4.go +@@ -1,3 +1,5 @@ ++// +build linux ++ + package bridge + + import ( Index: sysutils/docker-freebsd/files/patch-vendor_github.com_docker_libnetwork_drivers_bridge_setup__ipv6.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-vendor_github.com_docker_libnetwork_drivers_bridge_setup__ipv6.go @@ -0,0 +1,8 @@ +--- vendor/github.com/docker/libnetwork/drivers/bridge/setup_ipv6.go.orig 2020-09-04 14:54:57 UTC ++++ vendor/github.com/docker/libnetwork/drivers/bridge/setup_ipv6.go +@@ -1,3 +1,5 @@ ++// +build linux ++ + package bridge + + import ( Index: sysutils/docker-freebsd/files/patch-vendor_github.com_docker_libnetwork_drivers_bridge_setup__verify.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-vendor_github.com_docker_libnetwork_drivers_bridge_setup__verify.go @@ -0,0 +1,8 @@ +--- vendor/github.com/docker/libnetwork/drivers/bridge/setup_verify.go.orig 2020-09-04 14:54:57 UTC ++++ vendor/github.com/docker/libnetwork/drivers/bridge/setup_verify.go +@@ -1,3 +1,5 @@ ++// +build linux ++ + package bridge + + import ( Index: sysutils/docker-freebsd/files/patch-vendor_github.com_docker_libnetwork_drivers_freebsd_bridge_bridge.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-vendor_github.com_docker_libnetwork_drivers_freebsd_bridge_bridge.go @@ -0,0 +1,1097 @@ +--- vendor/github.com/docker/libnetwork/drivers/freebsd/bridge/bridge.go.orig 2020-09-04 14:57:27 UTC ++++ 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 ++} Index: sysutils/docker-freebsd/files/patch-vendor_github.com_docker_libnetwork_drivers_freebsd_bridge_bridge__store.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-vendor_github.com_docker_libnetwork_drivers_freebsd_bridge_bridge__store.go @@ -0,0 +1,387 @@ +--- vendor/github.com/docker/libnetwork/drivers/freebsd/bridge/bridge_store.go.orig 2020-09-04 14:57:27 UTC ++++ 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 ++} Index: sysutils/docker-freebsd/files/patch-vendor_github.com_docker_libnetwork_drivers_freebsd_bridge_errors.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-vendor_github.com_docker_libnetwork_drivers_freebsd_bridge_errors.go @@ -0,0 +1,123 @@ +--- vendor/github.com/docker/libnetwork/drivers/freebsd/bridge/errors.go.orig 2020-09-04 14:57:27 UTC ++++ 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() {} Index: sysutils/docker-freebsd/files/patch-vendor_github.com_docker_libnetwork_drivers_freebsd_bridge_port__mapping.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-vendor_github.com_docker_libnetwork_drivers_freebsd_bridge_port__mapping.go @@ -0,0 +1,238 @@ +--- vendor/github.com/docker/libnetwork/drivers/freebsd/bridge/port_mapping.go.orig 2020-09-04 14:57:27 UTC ++++ 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 ++} Index: sysutils/docker-freebsd/files/patch-vendor_github.com_docker_libnetwork_iptables_conntrack.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-vendor_github.com_docker_libnetwork_iptables_conntrack.go @@ -0,0 +1,8 @@ +--- vendor/github.com/docker/libnetwork/iptables/conntrack.go.orig 2020-09-04 14:54:57 UTC ++++ vendor/github.com/docker/libnetwork/iptables/conntrack.go +@@ -1,3 +1,5 @@ ++// +build !freebsd ++ + package iptables + + import ( Index: sysutils/docker-freebsd/files/patch-vendor_github.com_docker_libnetwork_netutils_utils__freebsd.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-vendor_github.com_docker_libnetwork_netutils_utils__freebsd.go @@ -0,0 +1,96 @@ +--- vendor/github.com/docker/libnetwork/netutils/utils_freebsd.go.orig 2020-10-23 18:37:21 UTC ++++ vendor/github.com/docker/libnetwork/netutils/utils_freebsd.go +@@ -1,9 +1,15 @@ + package netutils + + import ( ++ "fmt" + "net" ++ "os/exec" ++ "strings" + +- "github.com/docker/libnetwork/types" ++ "github.com/docker/libnetwork/ipamutils" ++ "github.com/docker/libnetwork/ns" ++ "github.com/docker/libnetwork/osl" ++ "github.com/pkg/errors" + ) + + // ElectInterfaceAddresses looks for an interface on the OS with the specified name +@@ -13,11 +19,74 @@ import ( + // list the first IPv4 address which does not conflict with other + // interfaces on the system. + func ElectInterfaceAddresses(name string) ([]*net.IPNet, []*net.IPNet, error) { +- return nil, nil, types.NotImplementedErrorf("not supported on freebsd") ++ var ( ++ v4Nets []*net.IPNet ++ v6Nets []*net.IPNet ++ ) ++ ++ defer osl.InitOSContext()() ++ ++ link, _ := ns.NlHandle().LinkByName(name) ++ // disabled on freebsd for now ++ // if link != nil { ++ // v4addr, err := ns.NlHandle().AddrList(link, netlink.FAMILY_V4) ++ // if err != nil { ++ // return nil, nil, err ++ // } ++ // v6addr, err := ns.NlHandle().AddrList(link, netlink.FAMILY_V6) ++ // if err != nil { ++ // return nil, nil, err ++ // } ++ // for _, nlAddr := range v4addr { ++ // v4Nets = append(v4Nets, nlAddr.IPNet) ++ // } ++ // for _, nlAddr := range v6addr { ++ // v6Nets = append(v6Nets, nlAddr.IPNet) ++ // } ++ // } ++ ++ if link == nil || len(v4Nets) == 0 { ++ // Choose from predefined local scope networks ++ v4Net, err := FindAvailableNetwork(ipamutils.PredefinedLocalScopeDefaultNetworks) ++ if err != nil { ++ return nil, nil, errors.Wrapf(err, "PredefinedLocalScopeDefaultNetworks List: %+v", ++ ipamutils.PredefinedLocalScopeDefaultNetworks) ++ } ++ v4Nets = append(v4Nets, v4Net) ++ } ++ ++ return v4Nets, v6Nets, nil + } + + // 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") + } Index: sysutils/docker-freebsd/files/patch-vendor_github.com_docker_libnetwork_ns_init__freebsd.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-vendor_github.com_docker_libnetwork_ns_init__freebsd.go @@ -0,0 +1,100 @@ +--- vendor/github.com/docker/libnetwork/ns/init_freebsd.go.orig 2020-10-23 18:37:27 UTC ++++ vendor/github.com/docker/libnetwork/ns/init_freebsd.go +@@ -0,0 +1,97 @@ ++package ns ++ ++import ( ++ "fmt" ++ "os/exec" ++ "strings" ++ "sync" ++ "time" ++ ++ "github.com/sirupsen/logrus" ++ "github.com/vishvananda/netlink" ++ "github.com/vishvananda/netns" ++) ++ ++var ( ++ initNs netns.NsHandle ++ initNl *netlink.Handle ++ initOnce sync.Once ++ // NetlinkSocketsTimeout represents the default timeout duration for the sockets ++ NetlinkSocketsTimeout = 3 * time.Second ++) ++ ++// Init initializes a new network namespace ++func Init() { ++ var err error ++ initNs, err = netns.Get() ++ if err != nil { ++ logrus.Errorf("could not get initial namespace: %v", err) ++ } ++ initNl, err = netlink.NewHandle(getSupportedNlFamilies()...) ++ if err != nil { ++ logrus.Errorf("could not create netlink handle on initial namespace: %v", err) ++ } ++ err = initNl.SetSocketTimeout(NetlinkSocketsTimeout) ++ if err != nil { ++ logrus.Warnf("Failed to set the timeout on the default netlink handle sockets: %v", err) ++ } ++} ++ ++// SetNamespace sets the initial namespace handler ++func SetNamespace() error { ++ initOnce.Do(Init) ++ if err := netns.Set(initNs); err != nil { ++ linkInfo, linkErr := getLink() ++ if linkErr != nil { ++ linkInfo = linkErr.Error() ++ } ++ return fmt.Errorf("failed to set to initial namespace, %v, initns fd %d: %v", linkInfo, initNs, err) ++ } ++ return nil ++} ++ ++// ParseHandlerInt transforms the namespace handler into an integer ++func ParseHandlerInt() int { ++ return int(getHandler()) ++} ++ ++// GetHandler returns the namespace handler ++func getHandler() netns.NsHandle { ++ initOnce.Do(Init) ++ return initNs ++} ++ ++func getLink() (string, error) { ++ return "", fmt.Errorf("getLink not implemented on freebsd") ++} ++ ++// NlHandle returns the netlink handler ++func NlHandle() *netlink.Handle { ++ initOnce.Do(Init) ++ return initNl ++} ++ ++func getSupportedNlFamilies() []int { ++ // not implemented on freebsd ++ return []int{} ++} ++ ++// API check on required xfrm modules (xfrm_user, xfrm_algo) ++func checkXfrmSocket() error { ++ return fmt.Errorf("checkXfrmSocket not implemented on freebsd") ++} ++ ++func loadNfConntrackModules() error { ++ if out, err := exec.Command("modprobe", "-va", "nf_conntrack").CombinedOutput(); err != nil { ++ return fmt.Errorf("Running modprobe nf_conntrack failed with message: `%s`, error: %v", strings.TrimSpace(string(out)), err) ++ } ++ if out, err := exec.Command("modprobe", "-va", "nf_conntrack_netlink").CombinedOutput(); err != nil { ++ return fmt.Errorf("Running modprobe nf_conntrack_netlink failed with message: `%s`, error: %v", strings.TrimSpace(string(out)), err) ++ } ++ return nil ++} ++ ++// API check on required nf_conntrack* modules (nf_conntrack, nf_conntrack_netlink) ++func checkNfSocket() error { ++ return fmt.Errorf("checkNfSocket not implemented on freebsd") ++} Index: sysutils/docker-freebsd/files/patch-vendor_github.com_docker_libnetwork_ns_init__unspecified.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-vendor_github.com_docker_libnetwork_ns_init__unspecified.go @@ -0,0 +1,4 @@ +--- vendor/github.com/docker/libnetwork/ns/init_unspecified.go.orig 2020-09-04 14:57:27 UTC ++++ vendor/github.com/docker/libnetwork/ns/init_unspecified.go +@@ -0,0 +1 @@ ++package ns Index: sysutils/docker-freebsd/files/patch-vendor_github.com_docker_libnetwork_portmapper_mapper__freebsd.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-vendor_github.com_docker_libnetwork_portmapper_mapper__freebsd.go @@ -0,0 +1,34 @@ +--- vendor/github.com/docker/libnetwork/portmapper/mapper_freebsd.go.orig 2019-06-24 18:33:51 UTC ++++ vendor/github.com/docker/libnetwork/portmapper/mapper_freebsd.go +@@ -0,0 +1,31 @@ ++package portmapper ++ ++import ( ++ "net" ++ "sync" ++ ++ "github.com/docker/libnetwork/portallocator" ++) ++ ++// PortMapper manages the network address translation ++type PortMapper struct { ++ bridgeName string ++ ++ // udp:ip:port ++ currentMappings map[string]*mapping ++ lock sync.Mutex ++ ++ proxyPath string ++ ++ Allocator *portallocator.PortAllocator ++} ++ ++// AppendForwardingTableEntry adds a port mapping to the forwarding table ++func (pm *PortMapper) AppendForwardingTableEntry(proto string, sourceIP net.IP, sourcePort int, containerIP string, containerPort int) error { ++ return nil ++} ++ ++// DeleteForwardingTableEntry removes a port mapping from the forwarding table ++func (pm *PortMapper) DeleteForwardingTableEntry(proto string, sourceIP net.IP, sourcePort int, containerIP string, containerPort int) error { ++ return nil ++} Index: sysutils/docker-freebsd/files/patch-vendor_github.com_docker_libnetwork_portmapper_proxy__freebsd.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-vendor_github.com_docker_libnetwork_portmapper_proxy__freebsd.go @@ -0,0 +1,41 @@ +--- vendor/github.com/docker/libnetwork/portmapper/proxy_freebsd.go.orig 2019-06-24 18:17:46 UTC ++++ vendor/github.com/docker/libnetwork/portmapper/proxy_freebsd.go +@@ -0,0 +1,38 @@ ++package portmapper ++ ++import ( ++ "net" ++ "os/exec" ++ "strconv" ++ "syscall" ++) ++ ++func newProxyCommand(proto string, hostIP net.IP, hostPort int, containerIP net.IP, containerPort int, proxyPath string) (userlandProxy, error) { ++ path := proxyPath ++ if proxyPath == "" { ++ cmd, err := exec.LookPath(userlandProxyCommandName) ++ if err != nil { ++ return nil, err ++ } ++ path = cmd ++ } ++ ++ args := []string{ ++ path, ++ "-proto", proto, ++ "-host-ip", hostIP.String(), ++ "-host-port", strconv.Itoa(hostPort), ++ "-container-ip", containerIP.String(), ++ "-container-port", strconv.Itoa(containerPort), ++ } ++ ++ return &proxyCommand{ ++ cmd: &exec.Cmd{ ++ Path: path, ++ Args: args, ++ SysProcAttr: &syscall.SysProcAttr{ ++ Pdeathsig: syscall.SIGTERM, // send a sigterm to the proxy if the daemon process dies ++ }, ++ }, ++ }, nil ++} Index: sysutils/docker-freebsd/files/patch-vendor_github.com_docker_libnetwork_portmapper_proxy__unix.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-vendor_github.com_docker_libnetwork_portmapper_proxy__unix.go @@ -0,0 +1,39 @@ +--- vendor/github.com/docker/libnetwork/portmapper/proxy_unix.go.orig 2020-09-04 14:57:27 UTC ++++ vendor/github.com/docker/libnetwork/portmapper/proxy_unix.go +@@ -0,0 +1,36 @@ ++// +build solaris,freebsd +build !linux ++ ++package portmapper ++ ++import ( ++ "net" ++ "os/exec" ++ "strconv" ++) ++ ++func newProxyCommand(proto string, hostIP net.IP, hostPort int, containerIP net.IP, containerPort int, proxyPath string) (userlandProxy, error) { ++ path := proxyPath ++ if proxyPath == "" { ++ cmd, err := exec.LookPath(userlandProxyCommandName) ++ if err != nil { ++ return nil, err ++ } ++ path = cmd ++ } ++ ++ args := []string{ ++ path, ++ "-proto", proto, ++ "-host-ip", hostIP.String(), ++ "-host-port", strconv.Itoa(hostPort), ++ "-container-ip", containerIP.String(), ++ "-container-port", strconv.Itoa(containerPort), ++ } ++ ++ return &proxyCommand{ ++ cmd: &exec.Cmd{ ++ Path: path, ++ Args: args, ++ }, ++ }, nil ++} Index: sysutils/docker-freebsd/files/patch-vendor_github.com_docker_libnetwork_resolver__freebsd.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-vendor_github.com_docker_libnetwork_resolver__freebsd.go @@ -0,0 +1,9 @@ +--- vendor/github.com/docker/libnetwork/resolver_freebsd.go.orig 2020-09-18 09:01:00 UTC ++++ vendor/github.com/docker/libnetwork/resolver_freebsd.go +@@ -0,0 +1,6 @@ ++package libnetwork ++ ++ ++func (r *resolver) setupIPTable() error { ++ return nil ++} Index: sysutils/docker-freebsd/files/patch-vendor_github.com_docker_libnetwork_resolver__unix.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-vendor_github.com_docker_libnetwork_resolver__unix.go @@ -0,0 +1,8 @@ +--- vendor/github.com/docker/libnetwork/resolver_unix.go.orig 2020-09-18 09:00:58 UTC ++++ vendor/github.com/docker/libnetwork/resolver_unix.go +@@ -1,4 +1,4 @@ +-// +build !windows ++// +build !windows,!freebsd + + package libnetwork + Index: sysutils/docker-freebsd/files/patch-vendor_github.com_docker_libnetwork_service__common.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-vendor_github.com_docker_libnetwork_service__common.go @@ -0,0 +1,8 @@ +--- vendor/github.com/docker/libnetwork/service_common.go.orig 2019-10-07 21:12:15 UTC ++++ vendor/github.com/docker/libnetwork/service_common.go +@@ -1,5 +1,3 @@ +-// +build linux windows +- + package libnetwork + + import ( Index: sysutils/docker-freebsd/files/patch-vendor_github.com_docker_libnetwork_service__freebsd.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-vendor_github.com_docker_libnetwork_service__freebsd.go @@ -0,0 +1,305 @@ +--- vendor/github.com/docker/libnetwork/service_freebsd.go.orig 2020-09-04 09:13:43 UTC ++++ vendor/github.com/docker/libnetwork/service_freebsd.go +@@ -0,0 +1,302 @@ ++package libnetwork ++ ++import ( ++ "fmt" ++ "io" ++ "io/ioutil" ++ "net" ++ "os" ++ "os/exec" ++ "strings" ++ "sync" ++ ++ "github.com/docker/docker/pkg/reexec" ++ "github.com/gogo/protobuf/proto" ++ "github.com/ishidawataru/sctp" ++ "github.com/sirupsen/logrus" ++) ++ ++func init() { ++ reexec.Register("fwmarker", fwMarker) ++ reexec.Register("redirector", redirector) ++} ++ ++// Populate all loadbalancers on the network that the passed endpoint ++// belongs to, into this sandbox. ++func (sb *sandbox) populateLoadBalancers(ep *endpoint) { ++ // This is an interface less endpoint. Nothing to do. ++ if ep.Iface() == nil { ++ return ++ } ++ ++ n := ep.getNetwork() ++ eIP := ep.Iface().Address() ++ ++ if n.ingress { ++ if err := addRedirectRules(sb.Key(), eIP, ep.ingressPorts); err != nil { ++ logrus.Errorf("Failed to add redirect rules for ep %s (%.7s): %v", ep.Name(), ep.ID(), err) ++ } ++ } ++} ++ ++func (n *network) findLBEndpointSandbox() (*endpoint, *sandbox, error) { ++ // TODO: get endpoint from store? See EndpointInfo() ++ var ep *endpoint ++ // Find this node's LB sandbox endpoint: there should be exactly one ++ for _, e := range n.Endpoints() { ++ epi := e.Info() ++ if epi != nil && epi.LoadBalancer() { ++ ep = e.(*endpoint) ++ break ++ } ++ } ++ if ep == nil { ++ return nil, nil, fmt.Errorf("Unable to find load balancing endpoint for network %s", n.ID()) ++ } ++ // Get the load balancer sandbox itself as well ++ sb, ok := ep.getSandbox() ++ if !ok { ++ return nil, nil, fmt.Errorf("Unable to get sandbox for %s(%s) in for %s", ep.Name(), ep.ID(), n.ID()) ++ } ++ ep = sb.getEndpoint(ep.ID()) ++ if ep == nil { ++ return nil, nil, fmt.Errorf("Load balancing endpoint %s(%s) removed from %s", ep.Name(), ep.ID(), n.ID()) ++ } ++ return ep, sb, nil ++} ++ ++// Searches the OS sandbox for the name of the endpoint interface ++// within the sandbox. This is required for adding/removing IP ++// aliases to the interface. ++func findIfaceDstName(sb *sandbox, ep *endpoint) string { ++ srcName := ep.Iface().SrcName() ++ for _, i := range sb.osSbox.Info().Interfaces() { ++ if i.SrcName() == srcName { ++ return i.DstName() ++ } ++ } ++ return "" ++} ++ ++// Add loadbalancer backend to the loadbalncer sandbox for the network. ++// If needed add the service as well. ++func (n *network) addLBBackend(ip net.IP, lb *loadBalancer) { ++ //return fmt.Errorf("not supported") ++} ++ ++// Remove loadbalancer backend the load balancing endpoint for this ++// network. If 'rmService' is true, then remove the service entry as well. ++// If 'fullRemove' is true then completely remove the entry, otherwise ++// just deweight it for now. ++func (n *network) rmLBBackend(ip net.IP, lb *loadBalancer, rmService bool, fullRemove bool) { ++ //return fmt.Errorf("not supported") ++} ++ ++const ingressChain = "DOCKER-INGRESS" ++ ++var ( ++ ingressOnce sync.Once ++ ingressMu sync.Mutex // lock for operations on ingress ++ ingressProxyTbl = make(map[string]io.Closer) ++ portConfigMu sync.Mutex ++ portConfigTbl = make(map[PortConfig]int) ++) ++ ++func filterPortConfigs(ingressPorts []*PortConfig, isDelete bool) []*PortConfig { ++ portConfigMu.Lock() ++ iPorts := make([]*PortConfig, 0, len(ingressPorts)) ++ for _, pc := range ingressPorts { ++ if isDelete { ++ if cnt, ok := portConfigTbl[*pc]; ok { ++ // This is the last reference to this ++ // port config. Delete the port config ++ // and add it to filtered list to be ++ // plumbed. ++ if cnt == 1 { ++ delete(portConfigTbl, *pc) ++ iPorts = append(iPorts, pc) ++ continue ++ } ++ ++ portConfigTbl[*pc] = cnt - 1 ++ } ++ ++ continue ++ } ++ ++ if cnt, ok := portConfigTbl[*pc]; ok { ++ portConfigTbl[*pc] = cnt + 1 ++ continue ++ } ++ ++ // We are adding it for the first time. Add it to the ++ // filter list to be plumbed. ++ portConfigTbl[*pc] = 1 ++ iPorts = append(iPorts, pc) ++ } ++ portConfigMu.Unlock() ++ ++ return iPorts ++} ++ ++func programIngress(gwIP net.IP, ingressPorts []*PortConfig, isDelete bool) error { ++ return fmt.Errorf("not supported") ++} ++ ++// In the filter table FORWARD chain the first rule should be to jump to ++// DOCKER-USER so the user is able to filter packet first. ++// The second rule should be jump to INGRESS-CHAIN. ++// This chain has the rules to allow access to the published ports for swarm tasks ++// from local bridge networks and docker_gwbridge (ie:taks on other swarm networks) ++func arrangeIngressFilterRule() { ++ //return fmt.Errorf("not supported") ++} ++ ++func findOIFName(ip net.IP) (string, error) { ++ return "", fmt.Errorf("not supported") ++} ++ ++func plumbProxy(iPort *PortConfig, isDelete bool) error { ++ var ( ++ err error ++ l io.Closer ++ ) ++ ++ portSpec := fmt.Sprintf("%d/%s", iPort.PublishedPort, strings.ToLower(PortConfig_Protocol_name[int32(iPort.Protocol)])) ++ if isDelete { ++ if listener, ok := ingressProxyTbl[portSpec]; ok { ++ if listener != nil { ++ listener.Close() ++ } ++ } ++ ++ return nil ++ } ++ ++ switch iPort.Protocol { ++ case ProtocolTCP: ++ l, err = net.ListenTCP("tcp", &net.TCPAddr{Port: int(iPort.PublishedPort)}) ++ case ProtocolUDP: ++ l, err = net.ListenUDP("udp", &net.UDPAddr{Port: int(iPort.PublishedPort)}) ++ case ProtocolSCTP: ++ l, err = sctp.ListenSCTP("sctp", &sctp.SCTPAddr{Port: int(iPort.PublishedPort)}) ++ default: ++ err = fmt.Errorf("unknown protocol %v", iPort.Protocol) ++ } ++ ++ if err != nil { ++ return err ++ } ++ ++ ingressProxyTbl[portSpec] = l ++ ++ return nil ++} ++ ++func writePortsToFile(ports []*PortConfig) (string, error) { ++ f, err := ioutil.TempFile("", "port_configs") ++ if err != nil { ++ return "", err ++ } ++ defer f.Close() ++ ++ buf, _ := proto.Marshal(&EndpointRecord{ ++ IngressPorts: ports, ++ }) ++ ++ n, err := f.Write(buf) ++ if err != nil { ++ return "", err ++ } ++ ++ if n < len(buf) { ++ return "", io.ErrShortWrite ++ } ++ ++ return f.Name(), nil ++} ++ ++func readPortsFromFile(fileName string) ([]*PortConfig, error) { ++ buf, err := ioutil.ReadFile(fileName) ++ if err != nil { ++ return nil, err ++ } ++ ++ var epRec EndpointRecord ++ err = proto.Unmarshal(buf, &epRec) ++ if err != nil { ++ return nil, err ++ } ++ ++ return epRec.IngressPorts, nil ++} ++ ++// Invoke fwmarker reexec routine to mark vip destined packets with ++// the passed firewall mark. ++func invokeFWMarker(path string, vip net.IP, fwMark uint32, ingressPorts []*PortConfig, eIP *net.IPNet, isDelete bool, lbMode string) error { ++ var ingressPortsFile string ++ ++ if len(ingressPorts) != 0 { ++ var err error ++ ingressPortsFile, err = writePortsToFile(ingressPorts) ++ if err != nil { ++ return err ++ } ++ ++ defer os.Remove(ingressPortsFile) ++ } ++ ++ addDelOpt := "-A" ++ if isDelete { ++ addDelOpt = "-D" ++ } ++ ++ cmd := &exec.Cmd{ ++ Path: reexec.Self(), ++ Args: append([]string{"fwmarker"}, path, vip.String(), fmt.Sprintf("%d", fwMark), addDelOpt, ingressPortsFile, eIP.String(), lbMode), ++ Stdout: os.Stdout, ++ Stderr: os.Stderr, ++ } ++ ++ if err := cmd.Run(); err != nil { ++ return fmt.Errorf("reexec failed: %v", err) ++ } ++ ++ return nil ++} ++ ++// Firewall marker reexec function. ++func fwMarker() { ++ //return fmt.Errorf("not supported") ++} ++ ++func addRedirectRules(path string, eIP *net.IPNet, ingressPorts []*PortConfig) error { ++ var ingressPortsFile string ++ ++ if len(ingressPorts) != 0 { ++ var err error ++ ingressPortsFile, err = writePortsToFile(ingressPorts) ++ if err != nil { ++ return err ++ } ++ defer os.Remove(ingressPortsFile) ++ } ++ ++ cmd := &exec.Cmd{ ++ Path: reexec.Self(), ++ Args: append([]string{"redirector"}, path, eIP.String(), ingressPortsFile), ++ Stdout: os.Stdout, ++ Stderr: os.Stderr, ++ } ++ ++ if err := cmd.Run(); err != nil { ++ return fmt.Errorf("reexec failed: %v", err) ++ } ++ ++ return nil ++} ++ ++// Redirector reexec function. ++func redirector() { ++ //return fmt.Errorf("not supported") ++} Index: sysutils/docker-freebsd/files/patch-vendor_github.com_docker_libnetwork_service__unsupported.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-vendor_github.com_docker_libnetwork_service__unsupported.go @@ -0,0 +1,17 @@ +--- vendor/github.com/docker/libnetwork/service_unsupported.go.orig 2020-09-04 14:54:57 UTC ++++ vendor/github.com/docker/libnetwork/service_unsupported.go +@@ -1,4 +1,4 @@ +-// +build !linux,!windows ++// +build !linux,!windows,!freebsd + + package libnetwork + +@@ -18,7 +18,7 @@ func (c *controller) rmServiceBinding(name, sid, nid, + return fmt.Errorf("not supported") + } + +-func (sb *sandbox) populateLoadBalancers(ep *endpoint) { ++func (sb *sandbox) populateLoadbalancers(ep *endpoint) { + } + + func arrangeIngressFilterRule() { Index: sysutils/docker-freebsd/files/patch-vendor_github.com_docker_swarmkit_manager_allocator_cnmallocator_drivers__unsupported.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-vendor_github.com_docker_swarmkit_manager_allocator_cnmallocator_drivers__unsupported.go @@ -0,0 +1,15 @@ +Fix build error on FreeBSD + +swarmkit/manager/allocator/cnmallocator/drivers_unsupported.go:9:7: const initializer cannot be nil + +--- vendor/github.com/docker/swarmkit/manager/allocator/cnmallocator/drivers_unsupported.go.orig 2019-03-08 08:00:27 UTC ++++ vendor/github.com/docker/swarmkit/manager/allocator/cnmallocator/drivers_unsupported.go +@@ -6,7 +6,7 @@ import ( + "github.com/docker/swarmkit/manager/allocator/networkallocator" + ) + +-const initializers = nil ++var initializers = []initializer{} + + // PredefinedNetworks returns the list of predefined network structures + func PredefinedNetworks() []networkallocator.PredefinedNetworkData { Index: sysutils/docker-freebsd/files/patch-vendor_github.com_docker_swarmkit_node_node.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-vendor_github.com_docker_swarmkit_node_node.go @@ -0,0 +1,23 @@ +--- vendor/github.com/docker/swarmkit/node/node.go.orig 2019-10-07 21:12:15 UTC ++++ vendor/github.com/docker/swarmkit/node/node.go +@@ -21,7 +21,6 @@ import ( + + "github.com/docker/docker/pkg/plugingetter" + "github.com/docker/go-metrics" +- "github.com/docker/libnetwork/drivers/overlay/overlayutils" + "github.com/docker/swarmkit/agent" + "github.com/docker/swarmkit/agent/exec" + "github.com/docker/swarmkit/api" +@@ -274,11 +273,7 @@ func (n *Node) currentRole() api.NodeRole { + + // configVXLANUDPPort sets vxlan port in libnetwork + func configVXLANUDPPort(ctx context.Context, vxlanUDPPort uint32) { +- if err := overlayutils.ConfigVXLANUDPPort(vxlanUDPPort); err != nil { +- log.G(ctx).WithError(err).Error("failed to configure VXLAN UDP port") +- return +- } +- logrus.Infof("initialized VXLAN UDP port to %d ", vxlanUDPPort) ++ logrus.Infof("VXLAN UDP not supported on FreeBSD") + } + + func (n *Node) run(ctx context.Context) (err error) { Index: sysutils/docker-freebsd/files/patch-vendor_github.com_godbus_dbus_transport__freebsd.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-vendor_github.com_godbus_dbus_transport__freebsd.go @@ -0,0 +1,9 @@ +--- vendor/github.com/godbus/dbus/transport_freebsd.go.orig 2019-02-26 21:19:13 UTC ++++ vendor/github.com/godbus/dbus/transport_freebsd.go +@@ -0,0 +1,6 @@ ++package dbus ++ ++func (t *unixTransport) SendNullByte() error { ++ _, err := t.Write([]byte{0}) ++ return err ++} Index: sysutils/docker-freebsd/files/patch-vendor_github.com_moby_buildkit_executor_oci_spec__unix.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-vendor_github.com_moby_buildkit_executor_oci_spec__unix.go @@ -0,0 +1,20 @@ +--- vendor/github.com/moby/buildkit/executor/oci/spec_unix.go.orig 2019-08-22 20:57:25 UTC ++++ vendor/github.com/moby/buildkit/executor/oci/spec_unix.go +@@ -8,7 +8,6 @@ import ( + "sync" + + "github.com/containerd/containerd/containers" +- "github.com/containerd/containerd/contrib/seccomp" + "github.com/containerd/containerd/mount" + "github.com/containerd/containerd/namespaces" + "github.com/containerd/containerd/oci" +@@ -40,7 +39,8 @@ func GenerateSpec(ctx context.Context, meta executor.M + if meta.SecurityMode == pb.SecurityMode_INSECURE { + opts = append(opts, entitlements.WithInsecureSpec()) + } else if system.SeccompSupported() && meta.SecurityMode == pb.SecurityMode_SANDBOX { +- opts = append(opts, seccomp.WithDefaultProfile()) ++ // TODO ++ return nil, nil, errors.New("TODO Seccomp Sandbox not supported on FreeBSD") + } + + switch processMode { Index: sysutils/docker-freebsd/files/patch-vendor_github.com_moby_buildkit_executor_runcexecutor_executor.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-vendor_github.com_moby_buildkit_executor_runcexecutor_executor.go @@ -0,0 +1,21 @@ +--- vendor/github.com/moby/buildkit/executor/runcexecutor/executor.go.orig 2019-08-22 20:57:25 UTC ++++ vendor/github.com/moby/buildkit/executor/runcexecutor/executor.go +@@ -22,7 +22,6 @@ import ( + "github.com/moby/buildkit/identity" + "github.com/moby/buildkit/solver/pb" + "github.com/moby/buildkit/util/network" +- rootlessspecconv "github.com/moby/buildkit/util/rootless/specconv" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + ) +@@ -246,9 +245,7 @@ func (w *runcExecutor) Exec(ctx context.Context, meta + + spec.Process.OOMScoreAdj = w.oomScoreAdj + if w.rootless { +- if err := rootlessspecconv.ToRootless(spec); err != nil { +- return err +- } ++ return errors.New("TODO: Rootless not implemented in FreeBSD!") + } + + if err := json.NewEncoder(f).Encode(spec); err != nil { Index: sysutils/docker-freebsd/files/patch-vendor_github.com_moby_buildkit_snapshot_localmounter__freebsd.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-vendor_github.com_moby_buildkit_snapshot_localmounter__freebsd.go @@ -0,0 +1,29 @@ +--- vendor/github.com/moby/buildkit/snapshot/localmounter_freebsd.go.orig 2019-03-06 19:37:34 UTC ++++ vendor/github.com/moby/buildkit/snapshot/localmounter_freebsd.go +@@ -0,0 +1,26 @@ ++package snapshot ++ ++import ( ++ "os" ++ ++ "github.com/containerd/containerd/mount" ++) ++ ++func (lm *localMounter) Unmount() error { ++ lm.mu.Lock() ++ defer lm.mu.Unlock() ++ ++ if lm.target != "" { ++ if err := mount.Unmount(lm.target, 0); err != nil { ++ return err ++ } ++ os.RemoveAll(lm.target) ++ lm.target = "" ++ } ++ ++ if lm.release != nil { ++ return lm.release() ++ } ++ ++ return nil ++} Index: sysutils/docker-freebsd/files/patch-vendor_github.com_moby_buildkit_snapshot_localmounter__unix.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-vendor_github.com_moby_buildkit_snapshot_localmounter__unix.go @@ -0,0 +1,8 @@ +--- vendor/github.com/moby/buildkit/snapshot/localmounter_unix.go.orig 2019-02-26 00:29:56 UTC ++++ vendor/github.com/moby/buildkit/snapshot/localmounter_unix.go +@@ -1,4 +1,4 @@ +-// +build !windows ++// +build !windows,!freebsd + + package snapshot + Index: sysutils/docker-freebsd/files/patch-vendor_github.com_moby_buildkit_util_entitlements_security__freebsd.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-vendor_github.com_moby_buildkit_util_entitlements_security__freebsd.go @@ -0,0 +1,70 @@ +--- vendor/github.com/moby/buildkit/util/entitlements/security_freebsd.go.orig 2019-06-24 18:24:33 UTC ++++ vendor/github.com/moby/buildkit/util/entitlements/security_freebsd.go +@@ -0,0 +1,67 @@ ++package entitlements ++ ++import ( ++ "context" ++ ++ "github.com/containerd/containerd/containers" ++ "github.com/containerd/containerd/oci" ++ specs "github.com/opencontainers/runtime-spec/specs-go" ++) ++ ++// WithInsecureSpec sets spec with All capability. ++func WithInsecureSpec() oci.SpecOpts { ++ return func(_ context.Context, _ oci.Client, _ *containers.Container, s *specs.Spec) error { ++ addCaps := []string{ ++ "CAP_FSETID", ++ "CAP_KILL", ++ "CAP_FOWNER", ++ "CAP_MKNOD", ++ "CAP_CHOWN", ++ "CAP_DAC_OVERRIDE", ++ "CAP_NET_RAW", ++ "CAP_SETGID", ++ "CAP_SETUID", ++ "CAP_SETPCAP", ++ "CAP_SETFCAP", ++ "CAP_NET_BIND_SERVICE", ++ "CAP_SYS_CHROOT", ++ "CAP_AUDIT_WRITE", ++ "CAP_MAC_ADMIN", ++ "CAP_MAC_OVERRIDE", ++ "CAP_DAC_READ_SEARCH", ++ "CAP_SYS_PTRACE", ++ "CAP_SYS_MODULE", ++ "CAP_SYSLOG", ++ "CAP_SYS_RAWIO", ++ "CAP_SYS_ADMIN", ++ "CAP_LINUX_IMMUTABLE", ++ "CAP_SYS_BOOT", ++ "CAP_SYS_NICE", ++ "CAP_SYS_PACCT", ++ "CAP_SYS_TTY_CONFIG", ++ "CAP_SYS_TIME", ++ "CAP_WAKE_ALARM", ++ "CAP_AUDIT_READ", ++ "CAP_AUDIT_CONTROL", ++ "CAP_SYS_RESOURCE", ++ "CAP_BLOCK_SUSPEND", ++ "CAP_IPC_LOCK", ++ "CAP_IPC_OWNER", ++ "CAP_LEASE", ++ "CAP_NET_ADMIN", ++ "CAP_NET_BROADCAST", ++ } ++ for _, cap := range addCaps { ++ s.Process.Capabilities.Bounding = append(s.Process.Capabilities.Bounding, cap) ++ s.Process.Capabilities.Ambient = append(s.Process.Capabilities.Ambient, cap) ++ s.Process.Capabilities.Effective = append(s.Process.Capabilities.Effective, cap) ++ s.Process.Capabilities.Inheritable = append(s.Process.Capabilities.Inheritable, cap) ++ s.Process.Capabilities.Permitted = append(s.Process.Capabilities.Permitted, cap) ++ } ++ s.Linux.ReadonlyPaths = []string{} ++ s.Linux.MaskedPaths = []string{} ++ s.Process.ApparmorProfile = "" ++ ++ return nil ++ } ++} Index: sysutils/docker-freebsd/files/patch-vendor_github.com_tonistiigi_fsutil_copy_copy__unix.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-vendor_github.com_tonistiigi_fsutil_copy_copy__unix.go @@ -0,0 +1,45 @@ +--- vendor/github.com/tonistiigi/fsutil/copy/copy_unix.go.orig 2020-09-18 09:00:57 UTC ++++ vendor/github.com/tonistiigi/fsutil/copy/copy_unix.go +@@ -3,6 +3,7 @@ + package fs + + import ( ++ "io" + "os" + "syscall" + +@@ -52,10 +53,33 @@ func (c *copier) copyFileInfo(fi os.FileInfo, name str + return nil + } + ++func copyFile(source, target string) error { ++ src, err := os.Open(source) ++ if err != nil { ++ return errors.Wrapf(err, "failed to open source %s", source) ++ } ++ defer src.Close() ++ tgt, err := os.Create(target) ++ if err != nil { ++ return errors.Wrapf(err, "failed to open target %s", target) ++ } ++ defer tgt.Close() ++ ++ return copyFileContent(tgt, src) ++} ++ ++func copyFileContent(dst, src *os.File) error { ++ _, err := io.Copy(dst, src) ++ if(err != nil) { ++ return err ++ } ++ return nil ++} ++ + func copyDevice(dst string, fi os.FileInfo) error { + st, ok := fi.Sys().(*syscall.Stat_t) + if !ok { + return errors.New("unsupported stat type") + } +- return unix.Mknod(dst, uint32(fi.Mode()), int(st.Rdev)) ++ return unix.Mknod(dst, uint32(fi.Mode()), st.Rdev) + } Index: sysutils/docker-freebsd/files/patch-vendor_github.com_tonistiigi_fsutil_diskwriter__unix.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-vendor_github.com_tonistiigi_fsutil_diskwriter__unix.go @@ -0,0 +1,11 @@ +--- vendor/github.com/tonistiigi/fsutil/diskwriter_unix.go.orig 2019-02-26 00:29:56 UTC ++++ vendor/github.com/tonistiigi/fsutil/diskwriter_unix.go +@@ -45,7 +45,7 @@ func handleTarTypeBlockCharFifo(path string, stat *typ + mode |= syscall.S_IFBLK + } + +- if err := syscall.Mknod(path, mode, int(mkdev(stat.Devmajor, stat.Devminor))); err != nil { ++ if err := syscall.Mknod(path, mode, uint64(mkdev(stat.Devmajor, stat.Devminor))); err != nil { + return err + } + return nil Index: sysutils/docker-freebsd/files/patch-vendor_github.com_tonistiigi_fsutil_stat__unix.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-vendor_github.com_tonistiigi_fsutil_stat__unix.go @@ -0,0 +1,11 @@ +--- vendor/github.com/tonistiigi/fsutil/stat_unix.go.orig 2019-06-18 21:30:11 UTC ++++ vendor/github.com/tonistiigi/fsutil/stat_unix.go +@@ -45,7 +45,7 @@ func setUnixOpt(fi os.FileInfo, stat *types.Stat, path + stat.Devminor = int64(minor(uint64(s.Rdev))) + } + +- ino := s.Ino ++ ino := uint64(s.Ino) + linked := false + if seenFiles != nil { + if s.Nlink > 1 { Index: sysutils/docker-freebsd/files/patch-vendor_github.com_vishvananda_netlink_filter.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-vendor_github.com_vishvananda_netlink_filter.go @@ -0,0 +1,8 @@ +--- vendor/github.com/vishvananda/netlink/filter.go.orig 2020-09-04 14:54:59 UTC ++++ vendor/github.com/vishvananda/netlink/filter.go +@@ -1,3 +1,5 @@ ++// +build linux ++ + package netlink + + import ( Index: sysutils/docker-freebsd/files/patch-vendor_github.com_vishvananda_netlink_handle__unspecified.go =================================================================== --- /dev/null +++ sysutils/docker-freebsd/files/patch-vendor_github.com_vishvananda_netlink_handle__unspecified.go @@ -0,0 +1,21 @@ +--- vendor/github.com/vishvananda/netlink/handle_unspecified.go.orig 2020-10-23 18:37:22 UTC ++++ vendor/github.com/vishvananda/netlink/handle_unspecified.go +@@ -185,18 +185,6 @@ func (h *Handle) ClassList(link Link, parent uint32) ( + 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 + } Index: sysutils/docker-freebsd/pkg-descr =================================================================== --- sysutils/docker-freebsd/pkg-descr +++ sysutils/docker-freebsd/pkg-descr @@ -1,12 +1,4 @@ -Docker is an open source project to pack, ship and run any -application as a lightweight container. +Moby is an open-source project created by Docker to enable and accelerate +software containerization. -Docker containers are both hardware-agnostic and platform-agnostic. -This means they can run anywhere, from your laptop to the largest -EC2 compute instance and everything in between - and they don't -require you to use a particular language, framework or packaging -system. That makes them great building blocks for deploying and -scaling web apps, databases, and backend services without depending -on a particular stack or provider. - -WWW: https://github.com/kvasdopil/docker +WWW: https://github.com/docker/engine