diff options
Diffstat (limited to 'vendor/github.com/docker/libnetwork/osl')
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 "" +} |