summaryrefslogtreecommitdiff
path: root/vendor/github.com/docker/libnetwork/osl
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/docker/libnetwork/osl')
-rw-r--r--vendor/github.com/docker/libnetwork/osl/interface_freebsd.go4
-rw-r--r--vendor/github.com/docker/libnetwork/osl/interface_linux.go460
-rw-r--r--vendor/github.com/docker/libnetwork/osl/interface_windows.go4
-rw-r--r--vendor/github.com/docker/libnetwork/osl/kernel/knobs.go16
-rw-r--r--vendor/github.com/docker/libnetwork/osl/kernel/knobs_linux.go47
-rw-r--r--vendor/github.com/docker/libnetwork/osl/kernel/knobs_unsupported.go7
-rw-r--r--vendor/github.com/docker/libnetwork/osl/namespace_linux.go657
-rw-r--r--vendor/github.com/docker/libnetwork/osl/namespace_unsupported.go17
-rw-r--r--vendor/github.com/docker/libnetwork/osl/namespace_windows.go38
-rw-r--r--vendor/github.com/docker/libnetwork/osl/neigh_freebsd.go4
-rw-r--r--vendor/github.com/docker/libnetwork/osl/neigh_linux.go194
-rw-r--r--vendor/github.com/docker/libnetwork/osl/neigh_windows.go4
-rw-r--r--vendor/github.com/docker/libnetwork/osl/options_linux.go73
-rw-r--r--vendor/github.com/docker/libnetwork/osl/route_linux.go203
-rw-r--r--vendor/github.com/docker/libnetwork/osl/sandbox.go187
-rw-r--r--vendor/github.com/docker/libnetwork/osl/sandbox_freebsd.go44
-rw-r--r--vendor/github.com/docker/libnetwork/osl/sandbox_unsupported.go22
17 files changed, 1981 insertions, 0 deletions
diff --git a/vendor/github.com/docker/libnetwork/osl/interface_freebsd.go b/vendor/github.com/docker/libnetwork/osl/interface_freebsd.go
new file mode 100644
index 000000000..9c0141fd9
--- /dev/null
+++ b/vendor/github.com/docker/libnetwork/osl/interface_freebsd.go
@@ -0,0 +1,4 @@
+package osl
+
+// IfaceOption is a function option type to set interface options
+type IfaceOption func()
diff --git a/vendor/github.com/docker/libnetwork/osl/interface_linux.go b/vendor/github.com/docker/libnetwork/osl/interface_linux.go
new file mode 100644
index 000000000..4f8ff3d63
--- /dev/null
+++ b/vendor/github.com/docker/libnetwork/osl/interface_linux.go
@@ -0,0 +1,460 @@
+package osl
+
+import (
+ "fmt"
+ "net"
+ "regexp"
+ "sync"
+ "syscall"
+ "time"
+
+ "github.com/docker/libnetwork/ns"
+ "github.com/docker/libnetwork/types"
+ "github.com/sirupsen/logrus"
+ "github.com/vishvananda/netlink"
+ "github.com/vishvananda/netns"
+)
+
+// IfaceOption is a function option type to set interface options
+type IfaceOption func(i *nwIface)
+
+type nwIface struct {
+ srcName string
+ dstName string
+ master string
+ dstMaster string
+ mac net.HardwareAddr
+ address *net.IPNet
+ addressIPv6 *net.IPNet
+ llAddrs []*net.IPNet
+ routes []*net.IPNet
+ bridge bool
+ ns *networkNamespace
+ sync.Mutex
+}
+
+func (i *nwIface) SrcName() string {
+ i.Lock()
+ defer i.Unlock()
+
+ return i.srcName
+}
+
+func (i *nwIface) DstName() string {
+ i.Lock()
+ defer i.Unlock()
+
+ return i.dstName
+}
+
+func (i *nwIface) DstMaster() string {
+ i.Lock()
+ defer i.Unlock()
+
+ return i.dstMaster
+}
+
+func (i *nwIface) Bridge() bool {
+ i.Lock()
+ defer i.Unlock()
+
+ return i.bridge
+}
+
+func (i *nwIface) Master() string {
+ i.Lock()
+ defer i.Unlock()
+
+ return i.master
+}
+
+func (i *nwIface) MacAddress() net.HardwareAddr {
+ i.Lock()
+ defer i.Unlock()
+
+ return types.GetMacCopy(i.mac)
+}
+
+func (i *nwIface) Address() *net.IPNet {
+ i.Lock()
+ defer i.Unlock()
+
+ return types.GetIPNetCopy(i.address)
+}
+
+func (i *nwIface) AddressIPv6() *net.IPNet {
+ i.Lock()
+ defer i.Unlock()
+
+ return types.GetIPNetCopy(i.addressIPv6)
+}
+
+func (i *nwIface) LinkLocalAddresses() []*net.IPNet {
+ i.Lock()
+ defer i.Unlock()
+
+ return i.llAddrs
+}
+
+func (i *nwIface) Routes() []*net.IPNet {
+ i.Lock()
+ defer i.Unlock()
+
+ routes := make([]*net.IPNet, len(i.routes))
+ for index, route := range i.routes {
+ r := types.GetIPNetCopy(route)
+ routes[index] = r
+ }
+
+ return routes
+}
+
+func (n *networkNamespace) Interfaces() []Interface {
+ n.Lock()
+ defer n.Unlock()
+
+ ifaces := make([]Interface, len(n.iFaces))
+
+ for i, iface := range n.iFaces {
+ ifaces[i] = iface
+ }
+
+ return ifaces
+}
+
+func (i *nwIface) Remove() error {
+ i.Lock()
+ n := i.ns
+ i.Unlock()
+
+ n.Lock()
+ isDefault := n.isDefault
+ nlh := n.nlHandle
+ n.Unlock()
+
+ // Find the network interface identified by the DstName attribute.
+ iface, err := nlh.LinkByName(i.DstName())
+ if err != nil {
+ return err
+ }
+
+ // Down the interface before configuring
+ if err := nlh.LinkSetDown(iface); err != nil {
+ return err
+ }
+
+ err = nlh.LinkSetName(iface, i.SrcName())
+ if err != nil {
+ logrus.Debugf("LinkSetName failed for interface %s: %v", i.SrcName(), err)
+ return err
+ }
+
+ // if it is a bridge just delete it.
+ if i.Bridge() {
+ if err := nlh.LinkDel(iface); err != nil {
+ return fmt.Errorf("failed deleting bridge %q: %v", i.SrcName(), err)
+ }
+ } else if !isDefault {
+ // Move the network interface to caller namespace.
+ if err := nlh.LinkSetNsFd(iface, ns.ParseHandlerInt()); err != nil {
+ logrus.Debugf("LinkSetNsPid failed for interface %s: %v", i.SrcName(), err)
+ return err
+ }
+ }
+
+ n.Lock()
+ for index, intf := range n.iFaces {
+ if intf == i {
+ n.iFaces = append(n.iFaces[:index], n.iFaces[index+1:]...)
+ break
+ }
+ }
+ n.Unlock()
+
+ n.checkLoV6()
+
+ return nil
+}
+
+// Returns the sandbox's side veth interface statistics
+func (i *nwIface) Statistics() (*types.InterfaceStatistics, error) {
+ i.Lock()
+ n := i.ns
+ i.Unlock()
+
+ l, err := n.nlHandle.LinkByName(i.DstName())
+ if err != nil {
+ return nil, fmt.Errorf("failed to retrieve the statistics for %s in netns %s: %v", i.DstName(), n.path, err)
+ }
+
+ stats := l.Attrs().Statistics
+ if stats == nil {
+ return nil, fmt.Errorf("no statistics were returned")
+ }
+
+ return &types.InterfaceStatistics{
+ RxBytes: uint64(stats.RxBytes),
+ TxBytes: uint64(stats.TxBytes),
+ RxPackets: uint64(stats.RxPackets),
+ TxPackets: uint64(stats.TxPackets),
+ RxDropped: uint64(stats.RxDropped),
+ TxDropped: uint64(stats.TxDropped),
+ }, nil
+}
+
+func (n *networkNamespace) findDst(srcName string, isBridge bool) string {
+ n.Lock()
+ defer n.Unlock()
+
+ for _, i := range n.iFaces {
+ // The master should match the srcname of the interface and the
+ // master interface should be of type bridge, if searching for a bridge type
+ if i.SrcName() == srcName && (!isBridge || i.Bridge()) {
+ return i.DstName()
+ }
+ }
+
+ return ""
+}
+
+func (n *networkNamespace) AddInterface(srcName, dstPrefix string, options ...IfaceOption) error {
+ i := &nwIface{srcName: srcName, dstName: dstPrefix, ns: n}
+ i.processInterfaceOptions(options...)
+
+ if i.master != "" {
+ i.dstMaster = n.findDst(i.master, true)
+ if i.dstMaster == "" {
+ return fmt.Errorf("could not find an appropriate master %q for %q",
+ i.master, i.srcName)
+ }
+ }
+
+ n.Lock()
+ if n.isDefault {
+ i.dstName = i.srcName
+ } else {
+ i.dstName = fmt.Sprintf("%s%d", dstPrefix, n.nextIfIndex[dstPrefix])
+ n.nextIfIndex[dstPrefix]++
+ }
+
+ path := n.path
+ isDefault := n.isDefault
+ nlh := n.nlHandle
+ nlhHost := ns.NlHandle()
+ n.Unlock()
+
+ // If it is a bridge interface we have to create the bridge inside
+ // the namespace so don't try to lookup the interface using srcName
+ if i.bridge {
+ link := &netlink.Bridge{
+ LinkAttrs: netlink.LinkAttrs{
+ Name: i.srcName,
+ },
+ }
+ if err := nlh.LinkAdd(link); err != nil {
+ return fmt.Errorf("failed to create bridge %q: %v", i.srcName, err)
+ }
+ } else {
+ // Find the network interface identified by the SrcName attribute.
+ iface, err := nlhHost.LinkByName(i.srcName)
+ if err != nil {
+ return fmt.Errorf("failed to get link by name %q: %v", i.srcName, err)
+ }
+
+ // Move the network interface to the destination
+ // namespace only if the namespace is not a default
+ // type
+ if !isDefault {
+ newNs, err := netns.GetFromPath(path)
+ if err != nil {
+ return fmt.Errorf("failed get network namespace %q: %v", path, err)
+ }
+ defer newNs.Close()
+ if err := nlhHost.LinkSetNsFd(iface, int(newNs)); err != nil {
+ return fmt.Errorf("failed to set namespace on link %q: %v", i.srcName, err)
+ }
+ }
+ }
+
+ // Find the network interface identified by the SrcName attribute.
+ iface, err := nlh.LinkByName(i.srcName)
+ if err != nil {
+ return fmt.Errorf("failed to get link by name %q: %v", i.srcName, err)
+ }
+
+ // Down the interface before configuring
+ if err := nlh.LinkSetDown(iface); err != nil {
+ return fmt.Errorf("failed to set link down: %v", err)
+ }
+
+ // Configure the interface now this is moved in the proper namespace.
+ if err := configureInterface(nlh, iface, i); err != nil {
+ // If configuring the device fails move it back to the host namespace
+ // and change the name back to the source name. This allows the caller
+ // to properly cleanup the interface. Its important especially for
+ // interfaces with global attributes, ex: vni id for vxlan interfaces.
+ if nerr := nlh.LinkSetName(iface, i.SrcName()); nerr != nil {
+ logrus.Errorf("renaming interface (%s->%s) failed, %v after config error %v", i.DstName(), i.SrcName(), nerr, err)
+ }
+ if nerr := nlh.LinkSetNsFd(iface, ns.ParseHandlerInt()); nerr != nil {
+ logrus.Errorf("moving interface %s to host ns failed, %v, after config error %v", i.SrcName(), nerr, err)
+ }
+ return err
+ }
+
+ // Up the interface.
+ cnt := 0
+ for err = nlh.LinkSetUp(iface); err != nil && cnt < 3; cnt++ {
+ logrus.Debugf("retrying link setup because of: %v", err)
+ time.Sleep(10 * time.Millisecond)
+ err = nlh.LinkSetUp(iface)
+ }
+ if err != nil {
+ return fmt.Errorf("failed to set link up: %v", err)
+ }
+
+ // Set the routes on the interface. This can only be done when the interface is up.
+ if err := setInterfaceRoutes(nlh, iface, i); err != nil {
+ return fmt.Errorf("error setting interface %q routes to %q: %v", iface.Attrs().Name, i.Routes(), err)
+ }
+
+ n.Lock()
+ n.iFaces = append(n.iFaces, i)
+ n.Unlock()
+
+ n.checkLoV6()
+
+ return nil
+}
+
+func configureInterface(nlh *netlink.Handle, iface netlink.Link, i *nwIface) error {
+ ifaceName := iface.Attrs().Name
+ ifaceConfigurators := []struct {
+ Fn func(*netlink.Handle, netlink.Link, *nwIface) error
+ ErrMessage string
+ }{
+ {setInterfaceName, fmt.Sprintf("error renaming interface %q to %q", ifaceName, i.DstName())},
+ {setInterfaceMAC, fmt.Sprintf("error setting interface %q MAC to %q", ifaceName, i.MacAddress())},
+ {setInterfaceIP, fmt.Sprintf("error setting interface %q IP to %v", ifaceName, i.Address())},
+ {setInterfaceIPv6, fmt.Sprintf("error setting interface %q IPv6 to %v", ifaceName, i.AddressIPv6())},
+ {setInterfaceMaster, fmt.Sprintf("error setting interface %q master to %q", ifaceName, i.DstMaster())},
+ {setInterfaceLinkLocalIPs, fmt.Sprintf("error setting interface %q link local IPs to %v", ifaceName, i.LinkLocalAddresses())},
+ }
+
+ for _, config := range ifaceConfigurators {
+ if err := config.Fn(nlh, iface, i); err != nil {
+ return fmt.Errorf("%s: %v", config.ErrMessage, err)
+ }
+ }
+ return nil
+}
+
+func setInterfaceMaster(nlh *netlink.Handle, iface netlink.Link, i *nwIface) error {
+ if i.DstMaster() == "" {
+ return nil
+ }
+
+ return nlh.LinkSetMaster(iface, &netlink.Bridge{
+ LinkAttrs: netlink.LinkAttrs{Name: i.DstMaster()}})
+}
+
+func setInterfaceMAC(nlh *netlink.Handle, iface netlink.Link, i *nwIface) error {
+ if i.MacAddress() == nil {
+ return nil
+ }
+ return nlh.LinkSetHardwareAddr(iface, i.MacAddress())
+}
+
+func setInterfaceIP(nlh *netlink.Handle, iface netlink.Link, i *nwIface) error {
+ if i.Address() == nil {
+ return nil
+ }
+ if err := checkRouteConflict(nlh, i.Address(), netlink.FAMILY_V4); err != nil {
+ return err
+ }
+ ipAddr := &netlink.Addr{IPNet: i.Address(), Label: ""}
+ return nlh.AddrAdd(iface, ipAddr)
+}
+
+func setInterfaceIPv6(nlh *netlink.Handle, iface netlink.Link, i *nwIface) error {
+ if i.AddressIPv6() == nil {
+ return nil
+ }
+ if err := checkRouteConflict(nlh, i.AddressIPv6(), netlink.FAMILY_V6); err != nil {
+ return err
+ }
+ if err := setIPv6(i.ns.path, i.DstName(), true); err != nil {
+ return fmt.Errorf("failed to enable ipv6: %v", err)
+ }
+ ipAddr := &netlink.Addr{IPNet: i.AddressIPv6(), Label: "", Flags: syscall.IFA_F_NODAD}
+ return nlh.AddrAdd(iface, ipAddr)
+}
+
+func setInterfaceLinkLocalIPs(nlh *netlink.Handle, iface netlink.Link, i *nwIface) error {
+ for _, llIP := range i.LinkLocalAddresses() {
+ ipAddr := &netlink.Addr{IPNet: llIP}
+ if err := nlh.AddrAdd(iface, ipAddr); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func setInterfaceName(nlh *netlink.Handle, iface netlink.Link, i *nwIface) error {
+ return nlh.LinkSetName(iface, i.DstName())
+}
+
+func setInterfaceRoutes(nlh *netlink.Handle, iface netlink.Link, i *nwIface) error {
+ for _, route := range i.Routes() {
+ err := nlh.RouteAdd(&netlink.Route{
+ Scope: netlink.SCOPE_LINK,
+ LinkIndex: iface.Attrs().Index,
+ Dst: route,
+ })
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// In older kernels (like the one in Centos 6.6 distro) sysctl does not have netns support. Therefore
+// we cannot gather the statistics from /sys/class/net/<dev>/statistics/<counter> files. Per-netns stats
+// are naturally found in /proc/net/dev in kernels which support netns (ifconfig relies on that).
+const (
+ netStatsFile = "/proc/net/dev"
+ base = "[ ]*%s:([ ]+[0-9]+){16}"
+)
+
+func scanInterfaceStats(data, ifName string, i *types.InterfaceStatistics) error {
+ var (
+ bktStr string
+ bkt uint64
+ )
+
+ regex := fmt.Sprintf(base, ifName)
+ re := regexp.MustCompile(regex)
+ line := re.FindString(data)
+
+ _, err := fmt.Sscanf(line, "%s %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d",
+ &bktStr, &i.RxBytes, &i.RxPackets, &i.RxErrors, &i.RxDropped, &bkt, &bkt, &bkt,
+ &bkt, &i.TxBytes, &i.TxPackets, &i.TxErrors, &i.TxDropped, &bkt, &bkt, &bkt, &bkt)
+
+ return err
+}
+
+func checkRouteConflict(nlh *netlink.Handle, address *net.IPNet, family int) error {
+ routes, err := nlh.RouteList(nil, family)
+ if err != nil {
+ return err
+ }
+ for _, route := range routes {
+ if route.Dst != nil {
+ if route.Dst.Contains(address.IP) || address.Contains(route.Dst.IP) {
+ return fmt.Errorf("cannot program address %v in sandbox interface because it conflicts with existing route %s",
+ address, route)
+ }
+ }
+ }
+ return nil
+}
diff --git a/vendor/github.com/docker/libnetwork/osl/interface_windows.go b/vendor/github.com/docker/libnetwork/osl/interface_windows.go
new file mode 100644
index 000000000..9c0141fd9
--- /dev/null
+++ b/vendor/github.com/docker/libnetwork/osl/interface_windows.go
@@ -0,0 +1,4 @@
+package osl
+
+// IfaceOption is a function option type to set interface options
+type IfaceOption func()
diff --git a/vendor/github.com/docker/libnetwork/osl/kernel/knobs.go b/vendor/github.com/docker/libnetwork/osl/kernel/knobs.go
new file mode 100644
index 000000000..a7cd7dbb7
--- /dev/null
+++ b/vendor/github.com/docker/libnetwork/osl/kernel/knobs.go
@@ -0,0 +1,16 @@
+package kernel
+
+type conditionalCheck func(val1, val2 string) bool
+
+// OSValue represents a tuple, value defined, check function when to apply the value
+type OSValue struct {
+ Value string
+ CheckFn conditionalCheck
+}
+
+func propertyIsValid(val1, val2 string, check conditionalCheck) bool {
+ if check == nil || check(val1, val2) {
+ return true
+ }
+ return false
+}
diff --git a/vendor/github.com/docker/libnetwork/osl/kernel/knobs_linux.go b/vendor/github.com/docker/libnetwork/osl/kernel/knobs_linux.go
new file mode 100644
index 000000000..964280650
--- /dev/null
+++ b/vendor/github.com/docker/libnetwork/osl/kernel/knobs_linux.go
@@ -0,0 +1,47 @@
+package kernel
+
+import (
+ "io/ioutil"
+ "path"
+ "strings"
+
+ "github.com/sirupsen/logrus"
+)
+
+// writeSystemProperty writes the value to a path under /proc/sys as determined from the key.
+// For e.g. net.ipv4.ip_forward translated to /proc/sys/net/ipv4/ip_forward.
+func writeSystemProperty(key, value string) error {
+ keyPath := strings.Replace(key, ".", "/", -1)
+ return ioutil.WriteFile(path.Join("/proc/sys", keyPath), []byte(value), 0644)
+}
+
+// readSystemProperty reads the value from the path under /proc/sys and returns it
+func readSystemProperty(key string) (string, error) {
+ keyPath := strings.Replace(key, ".", "/", -1)
+ value, err := ioutil.ReadFile(path.Join("/proc/sys", keyPath))
+ if err != nil {
+ return "", err
+ }
+ return strings.TrimSpace(string(value)), nil
+}
+
+// ApplyOSTweaks applies the configuration values passed as arguments
+func ApplyOSTweaks(osConfig map[string]*OSValue) {
+ for k, v := range osConfig {
+ // read the existing property from disk
+ oldv, err := readSystemProperty(k)
+ if err != nil {
+ logrus.WithError(err).Errorf("error reading the kernel parameter %s", k)
+ continue
+ }
+
+ if propertyIsValid(oldv, v.Value, v.CheckFn) {
+ // write new prop value to disk
+ if err := writeSystemProperty(k, v.Value); err != nil {
+ logrus.WithError(err).Errorf("error setting the kernel parameter %s = %s, (leaving as %s)", k, v.Value, oldv)
+ continue
+ }
+ logrus.Debugf("updated kernel parameter %s = %s (was %s)", k, v.Value, oldv)
+ }
+ }
+}
diff --git a/vendor/github.com/docker/libnetwork/osl/kernel/knobs_unsupported.go b/vendor/github.com/docker/libnetwork/osl/kernel/knobs_unsupported.go
new file mode 100644
index 000000000..32f258f41
--- /dev/null
+++ b/vendor/github.com/docker/libnetwork/osl/kernel/knobs_unsupported.go
@@ -0,0 +1,7 @@
+// +build !linux
+
+package kernel
+
+// ApplyOSTweaks applies the configuration values passed as arguments
+func ApplyOSTweaks(osConfig map[string]*OSValue) {
+}
diff --git a/vendor/github.com/docker/libnetwork/osl/namespace_linux.go b/vendor/github.com/docker/libnetwork/osl/namespace_linux.go
new file mode 100644
index 000000000..abb748b56
--- /dev/null
+++ b/vendor/github.com/docker/libnetwork/osl/namespace_linux.go
@@ -0,0 +1,657 @@
+package osl
+
+import (
+ "fmt"
+ "io/ioutil"
+ "net"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "runtime"
+ "strconv"
+ "strings"
+ "sync"
+ "syscall"
+ "time"
+
+ "github.com/docker/docker/pkg/reexec"
+ "github.com/docker/libnetwork/ns"
+ "github.com/docker/libnetwork/osl/kernel"
+ "github.com/docker/libnetwork/types"
+ "github.com/sirupsen/logrus"
+ "github.com/vishvananda/netlink"
+ "github.com/vishvananda/netns"
+)
+
+const defaultPrefix = "/var/run/docker"
+
+func init() {
+ reexec.Register("set-ipv6", reexecSetIPv6)
+}
+
+var (
+ once sync.Once
+ garbagePathMap = make(map[string]bool)
+ gpmLock sync.Mutex
+ gpmWg sync.WaitGroup
+ gpmCleanupPeriod = 60 * time.Second
+ gpmChan = make(chan chan struct{})
+ prefix = defaultPrefix
+ loadBalancerConfig = map[string]*kernel.OSValue{
+ // expires connection from the IPVS connection table when the backend is not available
+ // more info: https://github.com/torvalds/linux/blob/master/Documentation/networking/ipvs-sysctl.txt#L126:1
+ "net.ipv4.vs.expire_nodest_conn": {"1", nil},
+ }
+)
+
+// The networkNamespace type is the linux implementation of the Sandbox
+// interface. It represents a linux network namespace, and moves an interface
+// into it when called on method AddInterface or sets the gateway etc.
+type networkNamespace struct {
+ path string
+ iFaces []*nwIface
+ gw net.IP
+ gwv6 net.IP
+ staticRoutes []*types.StaticRoute
+ neighbors []*neigh
+ nextIfIndex map[string]int
+ isDefault bool
+ nlHandle *netlink.Handle
+ loV6Enabled bool
+ sync.Mutex
+}
+
+// SetBasePath sets the base url prefix for the ns path
+func SetBasePath(path string) {
+ prefix = path
+}
+
+func init() {
+ reexec.Register("netns-create", reexecCreateNamespace)
+}
+
+func basePath() string {
+ return filepath.Join(prefix, "netns")
+}
+
+func createBasePath() {
+ err := os.MkdirAll(basePath(), 0755)
+ if err != nil {
+ panic("Could not create net namespace path directory")
+ }
+
+ // Start the garbage collection go routine
+ go removeUnusedPaths()
+}
+
+func removeUnusedPaths() {
+ gpmLock.Lock()
+ period := gpmCleanupPeriod
+ gpmLock.Unlock()
+
+ ticker := time.NewTicker(period)
+ for {
+ var (
+ gc chan struct{}
+ gcOk bool
+ )
+
+ select {
+ case <-ticker.C:
+ case gc, gcOk = <-gpmChan:
+ }
+
+ gpmLock.Lock()
+ pathList := make([]string, 0, len(garbagePathMap))
+ for path := range garbagePathMap {
+ pathList = append(pathList, path)
+ }
+ garbagePathMap = make(map[string]bool)
+ gpmWg.Add(1)
+ gpmLock.Unlock()
+
+ for _, path := range pathList {
+ os.Remove(path)
+ }
+
+ gpmWg.Done()
+ if gcOk {
+ close(gc)
+ }
+ }
+}
+
+func addToGarbagePaths(path string) {
+ gpmLock.Lock()
+ garbagePathMap[path] = true
+ gpmLock.Unlock()
+}
+
+func removeFromGarbagePaths(path string) {
+ gpmLock.Lock()
+ delete(garbagePathMap, path)
+ gpmLock.Unlock()
+}
+
+// GC triggers garbage collection of namespace path right away
+// and waits for it.
+func GC() {
+ gpmLock.Lock()
+ if len(garbagePathMap) == 0 {
+ // No need for GC if map is empty
+ gpmLock.Unlock()
+ return
+ }
+ gpmLock.Unlock()
+
+ // if content exists in the garbage paths
+ // we can trigger GC to run, providing a
+ // channel to be notified on completion
+ waitGC := make(chan struct{})
+ gpmChan <- waitGC
+ // wait for GC completion
+ <-waitGC
+}
+
+// GenerateKey generates a sandbox key based on the passed
+// container id.
+func GenerateKey(containerID string) string {
+ maxLen := 12
+ // Read sandbox key from host for overlay
+ if strings.HasPrefix(containerID, "-") {
+ var (
+ index int
+ indexStr string
+ tmpkey string
+ )
+ dir, err := ioutil.ReadDir(basePath())
+ if err != nil {
+ return ""
+ }
+
+ for _, v := range dir {
+ id := v.Name()
+ if strings.HasSuffix(id, containerID[:maxLen-1]) {
+ indexStr = strings.TrimSuffix(id, containerID[:maxLen-1])
+ tmpindex, err := strconv.Atoi(indexStr)
+ if err != nil {
+ return ""
+ }
+ if tmpindex > index {
+ index = tmpindex
+ tmpkey = id
+ }
+
+ }
+ }
+ containerID = tmpkey
+ if containerID == "" {
+ return ""
+ }
+ }
+
+ if len(containerID) < maxLen {
+ maxLen = len(containerID)
+ }
+
+ return basePath() + "/" + containerID[:maxLen]
+}
+
+// NewSandbox provides a new sandbox instance created in an os specific way
+// provided a key which uniquely identifies the sandbox
+func NewSandbox(key string, osCreate, isRestore bool) (Sandbox, error) {
+ if !isRestore {
+ err := createNetworkNamespace(key, osCreate)
+ if err != nil {
+ return nil, err
+ }
+ } else {
+ once.Do(createBasePath)
+ }
+
+ n := &networkNamespace{path: key, isDefault: !osCreate, nextIfIndex: make(map[string]int)}
+
+ sboxNs, err := netns.GetFromPath(n.path)
+ if err != nil {
+ return nil, fmt.Errorf("failed get network namespace %q: %v", n.path, err)
+ }
+ defer sboxNs.Close()
+
+ n.nlHandle, err = netlink.NewHandleAt(sboxNs, syscall.NETLINK_ROUTE)
+ if err != nil {
+ return nil, fmt.Errorf("failed to create a netlink handle: %v", err)
+ }
+
+ err = n.nlHandle.SetSocketTimeout(ns.NetlinkSocketsTimeout)
+ if err != nil {
+ logrus.Warnf("Failed to set the timeout on the sandbox netlink handle sockets: %v", err)
+ }
+ // In live-restore mode, IPV6 entries are getting cleaned up due to below code
+ // We should retain IPV6 configurations in live-restore mode when Docker Daemon
+ // comes back. It should work as it is on other cases
+ // As starting point, disable IPv6 on all interfaces
+ if !isRestore && !n.isDefault {
+ err = setIPv6(n.path, "all", false)
+ if err != nil {
+ logrus.Warnf("Failed to disable IPv6 on all interfaces on network namespace %q: %v", n.path, err)
+ }
+ }
+
+ if err = n.loopbackUp(); err != nil {
+ n.nlHandle.Delete()
+ return nil, err
+ }
+
+ return n, nil
+}
+
+func (n *networkNamespace) InterfaceOptions() IfaceOptionSetter {
+ return n
+}
+
+func (n *networkNamespace) NeighborOptions() NeighborOptionSetter {
+ return n
+}
+
+func mountNetworkNamespace(basePath string, lnPath string) error {
+ return syscall.Mount(basePath, lnPath, "bind", syscall.MS_BIND, "")
+}
+
+// GetSandboxForExternalKey returns sandbox object for the supplied path
+func GetSandboxForExternalKey(basePath string, key string) (Sandbox, error) {
+ if err := createNamespaceFile(key); err != nil {
+ return nil, err
+ }
+
+ if err := mountNetworkNamespace(basePath, key); err != nil {
+ return nil, err
+ }
+ n := &networkNamespace{path: key, nextIfIndex: make(map[string]int)}
+
+ sboxNs, err := netns.GetFromPath(n.path)
+ if err != nil {
+ return nil, fmt.Errorf("failed get network namespace %q: %v", n.path, err)
+ }
+ defer sboxNs.Close()
+
+ n.nlHandle, err = netlink.NewHandleAt(sboxNs, syscall.NETLINK_ROUTE)
+ if err != nil {
+ return nil, fmt.Errorf("failed to create a netlink handle: %v", err)
+ }
+
+ err = n.nlHandle.SetSocketTimeout(ns.NetlinkSocketsTimeout)
+ if err != nil {
+ logrus.Warnf("Failed to set the timeout on the sandbox netlink handle sockets: %v", err)
+ }
+
+ // As starting point, disable IPv6 on all interfaces
+ err = setIPv6(n.path, "all", false)
+ if err != nil {
+ logrus.Warnf("Failed to disable IPv6 on all interfaces on network namespace %q: %v", n.path, err)
+ }
+
+ if err = n.loopbackUp(); err != nil {
+ n.nlHandle.Delete()
+ return nil, err
+ }
+
+ return n, nil
+}
+
+func reexecCreateNamespace() {
+ if len(os.Args) < 2 {
+ logrus.Fatal("no namespace path provided")
+ }
+ if err := mountNetworkNamespace("/proc/self/ns/net", os.Args[1]); err != nil {
+ logrus.Fatal(err)
+ }
+}
+
+func createNetworkNamespace(path string, osCreate bool) error {
+ if err := createNamespaceFile(path); err != nil {
+ return err
+ }
+
+ cmd := &exec.Cmd{
+ Path: reexec.Self(),
+ Args: append([]string{"netns-create"}, path),
+ Stdout: os.Stdout,
+ Stderr: os.Stderr,
+ }
+ if osCreate {
+ cmd.SysProcAttr = &syscall.SysProcAttr{}
+ cmd.SysProcAttr.Cloneflags = syscall.CLONE_NEWNET
+ }
+ if err := cmd.Run(); err != nil {
+ return fmt.Errorf("namespace creation reexec command failed: %v", err)
+ }
+
+ return nil
+}
+
+func unmountNamespaceFile(path string) {
+ if _, err := os.Stat(path); err == nil {
+ syscall.Unmount(path, syscall.MNT_DETACH)
+ }
+}
+
+func createNamespaceFile(path string) (err error) {
+ var f *os.File
+
+ once.Do(createBasePath)
+ // Remove it from garbage collection list if present
+ removeFromGarbagePaths(path)
+
+ // If the path is there unmount it first
+ unmountNamespaceFile(path)
+
+ // wait for garbage collection to complete if it is in progress
+ // before trying to create the file.
+ gpmWg.Wait()
+
+ if f, err = os.Create(path); err == nil {
+ f.Close()
+ }
+
+ return err
+}
+
+func (n *networkNamespace) loopbackUp() error {
+ iface, err := n.nlHandle.LinkByName("lo")
+ if err != nil {
+ return err
+ }
+ return n.nlHandle.LinkSetUp(iface)
+}
+
+func (n *networkNamespace) GetLoopbackIfaceName() string {
+ return "lo"
+}
+
+func (n *networkNamespace) AddAliasIP(ifName string, ip *net.IPNet) error {
+ iface, err := n.nlHandle.LinkByName(ifName)
+ if err != nil {
+ return err
+ }
+ return n.nlHandle.AddrAdd(iface, &netlink.Addr{IPNet: ip})
+}
+
+func (n *networkNamespace) RemoveAliasIP(ifName string, ip *net.IPNet) error {
+ iface, err := n.nlHandle.LinkByName(ifName)
+ if err != nil {
+ return err
+ }
+ return n.nlHandle.AddrDel(iface, &netlink.Addr{IPNet: ip})
+}
+
+func (n *networkNamespace) InvokeFunc(f func()) error {
+ return nsInvoke(n.nsPath(), func(nsFD int) error { return nil }, func(callerFD int) error {
+ f()
+ return nil
+ })
+}
+
+// InitOSContext initializes OS context while configuring network resources
+func InitOSContext() func() {
+ runtime.LockOSThread()
+ if err := ns.SetNamespace(); err != nil {
+ logrus.Error(err)
+ }
+ return runtime.UnlockOSThread
+}
+
+func nsInvoke(path string, prefunc func(nsFD int) error, postfunc func(callerFD int) error) error {
+ defer InitOSContext()()
+
+ newNs, err := netns.GetFromPath(path)
+ if err != nil {
+ return fmt.Errorf("failed get network namespace %q: %v", path, err)
+ }
+ defer newNs.Close()
+
+ // Invoked before the namespace switch happens but after the namespace file
+ // handle is obtained.
+ if err := prefunc(int(newNs)); err != nil {
+ return fmt.Errorf("failed in prefunc: %v", err)
+ }
+
+ if err = netns.Set(newNs); err != nil {
+ return err
+ }
+ defer ns.SetNamespace()
+
+ // Invoked after the namespace switch.
+ return postfunc(ns.ParseHandlerInt())
+}
+
+func (n *networkNamespace) nsPath() string {
+ n.Lock()
+ defer n.Unlock()
+
+ return n.path
+}
+
+func (n *networkNamespace) Info() Info {
+ return n
+}
+
+func (n *networkNamespace) Key() string {
+ return n.path
+}
+
+func (n *networkNamespace) Destroy() error {
+ if n.nlHandle != nil {
+ n.nlHandle.Delete()
+ }
+ // Assuming no running process is executing in this network namespace,
+ // unmounting is sufficient to destroy it.
+ if err := syscall.Unmount(n.path, syscall.MNT_DETACH); err != nil {
+ return err
+ }
+
+ // Stash it into the garbage collection list
+ addToGarbagePaths(n.path)
+ return nil
+}
+
+// Restore restore the network namespace
+func (n *networkNamespace) Restore(ifsopt map[string][]IfaceOption, routes []*types.StaticRoute, gw net.IP, gw6 net.IP) error {
+ // restore interfaces
+ for name, opts := range ifsopt {
+ if !strings.Contains(name, "+") {
+ return fmt.Errorf("wrong iface name in restore osl sandbox interface: %s", name)
+ }
+ seps := strings.Split(name, "+")
+ srcName := seps[0]
+ dstPrefix := seps[1]
+ i := &nwIface{srcName: srcName, dstName: dstPrefix, ns: n}
+ i.processInterfaceOptions(opts...)
+ if i.master != "" {
+ i.dstMaster = n.findDst(i.master, true)
+ if i.dstMaster == "" {
+ return fmt.Errorf("could not find an appropriate master %q for %q",
+ i.master, i.srcName)
+ }
+ }
+ if n.isDefault {
+ i.dstName = i.srcName
+ } else {
+ links, err := n.nlHandle.LinkList()
+ if err != nil {
+ return fmt.Errorf("failed to retrieve list of links in network namespace %q during restore", n.path)
+ }
+ // due to the docker network connect/disconnect, so the dstName should
+ // restore from the namespace
+ for _, link := range links {
+ addrs, err := n.nlHandle.AddrList(link, netlink.FAMILY_V4)
+ if err != nil {
+ return err
+ }
+ ifaceName := link.Attrs().Name
+ if strings.HasPrefix(ifaceName, "vxlan") {
+ if i.dstName == "vxlan" {
+ i.dstName = ifaceName
+ break
+ }
+ }
+ // find the interface name by ip
+ if i.address != nil {
+ for _, addr := range addrs {
+ if addr.IPNet.String() == i.address.String() {
+ i.dstName = ifaceName
+ break
+ }
+ continue
+ }
+ if i.dstName == ifaceName {
+ break
+ }
+ }
+ // This is to find the interface name of the pair in overlay sandbox
+ if strings.HasPrefix(ifaceName, "veth") {
+ if i.master != "" && i.dstName == "veth" {
+ i.dstName = ifaceName
+ }
+ }
+ }
+
+ var index int
+ indexStr := strings.TrimPrefix(i.dstName, dstPrefix)
+ if indexStr != "" {
+ index, err = strconv.Atoi(indexStr)
+ if err != nil {
+ return err
+ }
+ }
+ index++
+ n.Lock()
+ if index > n.nextIfIndex[dstPrefix] {
+ n.nextIfIndex[dstPrefix] = index
+ }
+ n.iFaces = append(n.iFaces, i)
+ n.Unlock()
+ }
+ }
+
+ // restore routes
+ for _, r := range routes {
+ n.Lock()
+ n.staticRoutes = append(n.staticRoutes, r)
+ n.Unlock()
+ }
+
+ // restore gateway
+ if len(gw) > 0 {
+ n.Lock()
+ n.gw = gw
+ n.Unlock()
+ }
+
+ if len(gw6) > 0 {
+ n.Lock()
+ n.gwv6 = gw6
+ n.Unlock()
+ }
+
+ return nil
+}
+
+// Checks whether IPv6 needs to be enabled/disabled on the loopback interface
+func (n *networkNamespace) checkLoV6() {
+ var (
+ enable = false
+ action = "disable"
+ )
+
+ n.Lock()
+ for _, iface := range n.iFaces {
+ if iface.AddressIPv6() != nil {
+ enable = true
+ action = "enable"
+ break
+ }
+ }
+ n.Unlock()
+
+ if n.loV6Enabled == enable {
+ return
+ }
+
+ if err := setIPv6(n.path, "lo", enable); err != nil {
+ logrus.Warnf("Failed to %s IPv6 on loopback interface on network namespace %q: %v", action, n.path, err)
+ }
+
+ n.loV6Enabled = enable
+}
+
+func reexecSetIPv6() {
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
+ if len(os.Args) < 3 {
+ logrus.Errorf("invalid number of arguments for %s", os.Args[0])
+ os.Exit(1)
+ }
+
+ ns, err := netns.GetFromPath(os.Args[1])
+ if err != nil {
+ logrus.Errorf("failed get network namespace %q: %v", os.Args[1], err)
+ os.Exit(2)
+ }
+ defer ns.Close()
+
+ if err = netns.Set(ns); err != nil {
+ logrus.Errorf("setting into container netns %q failed: %v", os.Args[1], err)
+ os.Exit(3)
+ }
+
+ var (
+ action = "disable"
+ value = byte('1')
+ path = fmt.Sprintf("/proc/sys/net/ipv6/conf/%s/disable_ipv6", os.Args[2])
+ )
+
+ if os.Args[3] == "true" {
+ action = "enable"
+ value = byte('0')
+ }
+
+ if _, err := os.Stat(path); err != nil {
+ if os.IsNotExist(err) {
+ logrus.Warnf("file does not exist: %s : %v Has IPv6 been disabled in this node's kernel?", path, err)
+ os.Exit(0)
+ }
+ logrus.Errorf("failed to stat %s : %v", path, err)
+ os.Exit(5)
+ }
+
+ if err = ioutil.WriteFile(path, []byte{value, '\n'}, 0644); err != nil {
+ logrus.Errorf("failed to %s IPv6 forwarding for container's interface %s: %v", action, os.Args[2], err)
+ os.Exit(4)
+ }
+
+ os.Exit(0)
+}
+
+func setIPv6(path, iface string, enable bool) error {
+ cmd := &exec.Cmd{
+ Path: reexec.Self(),
+ Args: append([]string{"set-ipv6"}, path, iface, strconv.FormatBool(enable)),
+ Stdout: os.Stdout,
+ Stderr: os.Stderr,
+ }
+ if err := cmd.Run(); err != nil {
+ return fmt.Errorf("reexec to set IPv6 failed: %v", err)
+ }
+ return nil
+}
+
+// ApplyOSTweaks applies linux configs on the sandbox
+func (n *networkNamespace) ApplyOSTweaks(types []SandboxType) {
+ for _, t := range types {
+ switch t {
+ case SandboxTypeLoadBalancer:
+ kernel.ApplyOSTweaks(loadBalancerConfig)
+ }
+ }
+}
diff --git a/vendor/github.com/docker/libnetwork/osl/namespace_unsupported.go b/vendor/github.com/docker/libnetwork/osl/namespace_unsupported.go
new file mode 100644
index 000000000..74372e249
--- /dev/null
+++ b/vendor/github.com/docker/libnetwork/osl/namespace_unsupported.go
@@ -0,0 +1,17 @@
+// +build !linux,!windows,!freebsd
+
+package osl
+
+// GC triggers garbage collection of namespace path right away
+// and waits for it.
+func GC() {
+}
+
+// GetSandboxForExternalKey returns sandbox object for the supplied path
+func GetSandboxForExternalKey(path string, key string) (Sandbox, error) {
+ return nil, nil
+}
+
+// SetBasePath sets the base url prefix for the ns path
+func SetBasePath(path string) {
+}
diff --git a/vendor/github.com/docker/libnetwork/osl/namespace_windows.go b/vendor/github.com/docker/libnetwork/osl/namespace_windows.go
new file mode 100644
index 000000000..49503c00f
--- /dev/null
+++ b/vendor/github.com/docker/libnetwork/osl/namespace_windows.go
@@ -0,0 +1,38 @@
+package osl
+
+import "testing"
+
+// GenerateKey generates a sandbox key based on the passed
+// container id.
+func GenerateKey(containerID string) string {
+ return containerID
+}
+
+// NewSandbox provides a new sandbox instance created in an os specific way
+// provided a key which uniquely identifies the sandbox
+func NewSandbox(key string, osCreate, isRestore bool) (Sandbox, error) {
+ return nil, nil
+}
+
+func GetSandboxForExternalKey(path string, key string) (Sandbox, error) {
+ return nil, nil
+}
+
+// GC triggers garbage collection of namespace path right away
+// and waits for it.
+func GC() {
+}
+
+// InitOSContext initializes OS context while configuring network resources
+func InitOSContext() func() {
+ return func() {}
+}
+
+// SetupTestOSContext sets up a separate test OS context in which tests will be executed.
+func SetupTestOSContext(t *testing.T) func() {
+ return func() {}
+}
+
+// SetBasePath sets the base url prefix for the ns path
+func SetBasePath(path string) {
+}
diff --git a/vendor/github.com/docker/libnetwork/osl/neigh_freebsd.go b/vendor/github.com/docker/libnetwork/osl/neigh_freebsd.go
new file mode 100644
index 000000000..280f00639
--- /dev/null
+++ b/vendor/github.com/docker/libnetwork/osl/neigh_freebsd.go
@@ -0,0 +1,4 @@
+package osl
+
+// NeighOption is a function option type to set neighbor options
+type NeighOption func()
diff --git a/vendor/github.com/docker/libnetwork/osl/neigh_linux.go b/vendor/github.com/docker/libnetwork/osl/neigh_linux.go
new file mode 100644
index 000000000..6bf1c16dc
--- /dev/null
+++ b/vendor/github.com/docker/libnetwork/osl/neigh_linux.go
@@ -0,0 +1,194 @@
+package osl
+
+import (
+ "bytes"
+ "fmt"
+ "net"
+
+ "github.com/sirupsen/logrus"
+ "github.com/vishvananda/netlink"
+)
+
+// NeighborSearchError indicates that the neighbor is already present
+type NeighborSearchError struct {
+ ip net.IP
+ mac net.HardwareAddr
+ present bool
+}
+
+func (n NeighborSearchError) Error() string {
+ return fmt.Sprintf("Search neighbor failed for IP %v, mac %v, present in db:%t", n.ip, n.mac, n.present)
+}
+
+// NeighOption is a function option type to set interface options
+type NeighOption func(nh *neigh)
+
+type neigh struct {
+ dstIP net.IP
+ dstMac net.HardwareAddr
+ linkName string
+ linkDst string
+ family int
+}
+
+func (n *networkNamespace) findNeighbor(dstIP net.IP, dstMac net.HardwareAddr) *neigh {
+ n.Lock()
+ defer n.Unlock()
+
+ for _, nh := range n.neighbors {
+ if nh.dstIP.Equal(dstIP) && bytes.Equal(nh.dstMac, dstMac) {
+ return nh
+ }
+ }
+
+ return nil
+}
+
+func (n *networkNamespace) DeleteNeighbor(dstIP net.IP, dstMac net.HardwareAddr, osDelete bool) error {
+ var (
+ iface netlink.Link
+ err error
+ )
+
+ nh := n.findNeighbor(dstIP, dstMac)
+ if nh == nil {
+ return NeighborSearchError{dstIP, dstMac, false}
+ }
+
+ if osDelete {
+ n.Lock()
+ nlh := n.nlHandle
+ n.Unlock()
+
+ if nh.linkDst != "" {
+ iface, err = nlh.LinkByName(nh.linkDst)
+ if err != nil {
+ return fmt.Errorf("could not find interface with destination name %s: %v",
+ nh.linkDst, err)
+ }
+ }
+
+ nlnh := &netlink.Neigh{
+ IP: dstIP,
+ State: netlink.NUD_PERMANENT,
+ Family: nh.family,
+ }
+
+ if nlnh.Family > 0 {
+ nlnh.HardwareAddr = dstMac
+ nlnh.Flags = netlink.NTF_SELF
+ }
+
+ if nh.linkDst != "" {
+ nlnh.LinkIndex = iface.Attrs().Index
+ }
+
+ // If the kernel deletion fails for the neighbor entry still remote it
+ // from the namespace cache. Otherwise if the neighbor moves back to the
+ // same host again, kernel update can fail.
+ if err := nlh.NeighDel(nlnh); err != nil {
+ logrus.Warnf("Deleting neighbor IP %s, mac %s failed, %v", dstIP, dstMac, err)
+ }
+
+ // Delete the dynamic entry in the bridge
+ if nlnh.Family > 0 {
+ nlnh := &netlink.Neigh{
+ IP: dstIP,
+ Family: nh.family,
+ }
+
+ nlnh.HardwareAddr = dstMac
+ nlnh.Flags = netlink.NTF_MASTER
+ if nh.linkDst != "" {
+ nlnh.LinkIndex = iface.Attrs().Index
+ }
+ nlh.NeighDel(nlnh)
+ }
+ }
+
+ n.Lock()
+ for i, nh := range n.neighbors {
+ if nh.dstIP.Equal(dstIP) && bytes.Equal(nh.dstMac, dstMac) {
+ n.neighbors = append(n.neighbors[:i], n.neighbors[i+1:]...)
+ break
+ }
+ }
+ n.Unlock()
+ logrus.Debugf("Neighbor entry deleted for IP %v, mac %v osDelete:%t", dstIP, dstMac, osDelete)
+
+ return nil
+}
+
+func (n *networkNamespace) AddNeighbor(dstIP net.IP, dstMac net.HardwareAddr, force bool, options ...NeighOption) error {
+ var (
+ iface netlink.Link
+ err error
+ neighborAlreadyPresent bool
+ )
+
+ // If the namespace already has the neighbor entry but the AddNeighbor is called
+ // because of a miss notification (force flag) program the kernel anyway.
+ nh := n.findNeighbor(dstIP, dstMac)
+ if nh != nil {
+ neighborAlreadyPresent = true
+ logrus.Warnf("Neighbor entry already present for IP %v, mac %v neighbor:%+v forceUpdate:%t", dstIP, dstMac, nh, force)
+ if !force {
+ return NeighborSearchError{dstIP, dstMac, true}
+ }
+ }
+
+ nh = &neigh{
+ dstIP: dstIP,
+ dstMac: dstMac,
+ }
+
+ nh.processNeighOptions(options...)
+
+ if nh.linkName != "" {
+ nh.linkDst = n.findDst(nh.linkName, false)
+ if nh.linkDst == "" {
+ return fmt.Errorf("could not find the interface with name %s", nh.linkName)
+ }
+ }
+
+ n.Lock()
+ nlh := n.nlHandle
+ n.Unlock()
+
+ if nh.linkDst != "" {
+ iface, err = nlh.LinkByName(nh.linkDst)
+ if err != nil {
+ return fmt.Errorf("could not find interface with destination name %s: %v", nh.linkDst, err)
+ }
+ }
+
+ nlnh := &netlink.Neigh{
+ IP: dstIP,
+ HardwareAddr: dstMac,
+ State: netlink.NUD_PERMANENT,
+ Family: nh.family,
+ }
+
+ if nlnh.Family > 0 {
+ nlnh.Flags = netlink.NTF_SELF
+ }
+
+ if nh.linkDst != "" {
+ nlnh.LinkIndex = iface.Attrs().Index
+ }
+
+ if err := nlh.NeighSet(nlnh); err != nil {
+ return fmt.Errorf("could not add neighbor entry:%+v error:%v", nlnh, err)
+ }
+
+ if neighborAlreadyPresent {
+ return nil
+ }
+
+ n.Lock()
+ n.neighbors = append(n.neighbors, nh)
+ n.Unlock()
+ logrus.Debugf("Neighbor entry added for IP:%v, mac:%v on ifc:%s", dstIP, dstMac, nh.linkName)
+
+ return nil
+}
diff --git a/vendor/github.com/docker/libnetwork/osl/neigh_windows.go b/vendor/github.com/docker/libnetwork/osl/neigh_windows.go
new file mode 100644
index 000000000..280f00639
--- /dev/null
+++ b/vendor/github.com/docker/libnetwork/osl/neigh_windows.go
@@ -0,0 +1,4 @@
+package osl
+
+// NeighOption is a function option type to set neighbor options
+type NeighOption func()
diff --git a/vendor/github.com/docker/libnetwork/osl/options_linux.go b/vendor/github.com/docker/libnetwork/osl/options_linux.go
new file mode 100644
index 000000000..818669647
--- /dev/null
+++ b/vendor/github.com/docker/libnetwork/osl/options_linux.go
@@ -0,0 +1,73 @@
+package osl
+
+import "net"
+
+func (nh *neigh) processNeighOptions(options ...NeighOption) {
+ for _, opt := range options {
+ if opt != nil {
+ opt(nh)
+ }
+ }
+}
+
+func (n *networkNamespace) LinkName(name string) NeighOption {
+ return func(nh *neigh) {
+ nh.linkName = name
+ }
+}
+
+func (n *networkNamespace) Family(family int) NeighOption {
+ return func(nh *neigh) {
+ nh.family = family
+ }
+}
+
+func (i *nwIface) processInterfaceOptions(options ...IfaceOption) {
+ for _, opt := range options {
+ if opt != nil {
+ opt(i)
+ }
+ }
+}
+
+func (n *networkNamespace) Bridge(isBridge bool) IfaceOption {
+ return func(i *nwIface) {
+ i.bridge = isBridge
+ }
+}
+
+func (n *networkNamespace) Master(name string) IfaceOption {
+ return func(i *nwIface) {
+ i.master = name
+ }
+}
+
+func (n *networkNamespace) MacAddress(mac net.HardwareAddr) IfaceOption {
+ return func(i *nwIface) {
+ i.mac = mac
+ }
+}
+
+func (n *networkNamespace) Address(addr *net.IPNet) IfaceOption {
+ return func(i *nwIface) {
+ i.address = addr
+ }
+}
+
+func (n *networkNamespace) AddressIPv6(addr *net.IPNet) IfaceOption {
+ return func(i *nwIface) {
+ i.addressIPv6 = addr
+ }
+}
+
+func (n *networkNamespace) LinkLocalAddresses(list []*net.IPNet) IfaceOption {
+ return func(i *nwIface) {
+ i.llAddrs = list
+ }
+}
+
+func (n *networkNamespace) Routes(routes []*net.IPNet) IfaceOption {
+ return func(i *nwIface) {
+ i.routes = routes
+ }
+}
diff --git a/vendor/github.com/docker/libnetwork/osl/route_linux.go b/vendor/github.com/docker/libnetwork/osl/route_linux.go
new file mode 100644
index 000000000..a9ff191b3
--- /dev/null
+++ b/vendor/github.com/docker/libnetwork/osl/route_linux.go
@@ -0,0 +1,203 @@
+package osl
+
+import (
+ "fmt"
+ "net"
+
+ "github.com/docker/libnetwork/types"
+ "github.com/vishvananda/netlink"
+)
+
+func (n *networkNamespace) Gateway() net.IP {
+ n.Lock()
+ defer n.Unlock()
+
+ return n.gw
+}
+
+func (n *networkNamespace) GatewayIPv6() net.IP {
+ n.Lock()
+ defer n.Unlock()
+
+ return n.gwv6
+}
+
+func (n *networkNamespace) StaticRoutes() []*types.StaticRoute {
+ n.Lock()
+ defer n.Unlock()
+
+ routes := make([]*types.StaticRoute, len(n.staticRoutes))
+ for i, route := range n.staticRoutes {
+ r := route.GetCopy()
+ routes[i] = r
+ }
+
+ return routes
+}
+
+func (n *networkNamespace) setGateway(gw net.IP) {
+ n.Lock()
+ n.gw = gw
+ n.Unlock()
+}
+
+func (n *networkNamespace) setGatewayIPv6(gwv6 net.IP) {
+ n.Lock()
+ n.gwv6 = gwv6
+ n.Unlock()
+}
+
+func (n *networkNamespace) SetGateway(gw net.IP) error {
+ // Silently return if the gateway is empty
+ if len(gw) == 0 {
+ return nil
+ }
+
+ err := n.programGateway(gw, true)
+ if err == nil {
+ n.setGateway(gw)
+ }
+
+ return err
+}
+
+func (n *networkNamespace) UnsetGateway() error {
+ gw := n.Gateway()
+
+ // Silently return if the gateway is empty
+ if len(gw) == 0 {
+ return nil
+ }
+
+ err := n.programGateway(gw, false)
+ if err == nil {
+ n.setGateway(net.IP{})
+ }
+
+ return err
+}
+
+func (n *networkNamespace) programGateway(gw net.IP, isAdd bool) error {
+ gwRoutes, err := n.nlHandle.RouteGet(gw)
+ if err != nil {
+ return fmt.Errorf("route for the gateway %s could not be found: %v", gw, err)
+ }
+
+ var linkIndex int
+ for _, gwRoute := range gwRoutes {
+ if gwRoute.Gw == nil {
+ linkIndex = gwRoute.LinkIndex
+ break
+ }
+ }
+
+ if linkIndex == 0 {
+ return fmt.Errorf("Direct route for the gateway %s could not be found", gw)
+ }
+
+ if isAdd {
+ return n.nlHandle.RouteAdd(&netlink.Route{
+ Scope: netlink.SCOPE_UNIVERSE,
+ LinkIndex: linkIndex,
+ Gw: gw,
+ })
+ }
+
+ return n.nlHandle.RouteDel(&netlink.Route{
+ Scope: netlink.SCOPE_UNIVERSE,
+ LinkIndex: linkIndex,
+ Gw: gw,
+ })
+}
+
+// Program a route in to the namespace routing table.
+func (n *networkNamespace) programRoute(path string, dest *net.IPNet, nh net.IP) error {
+ gwRoutes, err := n.nlHandle.RouteGet(nh)
+ if err != nil {
+ return fmt.Errorf("route for the next hop %s could not be found: %v", nh, err)
+ }
+
+ return n.nlHandle.RouteAdd(&netlink.Route{
+ Scope: netlink.SCOPE_UNIVERSE,
+ LinkIndex: gwRoutes[0].LinkIndex,
+ Gw: nh,
+ Dst: dest,
+ })
+}
+
+// Delete a route from the namespace routing table.
+func (n *networkNamespace) removeRoute(path string, dest *net.IPNet, nh net.IP) error {
+ gwRoutes, err := n.nlHandle.RouteGet(nh)
+ if err != nil {
+ return fmt.Errorf("route for the next hop could not be found: %v", err)
+ }
+
+ return n.nlHandle.RouteDel(&netlink.Route{
+ Scope: netlink.SCOPE_UNIVERSE,
+ LinkIndex: gwRoutes[0].LinkIndex,
+ Gw: nh,
+ Dst: dest,
+ })
+}
+
+func (n *networkNamespace) SetGatewayIPv6(gwv6 net.IP) error {
+ // Silently return if the gateway is empty
+ if len(gwv6) == 0 {
+ return nil
+ }
+
+ err := n.programGateway(gwv6, true)
+ if err == nil {
+ n.setGatewayIPv6(gwv6)
+ }
+
+ return err
+}
+
+func (n *networkNamespace) UnsetGatewayIPv6() error {
+ gwv6 := n.GatewayIPv6()
+
+ // Silently return if the gateway is empty
+ if len(gwv6) == 0 {
+ return nil
+ }
+
+ err := n.programGateway(gwv6, false)
+ if err == nil {
+ n.Lock()
+ n.gwv6 = net.IP{}
+ n.Unlock()
+ }
+
+ return err
+}
+
+func (n *networkNamespace) AddStaticRoute(r *types.StaticRoute) error {
+ err := n.programRoute(n.nsPath(), r.Destination, r.NextHop)
+ if err == nil {
+ n.Lock()
+ n.staticRoutes = append(n.staticRoutes, r)
+ n.Unlock()
+ }
+ return err
+}
+
+func (n *networkNamespace) RemoveStaticRoute(r *types.StaticRoute) error {
+
+ err := n.removeRoute(n.nsPath(), r.Destination, r.NextHop)
+ if err == nil {
+ n.Lock()
+ lastIndex := len(n.staticRoutes) - 1
+ for i, v := range n.staticRoutes {
+ if v == r {
+ // Overwrite the route we're removing with the last element
+ n.staticRoutes[i] = n.staticRoutes[lastIndex]
+ // Shorten the slice to trim the extra element
+ n.staticRoutes = n.staticRoutes[:lastIndex]
+ break
+ }
+ }
+ n.Unlock()
+ }
+ return err
+}
diff --git a/vendor/github.com/docker/libnetwork/osl/sandbox.go b/vendor/github.com/docker/libnetwork/osl/sandbox.go
new file mode 100644
index 000000000..5019e068d
--- /dev/null
+++ b/vendor/github.com/docker/libnetwork/osl/sandbox.go
@@ -0,0 +1,187 @@
+// Package osl describes structures and interfaces which abstract os entities
+package osl
+
+import (
+ "net"
+
+ "github.com/docker/libnetwork/types"
+)
+
+// SandboxType specify the time of the sandbox, this can be used to apply special configs
+type SandboxType int
+
+const (
+ // SandboxTypeIngress indicates that the sandbox is for the ingress
+ SandboxTypeIngress = iota
+ // SandboxTypeLoadBalancer indicates that the sandbox is a load balancer
+ SandboxTypeLoadBalancer = iota
+)
+
+// Sandbox represents a network sandbox, identified by a specific key. It
+// holds a list of Interfaces, routes etc, and more can be added dynamically.
+type Sandbox interface {
+ // The path where the network namespace is mounted.
+ Key() string
+
+ // Add an existing Interface to this sandbox. The operation will rename
+ // from the Interface SrcName to DstName as it moves, and reconfigure the
+ // interface according to the specified settings. The caller is expected
+ // to only provide a prefix for DstName. The AddInterface api will auto-generate
+ // an appropriate suffix for the DstName to disambiguate.
+ AddInterface(SrcName string, DstPrefix string, options ...IfaceOption) error
+
+ // Set default IPv4 gateway for the sandbox
+ SetGateway(gw net.IP) error
+
+ // Set default IPv6 gateway for the sandbox
+ SetGatewayIPv6(gw net.IP) error
+
+ // Unset the previously set default IPv4 gateway in the sandbox
+ UnsetGateway() error
+
+ // Unset the previously set default IPv6 gateway in the sandbox
+ UnsetGatewayIPv6() error
+
+ // GetLoopbackIfaceName returns the name of the loopback interface
+ GetLoopbackIfaceName() string
+
+ // AddAliasIP adds the passed IP address to the named interface
+ AddAliasIP(ifName string, ip *net.IPNet) error
+
+ // RemoveAliasIP removes the passed IP address from the named interface
+ RemoveAliasIP(ifName string, ip *net.IPNet) error
+
+ // Add a static route to the sandbox.
+ AddStaticRoute(*types.StaticRoute) error
+
+ // Remove a static route from the sandbox.
+ RemoveStaticRoute(*types.StaticRoute) error
+
+ // AddNeighbor adds a neighbor entry into the sandbox.
+ AddNeighbor(dstIP net.IP, dstMac net.HardwareAddr, force bool, option ...NeighOption) error
+
+ // DeleteNeighbor deletes neighbor entry from the sandbox.
+ DeleteNeighbor(dstIP net.IP, dstMac net.HardwareAddr, osDelete bool) error
+
+ // Returns an interface with methods to set neighbor options.
+ NeighborOptions() NeighborOptionSetter
+
+ // Returns an interface with methods to set interface options.
+ InterfaceOptions() IfaceOptionSetter
+
+ //Invoke
+ InvokeFunc(func()) error
+
+ // Returns an interface with methods to get sandbox state.
+ Info() Info
+
+ // Destroy the sandbox
+ Destroy() error
+
+ // restore sandbox
+ Restore(ifsopt map[string][]IfaceOption, routes []*types.StaticRoute, gw net.IP, gw6 net.IP) error
+
+ // ApplyOSTweaks applies operating system specific knobs on the sandbox
+ ApplyOSTweaks([]SandboxType)
+}
+
+// NeighborOptionSetter interface defines the option setter methods for interface options
+type NeighborOptionSetter interface {
+ // LinkName returns an option setter to set the srcName of the link that should
+ // be used in the neighbor entry
+ LinkName(string) NeighOption
+
+ // Family returns an option setter to set the address family for the neighbor
+ // entry. eg. AF_BRIDGE
+ Family(int) NeighOption
+}
+
+// IfaceOptionSetter interface defines the option setter methods for interface options.
+type IfaceOptionSetter interface {
+ // Bridge returns an option setter to set if the interface is a bridge.
+ Bridge(bool) IfaceOption
+
+ // MacAddress returns an option setter to set the MAC address.
+ MacAddress(net.HardwareAddr) IfaceOption
+
+ // Address returns an option setter to set IPv4 address.
+ Address(*net.IPNet) IfaceOption
+
+ // Address returns an option setter to set IPv6 address.
+ AddressIPv6(*net.IPNet) IfaceOption
+
+ // LinkLocalAddresses returns an option setter to set the link-local IP addresses.
+ LinkLocalAddresses([]*net.IPNet) IfaceOption
+
+ // Master returns an option setter to set the master interface if any for this
+ // interface. The master interface name should refer to the srcname of a
+ // previously added interface of type bridge.
+ Master(string) IfaceOption
+
+ // Address returns an option setter to set interface routes.
+ Routes([]*net.IPNet) IfaceOption
+}
+
+// Info represents all possible information that
+// the driver wants to place in the sandbox which includes
+// interfaces, routes and gateway
+type Info interface {
+ // The collection of Interface previously added with the AddInterface
+ // method. Note that this doesn't include network interfaces added in any
+ // other way (such as the default loopback interface which is automatically
+ // created on creation of a sandbox).
+ Interfaces() []Interface
+
+ // IPv4 gateway for the sandbox.
+ Gateway() net.IP
+
+ // IPv6 gateway for the sandbox.
+ GatewayIPv6() net.IP
+
+ // Additional static routes for the sandbox. (Note that directly
+ // connected routes are stored on the particular interface they refer to.)
+ StaticRoutes() []*types.StaticRoute
+
+ // TODO: Add ip tables etc.
+}
+
+// Interface represents the settings and identity of a network device. It is
+// used as a return type for Network.Link, and it is common practice for the
+// caller to use this information when moving interface SrcName from host
+// namespace to DstName in a different net namespace with the appropriate
+// network settings.
+type Interface interface {
+ // The name of the interface in the origin network namespace.
+ SrcName() string
+
+ // The name that will be assigned to the interface once moves inside a
+ // network namespace. When the caller passes in a DstName, it is only
+ // expected to pass a prefix. The name will modified with an appropriately
+ // auto-generated suffix.
+ DstName() string
+
+ // IPv4 address for the interface.
+ Address() *net.IPNet
+
+ // IPv6 address for the interface.
+ AddressIPv6() *net.IPNet
+
+ // LinkLocalAddresses returns the link-local IP addresses assigned to the interface.
+ LinkLocalAddresses() []*net.IPNet
+
+ // IP routes for the interface.
+ Routes() []*net.IPNet
+
+ // Bridge returns true if the interface is a bridge
+ Bridge() bool
+
+ // Master returns the srcname of the master interface for this interface.
+ Master() string
+
+ // Remove an interface from the sandbox by renaming to original name
+ // and moving it out of the sandbox.
+ Remove() error
+
+ // Statistics returns the statistics for this interface
+ Statistics() (*types.InterfaceStatistics, error)
+}
diff --git a/vendor/github.com/docker/libnetwork/osl/sandbox_freebsd.go b/vendor/github.com/docker/libnetwork/osl/sandbox_freebsd.go
new file mode 100644
index 000000000..e5bc6278e
--- /dev/null
+++ b/vendor/github.com/docker/libnetwork/osl/sandbox_freebsd.go
@@ -0,0 +1,44 @@
+package osl
+
+import "testing"
+
+// GenerateKey generates a sandbox key based on the passed
+// container id.
+func GenerateKey(containerID string) string {
+ maxLen := 12
+ if len(containerID) < maxLen {
+ maxLen = len(containerID)
+ }
+
+ return containerID[:maxLen]
+}
+
+// NewSandbox provides a new sandbox instance created in an os specific way
+// provided a key which uniquely identifies the sandbox
+func NewSandbox(key string, osCreate, isRestore bool) (Sandbox, error) {
+ return nil, nil
+}
+
+// GetSandboxForExternalKey returns sandbox object for the supplied path
+func GetSandboxForExternalKey(path string, key string) (Sandbox, error) {
+ return nil, nil
+}
+
+// GC triggers garbage collection of namespace path right away
+// and waits for it.
+func GC() {
+}
+
+// InitOSContext initializes OS context while configuring network resources
+func InitOSContext() func() {
+ return func() {}
+}
+
+// SetupTestOSContext sets up a separate test OS context in which tests will be executed.
+func SetupTestOSContext(t *testing.T) func() {
+ return func() {}
+}
+
+// SetBasePath sets the base url prefix for the ns path
+func SetBasePath(path string) {
+}
diff --git a/vendor/github.com/docker/libnetwork/osl/sandbox_unsupported.go b/vendor/github.com/docker/libnetwork/osl/sandbox_unsupported.go
new file mode 100644
index 000000000..51a656c80
--- /dev/null
+++ b/vendor/github.com/docker/libnetwork/osl/sandbox_unsupported.go
@@ -0,0 +1,22 @@
+// +build !linux,!windows,!freebsd
+
+package osl
+
+import "errors"
+
+var (
+ // ErrNotImplemented is for platforms which don't implement sandbox
+ ErrNotImplemented = errors.New("not implemented")
+)
+
+// NewSandbox provides a new sandbox instance created in an os specific way
+// provided a key which uniquely identifies the sandbox
+func NewSandbox(key string, osCreate, isRestore bool) (Sandbox, error) {
+ return nil, ErrNotImplemented
+}
+
+// GenerateKey generates a sandbox key based on the passed
+// container id.
+func GenerateKey(containerID string) string {
+ return ""
+}