From bc7a6f2556c476ff1af927aac72c369ae73b5a2f Mon Sep 17 00:00:00 2001
From: Tonis Tiigi <tonistiigi@gmail.com>
Date: Wed, 10 Jul 2019 14:42:30 -0700
Subject: [PATCH 1/6] add cni networking support

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
---
 cmd/buildkitd/config/config.go                |   8 +
 cmd/buildkitd/main.go                         |  16 +
 cmd/buildkitd/main_containerd_worker.go       |  35 +-
 cmd/buildkitd/main_oci_worker.go              |  34 +-
 cmd/buildkitd/main_unix.go                    |   4 +
 go.mod                                        |   2 +
 go.sum                                        |  12 +
 hack/dockerfiles/test.buildkit.Dockerfile     |  15 +-
 util/network/cni.go                           |  91 ++++++
 util/network/netns_create/hook.go             |  51 +++
 util/network/network.go                       |  42 ++-
 .../github.com/containerd/go-cni/.travis.yml  |  22 ++
 vendor/github.com/containerd/go-cni/LICENSE   | 201 ++++++++++++
 vendor/github.com/containerd/go-cni/README.md |  60 ++++
 vendor/github.com/containerd/go-cni/cni.go    | 219 +++++++++++++
 vendor/github.com/containerd/go-cni/errors.go |  55 ++++
 vendor/github.com/containerd/go-cni/helper.go |  41 +++
 .../github.com/containerd/go-cni/namespace.go |  75 +++++
 .../containerd/go-cni/namespace_opts.go       |  67 ++++
 vendor/github.com/containerd/go-cni/opts.go   | 245 ++++++++++++++
 vendor/github.com/containerd/go-cni/result.go | 106 +++++++
 .../github.com/containerd/go-cni/testutils.go |  78 +++++
 vendor/github.com/containerd/go-cni/types.go  |  55 ++++
 .../github.com/containerd/go-cni/vendor.conf  |   6 +
 .../containernetworking/cni/LICENSE           | 202 ++++++++++++
 .../containernetworking/cni/libcni/api.go     | 219 +++++++++++++
 .../containernetworking/cni/libcni/conf.go    | 259 +++++++++++++++
 .../cni/pkg/invoke/args.go                    |  82 +++++
 .../cni/pkg/invoke/delegate.go                |  53 ++++
 .../cni/pkg/invoke/exec.go                    |  95 ++++++
 .../cni/pkg/invoke/find.go                    |  43 +++
 .../cni/pkg/invoke/os_unix.go                 |  20 ++
 .../cni/pkg/invoke/os_windows.go              |  18 ++
 .../cni/pkg/invoke/raw_exec.go                |  59 ++++
 .../cni/pkg/types/020/types.go                | 135 ++++++++
 .../containernetworking/cni/pkg/types/args.go | 112 +++++++
 .../cni/pkg/types/current/types.go            | 300 ++++++++++++++++++
 .../cni/pkg/types/types.go                    | 191 +++++++++++
 .../cni/pkg/version/conf.go                   |  37 +++
 .../cni/pkg/version/plugin.go                 |  81 +++++
 .../cni/pkg/version/reconcile.go              |  49 +++
 .../cni/pkg/version/version.go                |  61 ++++
 vendor/modules.txt                            |   9 +
 worker/containerd/containerd.go               |  13 +-
 worker/runc/runc.go                           |  10 +-
 45 files changed, 3574 insertions(+), 14 deletions(-)
 create mode 100644 util/network/cni.go
 create mode 100644 util/network/netns_create/hook.go
 create mode 100644 vendor/github.com/containerd/go-cni/.travis.yml
 create mode 100644 vendor/github.com/containerd/go-cni/LICENSE
 create mode 100644 vendor/github.com/containerd/go-cni/README.md
 create mode 100644 vendor/github.com/containerd/go-cni/cni.go
 create mode 100644 vendor/github.com/containerd/go-cni/errors.go
 create mode 100644 vendor/github.com/containerd/go-cni/helper.go
 create mode 100644 vendor/github.com/containerd/go-cni/namespace.go
 create mode 100644 vendor/github.com/containerd/go-cni/namespace_opts.go
 create mode 100644 vendor/github.com/containerd/go-cni/opts.go
 create mode 100644 vendor/github.com/containerd/go-cni/result.go
 create mode 100644 vendor/github.com/containerd/go-cni/testutils.go
 create mode 100644 vendor/github.com/containerd/go-cni/types.go
 create mode 100644 vendor/github.com/containerd/go-cni/vendor.conf
 create mode 100644 vendor/github.com/containernetworking/cni/LICENSE
 create mode 100644 vendor/github.com/containernetworking/cni/libcni/api.go
 create mode 100644 vendor/github.com/containernetworking/cni/libcni/conf.go
 create mode 100644 vendor/github.com/containernetworking/cni/pkg/invoke/args.go
 create mode 100644 vendor/github.com/containernetworking/cni/pkg/invoke/delegate.go
 create mode 100644 vendor/github.com/containernetworking/cni/pkg/invoke/exec.go
 create mode 100644 vendor/github.com/containernetworking/cni/pkg/invoke/find.go
 create mode 100644 vendor/github.com/containernetworking/cni/pkg/invoke/os_unix.go
 create mode 100644 vendor/github.com/containernetworking/cni/pkg/invoke/os_windows.go
 create mode 100644 vendor/github.com/containernetworking/cni/pkg/invoke/raw_exec.go
 create mode 100644 vendor/github.com/containernetworking/cni/pkg/types/020/types.go
 create mode 100644 vendor/github.com/containernetworking/cni/pkg/types/args.go
 create mode 100644 vendor/github.com/containernetworking/cni/pkg/types/current/types.go
 create mode 100644 vendor/github.com/containernetworking/cni/pkg/types/types.go
 create mode 100644 vendor/github.com/containernetworking/cni/pkg/version/conf.go
 create mode 100644 vendor/github.com/containernetworking/cni/pkg/version/plugin.go
 create mode 100644 vendor/github.com/containernetworking/cni/pkg/version/reconcile.go
 create mode 100644 vendor/github.com/containernetworking/cni/pkg/version/version.go

diff --git a/cmd/buildkitd/config/config.go b/cmd/buildkitd/config/config.go
index bd568574326e..0c9df323c153 100644
--- a/cmd/buildkitd/config/config.go
+++ b/cmd/buildkitd/config/config.go
@@ -58,6 +58,12 @@ type GCConfig struct {
 	GCPolicy      []GCPolicy `toml:"gcpolicy"`
 }
 
+type NetworkConfig struct {
+	Mode          string `toml:"networkMode"`
+	CNIConfigPath string `toml:"cniConfigPath"`
+	CNIBinaryPath string `toml:"cniBinaryPath"`
+}
+
 type OCIConfig struct {
 	Enabled          *bool             `toml:"enabled"`
 	Labels           map[string]string `toml:"labels"`
@@ -66,6 +72,7 @@ type OCIConfig struct {
 	Rootless         bool              `toml:"rootless"`
 	NoProcessSandbox bool              `toml:"noProcessSandbox"`
 	GCConfig
+	NetworkConfig
 	// UserRemapUnsupported is unsupported key for testing. The feature is
 	// incomplete and the intention is to make it default without config.
 	UserRemapUnsupported string `toml:"userRemapUnsupported"`
@@ -78,6 +85,7 @@ type ContainerdConfig struct {
 	Platforms []string          `toml:"platforms"`
 	Namespace string            `toml:"namespace"`
 	GCConfig
+	NetworkConfig
 }
 
 type GCPolicy struct {
diff --git a/cmd/buildkitd/main.go b/cmd/buildkitd/main.go
index afcbc248c330..078d8467ece6 100644
--- a/cmd/buildkitd/main.go
+++ b/cmd/buildkitd/main.go
@@ -350,6 +350,19 @@ func defaultConf() (config.Config, *toml.MetaData, error) {
 	return cfg, md, nil
 }
 
+func setDefaultNetworkConfig(nc config.NetworkConfig) config.NetworkConfig {
+	if nc.Mode == "" {
+		nc.Mode = "auto"
+	}
+	if nc.CNIConfigPath == "" {
+		nc.CNIConfigPath = "/etc/buildkit/cni.json"
+	}
+	if nc.CNIBinaryPath == "" {
+		nc.CNIBinaryPath = "/opt/cni/bin"
+	}
+	return nc
+}
+
 func setDefaultConfig(cfg *config.Config) {
 	orig := *cfg
 
@@ -368,6 +381,9 @@ func setDefaultConfig(cfg *config.Config) {
 		cfg.Workers.Containerd.Platforms = binfmt_misc.SupportedPlatforms()
 	}
 
+	cfg.Workers.OCI.NetworkConfig = setDefaultNetworkConfig(cfg.Workers.OCI.NetworkConfig)
+	cfg.Workers.Containerd.NetworkConfig = setDefaultNetworkConfig(cfg.Workers.Containerd.NetworkConfig)
+
 	if system.RunningInUserNS() {
 		// if buildkitd is being executed as the mapped-root (not only EUID==0 but also $USER==root)
 		// in a user namespace, we need to enable the rootless mode but
diff --git a/cmd/buildkitd/main_containerd_worker.go b/cmd/buildkitd/main_containerd_worker.go
index e3e933831545..01a85eb44963 100644
--- a/cmd/buildkitd/main_containerd_worker.go
+++ b/cmd/buildkitd/main_containerd_worker.go
@@ -10,6 +10,7 @@ import (
 
 	ctd "github.com/containerd/containerd"
 	"github.com/moby/buildkit/cmd/buildkitd/config"
+	"github.com/moby/buildkit/util/network"
 	"github.com/moby/buildkit/worker"
 	"github.com/moby/buildkit/worker/base"
 	"github.com/moby/buildkit/worker/containerd"
@@ -69,6 +70,21 @@ func init() {
 			Value:  defaultConf.Workers.Containerd.Namespace,
 			Hidden: true,
 		},
+		cli.StringFlag{
+			Name:  "containerd-worker-net",
+			Usage: "worker network type (auto, cni or host)",
+			Value: defaultConf.Workers.Containerd.NetworkConfig.Mode,
+		},
+		cli.StringFlag{
+			Name:  "containerd-cni-config-path",
+			Usage: "path of cni config file",
+			Value: defaultConf.Workers.Containerd.NetworkConfig.CNIConfigPath,
+		},
+		cli.StringFlag{
+			Name:  "containerd-cni-binary-dir",
+			Usage: "path of cni binary files",
+			Value: defaultConf.Workers.Containerd.NetworkConfig.CNIBinaryPath,
+		},
 	}
 
 	if defaultConf.Workers.Containerd.GC == nil || *defaultConf.Workers.Containerd.GC {
@@ -158,6 +174,16 @@ func applyContainerdFlags(c *cli.Context, cfg *config.Config) error {
 		cfg.Workers.Containerd.GCKeepStorage = c.GlobalInt64("containerd-worker-gc-keepstorage") * 1e6
 	}
 
+	if c.GlobalIsSet("containerd-worker-net") {
+		cfg.Workers.Containerd.NetworkConfig.Mode = c.GlobalString("containerd-worker-net")
+	}
+	if c.GlobalIsSet("containerd-cni-config-path") {
+		cfg.Workers.Containerd.NetworkConfig.CNIConfigPath = c.GlobalString("containerd-cni-worker-path")
+	}
+	if c.GlobalIsSet("containerd-cni-binary-dir") {
+		cfg.Workers.Containerd.NetworkConfig.CNIBinaryPath = c.GlobalString("containerd-cni-binary-dir")
+	}
+
 	return nil
 }
 
@@ -174,7 +200,14 @@ func containerdWorkerInitializer(c *cli.Context, common workerInitializerOpt) ([
 
 	dns := getDNSConfig(common.config.DNS)
 
-	opt, err := containerd.NewWorkerOpt(common.config.Root, cfg.Address, ctd.DefaultSnapshotter, cfg.Namespace, cfg.Labels, dns, ctd.WithTimeout(60*time.Second))
+	nc := network.Opt{
+		Root:          common.config.Root,
+		Mode:          common.config.Workers.Containerd.NetworkConfig.Mode,
+		CNIConfigPath: common.config.Workers.Containerd.CNIConfigPath,
+		CNIBinaryDir:  common.config.Workers.Containerd.CNIBinaryPath,
+	}
+
+	opt, err := containerd.NewWorkerOpt(common.config.Root, cfg.Address, ctd.DefaultSnapshotter, cfg.Namespace, cfg.Labels, dns, nc, ctd.WithTimeout(60*time.Second))
 	if err != nil {
 		return nil, err
 	}
diff --git a/cmd/buildkitd/main_oci_worker.go b/cmd/buildkitd/main_oci_worker.go
index 93f94a97bf4c..c450b5a9f9fe 100644
--- a/cmd/buildkitd/main_oci_worker.go
+++ b/cmd/buildkitd/main_oci_worker.go
@@ -11,6 +11,7 @@ import (
 	"github.com/containerd/containerd/snapshots/overlay"
 	"github.com/moby/buildkit/cmd/buildkitd/config"
 	"github.com/moby/buildkit/executor/oci"
+	"github.com/moby/buildkit/util/network"
 	"github.com/moby/buildkit/worker"
 	"github.com/moby/buildkit/worker/base"
 	"github.com/moby/buildkit/worker/runc"
@@ -53,6 +54,21 @@ func init() {
 			Name:  "oci-worker-platform",
 			Usage: "override supported platforms for worker",
 		},
+		cli.StringFlag{
+			Name:  "oci-worker-net",
+			Usage: "worker network type (auto, cni or host)",
+			Value: defaultConf.Workers.OCI.NetworkConfig.Mode,
+		},
+		cli.StringFlag{
+			Name:  "oci-cni-config-path",
+			Usage: "path of cni config file",
+			Value: defaultConf.Workers.OCI.NetworkConfig.CNIConfigPath,
+		},
+		cli.StringFlag{
+			Name:  "oci-cni-binary-dir",
+			Usage: "path of cni binary files",
+			Value: defaultConf.Workers.OCI.NetworkConfig.CNIBinaryPath,
+		},
 	}
 	n := "oci-worker-rootless"
 	u := "enable rootless mode"
@@ -154,6 +170,15 @@ func applyOCIFlags(c *cli.Context, cfg *config.Config) error {
 		cfg.Workers.OCI.GCKeepStorage = c.GlobalInt64("oci-worker-gc-keepstorage") * 1e6
 	}
 
+	if c.GlobalIsSet("oci-worker-net") {
+		cfg.Workers.OCI.NetworkConfig.Mode = c.GlobalString("oci-worker-net")
+	}
+	if c.GlobalIsSet("oci-cni-config-path") {
+		cfg.Workers.OCI.NetworkConfig.CNIConfigPath = c.GlobalString("oci-cni-worker-path")
+	}
+	if c.GlobalIsSet("oci-cni-binary-dir") {
+		cfg.Workers.OCI.NetworkConfig.CNIBinaryPath = c.GlobalString("oci-cni-binary-dir")
+	}
 	return nil
 }
 
@@ -194,7 +219,14 @@ func ociWorkerInitializer(c *cli.Context, common workerInitializerOpt) ([]worker
 
 	dns := getDNSConfig(common.config.DNS)
 
-	opt, err := runc.NewWorkerOpt(common.config.Root, snFactory, cfg.Rootless, processMode, cfg.Labels, idmapping, dns)
+	nc := network.Opt{
+		Root:          common.config.Root,
+		Mode:          common.config.Workers.OCI.NetworkConfig.Mode,
+		CNIConfigPath: common.config.Workers.OCI.CNIConfigPath,
+		CNIBinaryDir:  common.config.Workers.OCI.CNIBinaryPath,
+	}
+
+	opt, err := runc.NewWorkerOpt(common.config.Root, snFactory, cfg.Rootless, processMode, cfg.Labels, idmapping, nc, dns)
 	if err != nil {
 		return nil, err
 	}
diff --git a/cmd/buildkitd/main_unix.go b/cmd/buildkitd/main_unix.go
index 93533adb1fdf..649fc0f5ba51 100644
--- a/cmd/buildkitd/main_unix.go
+++ b/cmd/buildkitd/main_unix.go
@@ -4,8 +4,12 @@ package main
 
 import (
 	"syscall"
+
+	"github.com/moby/buildkit/util/network/netns_create"
 )
 
 func init() {
+	netns_create.Handle()
+
 	syscall.Umask(0)
 }
diff --git a/go.mod b/go.mod
index 3d54653c7592..825c02e7ae78 100644
--- a/go.mod
+++ b/go.mod
@@ -12,9 +12,11 @@ require (
 	github.com/containerd/containerd v1.3.0-0.20190426060238-3a3f0aac8819
 	github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc
 	github.com/containerd/fifo v0.0.0-20180307165137-3d5202aec260 // indirect
+	github.com/containerd/go-cni v0.0.0-20190610170741-5a4663dad645
 	github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3
 	github.com/containerd/ttrpc v0.0.0-20190411181408-699c4e40d1e7 // indirect
 	github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd // indirect
+	github.com/containernetworking/cni v0.6.1-0.20180218032124-142cde0c766c // indirect
 	github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e
 	github.com/docker/cli v0.0.0-20190321234815-f40f9c240ab0
 	github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible
diff --git a/go.sum b/go.sum
index d407c5527ed6..0512165a2f67 100644
--- a/go.sum
+++ b/go.sum
@@ -23,12 +23,16 @@ github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc h1:TP+534wVl
 github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
 github.com/containerd/fifo v0.0.0-20180307165137-3d5202aec260 h1:XGyg7oTtD0DoRFhbpV6x1WfV0flKC4UxXU7ab1zC08U=
 github.com/containerd/fifo v0.0.0-20180307165137-3d5202aec260/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI=
+github.com/containerd/go-cni v0.0.0-20190610170741-5a4663dad645 h1:CCxW+4asjsbhMjWAznv/rTLYYI7Mcm6LovkzaPtD3rU=
+github.com/containerd/go-cni v0.0.0-20190610170741-5a4663dad645/go.mod h1:2wlRxCQdiBY+OcjNg5x8kI+5mEL1fGt25L4IzQHYJsM=
 github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3 h1:esQOJREg8nw8aXj6uCN5dfW5cKUBiEJ/+nni1Q/D/sw=
 github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0=
 github.com/containerd/ttrpc v0.0.0-20190411181408-699c4e40d1e7 h1:SKDlsIhYxNE1LO0xwuOR+3QWj3zRibVQu5jWIMQmOfU=
 github.com/containerd/ttrpc v0.0.0-20190411181408-699c4e40d1e7/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o=
 github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd h1:JNn81o/xG+8NEo3bC/vx9pbi/g2WI8mtP2/nXzu297Y=
 github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc=
+github.com/containernetworking/cni v0.6.1-0.20180218032124-142cde0c766c h1:X6Gxg2GV1i0UhDodKZYrp//lg8h3KANe8l3gtFHoi9M=
+github.com/containernetworking/cni v0.6.1-0.20180218032124-142cde0c766c/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY=
 github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e h1:Wf6HqHfScWJN9/ZjdUKyjop4mf3Qdd+1TvvltAvM3m8=
 github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -51,6 +55,7 @@ github.com/docker/go-units v0.3.1 h1:QAFdsA6jLCnglbqE6mUsHuPcJlntY94DkxHf4deHKIU
 github.com/docker/go-units v0.3.1/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
 github.com/docker/libnetwork v0.8.0-dev.2.0.20190604151032-3c26b4e7495e h1:/9dBUVUO865jROD5LfE232z9ssWlBlzIMVW0BaEn8DM=
 github.com/docker/libnetwork v0.8.0-dev.2.0.20190604151032-3c26b4e7495e/go.mod h1:93m0aTqz6z+g32wla4l4WxTrdtvBRmVzYRkYvasA5Z8=
+github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
 github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
 github.com/godbus/dbus v4.1.0+incompatible h1:WqqLRTsQic3apZUK9qC5sGNfXthmPXzUZ7nQPrNITa4=
 github.com/godbus/dbus v4.1.0+incompatible/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
@@ -77,6 +82,7 @@ github.com/hashicorp/golang-lru v0.0.0-20160207214719-a0d98a5f2880 h1:OaRuzt9oCK
 github.com/hashicorp/golang-lru v0.0.0-20160207214719-a0d98a5f2880/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
 github.com/hashicorp/uuid v0.0.0-20160311170451-ebb0a03e909c h1:nQcv325vxv2fFHJsOt53eSRf1eINt6vOdYUFfXs4rgk=
 github.com/hashicorp/uuid v0.0.0-20160311170451-ebb0a03e909c/go.mod h1:fHzc09UnyJyqyW+bFuq864eh+wC7dj65aXmXLRe5to0=
+github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
 github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
 github.com/ishidawataru/sctp v0.0.0-20180213033435-07191f837fed h1:3MJOWnAfq3T9eoCQTarEY2DMlUWYcBkBLf03dAMvEb8=
 github.com/ishidawataru/sctp v0.0.0-20180213033435-07191f837fed/go.mod h1:DM4VvS+hD/kDi1U1QsX2fnZowwBhqD0Dk3bRPKF/Oc8=
@@ -92,7 +98,9 @@ github.com/mitchellh/hashstructure v0.0.0-20170609045927-2bca23e0e452/go.mod h1:
 github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c h1:nXxl5PrvVm2L/wCy8dQu6DMTwH4oIuGN8GJDAlqDdVE=
 github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
 github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs=
 github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU=
 github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
 github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ=
 github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
@@ -119,6 +127,7 @@ github.com/sirupsen/logrus v1.0.3/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjM
 github.com/sirupsen/logrus v1.3.0 h1:hI/7Q+DtNZ2kINb6qt/lS+IyXnHQe9e90POfeewL/ME=
 github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
 github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
@@ -175,9 +184,12 @@ gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
 gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
 gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo=
+gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
 gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
+gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
 gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gotest.tools v2.1.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
 gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
diff --git a/hack/dockerfiles/test.buildkit.Dockerfile b/hack/dockerfiles/test.buildkit.Dockerfile
index 084ee2146b51..9ef4bf256b84 100644
--- a/hack/dockerfiles/test.buildkit.Dockerfile
+++ b/hack/dockerfiles/test.buildkit.Dockerfile
@@ -198,9 +198,17 @@ VOLUME /var/lib/containerd
 VOLUME /run/containerd
 ENTRYPOINT ["containerd"]
 
+FROM alpine AS cni-plugins
+RUN apk add --no-cache curl
+ENV CNI_VERSION=v0.8.1
+ARG TARGETOS
+ARG TARGETARCH
+WORKDIR /opt/cni/bin
+RUN curl -Ls https://github.com/containernetworking/plugins/releases/download/$CNI_VERSION/cni-plugins-$TARGETOS-$TARGETARCH-$CNI_VERSION.tgz | tar xzv
+
 FROM buildkit-base AS integration-tests
 ENV BUILDKIT_INTEGRATION_ROOTLESS_IDPAIR="1000:1000"
-RUN apt-get install -y --no-install-recommends uidmap sudo \ 
+RUN apt-get install -y --no-install-recommends uidmap sudo vim iptables \ 
   && useradd --create-home --home-dir /home/user --uid 1000 -s /bin/sh user \
   && echo "XDG_RUNTIME_DIR=/run/user/1000; export XDG_RUNTIME_DIR" >> /home/user/.profile \
   && mkdir -m 0700 -p /run/user/1000 \
@@ -212,9 +220,14 @@ COPY --from=containerd10 /out/containerd* /opt/containerd-1.0/bin/
 COPY --from=registry /bin/registry /usr/bin
 COPY --from=runc /usr/bin/runc /usr/bin
 COPY --from=containerd /out/containerd* /usr/bin/
+COPY --from=cni-plugins /opt/cni/bin/bridge /opt/cni/bin/host-local /opt/cni/bin
+COPY hack/fixtures/cni.json /etc/buildkit/cni.json
 COPY --from=binaries / /usr/bin/
 COPY . .
 
+FROM integration-tests AS dev-env
+VOLUME /var/lib/buildkit
+
 # To allow running buildkit in a container without CAP_SYS_ADMIN, we need to do either
 #  a) install newuidmap/newgidmap with file capabilities rather than SETUID (requires kernel >= 4.14)
 #  b) install newuidmap/newgidmap >= 20181125 (59c2dabb264ef7b3137f5edb52c0b31d5af0cf76)
diff --git a/util/network/cni.go b/util/network/cni.go
new file mode 100644
index 000000000000..087803c3a8d8
--- /dev/null
+++ b/util/network/cni.go
@@ -0,0 +1,91 @@
+package network
+
+import (
+	"os"
+	"path/filepath"
+	"syscall"
+
+	"github.com/containerd/containerd/oci"
+	"github.com/containerd/go-cni"
+	"github.com/moby/buildkit/identity"
+	"github.com/moby/buildkit/util/network/netns_create"
+	specs "github.com/opencontainers/runtime-spec/specs-go"
+	"github.com/pkg/errors"
+	"golang.org/x/sys/unix"
+)
+
+func NewCNIProvider(opt Opt) (Provider, error) {
+	if _, err := os.Stat(opt.CNIConfigPath); err != nil {
+		return nil, errors.Wrapf(err, "failed to read cni config %q", opt.CNIConfigPath)
+	}
+	if _, err := os.Stat(opt.CNIBinaryDir); err != nil {
+		return nil, errors.Wrapf(err, "failed to read cni binary dir %q", opt.CNIBinaryDir)
+	}
+
+	cniHandle, err := cni.New(
+		cni.WithMinNetworkCount(2),
+		cni.WithConfFile(opt.CNIConfigPath),
+		cni.WithPluginDir([]string{opt.CNIBinaryDir}),
+		cni.WithLoNetwork,
+		cni.WithInterfacePrefix(("eth")))
+	if err != nil {
+		return nil, err
+	}
+
+	if err != nil {
+		return nil, err
+	}
+
+	return &cniProvider{CNI: cniHandle, root: opt.Root}, nil
+}
+
+type cniProvider struct {
+	cni.CNI
+	root string
+}
+
+func (c *cniProvider) New() (Namespace, error) {
+	id := identity.NewID()
+	nsPath := filepath.Join(c.root, "net/cni", id)
+	if err := os.MkdirAll(filepath.Dir(nsPath), 0700); err != nil {
+		return nil, err
+	}
+
+	if err := netns_create.CreateNetNS(nsPath); err != nil {
+		return nil, err
+	}
+
+	if _, err := c.CNI.Setup(id, nsPath); err != nil {
+		return nil, errors.Wrap(err, "CNI setup error")
+	}
+
+	return &cniNS{path: nsPath, id: id, handle: c.CNI}, nil
+}
+
+type cniNS struct {
+	handle cni.CNI
+	id     string
+	path   string
+}
+
+func (ns *cniNS) Set(s *specs.Spec) {
+	oci.WithLinuxNamespace(specs.LinuxNamespace{
+		Type: specs.NetworkNamespace,
+		Path: ns.path,
+	})(nil, nil, nil, s)
+}
+
+func (ns *cniNS) Close() error {
+	err := ns.handle.Remove(ns.id, ns.path)
+
+	if err1 := unix.Unmount(ns.path, unix.MNT_DETACH); err1 != nil {
+		if err1 != syscall.EINVAL && err1 != syscall.ENOENT && err == nil {
+			err = errors.Wrap(err1, "error unmounting network namespace")
+		}
+	}
+	if err1 := os.RemoveAll(filepath.Dir(ns.path)); err1 != nil && !os.IsNotExist(err1) && err == nil {
+		err = errors.Wrap(err, "error removing network namespace")
+	}
+
+	return err
+}
diff --git a/util/network/netns_create/hook.go b/util/network/netns_create/hook.go
new file mode 100644
index 000000000000..d2a060c7b7d4
--- /dev/null
+++ b/util/network/netns_create/hook.go
@@ -0,0 +1,51 @@
+package netns_create
+
+import (
+	"log"
+	"os"
+	"os/exec"
+	"syscall"
+
+	"github.com/pkg/errors"
+	"golang.org/x/sys/unix"
+)
+
+const envKey = "BUILDKIT_CREATE_NS_PATH"
+
+func Handle() {
+	if path := os.Getenv(envKey); path != "" {
+		if err := handle(path); err != nil {
+			log.Printf("%v", err)
+			os.Exit(1)
+		}
+		os.Exit(0)
+	}
+}
+
+func handle(path string) error {
+	f, err := os.Create(path)
+	if err != nil {
+		return err
+	}
+	if err := f.Close(); err != nil {
+		return err
+	}
+	if err := unix.Mount("/proc/self/ns/net", path, "none", unix.MS_BIND, ""); err != nil {
+		return err
+	}
+	return nil
+}
+
+func CreateNetNS(path string) error {
+	cmd := exec.Command("/proc/self/exe")
+	cmd.Env = []string{envKey + "=" + path}
+	cmd.SysProcAttr = &syscall.SysProcAttr{
+		Cloneflags: unix.CLONE_NEWNET,
+	}
+	out, err := cmd.CombinedOutput()
+	if err != nil {
+		return errors.Wrap(err, string(out))
+
+	}
+	return nil
+}
diff --git a/util/network/network.go b/util/network/network.go
index 055a52da8bce..824d3edf3b7c 100644
--- a/util/network/network.go
+++ b/util/network/network.go
@@ -2,19 +2,51 @@ package network
 
 import (
 	"io"
+	"os"
 
 	"github.com/moby/buildkit/solver/pb"
 	specs "github.com/opencontainers/runtime-spec/specs-go"
+	"github.com/pkg/errors"
 )
 
-// Default returns the default network provider set
-func Default() map[pb.NetMode]Provider {
+type Opt struct {
+	Root          string
+	Mode          string
+	CNIConfigPath string
+	CNIBinaryDir  string
+}
+
+// Providers returns the network provider set
+func Providers(opt Opt) (map[pb.NetMode]Provider, error) {
+	var defaultProvider Provider
+	switch opt.Mode {
+	case "cni":
+		cniProvider, err := NewCNIProvider(opt)
+		if err != nil {
+			return nil, err
+		}
+		defaultProvider = cniProvider
+	case "host":
+		defaultProvider = NewHostProvider()
+	case "auto", "":
+		if _, err := os.Stat(opt.CNIConfigPath); err == nil {
+			cniProvider, err := NewCNIProvider(opt)
+			if err != nil {
+				return nil, err
+			}
+			defaultProvider = cniProvider
+		} else {
+			defaultProvider = NewHostProvider()
+		}
+	default:
+		return nil, errors.Errorf("invalid network mode: %q", opt.Mode)
+	}
+
 	return map[pb.NetMode]Provider{
-		// FIXME: still uses host if no provider configured
-		pb.NetMode_UNSET: NewHostProvider(),
+		pb.NetMode_UNSET: defaultProvider,
 		pb.NetMode_HOST:  NewHostProvider(),
 		pb.NetMode_NONE:  NewNoneProvider(),
-	}
+	}, nil
 }
 
 // Provider interface for Network
diff --git a/vendor/github.com/containerd/go-cni/.travis.yml b/vendor/github.com/containerd/go-cni/.travis.yml
new file mode 100644
index 000000000000..9fe2eadc60af
--- /dev/null
+++ b/vendor/github.com/containerd/go-cni/.travis.yml
@@ -0,0 +1,22 @@
+language: go
+go:
+  - 1.10.x
+  - tip
+
+go_import_path: github.com/containerd/go-cni
+
+install:
+  - go get -d
+  - go get -u github.com/vbatts/git-validation
+  - go get -u github.com/kunalkushwaha/ltag
+
+before_script:
+  - pushd ..; git clone https://github.com/containerd/project; popd
+
+script:
+    - DCO_VERBOSITY=-q ../project/script/validate/dco
+    - ../project/script/validate/fileheader ../project/
+    - go test -race -coverprofile=coverage.txt -covermode=atomic
+
+after_success:
+    - bash <(curl -s https://codecov.io/bash)
diff --git a/vendor/github.com/containerd/go-cni/LICENSE b/vendor/github.com/containerd/go-cni/LICENSE
new file mode 100644
index 000000000000..261eeb9e9f8b
--- /dev/null
+++ b/vendor/github.com/containerd/go-cni/LICENSE
@@ -0,0 +1,201 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/vendor/github.com/containerd/go-cni/README.md b/vendor/github.com/containerd/go-cni/README.md
new file mode 100644
index 000000000000..1bd2f0013fb7
--- /dev/null
+++ b/vendor/github.com/containerd/go-cni/README.md
@@ -0,0 +1,60 @@
+[![Build Status](https://travis-ci.org/containerd/go-cni.svg?branch=master)](https://travis-ci.org/containerd/go-cni)
+
+# go-cni
+
+A generic CNI library to provide APIs for CNI plugin interactions. The library provides APIs to:
+
+- Load CNI network config from different sources  
+- Setup networks for container namespace
+- Remove networks from container namespace
+- Query status of CNI network plugin initialization
+
+go-cni aims to support plugins that implement [Container Network Interface](https://github.com/containernetworking/cni)
+
+## Usage
+```go
+func main() {
+	id := "123456"
+	netns := "/proc/9999/ns/net"
+	defaultIfName := "eth0"
+	// Initialize library
+	l = gocni.New(gocni.WithMinNetworkCount(2),
+		gocni.WithPluginConfDir("/etc/mycni/net.d"),
+		gocni.WithPluginDir([]string{"/opt/mycni/bin", "/opt/cni/bin"}),
+		gocni.WithDefaultIfName(defaultIfName))
+	
+	// Load the cni configuration
+	err:= l.Load(gocni.WithLoNetwork, gocni.WithDefaultConf)
+        if err != nil{
+		log.Errorf("failed to load cni configuration: %v", err)
+		return 
+	}
+	
+	// Setup network for namespace.
+	labels := map[string]string{
+		"K8S_POD_NAMESPACE":          "namespace1",
+		"K8S_POD_NAME":               "pod1",
+		"K8S_POD_INFRA_CONTAINER_ID": id,
+	}
+	result, err := l.Setup(id, netns, gocni.WithLabels(labels))
+	if err != nil {
+		log.Errorf("failed to setup network for namespace %q: %v",id, err)
+		return 
+	}
+	
+	// Get IP of the default interface
+	IP := result.Interfaces[defaultIfName].IPConfigs[0].IP.String()
+	fmt.Printf("IP of the default interface %s:%s", defaultIfName, IP)
+}
+```
+
+## Project details
+
+The go-cni is a containerd sub-project, licensed under the [Apache 2.0 license](./LICENSE).
+As a containerd sub-project, you will find the:
+
+ * [Project governance](https://github.com/containerd/project/blob/master/GOVERNANCE.md),
+ * [Maintainers](https://github.com/containerd/project/blob/master/MAINTAINERS),
+ * and [Contributing guidelines](https://github.com/containerd/project/blob/master/CONTRIBUTING.md)
+
+information in our [`containerd/project`](https://github.com/containerd/project) repository.
diff --git a/vendor/github.com/containerd/go-cni/cni.go b/vendor/github.com/containerd/go-cni/cni.go
new file mode 100644
index 000000000000..bdd63b841bb7
--- /dev/null
+++ b/vendor/github.com/containerd/go-cni/cni.go
@@ -0,0 +1,219 @@
+/*
+   Copyright The containerd Authors.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+package cni
+
+import (
+	"fmt"
+	"strings"
+	"sync"
+
+	cnilibrary "github.com/containernetworking/cni/libcni"
+	"github.com/containernetworking/cni/pkg/types"
+	"github.com/containernetworking/cni/pkg/types/current"
+	"github.com/pkg/errors"
+)
+
+type CNI interface {
+	// Setup setup the network for the namespace
+	Setup(id string, path string, opts ...NamespaceOpts) (*CNIResult, error)
+	// Remove tears down the network of the namespace.
+	Remove(id string, path string, opts ...NamespaceOpts) error
+	// Load loads the cni network config
+	Load(opts ...CNIOpt) error
+	// Status checks the status of the cni initialization
+	Status() error
+	// GetConfig returns a copy of the CNI plugin configurations as parsed by CNI
+	GetConfig() *ConfigResult
+}
+
+type ConfigResult struct {
+	PluginDirs       []string
+	PluginConfDir    string
+	PluginMaxConfNum int
+	Prefix           string
+	Networks         []*ConfNetwork
+}
+
+type ConfNetwork struct {
+	Config *NetworkConfList
+	IFName string
+}
+
+// NetworkConfList is a source bytes to string version of cnilibrary.NetworkConfigList
+type NetworkConfList struct {
+	Name       string
+	CNIVersion string
+	Plugins    []*NetworkConf
+	Source     string
+}
+
+// NetworkConf is a source bytes to string conversion of cnilibrary.NetworkConfig
+type NetworkConf struct {
+	Network *types.NetConf
+	Source  string
+}
+
+type libcni struct {
+	config
+
+	cniConfig    cnilibrary.CNI
+	networkCount int // minimum network plugin configurations needed to initialize cni
+	networks     []*Network
+	sync.RWMutex
+}
+
+func defaultCNIConfig() *libcni {
+	return &libcni{
+		config: config{
+			pluginDirs:       []string{DefaultCNIDir},
+			pluginConfDir:    DefaultNetDir,
+			pluginMaxConfNum: DefaultMaxConfNum,
+			prefix:           DefaultPrefix,
+		},
+		cniConfig: &cnilibrary.CNIConfig{
+			Path: []string{DefaultCNIDir},
+		},
+		networkCount: 1,
+	}
+}
+
+// New creates a new libcni instance.
+func New(config ...CNIOpt) (CNI, error) {
+	cni := defaultCNIConfig()
+	var err error
+	for _, c := range config {
+		if err = c(cni); err != nil {
+			return nil, err
+		}
+	}
+	return cni, nil
+}
+
+// Load loads the latest config from cni config files.
+func (c *libcni) Load(opts ...CNIOpt) error {
+	var err error
+	c.Lock()
+	defer c.Unlock()
+	// Reset the networks on a load operation to ensure
+	// config happens on a clean slate
+	c.reset()
+
+	for _, o := range opts {
+		if err = o(c); err != nil {
+			return errors.Wrapf(ErrLoad, fmt.Sprintf("cni config load failed: %v", err))
+		}
+	}
+	return nil
+}
+
+// Status returns the status of CNI initialization.
+func (c *libcni) Status() error {
+	c.RLock()
+	defer c.RUnlock()
+	if len(c.networks) < c.networkCount {
+		return ErrCNINotInitialized
+	}
+	return nil
+}
+
+// Networks returns all the configured networks.
+// NOTE: Caller MUST NOT modify anything in the returned array.
+func (c *libcni) Networks() []*Network {
+	c.RLock()
+	defer c.RUnlock()
+	return append([]*Network{}, c.networks...)
+}
+
+// Setup setups the network in the namespace
+func (c *libcni) Setup(id string, path string, opts ...NamespaceOpts) (*CNIResult, error) {
+	if err := c.Status(); err != nil {
+		return nil, err
+	}
+	ns, err := newNamespace(id, path, opts...)
+	if err != nil {
+		return nil, err
+	}
+	var results []*current.Result
+	for _, network := range c.Networks() {
+		r, err := network.Attach(ns)
+		if err != nil {
+			return nil, err
+		}
+		results = append(results, r)
+	}
+	return c.GetCNIResultFromResults(results)
+}
+
+// Remove removes the network config from the namespace
+func (c *libcni) Remove(id string, path string, opts ...NamespaceOpts) error {
+	if err := c.Status(); err != nil {
+		return err
+	}
+	ns, err := newNamespace(id, path, opts...)
+	if err != nil {
+		return err
+	}
+	for _, network := range c.Networks() {
+		if err := network.Remove(ns); err != nil {
+			// Based on CNI spec v0.7.0, empty network namespace is allowed to
+			// do best effort cleanup. However, it is not handled consistently
+			// right now:
+			// https://github.com/containernetworking/plugins/issues/210
+			// TODO(random-liu): Remove the error handling when the issue is
+			// fixed and the CNI spec v0.6.0 support is deprecated.
+			if path == "" && strings.Contains(err.Error(), "no such file or directory") {
+				continue
+			}
+			return err
+		}
+	}
+	return nil
+}
+
+// GetConfig returns a copy of the CNI plugin configurations as parsed by CNI
+func (c *libcni) GetConfig() *ConfigResult {
+	c.RLock()
+	defer c.RUnlock()
+	r := &ConfigResult{
+		PluginDirs:       c.config.pluginDirs,
+		PluginConfDir:    c.config.pluginConfDir,
+		PluginMaxConfNum: c.config.pluginMaxConfNum,
+		Prefix:           c.config.prefix,
+	}
+	for _, network := range c.networks {
+		conf := &NetworkConfList{
+			Name:       network.config.Name,
+			CNIVersion: network.config.CNIVersion,
+			Source:     string(network.config.Bytes),
+		}
+		for _, plugin := range network.config.Plugins {
+			conf.Plugins = append(conf.Plugins, &NetworkConf{
+				Network: plugin.Network,
+				Source:  string(plugin.Bytes),
+			})
+		}
+		r.Networks = append(r.Networks, &ConfNetwork{
+			Config: conf,
+			IFName: network.ifName,
+		})
+	}
+	return r
+}
+
+func (c *libcni) reset() {
+	c.networks = nil
+}
diff --git a/vendor/github.com/containerd/go-cni/errors.go b/vendor/github.com/containerd/go-cni/errors.go
new file mode 100644
index 000000000000..28761711ed29
--- /dev/null
+++ b/vendor/github.com/containerd/go-cni/errors.go
@@ -0,0 +1,55 @@
+/*
+   Copyright The containerd Authors.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+package cni
+
+import (
+	"github.com/pkg/errors"
+)
+
+var (
+	ErrCNINotInitialized = errors.New("cni plugin not initialized")
+	ErrInvalidConfig     = errors.New("invalid cni config")
+	ErrNotFound          = errors.New("not found")
+	ErrRead              = errors.New("failed to read config file")
+	ErrInvalidResult     = errors.New("invalid result")
+	ErrLoad              = errors.New("failed to load cni config")
+)
+
+// IsCNINotInitialized returns true if the error is due to cni config not being initialized
+func IsCNINotInitialized(err error) bool {
+	return errors.Cause(err) == ErrCNINotInitialized
+}
+
+// IsInvalidConfig returns true if the error is invalid cni config
+func IsInvalidConfig(err error) bool {
+	return errors.Cause(err) == ErrInvalidConfig
+}
+
+// IsNotFound returns true if the error is due to a missing config or result
+func IsNotFound(err error) bool {
+	return errors.Cause(err) == ErrNotFound
+}
+
+// IsReadFailure return true if the error is a config read failure
+func IsReadFailure(err error) bool {
+	return errors.Cause(err) == ErrRead
+}
+
+// IsInvalidResult return true if the error is due to invalid cni result
+func IsInvalidResult(err error) bool {
+	return errors.Cause(err) == ErrInvalidResult
+}
diff --git a/vendor/github.com/containerd/go-cni/helper.go b/vendor/github.com/containerd/go-cni/helper.go
new file mode 100644
index 000000000000..6cde2b33294b
--- /dev/null
+++ b/vendor/github.com/containerd/go-cni/helper.go
@@ -0,0 +1,41 @@
+/*
+   Copyright The containerd Authors.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+package cni
+
+import (
+	"fmt"
+
+	"github.com/containernetworking/cni/pkg/types/current"
+)
+
+func validateInterfaceConfig(ipConf *current.IPConfig, ifs int) error {
+	if ipConf == nil {
+		return fmt.Errorf("invalid IP configuration")
+	}
+	if ipConf.Interface != nil && *ipConf.Interface > ifs {
+		return fmt.Errorf("invalid IP configuration with invalid interface %d", *ipConf.Interface)
+	}
+	return nil
+}
+
+func getIfName(prefix string, i int) string {
+	return fmt.Sprintf("%s%d", prefix, i)
+}
+
+func defaultInterface(prefix string) string {
+	return getIfName(prefix, 0)
+}
diff --git a/vendor/github.com/containerd/go-cni/namespace.go b/vendor/github.com/containerd/go-cni/namespace.go
new file mode 100644
index 000000000000..746c995eb984
--- /dev/null
+++ b/vendor/github.com/containerd/go-cni/namespace.go
@@ -0,0 +1,75 @@
+/*
+   Copyright The containerd Authors.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+package cni
+
+import (
+	cnilibrary "github.com/containernetworking/cni/libcni"
+	"github.com/containernetworking/cni/pkg/types/current"
+)
+
+type Network struct {
+	cni    cnilibrary.CNI
+	config *cnilibrary.NetworkConfigList
+	ifName string
+}
+
+func (n *Network) Attach(ns *Namespace) (*current.Result, error) {
+	r, err := n.cni.AddNetworkList(n.config, ns.config(n.ifName))
+	if err != nil {
+		return nil, err
+	}
+	return current.NewResultFromResult(r)
+}
+
+func (n *Network) Remove(ns *Namespace) error {
+	return n.cni.DelNetworkList(n.config, ns.config(n.ifName))
+}
+
+type Namespace struct {
+	id             string
+	path           string
+	capabilityArgs map[string]interface{}
+	args           map[string]string
+}
+
+func newNamespace(id, path string, opts ...NamespaceOpts) (*Namespace, error) {
+	ns := &Namespace{
+		id:             id,
+		path:           path,
+		capabilityArgs: make(map[string]interface{}),
+		args:           make(map[string]string),
+	}
+	for _, o := range opts {
+		if err := o(ns); err != nil {
+			return nil, err
+		}
+	}
+	return ns, nil
+}
+
+func (ns *Namespace) config(ifName string) *cnilibrary.RuntimeConf {
+	c := &cnilibrary.RuntimeConf{
+		ContainerID: ns.id,
+		NetNS:       ns.path,
+		IfName:      ifName,
+	}
+	for k, v := range ns.args {
+		c.Args = append(c.Args, [2]string{k, v})
+	}
+	c.CapabilityArgs = ns.capabilityArgs
+	return c
+}
diff --git a/vendor/github.com/containerd/go-cni/namespace_opts.go b/vendor/github.com/containerd/go-cni/namespace_opts.go
new file mode 100644
index 000000000000..e8092e85ecd7
--- /dev/null
+++ b/vendor/github.com/containerd/go-cni/namespace_opts.go
@@ -0,0 +1,67 @@
+/*
+   Copyright The containerd Authors.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+package cni
+
+type NamespaceOpts func(s *Namespace) error
+
+// Capabilities
+func WithCapabilityPortMap(portMapping []PortMapping) NamespaceOpts {
+	return func(c *Namespace) error {
+		c.capabilityArgs["portMappings"] = portMapping
+		return nil
+	}
+}
+
+func WithCapabilityIPRanges(ipRanges []IPRanges) NamespaceOpts {
+	return func(c *Namespace) error {
+		c.capabilityArgs["ipRanges"] = ipRanges
+		return nil
+	}
+}
+
+// WithCapabilityBandWitdh adds support for traffic shaping:
+// https://github.com/heptio/cni-plugins/tree/master/plugins/meta/bandwidth
+func WithCapabilityBandWidth(bandWidth BandWidth) NamespaceOpts {
+	return func(c *Namespace) error {
+		c.capabilityArgs["bandwidth"] = bandWidth
+		return nil
+	}
+}
+
+func WithCapability(name string, capability interface{}) NamespaceOpts {
+	return func(c *Namespace) error {
+		c.capabilityArgs[name] = capability
+		return nil
+	}
+}
+
+// Args
+func WithLabels(labels map[string]string) NamespaceOpts {
+	return func(c *Namespace) error {
+		for k, v := range labels {
+			c.args[k] = v
+		}
+		return nil
+	}
+}
+
+func WithArgs(k, v string) NamespaceOpts {
+	return func(c *Namespace) error {
+		c.args[k] = v
+		return nil
+	}
+}
diff --git a/vendor/github.com/containerd/go-cni/opts.go b/vendor/github.com/containerd/go-cni/opts.go
new file mode 100644
index 000000000000..4a6f66d05f0e
--- /dev/null
+++ b/vendor/github.com/containerd/go-cni/opts.go
@@ -0,0 +1,245 @@
+/*
+   Copyright The containerd Authors.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+package cni
+
+import (
+	"sort"
+	"strings"
+
+	cnilibrary "github.com/containernetworking/cni/libcni"
+	"github.com/pkg/errors"
+)
+
+type CNIOpt func(c *libcni) error
+
+// WithInterfacePrefix sets the prefix for network interfaces
+// e.g. eth or wlan
+func WithInterfacePrefix(prefix string) CNIOpt {
+	return func(c *libcni) error {
+		c.prefix = prefix
+		return nil
+	}
+}
+
+// WithPluginDir can be used to set the locations of
+// the cni plugin binaries
+func WithPluginDir(dirs []string) CNIOpt {
+	return func(c *libcni) error {
+		c.pluginDirs = dirs
+		c.cniConfig = &cnilibrary.CNIConfig{Path: dirs}
+		return nil
+	}
+}
+
+// WithPluginConfDir can be used to configure the
+// cni configuration directory.
+func WithPluginConfDir(dir string) CNIOpt {
+	return func(c *libcni) error {
+		c.pluginConfDir = dir
+		return nil
+	}
+}
+
+// WithPluginMaxConfNum can be used to configure the
+// max cni plugin config file num.
+func WithPluginMaxConfNum(max int) CNIOpt {
+	return func(c *libcni) error {
+		c.pluginMaxConfNum = max
+		return nil
+	}
+}
+
+// WithMinNetworkCount can be used to configure the
+// minimum networks to be configured and initialized
+// for the status to report success. By default its 1.
+func WithMinNetworkCount(count int) CNIOpt {
+	return func(c *libcni) error {
+		c.networkCount = count
+		return nil
+	}
+}
+
+// WithLoNetwork can be used to load the loopback
+// network config.
+func WithLoNetwork(c *libcni) error {
+	loConfig, _ := cnilibrary.ConfListFromBytes([]byte(`{
+"cniVersion": "0.3.1",
+"name": "cni-loopback",
+"plugins": [{
+  "type": "loopback"
+}]
+}`))
+
+	c.networks = append(c.networks, &Network{
+		cni:    c.cniConfig,
+		config: loConfig,
+		ifName: "lo",
+	})
+	return nil
+}
+
+// WithConf can be used to load config directly
+// from byte.
+func WithConf(bytes []byte) CNIOpt {
+	return WithConfIndex(bytes, 0)
+}
+
+// WithConfIndex can be used to load config directly
+// from byte and set the interface name's index.
+func WithConfIndex(bytes []byte, index int) CNIOpt {
+	return func(c *libcni) error {
+		conf, err := cnilibrary.ConfFromBytes(bytes)
+		if err != nil {
+			return err
+		}
+		confList, err := cnilibrary.ConfListFromConf(conf)
+		if err != nil {
+			return err
+		}
+		c.networks = append(c.networks, &Network{
+			cni:    c.cniConfig,
+			config: confList,
+			ifName: getIfName(c.prefix, index),
+		})
+		return nil
+	}
+}
+
+// WithConfFile can be used to load network config
+// from an .conf file. Supported with absolute fileName
+// with path only.
+func WithConfFile(fileName string) CNIOpt {
+	return func(c *libcni) error {
+		conf, err := cnilibrary.ConfFromFile(fileName)
+		if err != nil {
+			return err
+		}
+		// upconvert to conf list
+		confList, err := cnilibrary.ConfListFromConf(conf)
+		if err != nil {
+			return err
+		}
+		c.networks = append(c.networks, &Network{
+			cni:    c.cniConfig,
+			config: confList,
+			ifName: getIfName(c.prefix, 0),
+		})
+		return nil
+	}
+}
+
+// WithConfListFile can be used to load network config
+// from an .conflist file. Supported with absolute fileName
+// with path only.
+func WithConfListFile(fileName string) CNIOpt {
+	return func(c *libcni) error {
+		confList, err := cnilibrary.ConfListFromFile(fileName)
+		if err != nil {
+			return err
+		}
+		i := len(c.networks)
+		c.networks = append(c.networks, &Network{
+			cni:    c.cniConfig,
+			config: confList,
+			ifName: getIfName(c.prefix, i),
+		})
+		return nil
+	}
+}
+
+// WithDefaultConf can be used to detect the default network
+// config file from the configured cni config directory and load
+// it.
+// Since the CNI spec does not specify a way to detect default networks,
+// the convention chosen is - the first network configuration in the sorted
+// list of network conf files as the default network.
+func WithDefaultConf(c *libcni) error {
+	return loadFromConfDir(c, c.pluginMaxConfNum)
+}
+
+// WithAllConf can be used to detect all network config
+// files from the configured cni config directory and load
+// them.
+func WithAllConf(c *libcni) error {
+	return loadFromConfDir(c, 0)
+}
+
+// loadFromConfDir detects network config files from the
+// configured cni config directory and load them. max is
+// the maximum network config to load (max i<= 0 means no limit).
+func loadFromConfDir(c *libcni, max int) error {
+	files, err := cnilibrary.ConfFiles(c.pluginConfDir, []string{".conf", ".conflist", ".json"})
+	switch {
+	case err != nil:
+		return errors.Wrapf(ErrRead, "failed to read config file: %v", err)
+	case len(files) == 0:
+		return errors.Wrapf(ErrCNINotInitialized, "no network config found in %s", c.pluginConfDir)
+	}
+
+	// files contains the network config files associated with cni network.
+	// Use lexicographical way as a defined order for network config files.
+	sort.Strings(files)
+	// Since the CNI spec does not specify a way to detect default networks,
+	// the convention chosen is - the first network configuration in the sorted
+	// list of network conf files as the default network and choose the default
+	// interface provided during init as the network interface for this default
+	// network. For every other network use a generated interface id.
+	i := 0
+	var networks []*Network
+	for _, confFile := range files {
+		var confList *cnilibrary.NetworkConfigList
+		if strings.HasSuffix(confFile, ".conflist") {
+			confList, err = cnilibrary.ConfListFromFile(confFile)
+			if err != nil {
+				return errors.Wrapf(ErrInvalidConfig, "failed to load CNI config list file %s: %v", confFile, err)
+			}
+		} else {
+			conf, err := cnilibrary.ConfFromFile(confFile)
+			if err != nil {
+				return errors.Wrapf(ErrInvalidConfig, "failed to load CNI config file %s: %v", confFile, err)
+			}
+			// Ensure the config has a "type" so we know what plugin to run.
+			// Also catches the case where somebody put a conflist into a conf file.
+			if conf.Network.Type == "" {
+				return errors.Wrapf(ErrInvalidConfig, "network type not found in %s", confFile)
+			}
+
+			confList, err = cnilibrary.ConfListFromConf(conf)
+			if err != nil {
+				return errors.Wrapf(ErrInvalidConfig, "failed to convert CNI config file %s to list: %v", confFile, err)
+			}
+		}
+		if len(confList.Plugins) == 0 {
+			return errors.Wrapf(ErrInvalidConfig, "CNI config list %s has no networks, skipping", confFile)
+
+		}
+		networks = append(networks, &Network{
+			cni:    c.cniConfig,
+			config: confList,
+			ifName: getIfName(c.prefix, i),
+		})
+		i++
+		if i == max {
+			break
+		}
+	}
+	if len(networks) == 0 {
+		return errors.Wrapf(ErrCNINotInitialized, "no valid networks found in %s", c.pluginDirs)
+	}
+	c.networks = append(c.networks, networks...)
+	return nil
+}
diff --git a/vendor/github.com/containerd/go-cni/result.go b/vendor/github.com/containerd/go-cni/result.go
new file mode 100644
index 000000000000..1e958dc76bc1
--- /dev/null
+++ b/vendor/github.com/containerd/go-cni/result.go
@@ -0,0 +1,106 @@
+/*
+   Copyright The containerd Authors.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+package cni
+
+import (
+	"net"
+
+	"github.com/containernetworking/cni/pkg/types"
+	"github.com/containernetworking/cni/pkg/types/current"
+	"github.com/pkg/errors"
+)
+
+type IPConfig struct {
+	IP      net.IP
+	Gateway net.IP
+}
+
+type CNIResult struct {
+	Interfaces map[string]*Config
+	DNS        []types.DNS
+	Routes     []*types.Route
+}
+
+type Config struct {
+	IPConfigs []*IPConfig
+	Mac       string
+	Sandbox   string
+}
+
+// GetCNIResultFromResults returns a structured data containing the
+// interface configuration for each of the interfaces created in the namespace.
+// Conforms with
+// Result:
+// a) Interfaces list. Depending on the plugin, this can include the sandbox
+// (eg, container or hypervisor) interface name and/or the host interface
+// name, the hardware addresses of each interface, and details about the
+// sandbox (if any) the interface is in.
+// b) IP configuration assigned to each  interface. The IPv4 and/or IPv6 addresses,
+// gateways, and routes assigned to sandbox and/or host interfaces.
+// c) DNS information. Dictionary that includes DNS information for nameservers,
+// domain, search domains and options.
+func (c *libcni) GetCNIResultFromResults(results []*current.Result) (*CNIResult, error) {
+	c.RLock()
+	defer c.RUnlock()
+
+	r := &CNIResult{
+		Interfaces: make(map[string]*Config),
+	}
+
+	// Plugins may not need to return Interfaces in result if
+	// if there are no multiple interfaces created. In that case
+	// all configs should be applied against default interface
+	r.Interfaces[defaultInterface(c.prefix)] = &Config{}
+
+	// Walk through all the results
+	for _, result := range results {
+		// Walk through all the interface in each result
+		for _, intf := range result.Interfaces {
+			r.Interfaces[intf.Name] = &Config{
+				Mac:     intf.Mac,
+				Sandbox: intf.Sandbox,
+			}
+		}
+		// Walk through all the IPs in the result and attach it to corresponding
+		// interfaces
+		for _, ipConf := range result.IPs {
+			if err := validateInterfaceConfig(ipConf, len(result.Interfaces)); err != nil {
+				return nil, errors.Wrapf(ErrInvalidResult, "failed to valid interface config: %v", err)
+			}
+			name := c.getInterfaceName(result.Interfaces, ipConf)
+			r.Interfaces[name].IPConfigs = append(r.Interfaces[name].IPConfigs,
+				&IPConfig{IP: ipConf.Address.IP, Gateway: ipConf.Gateway})
+		}
+		r.DNS = append(r.DNS, result.DNS)
+		r.Routes = append(r.Routes, result.Routes...)
+	}
+	if _, ok := r.Interfaces[defaultInterface(c.prefix)]; !ok {
+		return nil, errors.Wrapf(ErrNotFound, "default network not found")
+	}
+	return r, nil
+}
+
+// getInterfaceName returns the interface name if the plugins
+// return the result with associated interfaces. If interface
+// is not present then default interface name is used
+func (c *libcni) getInterfaceName(interfaces []*current.Interface,
+	ipConf *current.IPConfig) string {
+	if ipConf.Interface != nil {
+		return interfaces[*ipConf.Interface].Name
+	}
+	return defaultInterface(c.prefix)
+}
diff --git a/vendor/github.com/containerd/go-cni/testutils.go b/vendor/github.com/containerd/go-cni/testutils.go
new file mode 100644
index 000000000000..d9453c8d983d
--- /dev/null
+++ b/vendor/github.com/containerd/go-cni/testutils.go
@@ -0,0 +1,78 @@
+/*
+   Copyright The containerd Authors.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+package cni
+
+import (
+	"fmt"
+	"io/ioutil"
+	"os"
+	"path"
+	"testing"
+)
+
+func makeTmpDir(prefix string) (string, error) {
+	tmpDir, err := ioutil.TempDir(os.TempDir(), prefix)
+	if err != nil {
+		return "", err
+	}
+	return tmpDir, nil
+}
+
+func makeFakeCNIConfig(t *testing.T) (string, string) {
+	cniDir, err := makeTmpDir("fakecni")
+	if err != nil {
+		t.Fatalf("Failed to create plugin config dir: %v", err)
+	}
+
+	cniConfDir := path.Join(cniDir, "net.d")
+	err = os.MkdirAll(cniConfDir, 0777)
+	if err != nil {
+		t.Fatalf("Failed to create network config dir: %v", err)
+	}
+
+	networkConfig1 := path.Join(cniConfDir, "mocknetwork1.conf")
+	f1, err := os.Create(networkConfig1)
+	if err != nil {
+		t.Fatalf("Failed to create network config %v: %v", f1, err)
+	}
+	networkConfig2 := path.Join(cniConfDir, "mocknetwork2.conf")
+	f2, err := os.Create(networkConfig2)
+	if err != nil {
+		t.Fatalf("Failed to create network config %v: %v", f2, err)
+	}
+
+	cfg1 := fmt.Sprintf(`{ "name": "%s", "type": "%s", "capabilities": {"portMappings": true}  }`, "plugin1", "fakecni")
+	_, err = f1.WriteString(cfg1)
+	if err != nil {
+		t.Fatalf("Failed to write network config file %v: %v", f1, err)
+	}
+	f1.Close()
+	cfg2 := fmt.Sprintf(`{ "name": "%s", "type": "%s", "capabilities": {"portMappings": true}  }`, "plugin2", "fakecni")
+	_, err = f2.WriteString(cfg2)
+	if err != nil {
+		t.Fatalf("Failed to write network config file %v: %v", f2, err)
+	}
+	f2.Close()
+	return cniDir, cniConfDir
+}
+
+func tearDownCNIConfig(t *testing.T, confDir string) {
+	err := os.RemoveAll(confDir)
+	if err != nil {
+		t.Fatalf("Failed to cleanup CNI configs: %v", err)
+	}
+}
diff --git a/vendor/github.com/containerd/go-cni/types.go b/vendor/github.com/containerd/go-cni/types.go
new file mode 100644
index 000000000000..8583050e48a5
--- /dev/null
+++ b/vendor/github.com/containerd/go-cni/types.go
@@ -0,0 +1,55 @@
+/*
+   Copyright The containerd Authors.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+package cni
+
+const (
+	CNIPluginName        = "cni"
+	DefaultNetDir        = "/etc/cni/net.d"
+	DefaultCNIDir        = "/opt/cni/bin"
+	DefaultMaxConfNum    = 1
+	VendorCNIDirTemplate = "%s/opt/%s/bin"
+	DefaultPrefix        = "eth"
+)
+
+type config struct {
+	pluginDirs       []string
+	pluginConfDir    string
+	pluginMaxConfNum int
+	prefix           string
+}
+
+type PortMapping struct {
+	HostPort      int32
+	ContainerPort int32
+	Protocol      string
+	HostIP        string
+}
+
+type IPRanges struct {
+	Subnet     string
+	RangeStart string
+	RangeEnd   string
+	Gateway    string
+}
+
+// BandWidth defines the ingress/egress rate and burst limits
+type BandWidth struct {
+	IngressRate  uint64
+	IngressBurst uint64
+	EgressRate   uint64
+	EgressBurst  uint64
+}
diff --git a/vendor/github.com/containerd/go-cni/vendor.conf b/vendor/github.com/containerd/go-cni/vendor.conf
new file mode 100644
index 000000000000..aefe9a108cfc
--- /dev/null
+++ b/vendor/github.com/containerd/go-cni/vendor.conf
@@ -0,0 +1,6 @@
+github.com/stretchr/testify b89eecf5ca5db6d3ba60b237ffe3df7bafb7662f
+github.com/davecgh/go-spew 8991bc29aa16c548c550c7ff78260e27b9ab7c73
+github.com/pmezard/go-difflib 792786c7400a136282c1664665ae0a8db921c6c2
+github.com/stretchr/objx 8a3f7159479fbc75b30357fbc48f380b7320f08e
+github.com/containernetworking/cni 142cde0c766cd6055cc7fdfdcb44579c0c9c35bf
+github.com/pkg/errors v0.8.0
diff --git a/vendor/github.com/containernetworking/cni/LICENSE b/vendor/github.com/containernetworking/cni/LICENSE
new file mode 100644
index 000000000000..8f71f43fee3f
--- /dev/null
+++ b/vendor/github.com/containernetworking/cni/LICENSE
@@ -0,0 +1,202 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "{}"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright {yyyy} {name of copyright owner}
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
diff --git a/vendor/github.com/containernetworking/cni/libcni/api.go b/vendor/github.com/containernetworking/cni/libcni/api.go
new file mode 100644
index 000000000000..a23cbb2c57b0
--- /dev/null
+++ b/vendor/github.com/containernetworking/cni/libcni/api.go
@@ -0,0 +1,219 @@
+// Copyright 2015 CNI authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package libcni
+
+import (
+	"os"
+	"strings"
+
+	"github.com/containernetworking/cni/pkg/invoke"
+	"github.com/containernetworking/cni/pkg/types"
+	"github.com/containernetworking/cni/pkg/version"
+)
+
+type RuntimeConf struct {
+	ContainerID string
+	NetNS       string
+	IfName      string
+	Args        [][2]string
+	// A dictionary of capability-specific data passed by the runtime
+	// to plugins as top-level keys in the 'runtimeConfig' dictionary
+	// of the plugin's stdin data.  libcni will ensure that only keys
+	// in this map which match the capabilities of the plugin are passed
+	// to the plugin
+	CapabilityArgs map[string]interface{}
+}
+
+type NetworkConfig struct {
+	Network *types.NetConf
+	Bytes   []byte
+}
+
+type NetworkConfigList struct {
+	Name       string
+	CNIVersion string
+	Plugins    []*NetworkConfig
+	Bytes      []byte
+}
+
+type CNI interface {
+	AddNetworkList(net *NetworkConfigList, rt *RuntimeConf) (types.Result, error)
+	DelNetworkList(net *NetworkConfigList, rt *RuntimeConf) error
+
+	AddNetwork(net *NetworkConfig, rt *RuntimeConf) (types.Result, error)
+	DelNetwork(net *NetworkConfig, rt *RuntimeConf) error
+}
+
+type CNIConfig struct {
+	Path []string
+}
+
+// CNIConfig implements the CNI interface
+var _ CNI = &CNIConfig{}
+
+func buildOneConfig(list *NetworkConfigList, orig *NetworkConfig, prevResult types.Result, rt *RuntimeConf) (*NetworkConfig, error) {
+	var err error
+
+	inject := map[string]interface{}{
+		"name":       list.Name,
+		"cniVersion": list.CNIVersion,
+	}
+	// Add previous plugin result
+	if prevResult != nil {
+		inject["prevResult"] = prevResult
+	}
+
+	// Ensure every config uses the same name and version
+	orig, err = InjectConf(orig, inject)
+	if err != nil {
+		return nil, err
+	}
+
+	return injectRuntimeConfig(orig, rt)
+}
+
+// This function takes a libcni RuntimeConf structure and injects values into
+// a "runtimeConfig" dictionary in the CNI network configuration JSON that
+// will be passed to the plugin on stdin.
+//
+// Only "capabilities arguments" passed by the runtime are currently injected.
+// These capabilities arguments are filtered through the plugin's advertised
+// capabilities from its config JSON, and any keys in the CapabilityArgs
+// matching plugin capabilities are added to the "runtimeConfig" dictionary
+// sent to the plugin via JSON on stdin.  For exmaple, if the plugin's
+// capabilities include "portMappings", and the CapabilityArgs map includes a
+// "portMappings" key, that key and its value are added to the "runtimeConfig"
+// dictionary to be passed to the plugin's stdin.
+func injectRuntimeConfig(orig *NetworkConfig, rt *RuntimeConf) (*NetworkConfig, error) {
+	var err error
+
+	rc := make(map[string]interface{})
+	for capability, supported := range orig.Network.Capabilities {
+		if !supported {
+			continue
+		}
+		if data, ok := rt.CapabilityArgs[capability]; ok {
+			rc[capability] = data
+		}
+	}
+
+	if len(rc) > 0 {
+		orig, err = InjectConf(orig, map[string]interface{}{"runtimeConfig": rc})
+		if err != nil {
+			return nil, err
+		}
+	}
+
+	return orig, nil
+}
+
+// AddNetworkList executes a sequence of plugins with the ADD command
+func (c *CNIConfig) AddNetworkList(list *NetworkConfigList, rt *RuntimeConf) (types.Result, error) {
+	var prevResult types.Result
+	for _, net := range list.Plugins {
+		pluginPath, err := invoke.FindInPath(net.Network.Type, c.Path)
+		if err != nil {
+			return nil, err
+		}
+
+		newConf, err := buildOneConfig(list, net, prevResult, rt)
+		if err != nil {
+			return nil, err
+		}
+
+		prevResult, err = invoke.ExecPluginWithResult(pluginPath, newConf.Bytes, c.args("ADD", rt))
+		if err != nil {
+			return nil, err
+		}
+	}
+
+	return prevResult, nil
+}
+
+// DelNetworkList executes a sequence of plugins with the DEL command
+func (c *CNIConfig) DelNetworkList(list *NetworkConfigList, rt *RuntimeConf) error {
+	for i := len(list.Plugins) - 1; i >= 0; i-- {
+		net := list.Plugins[i]
+
+		pluginPath, err := invoke.FindInPath(net.Network.Type, c.Path)
+		if err != nil {
+			return err
+		}
+
+		newConf, err := buildOneConfig(list, net, nil, rt)
+		if err != nil {
+			return err
+		}
+
+		if err := invoke.ExecPluginWithoutResult(pluginPath, newConf.Bytes, c.args("DEL", rt)); err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+// AddNetwork executes the plugin with the ADD command
+func (c *CNIConfig) AddNetwork(net *NetworkConfig, rt *RuntimeConf) (types.Result, error) {
+	pluginPath, err := invoke.FindInPath(net.Network.Type, c.Path)
+	if err != nil {
+		return nil, err
+	}
+
+	net, err = injectRuntimeConfig(net, rt)
+	if err != nil {
+		return nil, err
+	}
+
+	return invoke.ExecPluginWithResult(pluginPath, net.Bytes, c.args("ADD", rt))
+}
+
+// DelNetwork executes the plugin with the DEL command
+func (c *CNIConfig) DelNetwork(net *NetworkConfig, rt *RuntimeConf) error {
+	pluginPath, err := invoke.FindInPath(net.Network.Type, c.Path)
+	if err != nil {
+		return err
+	}
+
+	net, err = injectRuntimeConfig(net, rt)
+	if err != nil {
+		return err
+	}
+
+	return invoke.ExecPluginWithoutResult(pluginPath, net.Bytes, c.args("DEL", rt))
+}
+
+// GetVersionInfo reports which versions of the CNI spec are supported by
+// the given plugin.
+func (c *CNIConfig) GetVersionInfo(pluginType string) (version.PluginInfo, error) {
+	pluginPath, err := invoke.FindInPath(pluginType, c.Path)
+	if err != nil {
+		return nil, err
+	}
+
+	return invoke.GetVersionInfo(pluginPath)
+}
+
+// =====
+func (c *CNIConfig) args(action string, rt *RuntimeConf) *invoke.Args {
+	return &invoke.Args{
+		Command:     action,
+		ContainerID: rt.ContainerID,
+		NetNS:       rt.NetNS,
+		PluginArgs:  rt.Args,
+		IfName:      rt.IfName,
+		Path:        strings.Join(c.Path, string(os.PathListSeparator)),
+	}
+}
diff --git a/vendor/github.com/containernetworking/cni/libcni/conf.go b/vendor/github.com/containernetworking/cni/libcni/conf.go
new file mode 100644
index 000000000000..9834d715b59f
--- /dev/null
+++ b/vendor/github.com/containernetworking/cni/libcni/conf.go
@@ -0,0 +1,259 @@
+// Copyright 2015 CNI authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package libcni
+
+import (
+	"encoding/json"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"sort"
+)
+
+type NotFoundError struct {
+	Dir  string
+	Name string
+}
+
+func (e NotFoundError) Error() string {
+	return fmt.Sprintf(`no net configuration with name "%s" in %s`, e.Name, e.Dir)
+}
+
+type NoConfigsFoundError struct {
+	Dir string
+}
+
+func (e NoConfigsFoundError) Error() string {
+	return fmt.Sprintf(`no net configurations found in %s`, e.Dir)
+}
+
+func ConfFromBytes(bytes []byte) (*NetworkConfig, error) {
+	conf := &NetworkConfig{Bytes: bytes}
+	if err := json.Unmarshal(bytes, &conf.Network); err != nil {
+		return nil, fmt.Errorf("error parsing configuration: %s", err)
+	}
+	if conf.Network.Type == "" {
+		return nil, fmt.Errorf("error parsing configuration: missing 'type'")
+	}
+	return conf, nil
+}
+
+func ConfFromFile(filename string) (*NetworkConfig, error) {
+	bytes, err := ioutil.ReadFile(filename)
+	if err != nil {
+		return nil, fmt.Errorf("error reading %s: %s", filename, err)
+	}
+	return ConfFromBytes(bytes)
+}
+
+func ConfListFromBytes(bytes []byte) (*NetworkConfigList, error) {
+	rawList := make(map[string]interface{})
+	if err := json.Unmarshal(bytes, &rawList); err != nil {
+		return nil, fmt.Errorf("error parsing configuration list: %s", err)
+	}
+
+	rawName, ok := rawList["name"]
+	if !ok {
+		return nil, fmt.Errorf("error parsing configuration list: no name")
+	}
+	name, ok := rawName.(string)
+	if !ok {
+		return nil, fmt.Errorf("error parsing configuration list: invalid name type %T", rawName)
+	}
+
+	var cniVersion string
+	rawVersion, ok := rawList["cniVersion"]
+	if ok {
+		cniVersion, ok = rawVersion.(string)
+		if !ok {
+			return nil, fmt.Errorf("error parsing configuration list: invalid cniVersion type %T", rawVersion)
+		}
+	}
+
+	list := &NetworkConfigList{
+		Name:       name,
+		CNIVersion: cniVersion,
+		Bytes:      bytes,
+	}
+
+	var plugins []interface{}
+	plug, ok := rawList["plugins"]
+	if !ok {
+		return nil, fmt.Errorf("error parsing configuration list: no 'plugins' key")
+	}
+	plugins, ok = plug.([]interface{})
+	if !ok {
+		return nil, fmt.Errorf("error parsing configuration list: invalid 'plugins' type %T", plug)
+	}
+	if len(plugins) == 0 {
+		return nil, fmt.Errorf("error parsing configuration list: no plugins in list")
+	}
+
+	for i, conf := range plugins {
+		newBytes, err := json.Marshal(conf)
+		if err != nil {
+			return nil, fmt.Errorf("Failed to marshal plugin config %d: %v", i, err)
+		}
+		netConf, err := ConfFromBytes(newBytes)
+		if err != nil {
+			return nil, fmt.Errorf("Failed to parse plugin config %d: %v", i, err)
+		}
+		list.Plugins = append(list.Plugins, netConf)
+	}
+
+	return list, nil
+}
+
+func ConfListFromFile(filename string) (*NetworkConfigList, error) {
+	bytes, err := ioutil.ReadFile(filename)
+	if err != nil {
+		return nil, fmt.Errorf("error reading %s: %s", filename, err)
+	}
+	return ConfListFromBytes(bytes)
+}
+
+func ConfFiles(dir string, extensions []string) ([]string, error) {
+	// In part, adapted from rkt/networking/podenv.go#listFiles
+	files, err := ioutil.ReadDir(dir)
+	switch {
+	case err == nil: // break
+	case os.IsNotExist(err):
+		return nil, nil
+	default:
+		return nil, err
+	}
+
+	confFiles := []string{}
+	for _, f := range files {
+		if f.IsDir() {
+			continue
+		}
+		fileExt := filepath.Ext(f.Name())
+		for _, ext := range extensions {
+			if fileExt == ext {
+				confFiles = append(confFiles, filepath.Join(dir, f.Name()))
+			}
+		}
+	}
+	return confFiles, nil
+}
+
+func LoadConf(dir, name string) (*NetworkConfig, error) {
+	files, err := ConfFiles(dir, []string{".conf", ".json"})
+	switch {
+	case err != nil:
+		return nil, err
+	case len(files) == 0:
+		return nil, NoConfigsFoundError{Dir: dir}
+	}
+	sort.Strings(files)
+
+	for _, confFile := range files {
+		conf, err := ConfFromFile(confFile)
+		if err != nil {
+			return nil, err
+		}
+		if conf.Network.Name == name {
+			return conf, nil
+		}
+	}
+	return nil, NotFoundError{dir, name}
+}
+
+func LoadConfList(dir, name string) (*NetworkConfigList, error) {
+	files, err := ConfFiles(dir, []string{".conflist"})
+	if err != nil {
+		return nil, err
+	}
+	sort.Strings(files)
+
+	for _, confFile := range files {
+		conf, err := ConfListFromFile(confFile)
+		if err != nil {
+			return nil, err
+		}
+		if conf.Name == name {
+			return conf, nil
+		}
+	}
+
+	// Try and load a network configuration file (instead of list)
+	// from the same name, then upconvert.
+	singleConf, err := LoadConf(dir, name)
+	if err != nil {
+		// A little extra logic so the error makes sense
+		if _, ok := err.(NoConfigsFoundError); len(files) != 0 && ok {
+			// Config lists found but no config files found
+			return nil, NotFoundError{dir, name}
+		}
+
+		return nil, err
+	}
+	return ConfListFromConf(singleConf)
+}
+
+func InjectConf(original *NetworkConfig, newValues map[string]interface{}) (*NetworkConfig, error) {
+	config := make(map[string]interface{})
+	err := json.Unmarshal(original.Bytes, &config)
+	if err != nil {
+		return nil, fmt.Errorf("unmarshal existing network bytes: %s", err)
+	}
+
+	for key, value := range newValues {
+		if key == "" {
+			return nil, fmt.Errorf("keys cannot be empty")
+		}
+
+		if value == nil {
+			return nil, fmt.Errorf("key '%s' value must not be nil", key)
+		}
+
+		config[key] = value
+	}
+
+	newBytes, err := json.Marshal(config)
+	if err != nil {
+		return nil, err
+	}
+
+	return ConfFromBytes(newBytes)
+}
+
+// ConfListFromConf "upconverts" a network config in to a NetworkConfigList,
+// with the single network as the only entry in the list.
+func ConfListFromConf(original *NetworkConfig) (*NetworkConfigList, error) {
+	// Re-deserialize the config's json, then make a raw map configlist.
+	// This may seem a bit strange, but it's to make the Bytes fields
+	// actually make sense. Otherwise, the generated json is littered with
+	// golang default values.
+
+	rawConfig := make(map[string]interface{})
+	if err := json.Unmarshal(original.Bytes, &rawConfig); err != nil {
+		return nil, err
+	}
+
+	rawConfigList := map[string]interface{}{
+		"name":       original.Network.Name,
+		"cniVersion": original.Network.CNIVersion,
+		"plugins":    []interface{}{rawConfig},
+	}
+
+	b, err := json.Marshal(rawConfigList)
+	if err != nil {
+		return nil, err
+	}
+	return ConfListFromBytes(b)
+}
diff --git a/vendor/github.com/containernetworking/cni/pkg/invoke/args.go b/vendor/github.com/containernetworking/cni/pkg/invoke/args.go
new file mode 100644
index 000000000000..39b639723051
--- /dev/null
+++ b/vendor/github.com/containernetworking/cni/pkg/invoke/args.go
@@ -0,0 +1,82 @@
+// Copyright 2015 CNI authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package invoke
+
+import (
+	"os"
+	"strings"
+)
+
+type CNIArgs interface {
+	// For use with os/exec; i.e., return nil to inherit the
+	// environment from this process
+	AsEnv() []string
+}
+
+type inherited struct{}
+
+var inheritArgsFromEnv inherited
+
+func (_ *inherited) AsEnv() []string {
+	return nil
+}
+
+func ArgsFromEnv() CNIArgs {
+	return &inheritArgsFromEnv
+}
+
+type Args struct {
+	Command       string
+	ContainerID   string
+	NetNS         string
+	PluginArgs    [][2]string
+	PluginArgsStr string
+	IfName        string
+	Path          string
+}
+
+// Args implements the CNIArgs interface
+var _ CNIArgs = &Args{}
+
+func (args *Args) AsEnv() []string {
+	env := os.Environ()
+	pluginArgsStr := args.PluginArgsStr
+	if pluginArgsStr == "" {
+		pluginArgsStr = stringify(args.PluginArgs)
+	}
+
+	// Ensure that the custom values are first, so any value present in
+	// the process environment won't override them.
+	env = append([]string{
+		"CNI_COMMAND=" + args.Command,
+		"CNI_CONTAINERID=" + args.ContainerID,
+		"CNI_NETNS=" + args.NetNS,
+		"CNI_ARGS=" + pluginArgsStr,
+		"CNI_IFNAME=" + args.IfName,
+		"CNI_PATH=" + args.Path,
+	}, env...)
+	return env
+}
+
+// taken from rkt/networking/net_plugin.go
+func stringify(pluginArgs [][2]string) string {
+	entries := make([]string, len(pluginArgs))
+
+	for i, kv := range pluginArgs {
+		entries[i] = strings.Join(kv[:], "=")
+	}
+
+	return strings.Join(entries, ";")
+}
diff --git a/vendor/github.com/containernetworking/cni/pkg/invoke/delegate.go b/vendor/github.com/containernetworking/cni/pkg/invoke/delegate.go
new file mode 100644
index 000000000000..c78a69eebc8e
--- /dev/null
+++ b/vendor/github.com/containernetworking/cni/pkg/invoke/delegate.go
@@ -0,0 +1,53 @@
+// Copyright 2016 CNI authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package invoke
+
+import (
+	"fmt"
+	"os"
+	"path/filepath"
+
+	"github.com/containernetworking/cni/pkg/types"
+)
+
+func DelegateAdd(delegatePlugin string, netconf []byte) (types.Result, error) {
+	if os.Getenv("CNI_COMMAND") != "ADD" {
+		return nil, fmt.Errorf("CNI_COMMAND is not ADD")
+	}
+
+	paths := filepath.SplitList(os.Getenv("CNI_PATH"))
+
+	pluginPath, err := FindInPath(delegatePlugin, paths)
+	if err != nil {
+		return nil, err
+	}
+
+	return ExecPluginWithResult(pluginPath, netconf, ArgsFromEnv())
+}
+
+func DelegateDel(delegatePlugin string, netconf []byte) error {
+	if os.Getenv("CNI_COMMAND") != "DEL" {
+		return fmt.Errorf("CNI_COMMAND is not DEL")
+	}
+
+	paths := filepath.SplitList(os.Getenv("CNI_PATH"))
+
+	pluginPath, err := FindInPath(delegatePlugin, paths)
+	if err != nil {
+		return err
+	}
+
+	return ExecPluginWithoutResult(pluginPath, netconf, ArgsFromEnv())
+}
diff --git a/vendor/github.com/containernetworking/cni/pkg/invoke/exec.go b/vendor/github.com/containernetworking/cni/pkg/invoke/exec.go
new file mode 100644
index 000000000000..fc47e7c8253f
--- /dev/null
+++ b/vendor/github.com/containernetworking/cni/pkg/invoke/exec.go
@@ -0,0 +1,95 @@
+// Copyright 2015 CNI authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package invoke
+
+import (
+	"fmt"
+	"os"
+
+	"github.com/containernetworking/cni/pkg/types"
+	"github.com/containernetworking/cni/pkg/version"
+)
+
+func ExecPluginWithResult(pluginPath string, netconf []byte, args CNIArgs) (types.Result, error) {
+	return defaultPluginExec.WithResult(pluginPath, netconf, args)
+}
+
+func ExecPluginWithoutResult(pluginPath string, netconf []byte, args CNIArgs) error {
+	return defaultPluginExec.WithoutResult(pluginPath, netconf, args)
+}
+
+func GetVersionInfo(pluginPath string) (version.PluginInfo, error) {
+	return defaultPluginExec.GetVersionInfo(pluginPath)
+}
+
+var defaultPluginExec = &PluginExec{
+	RawExec:        &RawExec{Stderr: os.Stderr},
+	VersionDecoder: &version.PluginDecoder{},
+}
+
+type PluginExec struct {
+	RawExec interface {
+		ExecPlugin(pluginPath string, stdinData []byte, environ []string) ([]byte, error)
+	}
+	VersionDecoder interface {
+		Decode(jsonBytes []byte) (version.PluginInfo, error)
+	}
+}
+
+func (e *PluginExec) WithResult(pluginPath string, netconf []byte, args CNIArgs) (types.Result, error) {
+	stdoutBytes, err := e.RawExec.ExecPlugin(pluginPath, netconf, args.AsEnv())
+	if err != nil {
+		return nil, err
+	}
+
+	// Plugin must return result in same version as specified in netconf
+	versionDecoder := &version.ConfigDecoder{}
+	confVersion, err := versionDecoder.Decode(netconf)
+	if err != nil {
+		return nil, err
+	}
+
+	return version.NewResult(confVersion, stdoutBytes)
+}
+
+func (e *PluginExec) WithoutResult(pluginPath string, netconf []byte, args CNIArgs) error {
+	_, err := e.RawExec.ExecPlugin(pluginPath, netconf, args.AsEnv())
+	return err
+}
+
+// GetVersionInfo returns the version information available about the plugin.
+// For recent-enough plugins, it uses the information returned by the VERSION
+// command.  For older plugins which do not recognize that command, it reports
+// version 0.1.0
+func (e *PluginExec) GetVersionInfo(pluginPath string) (version.PluginInfo, error) {
+	args := &Args{
+		Command: "VERSION",
+
+		// set fake values required by plugins built against an older version of skel
+		NetNS:  "dummy",
+		IfName: "dummy",
+		Path:   "dummy",
+	}
+	stdin := []byte(fmt.Sprintf(`{"cniVersion":%q}`, version.Current()))
+	stdoutBytes, err := e.RawExec.ExecPlugin(pluginPath, stdin, args.AsEnv())
+	if err != nil {
+		if err.Error() == "unknown CNI_COMMAND: VERSION" {
+			return version.PluginSupports("0.1.0"), nil
+		}
+		return nil, err
+	}
+
+	return e.VersionDecoder.Decode(stdoutBytes)
+}
diff --git a/vendor/github.com/containernetworking/cni/pkg/invoke/find.go b/vendor/github.com/containernetworking/cni/pkg/invoke/find.go
new file mode 100644
index 000000000000..e815404c8591
--- /dev/null
+++ b/vendor/github.com/containernetworking/cni/pkg/invoke/find.go
@@ -0,0 +1,43 @@
+// Copyright 2015 CNI authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package invoke
+
+import (
+	"fmt"
+	"os"
+	"path/filepath"
+)
+
+// FindInPath returns the full path of the plugin by searching in the provided path
+func FindInPath(plugin string, paths []string) (string, error) {
+	if plugin == "" {
+		return "", fmt.Errorf("no plugin name provided")
+	}
+
+	if len(paths) == 0 {
+		return "", fmt.Errorf("no paths provided")
+	}
+
+	for _, path := range paths {
+		for _, fe := range ExecutableFileExtensions {
+			fullpath := filepath.Join(path, plugin) + fe
+			if fi, err := os.Stat(fullpath); err == nil && fi.Mode().IsRegular() {
+				return fullpath, nil
+			}
+		}
+	}
+
+	return "", fmt.Errorf("failed to find plugin %q in path %s", plugin, paths)
+}
diff --git a/vendor/github.com/containernetworking/cni/pkg/invoke/os_unix.go b/vendor/github.com/containernetworking/cni/pkg/invoke/os_unix.go
new file mode 100644
index 000000000000..bab5737a99d6
--- /dev/null
+++ b/vendor/github.com/containernetworking/cni/pkg/invoke/os_unix.go
@@ -0,0 +1,20 @@
+// Copyright 2016 CNI authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// +build darwin dragonfly freebsd linux netbsd opensbd solaris
+
+package invoke
+
+// Valid file extensions for plugin executables.
+var ExecutableFileExtensions = []string{""}
diff --git a/vendor/github.com/containernetworking/cni/pkg/invoke/os_windows.go b/vendor/github.com/containernetworking/cni/pkg/invoke/os_windows.go
new file mode 100644
index 000000000000..7665125b133c
--- /dev/null
+++ b/vendor/github.com/containernetworking/cni/pkg/invoke/os_windows.go
@@ -0,0 +1,18 @@
+// Copyright 2016 CNI authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package invoke
+
+// Valid file extensions for plugin executables.
+var ExecutableFileExtensions = []string{".exe", ""}
diff --git a/vendor/github.com/containernetworking/cni/pkg/invoke/raw_exec.go b/vendor/github.com/containernetworking/cni/pkg/invoke/raw_exec.go
new file mode 100644
index 000000000000..93f1e75d9fa7
--- /dev/null
+++ b/vendor/github.com/containernetworking/cni/pkg/invoke/raw_exec.go
@@ -0,0 +1,59 @@
+// Copyright 2016 CNI authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package invoke
+
+import (
+	"bytes"
+	"encoding/json"
+	"fmt"
+	"io"
+	"os/exec"
+
+	"github.com/containernetworking/cni/pkg/types"
+)
+
+type RawExec struct {
+	Stderr io.Writer
+}
+
+func (e *RawExec) ExecPlugin(pluginPath string, stdinData []byte, environ []string) ([]byte, error) {
+	stdout := &bytes.Buffer{}
+
+	c := exec.Cmd{
+		Env:    environ,
+		Path:   pluginPath,
+		Args:   []string{pluginPath},
+		Stdin:  bytes.NewBuffer(stdinData),
+		Stdout: stdout,
+		Stderr: e.Stderr,
+	}
+	if err := c.Run(); err != nil {
+		return nil, pluginErr(err, stdout.Bytes())
+	}
+
+	return stdout.Bytes(), nil
+}
+
+func pluginErr(err error, output []byte) error {
+	if _, ok := err.(*exec.ExitError); ok {
+		emsg := types.Error{}
+		if perr := json.Unmarshal(output, &emsg); perr != nil {
+			emsg.Msg = fmt.Sprintf("netplugin failed but error parsing its diagnostic message %q: %v", string(output), perr)
+		}
+		return &emsg
+	}
+
+	return err
+}
diff --git a/vendor/github.com/containernetworking/cni/pkg/types/020/types.go b/vendor/github.com/containernetworking/cni/pkg/types/020/types.go
new file mode 100644
index 000000000000..2833aba787f7
--- /dev/null
+++ b/vendor/github.com/containernetworking/cni/pkg/types/020/types.go
@@ -0,0 +1,135 @@
+// Copyright 2016 CNI authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package types020
+
+import (
+	"encoding/json"
+	"fmt"
+	"net"
+	"os"
+
+	"github.com/containernetworking/cni/pkg/types"
+)
+
+const ImplementedSpecVersion string = "0.2.0"
+
+var SupportedVersions = []string{"", "0.1.0", ImplementedSpecVersion}
+
+// Compatibility types for CNI version 0.1.0 and 0.2.0
+
+func NewResult(data []byte) (types.Result, error) {
+	result := &Result{}
+	if err := json.Unmarshal(data, result); err != nil {
+		return nil, err
+	}
+	return result, nil
+}
+
+func GetResult(r types.Result) (*Result, error) {
+	// We expect version 0.1.0/0.2.0 results
+	result020, err := r.GetAsVersion(ImplementedSpecVersion)
+	if err != nil {
+		return nil, err
+	}
+	result, ok := result020.(*Result)
+	if !ok {
+		return nil, fmt.Errorf("failed to convert result")
+	}
+	return result, nil
+}
+
+// Result is what gets returned from the plugin (via stdout) to the caller
+type Result struct {
+	CNIVersion string    `json:"cniVersion,omitempty"`
+	IP4        *IPConfig `json:"ip4,omitempty"`
+	IP6        *IPConfig `json:"ip6,omitempty"`
+	DNS        types.DNS `json:"dns,omitempty"`
+}
+
+func (r *Result) Version() string {
+	return ImplementedSpecVersion
+}
+
+func (r *Result) GetAsVersion(version string) (types.Result, error) {
+	for _, supportedVersion := range SupportedVersions {
+		if version == supportedVersion {
+			r.CNIVersion = version
+			return r, nil
+		}
+	}
+	return nil, fmt.Errorf("cannot convert version %q to %s", SupportedVersions, version)
+}
+
+func (r *Result) Print() error {
+	data, err := json.MarshalIndent(r, "", "    ")
+	if err != nil {
+		return err
+	}
+	_, err = os.Stdout.Write(data)
+	return err
+}
+
+// String returns a formatted string in the form of "[IP4: $1,][ IP6: $2,] DNS: $3" where
+// $1 represents the receiver's IPv4, $2 represents the receiver's IPv6 and $3 the
+// receiver's DNS. If $1 or $2 are nil, they won't be present in the returned string.
+func (r *Result) String() string {
+	var str string
+	if r.IP4 != nil {
+		str = fmt.Sprintf("IP4:%+v, ", *r.IP4)
+	}
+	if r.IP6 != nil {
+		str += fmt.Sprintf("IP6:%+v, ", *r.IP6)
+	}
+	return fmt.Sprintf("%sDNS:%+v", str, r.DNS)
+}
+
+// IPConfig contains values necessary to configure an interface
+type IPConfig struct {
+	IP      net.IPNet
+	Gateway net.IP
+	Routes  []types.Route
+}
+
+// net.IPNet is not JSON (un)marshallable so this duality is needed
+// for our custom IPNet type
+
+// JSON (un)marshallable types
+type ipConfig struct {
+	IP      types.IPNet   `json:"ip"`
+	Gateway net.IP        `json:"gateway,omitempty"`
+	Routes  []types.Route `json:"routes,omitempty"`
+}
+
+func (c *IPConfig) MarshalJSON() ([]byte, error) {
+	ipc := ipConfig{
+		IP:      types.IPNet(c.IP),
+		Gateway: c.Gateway,
+		Routes:  c.Routes,
+	}
+
+	return json.Marshal(ipc)
+}
+
+func (c *IPConfig) UnmarshalJSON(data []byte) error {
+	ipc := ipConfig{}
+	if err := json.Unmarshal(data, &ipc); err != nil {
+		return err
+	}
+
+	c.IP = net.IPNet(ipc.IP)
+	c.Gateway = ipc.Gateway
+	c.Routes = ipc.Routes
+	return nil
+}
diff --git a/vendor/github.com/containernetworking/cni/pkg/types/args.go b/vendor/github.com/containernetworking/cni/pkg/types/args.go
new file mode 100644
index 000000000000..bd8640fc969a
--- /dev/null
+++ b/vendor/github.com/containernetworking/cni/pkg/types/args.go
@@ -0,0 +1,112 @@
+// Copyright 2015 CNI authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package types
+
+import (
+	"encoding"
+	"fmt"
+	"reflect"
+	"strings"
+)
+
+// UnmarshallableBool typedef for builtin bool
+// because builtin type's methods can't be declared
+type UnmarshallableBool bool
+
+// UnmarshalText implements the encoding.TextUnmarshaler interface.
+// Returns boolean true if the string is "1" or "[Tt]rue"
+// Returns boolean false if the string is "0" or "[Ff]alse"
+func (b *UnmarshallableBool) UnmarshalText(data []byte) error {
+	s := strings.ToLower(string(data))
+	switch s {
+	case "1", "true":
+		*b = true
+	case "0", "false":
+		*b = false
+	default:
+		return fmt.Errorf("Boolean unmarshal error: invalid input %s", s)
+	}
+	return nil
+}
+
+// UnmarshallableString typedef for builtin string
+type UnmarshallableString string
+
+// UnmarshalText implements the encoding.TextUnmarshaler interface.
+// Returns the string
+func (s *UnmarshallableString) UnmarshalText(data []byte) error {
+	*s = UnmarshallableString(data)
+	return nil
+}
+
+// CommonArgs contains the IgnoreUnknown argument
+// and must be embedded by all Arg structs
+type CommonArgs struct {
+	IgnoreUnknown UnmarshallableBool `json:"ignoreunknown,omitempty"`
+}
+
+// GetKeyField is a helper function to receive Values
+// Values that represent a pointer to a struct
+func GetKeyField(keyString string, v reflect.Value) reflect.Value {
+	return v.Elem().FieldByName(keyString)
+}
+
+// UnmarshalableArgsError is used to indicate error unmarshalling args
+// from the args-string in the form "K=V;K2=V2;..."
+type UnmarshalableArgsError struct {
+	error
+}
+
+// LoadArgs parses args from a string in the form "K=V;K2=V2;..."
+func LoadArgs(args string, container interface{}) error {
+	if args == "" {
+		return nil
+	}
+
+	containerValue := reflect.ValueOf(container)
+
+	pairs := strings.Split(args, ";")
+	unknownArgs := []string{}
+	for _, pair := range pairs {
+		kv := strings.Split(pair, "=")
+		if len(kv) != 2 {
+			return fmt.Errorf("ARGS: invalid pair %q", pair)
+		}
+		keyString := kv[0]
+		valueString := kv[1]
+		keyField := GetKeyField(keyString, containerValue)
+		if !keyField.IsValid() {
+			unknownArgs = append(unknownArgs, pair)
+			continue
+		}
+		keyFieldIface := keyField.Addr().Interface()
+		u, ok := keyFieldIface.(encoding.TextUnmarshaler)
+		if !ok {
+			return UnmarshalableArgsError{fmt.Errorf(
+				"ARGS: cannot unmarshal into field '%s' - type '%s' does not implement encoding.TextUnmarshaler",
+				keyString, reflect.TypeOf(keyFieldIface))}
+		}
+		err := u.UnmarshalText([]byte(valueString))
+		if err != nil {
+			return fmt.Errorf("ARGS: error parsing value of pair %q: %v)", pair, err)
+		}
+	}
+
+	isIgnoreUnknown := GetKeyField("IgnoreUnknown", containerValue).Bool()
+	if len(unknownArgs) > 0 && !isIgnoreUnknown {
+		return fmt.Errorf("ARGS: unknown args %q", unknownArgs)
+	}
+	return nil
+}
diff --git a/vendor/github.com/containernetworking/cni/pkg/types/current/types.go b/vendor/github.com/containernetworking/cni/pkg/types/current/types.go
new file mode 100644
index 000000000000..caac92ba77ca
--- /dev/null
+++ b/vendor/github.com/containernetworking/cni/pkg/types/current/types.go
@@ -0,0 +1,300 @@
+// Copyright 2016 CNI authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package current
+
+import (
+	"encoding/json"
+	"fmt"
+	"net"
+	"os"
+
+	"github.com/containernetworking/cni/pkg/types"
+	"github.com/containernetworking/cni/pkg/types/020"
+)
+
+const ImplementedSpecVersion string = "0.3.1"
+
+var SupportedVersions = []string{"0.3.0", ImplementedSpecVersion}
+
+func NewResult(data []byte) (types.Result, error) {
+	result := &Result{}
+	if err := json.Unmarshal(data, result); err != nil {
+		return nil, err
+	}
+	return result, nil
+}
+
+func GetResult(r types.Result) (*Result, error) {
+	resultCurrent, err := r.GetAsVersion(ImplementedSpecVersion)
+	if err != nil {
+		return nil, err
+	}
+	result, ok := resultCurrent.(*Result)
+	if !ok {
+		return nil, fmt.Errorf("failed to convert result")
+	}
+	return result, nil
+}
+
+var resultConverters = []struct {
+	versions []string
+	convert  func(types.Result) (*Result, error)
+}{
+	{types020.SupportedVersions, convertFrom020},
+	{SupportedVersions, convertFrom030},
+}
+
+func convertFrom020(result types.Result) (*Result, error) {
+	oldResult, err := types020.GetResult(result)
+	if err != nil {
+		return nil, err
+	}
+
+	newResult := &Result{
+		CNIVersion: ImplementedSpecVersion,
+		DNS:        oldResult.DNS,
+		Routes:     []*types.Route{},
+	}
+
+	if oldResult.IP4 != nil {
+		newResult.IPs = append(newResult.IPs, &IPConfig{
+			Version: "4",
+			Address: oldResult.IP4.IP,
+			Gateway: oldResult.IP4.Gateway,
+		})
+		for _, route := range oldResult.IP4.Routes {
+			gw := route.GW
+			if gw == nil {
+				gw = oldResult.IP4.Gateway
+			}
+			newResult.Routes = append(newResult.Routes, &types.Route{
+				Dst: route.Dst,
+				GW:  gw,
+			})
+		}
+	}
+
+	if oldResult.IP6 != nil {
+		newResult.IPs = append(newResult.IPs, &IPConfig{
+			Version: "6",
+			Address: oldResult.IP6.IP,
+			Gateway: oldResult.IP6.Gateway,
+		})
+		for _, route := range oldResult.IP6.Routes {
+			gw := route.GW
+			if gw == nil {
+				gw = oldResult.IP6.Gateway
+			}
+			newResult.Routes = append(newResult.Routes, &types.Route{
+				Dst: route.Dst,
+				GW:  gw,
+			})
+		}
+	}
+
+	if len(newResult.IPs) == 0 {
+		return nil, fmt.Errorf("cannot convert: no valid IP addresses")
+	}
+
+	return newResult, nil
+}
+
+func convertFrom030(result types.Result) (*Result, error) {
+	newResult, ok := result.(*Result)
+	if !ok {
+		return nil, fmt.Errorf("failed to convert result")
+	}
+	newResult.CNIVersion = ImplementedSpecVersion
+	return newResult, nil
+}
+
+func NewResultFromResult(result types.Result) (*Result, error) {
+	version := result.Version()
+	for _, converter := range resultConverters {
+		for _, supportedVersion := range converter.versions {
+			if version == supportedVersion {
+				return converter.convert(result)
+			}
+		}
+	}
+	return nil, fmt.Errorf("unsupported CNI result22 version %q", version)
+}
+
+// Result is what gets returned from the plugin (via stdout) to the caller
+type Result struct {
+	CNIVersion string         `json:"cniVersion,omitempty"`
+	Interfaces []*Interface   `json:"interfaces,omitempty"`
+	IPs        []*IPConfig    `json:"ips,omitempty"`
+	Routes     []*types.Route `json:"routes,omitempty"`
+	DNS        types.DNS      `json:"dns,omitempty"`
+}
+
+// Convert to the older 0.2.0 CNI spec Result type
+func (r *Result) convertTo020() (*types020.Result, error) {
+	oldResult := &types020.Result{
+		CNIVersion: types020.ImplementedSpecVersion,
+		DNS:        r.DNS,
+	}
+
+	for _, ip := range r.IPs {
+		// Only convert the first IP address of each version as 0.2.0
+		// and earlier cannot handle multiple IP addresses
+		if ip.Version == "4" && oldResult.IP4 == nil {
+			oldResult.IP4 = &types020.IPConfig{
+				IP:      ip.Address,
+				Gateway: ip.Gateway,
+			}
+		} else if ip.Version == "6" && oldResult.IP6 == nil {
+			oldResult.IP6 = &types020.IPConfig{
+				IP:      ip.Address,
+				Gateway: ip.Gateway,
+			}
+		}
+
+		if oldResult.IP4 != nil && oldResult.IP6 != nil {
+			break
+		}
+	}
+
+	for _, route := range r.Routes {
+		is4 := route.Dst.IP.To4() != nil
+		if is4 && oldResult.IP4 != nil {
+			oldResult.IP4.Routes = append(oldResult.IP4.Routes, types.Route{
+				Dst: route.Dst,
+				GW:  route.GW,
+			})
+		} else if !is4 && oldResult.IP6 != nil {
+			oldResult.IP6.Routes = append(oldResult.IP6.Routes, types.Route{
+				Dst: route.Dst,
+				GW:  route.GW,
+			})
+		}
+	}
+
+	if oldResult.IP4 == nil && oldResult.IP6 == nil {
+		return nil, fmt.Errorf("cannot convert: no valid IP addresses")
+	}
+
+	return oldResult, nil
+}
+
+func (r *Result) Version() string {
+	return ImplementedSpecVersion
+}
+
+func (r *Result) GetAsVersion(version string) (types.Result, error) {
+	switch version {
+	case "0.3.0", ImplementedSpecVersion:
+		r.CNIVersion = version
+		return r, nil
+	case types020.SupportedVersions[0], types020.SupportedVersions[1], types020.SupportedVersions[2]:
+		return r.convertTo020()
+	}
+	return nil, fmt.Errorf("cannot convert version 0.3.x to %q", version)
+}
+
+func (r *Result) Print() error {
+	data, err := json.MarshalIndent(r, "", "    ")
+	if err != nil {
+		return err
+	}
+	_, err = os.Stdout.Write(data)
+	return err
+}
+
+// String returns a formatted string in the form of "[Interfaces: $1,][ IP: $2,] DNS: $3" where
+// $1 represents the receiver's Interfaces, $2 represents the receiver's IP addresses and $3 the
+// receiver's DNS. If $1 or $2 are nil, they won't be present in the returned string.
+func (r *Result) String() string {
+	var str string
+	if len(r.Interfaces) > 0 {
+		str += fmt.Sprintf("Interfaces:%+v, ", r.Interfaces)
+	}
+	if len(r.IPs) > 0 {
+		str += fmt.Sprintf("IP:%+v, ", r.IPs)
+	}
+	if len(r.Routes) > 0 {
+		str += fmt.Sprintf("Routes:%+v, ", r.Routes)
+	}
+	return fmt.Sprintf("%sDNS:%+v", str, r.DNS)
+}
+
+// Convert this old version result to the current CNI version result
+func (r *Result) Convert() (*Result, error) {
+	return r, nil
+}
+
+// Interface contains values about the created interfaces
+type Interface struct {
+	Name    string `json:"name"`
+	Mac     string `json:"mac,omitempty"`
+	Sandbox string `json:"sandbox,omitempty"`
+}
+
+func (i *Interface) String() string {
+	return fmt.Sprintf("%+v", *i)
+}
+
+// Int returns a pointer to the int value passed in.  Used to
+// set the IPConfig.Interface field.
+func Int(v int) *int {
+	return &v
+}
+
+// IPConfig contains values necessary to configure an IP address on an interface
+type IPConfig struct {
+	// IP version, either "4" or "6"
+	Version string
+	// Index into Result structs Interfaces list
+	Interface *int
+	Address   net.IPNet
+	Gateway   net.IP
+}
+
+func (i *IPConfig) String() string {
+	return fmt.Sprintf("%+v", *i)
+}
+
+// JSON (un)marshallable types
+type ipConfig struct {
+	Version   string      `json:"version"`
+	Interface *int        `json:"interface,omitempty"`
+	Address   types.IPNet `json:"address"`
+	Gateway   net.IP      `json:"gateway,omitempty"`
+}
+
+func (c *IPConfig) MarshalJSON() ([]byte, error) {
+	ipc := ipConfig{
+		Version:   c.Version,
+		Interface: c.Interface,
+		Address:   types.IPNet(c.Address),
+		Gateway:   c.Gateway,
+	}
+
+	return json.Marshal(ipc)
+}
+
+func (c *IPConfig) UnmarshalJSON(data []byte) error {
+	ipc := ipConfig{}
+	if err := json.Unmarshal(data, &ipc); err != nil {
+		return err
+	}
+
+	c.Version = ipc.Version
+	c.Interface = ipc.Interface
+	c.Address = net.IPNet(ipc.Address)
+	c.Gateway = ipc.Gateway
+	return nil
+}
diff --git a/vendor/github.com/containernetworking/cni/pkg/types/types.go b/vendor/github.com/containernetworking/cni/pkg/types/types.go
new file mode 100644
index 000000000000..4684a320762f
--- /dev/null
+++ b/vendor/github.com/containernetworking/cni/pkg/types/types.go
@@ -0,0 +1,191 @@
+// Copyright 2015 CNI authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package types
+
+import (
+	"encoding/json"
+	"errors"
+	"fmt"
+	"net"
+	"os"
+)
+
+// like net.IPNet but adds JSON marshalling and unmarshalling
+type IPNet net.IPNet
+
+// ParseCIDR takes a string like "10.2.3.1/24" and
+// return IPNet with "10.2.3.1" and /24 mask
+func ParseCIDR(s string) (*net.IPNet, error) {
+	ip, ipn, err := net.ParseCIDR(s)
+	if err != nil {
+		return nil, err
+	}
+
+	ipn.IP = ip
+	return ipn, nil
+}
+
+func (n IPNet) MarshalJSON() ([]byte, error) {
+	return json.Marshal((*net.IPNet)(&n).String())
+}
+
+func (n *IPNet) UnmarshalJSON(data []byte) error {
+	var s string
+	if err := json.Unmarshal(data, &s); err != nil {
+		return err
+	}
+
+	tmp, err := ParseCIDR(s)
+	if err != nil {
+		return err
+	}
+
+	*n = IPNet(*tmp)
+	return nil
+}
+
+// NetConf describes a network.
+type NetConf struct {
+	CNIVersion string `json:"cniVersion,omitempty"`
+
+	Name         string          `json:"name,omitempty"`
+	Type         string          `json:"type,omitempty"`
+	Capabilities map[string]bool `json:"capabilities,omitempty"`
+	IPAM         IPAM            `json:"ipam,omitempty"`
+	DNS          DNS             `json:"dns"`
+}
+
+type IPAM struct {
+	Type string `json:"type,omitempty"`
+}
+
+// NetConfList describes an ordered list of networks.
+type NetConfList struct {
+	CNIVersion string `json:"cniVersion,omitempty"`
+
+	Name    string     `json:"name,omitempty"`
+	Plugins []*NetConf `json:"plugins,omitempty"`
+}
+
+type ResultFactoryFunc func([]byte) (Result, error)
+
+// Result is an interface that provides the result of plugin execution
+type Result interface {
+	// The highest CNI specification result verison the result supports
+	// without having to convert
+	Version() string
+
+	// Returns the result converted into the requested CNI specification
+	// result version, or an error if conversion failed
+	GetAsVersion(version string) (Result, error)
+
+	// Prints the result in JSON format to stdout
+	Print() error
+
+	// Returns a JSON string representation of the result
+	String() string
+}
+
+func PrintResult(result Result, version string) error {
+	newResult, err := result.GetAsVersion(version)
+	if err != nil {
+		return err
+	}
+	return newResult.Print()
+}
+
+// DNS contains values interesting for DNS resolvers
+type DNS struct {
+	Nameservers []string `json:"nameservers,omitempty"`
+	Domain      string   `json:"domain,omitempty"`
+	Search      []string `json:"search,omitempty"`
+	Options     []string `json:"options,omitempty"`
+}
+
+type Route struct {
+	Dst net.IPNet
+	GW  net.IP
+}
+
+func (r *Route) String() string {
+	return fmt.Sprintf("%+v", *r)
+}
+
+// Well known error codes
+// see https://github.com/containernetworking/cni/blob/master/SPEC.md#well-known-error-codes
+const (
+	ErrUnknown                uint = iota // 0
+	ErrIncompatibleCNIVersion             // 1
+	ErrUnsupportedField                   // 2
+)
+
+type Error struct {
+	Code    uint   `json:"code"`
+	Msg     string `json:"msg"`
+	Details string `json:"details,omitempty"`
+}
+
+func (e *Error) Error() string {
+	details := ""
+	if e.Details != "" {
+		details = fmt.Sprintf("; %v", e.Details)
+	}
+	return fmt.Sprintf("%v%v", e.Msg, details)
+}
+
+func (e *Error) Print() error {
+	return prettyPrint(e)
+}
+
+// net.IPNet is not JSON (un)marshallable so this duality is needed
+// for our custom IPNet type
+
+// JSON (un)marshallable types
+type route struct {
+	Dst IPNet  `json:"dst"`
+	GW  net.IP `json:"gw,omitempty"`
+}
+
+func (r *Route) UnmarshalJSON(data []byte) error {
+	rt := route{}
+	if err := json.Unmarshal(data, &rt); err != nil {
+		return err
+	}
+
+	r.Dst = net.IPNet(rt.Dst)
+	r.GW = rt.GW
+	return nil
+}
+
+func (r Route) MarshalJSON() ([]byte, error) {
+	rt := route{
+		Dst: IPNet(r.Dst),
+		GW:  r.GW,
+	}
+
+	return json.Marshal(rt)
+}
+
+func prettyPrint(obj interface{}) error {
+	data, err := json.MarshalIndent(obj, "", "    ")
+	if err != nil {
+		return err
+	}
+	_, err = os.Stdout.Write(data)
+	return err
+}
+
+// NotImplementedError is used to indicate that a method is not implemented for the given platform
+var NotImplementedError = errors.New("Not Implemented")
diff --git a/vendor/github.com/containernetworking/cni/pkg/version/conf.go b/vendor/github.com/containernetworking/cni/pkg/version/conf.go
new file mode 100644
index 000000000000..3cca58bbeb8d
--- /dev/null
+++ b/vendor/github.com/containernetworking/cni/pkg/version/conf.go
@@ -0,0 +1,37 @@
+// Copyright 2016 CNI authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package version
+
+import (
+	"encoding/json"
+	"fmt"
+)
+
+// ConfigDecoder can decode the CNI version available in network config data
+type ConfigDecoder struct{}
+
+func (*ConfigDecoder) Decode(jsonBytes []byte) (string, error) {
+	var conf struct {
+		CNIVersion string `json:"cniVersion"`
+	}
+	err := json.Unmarshal(jsonBytes, &conf)
+	if err != nil {
+		return "", fmt.Errorf("decoding version from network config: %s", err)
+	}
+	if conf.CNIVersion == "" {
+		return "0.1.0", nil
+	}
+	return conf.CNIVersion, nil
+}
diff --git a/vendor/github.com/containernetworking/cni/pkg/version/plugin.go b/vendor/github.com/containernetworking/cni/pkg/version/plugin.go
new file mode 100644
index 000000000000..8a46728105cc
--- /dev/null
+++ b/vendor/github.com/containernetworking/cni/pkg/version/plugin.go
@@ -0,0 +1,81 @@
+// Copyright 2016 CNI authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package version
+
+import (
+	"encoding/json"
+	"fmt"
+	"io"
+)
+
+// PluginInfo reports information about CNI versioning
+type PluginInfo interface {
+	// SupportedVersions returns one or more CNI spec versions that the plugin
+	// supports.  If input is provided in one of these versions, then the plugin
+	// promises to use the same CNI version in its response
+	SupportedVersions() []string
+
+	// Encode writes this CNI version information as JSON to the given Writer
+	Encode(io.Writer) error
+}
+
+type pluginInfo struct {
+	CNIVersion_        string   `json:"cniVersion"`
+	SupportedVersions_ []string `json:"supportedVersions,omitempty"`
+}
+
+// pluginInfo implements the PluginInfo interface
+var _ PluginInfo = &pluginInfo{}
+
+func (p *pluginInfo) Encode(w io.Writer) error {
+	return json.NewEncoder(w).Encode(p)
+}
+
+func (p *pluginInfo) SupportedVersions() []string {
+	return p.SupportedVersions_
+}
+
+// PluginSupports returns a new PluginInfo that will report the given versions
+// as supported
+func PluginSupports(supportedVersions ...string) PluginInfo {
+	if len(supportedVersions) < 1 {
+		panic("programmer error: you must support at least one version")
+	}
+	return &pluginInfo{
+		CNIVersion_:        Current(),
+		SupportedVersions_: supportedVersions,
+	}
+}
+
+// PluginDecoder can decode the response returned by a plugin's VERSION command
+type PluginDecoder struct{}
+
+func (*PluginDecoder) Decode(jsonBytes []byte) (PluginInfo, error) {
+	var info pluginInfo
+	err := json.Unmarshal(jsonBytes, &info)
+	if err != nil {
+		return nil, fmt.Errorf("decoding version info: %s", err)
+	}
+	if info.CNIVersion_ == "" {
+		return nil, fmt.Errorf("decoding version info: missing field cniVersion")
+	}
+	if len(info.SupportedVersions_) == 0 {
+		if info.CNIVersion_ == "0.2.0" {
+			return PluginSupports("0.1.0", "0.2.0"), nil
+		}
+		return nil, fmt.Errorf("decoding version info: missing field supportedVersions")
+	}
+	return &info, nil
+}
diff --git a/vendor/github.com/containernetworking/cni/pkg/version/reconcile.go b/vendor/github.com/containernetworking/cni/pkg/version/reconcile.go
new file mode 100644
index 000000000000..25c3810b2aaf
--- /dev/null
+++ b/vendor/github.com/containernetworking/cni/pkg/version/reconcile.go
@@ -0,0 +1,49 @@
+// Copyright 2016 CNI authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package version
+
+import "fmt"
+
+type ErrorIncompatible struct {
+	Config    string
+	Supported []string
+}
+
+func (e *ErrorIncompatible) Details() string {
+	return fmt.Sprintf("config is %q, plugin supports %q", e.Config, e.Supported)
+}
+
+func (e *ErrorIncompatible) Error() string {
+	return fmt.Sprintf("incompatible CNI versions: %s", e.Details())
+}
+
+type Reconciler struct{}
+
+func (r *Reconciler) Check(configVersion string, pluginInfo PluginInfo) *ErrorIncompatible {
+	return r.CheckRaw(configVersion, pluginInfo.SupportedVersions())
+}
+
+func (*Reconciler) CheckRaw(configVersion string, supportedVersions []string) *ErrorIncompatible {
+	for _, supportedVersion := range supportedVersions {
+		if configVersion == supportedVersion {
+			return nil
+		}
+	}
+
+	return &ErrorIncompatible{
+		Config:    configVersion,
+		Supported: supportedVersions,
+	}
+}
diff --git a/vendor/github.com/containernetworking/cni/pkg/version/version.go b/vendor/github.com/containernetworking/cni/pkg/version/version.go
new file mode 100644
index 000000000000..efe8ea8716df
--- /dev/null
+++ b/vendor/github.com/containernetworking/cni/pkg/version/version.go
@@ -0,0 +1,61 @@
+// Copyright 2016 CNI authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package version
+
+import (
+	"fmt"
+
+	"github.com/containernetworking/cni/pkg/types"
+	"github.com/containernetworking/cni/pkg/types/020"
+	"github.com/containernetworking/cni/pkg/types/current"
+)
+
+// Current reports the version of the CNI spec implemented by this library
+func Current() string {
+	return "0.3.1"
+}
+
+// Legacy PluginInfo describes a plugin that is backwards compatible with the
+// CNI spec version 0.1.0.  In particular, a runtime compiled against the 0.1.0
+// library ought to work correctly with a plugin that reports support for
+// Legacy versions.
+//
+// Any future CNI spec versions which meet this definition should be added to
+// this list.
+var Legacy = PluginSupports("0.1.0", "0.2.0")
+var All = PluginSupports("0.1.0", "0.2.0", "0.3.0", "0.3.1")
+
+var resultFactories = []struct {
+	supportedVersions []string
+	newResult         types.ResultFactoryFunc
+}{
+	{current.SupportedVersions, current.NewResult},
+	{types020.SupportedVersions, types020.NewResult},
+}
+
+// Finds a Result object matching the requested version (if any) and asks
+// that object to parse the plugin result, returning an error if parsing failed.
+func NewResult(version string, resultBytes []byte) (types.Result, error) {
+	reconciler := &Reconciler{}
+	for _, resultFactory := range resultFactories {
+		err := reconciler.CheckRaw(version, resultFactory.supportedVersions)
+		if err == nil {
+			// Result supports this version
+			return resultFactory.newResult(resultBytes)
+		}
+	}
+
+	return nil, fmt.Errorf("unsupported CNI result version %q", version)
+}
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 889c9fbe52ef..38eb4b9226e1 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -100,12 +100,21 @@ github.com/containerd/continuity/driver
 github.com/containerd/continuity/proto
 # github.com/containerd/fifo v0.0.0-20180307165137-3d5202aec260
 github.com/containerd/fifo
+# github.com/containerd/go-cni v0.0.0-20190610170741-5a4663dad645
+github.com/containerd/go-cni
 # github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3
 github.com/containerd/go-runc
 # github.com/containerd/ttrpc v0.0.0-20190411181408-699c4e40d1e7
 github.com/containerd/ttrpc
 # github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd
 github.com/containerd/typeurl
+# github.com/containernetworking/cni v0.6.1-0.20180218032124-142cde0c766c
+github.com/containernetworking/cni/libcni
+github.com/containernetworking/cni/pkg/types
+github.com/containernetworking/cni/pkg/types/current
+github.com/containernetworking/cni/pkg/invoke
+github.com/containernetworking/cni/pkg/version
+github.com/containernetworking/cni/pkg/types/020
 # github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e
 github.com/coreos/go-systemd/daemon
 # github.com/davecgh/go-spew v1.1.1
diff --git a/worker/containerd/containerd.go b/worker/containerd/containerd.go
index 936f2cb26506..7d62642002e0 100644
--- a/worker/containerd/containerd.go
+++ b/worker/containerd/containerd.go
@@ -27,16 +27,16 @@ import (
 )
 
 // NewWorkerOpt creates a WorkerOpt.
-func NewWorkerOpt(root string, address, snapshotterName, ns string, labels map[string]string, dns *oci.DNSConfig, opts ...containerd.ClientOpt) (base.WorkerOpt, error) {
+func NewWorkerOpt(root string, address, snapshotterName, ns string, labels map[string]string, dns *oci.DNSConfig, nopt network.Opt, opts ...containerd.ClientOpt) (base.WorkerOpt, error) {
 	opts = append(opts, containerd.WithDefaultNamespace(ns))
 	client, err := containerd.New(address, opts...)
 	if err != nil {
 		return base.WorkerOpt{}, errors.Wrapf(err, "failed to connect client to %q . make sure containerd is running", address)
 	}
-	return newContainerd(root, client, snapshotterName, ns, labels, dns)
+	return newContainerd(root, client, snapshotterName, ns, labels, dns, nopt)
 }
 
-func newContainerd(root string, client *containerd.Client, snapshotterName, ns string, labels map[string]string, dns *oci.DNSConfig) (base.WorkerOpt, error) {
+func newContainerd(root string, client *containerd.Client, snapshotterName, ns string, labels map[string]string, dns *oci.DNSConfig, nopt network.Opt) (base.WorkerOpt, error) {
 	if strings.Contains(snapshotterName, "/") {
 		return base.WorkerOpt{}, errors.Errorf("bad snapshotter name: %q", snapshotterName)
 	}
@@ -103,11 +103,16 @@ func newContainerd(root string, client *containerd.Client, snapshotterName, ns s
 		}
 	}
 
+	np, err := network.Providers(nopt)
+	if err != nil {
+		return base.WorkerOpt{}, err
+	}
+
 	opt := base.WorkerOpt{
 		ID:            id,
 		Labels:        xlabels,
 		MetadataStore: md,
-		Executor:      containerdexecutor.New(client, root, "", network.Default(), dns),
+		Executor:      containerdexecutor.New(client, root, "", np, dns),
 		Snapshotter:   containerdsnapshot.NewSnapshotter(snapshotterName, client.SnapshotService(snapshotterName), cs, md, ns, gc, nil),
 		ContentStore:  cs,
 		Applier:       winlayers.NewFileSystemApplierWithWindows(cs, df),
diff --git a/worker/runc/runc.go b/worker/runc/runc.go
index 34ce4f0105a9..ddf075720f5d 100644
--- a/worker/runc/runc.go
+++ b/worker/runc/runc.go
@@ -34,7 +34,7 @@ type SnapshotterFactory struct {
 }
 
 // NewWorkerOpt creates a WorkerOpt.
-func NewWorkerOpt(root string, snFactory SnapshotterFactory, rootless bool, processMode oci.ProcessMode, labels map[string]string, idmap *idtools.IdentityMapping, dns *oci.DNSConfig) (base.WorkerOpt, error) {
+func NewWorkerOpt(root string, snFactory SnapshotterFactory, rootless bool, processMode oci.ProcessMode, labels map[string]string, idmap *idtools.IdentityMapping, nopt network.Opt, dns *oci.DNSConfig) (base.WorkerOpt, error) {
 	var opt base.WorkerOpt
 	name := "runc-" + snFactory.Name
 	root = filepath.Join(root, name)
@@ -45,6 +45,12 @@ func NewWorkerOpt(root string, snFactory SnapshotterFactory, rootless bool, proc
 	if err != nil {
 		return opt, err
 	}
+
+	np, err := network.Providers(nopt)
+	if err != nil {
+		return opt, err
+	}
+
 	exe, err := runcexecutor.New(runcexecutor.Opt{
 		// Root directory
 		Root: filepath.Join(root, "executor"),
@@ -53,7 +59,7 @@ func NewWorkerOpt(root string, snFactory SnapshotterFactory, rootless bool, proc
 		ProcessMode:     processMode,
 		IdentityMapping: idmap,
 		DNS:             dns,
-	}, network.Default())
+	}, np)
 	if err != nil {
 		return opt, err
 	}

From e7759a861b771943e422f9930f8dd2a387babf28 Mon Sep 17 00:00:00 2001
From: Tonis Tiigi <tonistiigi@gmail.com>
Date: Wed, 10 Jul 2019 16:55:04 -0700
Subject: [PATCH 2/6] client: add cni network tests

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
---
 client/client_test.go                     | 80 +++++++++++++++++++++++
 cmd/buildkitd/main_oci_worker.go          |  3 +
 hack/dockerfiles/test.buildkit.Dockerfile |  5 +-
 hack/fixtures/cni.json                    | 13 ++++
 util/testutil/echoserver/server.go        | 33 ++++++++++
 worker/runc/runc_test.go                  |  3 +-
 6 files changed, 134 insertions(+), 3 deletions(-)
 create mode 100644 hack/fixtures/cni.json
 create mode 100644 util/testutil/echoserver/server.go

diff --git a/client/client_test.go b/client/client_test.go
index 1de618f67df1..9445db16142f 100644
--- a/client/client_test.go
+++ b/client/client_test.go
@@ -36,6 +36,7 @@ import (
 	"github.com/moby/buildkit/util/contentutil"
 	"github.com/moby/buildkit/util/entitlements"
 	"github.com/moby/buildkit/util/testutil"
+	"github.com/moby/buildkit/util/testutil/echoserver"
 	"github.com/moby/buildkit/util/testutil/httpserver"
 	"github.com/moby/buildkit/util/testutil/integration"
 	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
@@ -97,6 +98,7 @@ func TestClientIntegration(t *testing.T) {
 		testPushByDigest,
 		testBasicInlineCacheImportExport,
 		testExportBusyboxLocal,
+		testBridgeNetworking,
 	}, mirrors)
 
 	integration.Run(t, []integration.Test{
@@ -109,12 +111,75 @@ func TestClientIntegration(t *testing.T) {
 			"insecure": securityInsecure,
 		}),
 	)
+
+	integration.Run(t, []integration.Test{
+		testHostNetworking,
+	},
+		mirrors,
+		integration.WithMatrix("netmode", map[string]interface{}{
+			"default": defaultNetwork,
+			"host":    hostNetwork,
+		}),
+	)
 }
 
 func newContainerd(cdAddress string) (*containerd.Client, error) {
 	return containerd.New(cdAddress, containerd.WithTimeout(60*time.Second))
 }
 
+func testBridgeNetworking(t *testing.T, sb integration.Sandbox) {
+	if os.Getenv("BUILDKIT_RUN_NETWORK_INTEGRATION_TESTS") == "" {
+		t.SkipNow()
+	}
+	if sb.Rootless() {
+		t.SkipNow()
+	}
+	c, err := New(context.TODO(), sb.Address())
+	require.NoError(t, err)
+	defer c.Close()
+
+	s, err := echoserver.NewTestServer("foo")
+	require.NoError(t, err)
+	addrParts := strings.Split(s.Addr().String(), ":")
+
+	def, err := llb.Image("busybox").Run(llb.Shlexf("sh -c 'nc 127.0.0.1 %s | grep foo'", addrParts[len(addrParts)-1])).Marshal()
+	require.NoError(t, err)
+
+	_, err = c.Solve(context.TODO(), def, SolveOpt{}, nil)
+	require.Error(t, err)
+}
+func testHostNetworking(t *testing.T, sb integration.Sandbox) {
+	if os.Getenv("BUILDKIT_RUN_NETWORK_INTEGRATION_TESTS") == "" {
+		t.SkipNow()
+	}
+	netMode := sb.Value("netmode")
+	var allowedEntitlements []entitlements.Entitlement
+	if netMode == hostNetwork {
+		allowedEntitlements = []entitlements.Entitlement{entitlements.EntitlementNetworkHost}
+	}
+	c, err := New(context.TODO(), sb.Address())
+	require.NoError(t, err)
+	defer c.Close()
+
+	s, err := echoserver.NewTestServer("foo")
+	require.NoError(t, err)
+	addrParts := strings.Split(s.Addr().String(), ":")
+
+	def, err := llb.Image("busybox").Run(llb.Shlexf("sh -c 'nc 127.0.0.1 %s | grep foo'", addrParts[len(addrParts)-1]), llb.Network(llb.NetModeHost)).Marshal()
+	require.NoError(t, err)
+
+	_, err = c.Solve(context.TODO(), def, SolveOpt{
+		AllowedEntitlements: allowedEntitlements,
+	}, nil)
+	if netMode == hostNetwork {
+		require.NoError(t, err)
+		t.Logf("host-noerror")
+	} else {
+		require.Error(t, err)
+		t.Logf("bridge-error")
+	}
+}
+
 // #877
 func testExportBusyboxLocal(t *testing.T, sb integration.Sandbox) {
 	c, err := New(context.TODO(), sb.Address())
@@ -2452,3 +2517,18 @@ func (*secModeInsecure) UpdateConfigFile(in string) string {
 
 var securitySandbox integration.ConfigUpdater = &secModeSandbox{}
 var securityInsecure integration.ConfigUpdater = &secModeInsecure{}
+
+type netModeHost struct{}
+
+func (*netModeHost) UpdateConfigFile(in string) string {
+	return in + "\n\ninsecure-entitlements = [\"network.host\"]\n"
+}
+
+type netModeDefault struct{}
+
+func (*netModeDefault) UpdateConfigFile(in string) string {
+	return in
+}
+
+var hostNetwork integration.ConfigUpdater = &netModeHost{}
+var defaultNetwork integration.ConfigUpdater = &netModeDefault{}
diff --git a/cmd/buildkitd/main_oci_worker.go b/cmd/buildkitd/main_oci_worker.go
index c450b5a9f9fe..bd2b03e37952 100644
--- a/cmd/buildkitd/main_oci_worker.go
+++ b/cmd/buildkitd/main_oci_worker.go
@@ -206,6 +206,9 @@ func ociWorkerInitializer(c *cli.Context, common workerInitializerOpt) ([]worker
 
 	if cfg.Rootless {
 		logrus.Debugf("running in rootless mode")
+		if common.config.Workers.OCI.NetworkConfig.Mode == "auto" {
+			common.config.Workers.OCI.NetworkConfig.Mode = "host"
+		}
 	}
 
 	processMode := oci.ProcessSandbox
diff --git a/hack/dockerfiles/test.buildkit.Dockerfile b/hack/dockerfiles/test.buildkit.Dockerfile
index 9ef4bf256b84..08c98ae4ae3b 100644
--- a/hack/dockerfiles/test.buildkit.Dockerfile
+++ b/hack/dockerfiles/test.buildkit.Dockerfile
@@ -198,7 +198,7 @@ VOLUME /var/lib/containerd
 VOLUME /run/containerd
 ENTRYPOINT ["containerd"]
 
-FROM alpine AS cni-plugins
+FROM --platform=$BUILDPLATFORM alpine AS cni-plugins
 RUN apk add --no-cache curl
 ENV CNI_VERSION=v0.8.1
 ARG TARGETOS
@@ -220,10 +220,11 @@ COPY --from=containerd10 /out/containerd* /opt/containerd-1.0/bin/
 COPY --from=registry /bin/registry /usr/bin
 COPY --from=runc /usr/bin/runc /usr/bin
 COPY --from=containerd /out/containerd* /usr/bin/
-COPY --from=cni-plugins /opt/cni/bin/bridge /opt/cni/bin/host-local /opt/cni/bin
+COPY --from=cni-plugins /opt/cni/bin/bridge /opt/cni/bin/host-local /opt/cni/bin/loopback /opt/cni/bin/
 COPY hack/fixtures/cni.json /etc/buildkit/cni.json
 COPY --from=binaries / /usr/bin/
 COPY . .
+ENV BUILDKIT_RUN_NETWORK_INTEGRATION_TESTS=1
 
 FROM integration-tests AS dev-env
 VOLUME /var/lib/buildkit
diff --git a/hack/fixtures/cni.json b/hack/fixtures/cni.json
new file mode 100644
index 000000000000..682412708b06
--- /dev/null
+++ b/hack/fixtures/cni.json
@@ -0,0 +1,13 @@
+{
+	"name": "buildkit",
+	"type": "bridge",
+	"bridge": "buildkit0",
+	"isDefaultGateway": true,
+	"forceAddress": false,
+	"ipMasq": true,
+	"hairpinMode": true,
+	"ipam": {
+		"type": "host-local",
+		"subnet": "10.10.0.0/16"
+	}
+}
\ No newline at end of file
diff --git a/util/testutil/echoserver/server.go b/util/testutil/echoserver/server.go
new file mode 100644
index 000000000000..efeac5ca38cf
--- /dev/null
+++ b/util/testutil/echoserver/server.go
@@ -0,0 +1,33 @@
+package echoserver
+
+import (
+	"io"
+	"net"
+)
+
+type TestServer interface {
+	io.Closer
+	Addr() net.Addr
+}
+
+func NewTestServer(response string) (TestServer, error) {
+	ln, err := net.Listen("tcp", ":")
+	if err != nil {
+		return nil, err
+	}
+	go func() {
+		for {
+			conn, err := ln.Accept()
+			if err != nil {
+				break
+			}
+			go handleConnection(conn, response)
+		}
+	}()
+	return ln, nil
+}
+
+func handleConnection(c net.Conn, response string) {
+	c.Write([]byte(response))
+	c.Close()
+}
diff --git a/worker/runc/runc_test.go b/worker/runc/runc_test.go
index 4060f2441a3f..f9f483c5696e 100644
--- a/worker/runc/runc_test.go
+++ b/worker/runc/runc_test.go
@@ -23,6 +23,7 @@ import (
 	"github.com/moby/buildkit/session"
 	"github.com/moby/buildkit/snapshot"
 	"github.com/moby/buildkit/source"
+	"github.com/moby/buildkit/util/network"
 	"github.com/moby/buildkit/worker/base"
 	"github.com/stretchr/testify/require"
 )
@@ -39,7 +40,7 @@ func newWorkerOpt(t *testing.T, processMode oci.ProcessMode) (base.WorkerOpt, fu
 		},
 	}
 	rootless := false
-	workerOpt, err := NewWorkerOpt(tmpdir, snFactory, rootless, processMode, nil, nil, nil)
+	workerOpt, err := NewWorkerOpt(tmpdir, snFactory, rootless, processMode, nil, nil, network.Opt{Mode: "host"}, nil)
 	require.NoError(t, err)
 
 	return workerOpt, cleanup

From 653c91e1149275e8d3bc2ac6f29d609822cc681e Mon Sep 17 00:00:00 2001
From: Tonis Tiigi <tonistiigi@gmail.com>
Date: Thu, 11 Jul 2019 16:47:45 -0700
Subject: [PATCH 3/6] network: add non-lazy cni network init

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
---
 hack/dockerfiles/test.buildkit.Dockerfile |  2 +-
 util/network/cni.go                       | 22 +++++++++++++++++++++-
 2 files changed, 22 insertions(+), 2 deletions(-)

diff --git a/hack/dockerfiles/test.buildkit.Dockerfile b/hack/dockerfiles/test.buildkit.Dockerfile
index 08c98ae4ae3b..5a1652f0a461 100644
--- a/hack/dockerfiles/test.buildkit.Dockerfile
+++ b/hack/dockerfiles/test.buildkit.Dockerfile
@@ -224,7 +224,7 @@ COPY --from=cni-plugins /opt/cni/bin/bridge /opt/cni/bin/host-local /opt/cni/bin
 COPY hack/fixtures/cni.json /etc/buildkit/cni.json
 COPY --from=binaries / /usr/bin/
 COPY . .
-ENV BUILDKIT_RUN_NETWORK_INTEGRATION_TESTS=1
+ENV BUILDKIT_RUN_NETWORK_INTEGRATION_TESTS=1 BUILDKIT_CNI_INIT_LOCK_PATH=/run/buildkit_cni_bridge.lock
 
 FROM integration-tests AS dev-env
 VOLUME /var/lib/buildkit
diff --git a/util/network/cni.go b/util/network/cni.go
index 087803c3a8d8..58955dd28420 100644
--- a/util/network/cni.go
+++ b/util/network/cni.go
@@ -7,6 +7,7 @@ import (
 
 	"github.com/containerd/containerd/oci"
 	"github.com/containerd/go-cni"
+	"github.com/gofrs/flock"
 	"github.com/moby/buildkit/identity"
 	"github.com/moby/buildkit/util/network/netns_create"
 	specs "github.com/opencontainers/runtime-spec/specs-go"
@@ -36,7 +37,11 @@ func NewCNIProvider(opt Opt) (Provider, error) {
 		return nil, err
 	}
 
-	return &cniProvider{CNI: cniHandle, root: opt.Root}, nil
+	cp := &cniProvider{CNI: cniHandle, root: opt.Root}
+	if err := cp.initNetwork(); err != nil {
+		return nil, err
+	}
+	return cp, nil
 }
 
 type cniProvider struct {
@@ -44,6 +49,21 @@ type cniProvider struct {
 	root string
 }
 
+func (c *cniProvider) initNetwork() error {
+	if v := os.Getenv("BUILDKIT_CNI_INIT_LOCK_PATH"); v != "" {
+		l := flock.New(v)
+		if err := l.Lock(); err != nil {
+			return err
+		}
+		defer l.Unlock()
+	}
+	ns, err := c.New()
+	if err != nil {
+		return err
+	}
+	return ns.Close()
+}
+
 func (c *cniProvider) New() (Namespace, error) {
 	id := identity.NewID()
 	nsPath := filepath.Join(c.root, "net/cni", id)

From e38512c0567f5a830369f940686444b95de32acf Mon Sep 17 00:00:00 2001
From: Tonis Tiigi <tonistiigi@gmail.com>
Date: Fri, 12 Jul 2019 10:22:25 -0700
Subject: [PATCH 4/6] testutil: add logs printing on error

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
---
 util/testutil/integration/containerd.go |  2 ++
 util/testutil/integration/oci.go        | 20 +++++++++++++-------
 2 files changed, 15 insertions(+), 7 deletions(-)

diff --git a/util/testutil/integration/containerd.go b/util/testutil/integration/containerd.go
index db2dac98d6e6..c1157dd1d7f6 100644
--- a/util/testutil/integration/containerd.go
+++ b/util/testutil/integration/containerd.go
@@ -4,6 +4,7 @@ import (
 	"bytes"
 	"fmt"
 	"io/ioutil"
+	"log"
 	"os"
 	"os/exec"
 	"path/filepath"
@@ -154,6 +155,7 @@ disabled_plugins = ["cri"]
 
 	buildkitdSock, stop, err := runBuildkitd(buildkitdArgs, logs, 0, 0)
 	if err != nil {
+		printLogs(logs, log.Println)
 		return nil, nil, err
 	}
 	deferF.append(stop)
diff --git a/util/testutil/integration/oci.go b/util/testutil/integration/oci.go
index 4868ec037b06..23d42fe099bd 100644
--- a/util/testutil/integration/oci.go
+++ b/util/testutil/integration/oci.go
@@ -5,6 +5,7 @@ import (
 	"bytes"
 	"fmt"
 	"io/ioutil"
+	"log"
 	"os"
 	"os/exec"
 	"path/filepath"
@@ -98,6 +99,7 @@ func (s *oci) New(opt ...SandboxOpt) (Sandbox, func() error, error) {
 	buildkitdSock, stop, err := runBuildkitd(buildkitdArgs, logs, s.uid, s.gid)
 	if err != nil {
 		deferF.F()()
+		printLogs(logs, log.Println)
 		return nil, nil, err
 	}
 
@@ -106,6 +108,16 @@ func (s *oci) New(opt ...SandboxOpt) (Sandbox, func() error, error) {
 	return &sandbox{address: buildkitdSock, mv: c.mv, logs: logs, cleanup: deferF, rootless: s.uid != 0}, deferF.F(), nil
 }
 
+func printLogs(logs map[string]*bytes.Buffer, f func(args ...interface{})) {
+	for name, l := range logs {
+		f(name)
+		s := bufio.NewScanner(l)
+		for s.Scan() {
+			f(s.Text())
+		}
+	}
+}
+
 type sandbox struct {
 	address  string
 	logs     map[string]*bytes.Buffer
@@ -119,13 +131,7 @@ func (sb *sandbox) Address() string {
 }
 
 func (sb *sandbox) PrintLogs(t *testing.T) {
-	for name, l := range sb.logs {
-		t.Log(name)
-		s := bufio.NewScanner(l)
-		for s.Scan() {
-			t.Log(s.Text())
-		}
-	}
+	printLogs(sb.logs, t.Log)
 }
 
 func (sb *sandbox) NewRegistry() (string, error) {

From 954b73dae1a9eba4230460027e3bb922a4ca64e1 Mon Sep 17 00:00:00 2001
From: Tonis Tiigi <tonistiigi@gmail.com>
Date: Fri, 12 Jul 2019 14:57:10 -0700
Subject: [PATCH 5/6] network: move ns creation from reexec to linkname

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
---
 cmd/buildkitd/main_unix.go        |  4 ---
 util/network/allowempty.s         |  0
 util/network/cni.go               |  5 +--
 util/network/cni_unsafe.go        | 16 +++++++++
 util/network/createns_linux.go    | 59 +++++++++++++++++++++++++++++++
 util/network/createns_nolinux.go  |  9 +++++
 util/network/netns_create/hook.go | 51 --------------------------
 7 files changed, 87 insertions(+), 57 deletions(-)
 create mode 100644 util/network/allowempty.s
 create mode 100644 util/network/cni_unsafe.go
 create mode 100644 util/network/createns_linux.go
 create mode 100644 util/network/createns_nolinux.go
 delete mode 100644 util/network/netns_create/hook.go

diff --git a/cmd/buildkitd/main_unix.go b/cmd/buildkitd/main_unix.go
index 649fc0f5ba51..93533adb1fdf 100644
--- a/cmd/buildkitd/main_unix.go
+++ b/cmd/buildkitd/main_unix.go
@@ -4,12 +4,8 @@ package main
 
 import (
 	"syscall"
-
-	"github.com/moby/buildkit/util/network/netns_create"
 )
 
 func init() {
-	netns_create.Handle()
-
 	syscall.Umask(0)
 }
diff --git a/util/network/allowempty.s b/util/network/allowempty.s
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/util/network/cni.go b/util/network/cni.go
index 58955dd28420..1b5e9d89afdd 100644
--- a/util/network/cni.go
+++ b/util/network/cni.go
@@ -9,7 +9,6 @@ import (
 	"github.com/containerd/go-cni"
 	"github.com/gofrs/flock"
 	"github.com/moby/buildkit/identity"
-	"github.com/moby/buildkit/util/network/netns_create"
 	specs "github.com/opencontainers/runtime-spec/specs-go"
 	"github.com/pkg/errors"
 	"golang.org/x/sys/unix"
@@ -71,11 +70,13 @@ func (c *cniProvider) New() (Namespace, error) {
 		return nil, err
 	}
 
-	if err := netns_create.CreateNetNS(nsPath); err != nil {
+	if err := createNetNS(nsPath); err != nil {
+		os.RemoveAll(filepath.Dir(nsPath))
 		return nil, err
 	}
 
 	if _, err := c.CNI.Setup(id, nsPath); err != nil {
+		os.RemoveAll(filepath.Dir(nsPath))
 		return nil, errors.Wrap(err, "CNI setup error")
 	}
 
diff --git a/util/network/cni_unsafe.go b/util/network/cni_unsafe.go
new file mode 100644
index 000000000000..05cfef85b107
--- /dev/null
+++ b/util/network/cni_unsafe.go
@@ -0,0 +1,16 @@
+// +build linux
+
+package network
+
+import (
+	_ "unsafe" // required for go:linkname.
+)
+
+//go:linkname beforeFork syscall.runtime_BeforeFork
+func beforeFork()
+
+//go:linkname afterFork syscall.runtime_AfterFork
+func afterFork()
+
+//go:linkname afterForkInChild syscall.runtime_AfterForkInChild
+func afterForkInChild()
diff --git a/util/network/createns_linux.go b/util/network/createns_linux.go
new file mode 100644
index 000000000000..3b6172705811
--- /dev/null
+++ b/util/network/createns_linux.go
@@ -0,0 +1,59 @@
+// +build linux
+
+package network
+
+import (
+	"os"
+	"syscall"
+	"unsafe"
+
+	"github.com/pkg/errors"
+	"golang.org/x/sys/unix"
+)
+
+func createNetNS(p string) error {
+	f, err := os.Create(p)
+	if err != nil {
+		return err
+	}
+	if err := f.Close(); err != nil {
+		return err
+	}
+	procNetNSBytes, err := syscall.BytePtrFromString("/proc/self/ns/net")
+	if err != nil {
+		return err
+	}
+	pBytes, err := syscall.BytePtrFromString(p)
+	if err != nil {
+		return err
+	}
+	beforeFork()
+
+	pid, _, errno := syscall.RawSyscall6(syscall.SYS_CLONE, uintptr(syscall.SIGCHLD)|unix.CLONE_NEWNET, 0, 0, 0, 0, 0)
+	if errno != 0 {
+		afterFork()
+		return errno
+	}
+
+	if pid != 0 {
+		afterFork()
+		var ws unix.WaitStatus
+		_, err = unix.Wait4(int(pid), &ws, 0, nil)
+		for err == syscall.EINTR {
+			_, err = unix.Wait4(int(pid), &ws, 0, nil)
+		}
+
+		if err != nil {
+			return errors.Wrapf(err, "failed to find pid=%d process", pid)
+		}
+		errno = syscall.Errno(ws.ExitStatus())
+		if errno != 0 {
+			return errors.Wrap(errno, "failed to mount")
+		}
+		return nil
+	}
+	afterForkInChild()
+	_, _, errno = syscall.RawSyscall6(syscall.SYS_MOUNT, uintptr(unsafe.Pointer(procNetNSBytes)), uintptr(unsafe.Pointer(pBytes)), 0, uintptr(unix.MS_BIND), 0, 0)
+	syscall.RawSyscall(syscall.SYS_EXIT, uintptr(errno), 0, 0)
+	panic("unreachable")
+}
diff --git a/util/network/createns_nolinux.go b/util/network/createns_nolinux.go
new file mode 100644
index 000000000000..baa2473fda95
--- /dev/null
+++ b/util/network/createns_nolinux.go
@@ -0,0 +1,9 @@
+// +build !linux
+
+package network
+
+import "github.com/pkg/errors"
+
+func createNetNS(p string) error {
+	return errors.Errorf("creating netns for cni not supported")
+}
diff --git a/util/network/netns_create/hook.go b/util/network/netns_create/hook.go
deleted file mode 100644
index d2a060c7b7d4..000000000000
--- a/util/network/netns_create/hook.go
+++ /dev/null
@@ -1,51 +0,0 @@
-package netns_create
-
-import (
-	"log"
-	"os"
-	"os/exec"
-	"syscall"
-
-	"github.com/pkg/errors"
-	"golang.org/x/sys/unix"
-)
-
-const envKey = "BUILDKIT_CREATE_NS_PATH"
-
-func Handle() {
-	if path := os.Getenv(envKey); path != "" {
-		if err := handle(path); err != nil {
-			log.Printf("%v", err)
-			os.Exit(1)
-		}
-		os.Exit(0)
-	}
-}
-
-func handle(path string) error {
-	f, err := os.Create(path)
-	if err != nil {
-		return err
-	}
-	if err := f.Close(); err != nil {
-		return err
-	}
-	if err := unix.Mount("/proc/self/ns/net", path, "none", unix.MS_BIND, ""); err != nil {
-		return err
-	}
-	return nil
-}
-
-func CreateNetNS(path string) error {
-	cmd := exec.Command("/proc/self/exe")
-	cmd.Env = []string{envKey + "=" + path}
-	cmd.SysProcAttr = &syscall.SysProcAttr{
-		Cloneflags: unix.CLONE_NEWNET,
-	}
-	out, err := cmd.CombinedOutput()
-	if err != nil {
-		return errors.Wrap(err, string(out))
-
-	}
-	return nil
-}

From acbdfc28514092c0830fd2597e1d5dd1180684dd Mon Sep 17 00:00:00 2001
From: Tonis Tiigi <tonistiigi@gmail.com>
Date: Mon, 15 Jul 2019 10:53:18 -0700
Subject: [PATCH 6/6] util: add warning if network fallback is used

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
---
 util/network/network.go | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/util/network/network.go b/util/network/network.go
index 824d3edf3b7c..1770e53bf8b0 100644
--- a/util/network/network.go
+++ b/util/network/network.go
@@ -7,6 +7,7 @@ import (
 	"github.com/moby/buildkit/solver/pb"
 	specs "github.com/opencontainers/runtime-spec/specs-go"
 	"github.com/pkg/errors"
+	"github.com/sirupsen/logrus"
 )
 
 type Opt struct {
@@ -36,6 +37,7 @@ func Providers(opt Opt) (map[pb.NetMode]Provider, error) {
 			}
 			defaultProvider = cniProvider
 		} else {
+			logrus.Warnf("using host network as the default")
 			defaultProvider = NewHostProvider()
 		}
 	default: