Update libnetwork vendor to current master to fix CI

Avoid a Sirupsen vs sirupsen class for logrus by updating to
master.

Signed-off-by: Matthew Heon <matthew.heon@gmail.com>
This commit is contained in:
Matthew Heon
2018-09-25 12:41:40 -04:00
parent e4ded6ce7f
commit bc5fac3a7c
30 changed files with 2552 additions and 58 deletions

View File

@ -97,4 +97,4 @@ github.com/openshift/imagebuilder master
github.com/ulikunitz/xz v0.5.4
github.com/mailru/easyjson 03f2033d19d5860aef995fe360ac7d395cd8ce65
github.com/coreos/go-iptables 25d087f3cffd9aedc0c2b7eff25f23cbf3c20fe1
github.com/docker/libnetwork v0.7.2-rc.1
github.com/docker/libnetwork 20461b8539336a4b5fcf551a86dd24ebae211984

View File

@ -1,6 +1,6 @@
# libnetwork - networking for containers
[![Circle CI](https://circleci.com/gh/docker/libnetwork/tree/master.svg?style=svg)](https://circleci.com/gh/docker/libnetwork/tree/master) [![Coverage Status](https://coveralls.io/repos/docker/libnetwork/badge.svg)](https://coveralls.io/r/docker/libnetwork) [![GoDoc](https://godoc.org/github.com/docker/libnetwork?status.svg)](https://godoc.org/github.com/docker/libnetwork)
[![Circle CI](https://circleci.com/gh/docker/libnetwork/tree/master.svg?style=svg)](https://circleci.com/gh/docker/libnetwork/tree/master) [![Coverage Status](https://coveralls.io/repos/docker/libnetwork/badge.svg)](https://coveralls.io/r/docker/libnetwork) [![GoDoc](https://godoc.org/github.com/docker/libnetwork?status.svg)](https://godoc.org/github.com/docker/libnetwork) [![Go Report Card](https://goreportcard.com/badge/github.com/docker/libnetwork)](https://goreportcard.com/report/github.com/docker/libnetwork)
Libnetwork provides a native Go implementation for connecting containers
@ -15,6 +15,17 @@ There are many networking solutions available to suit a broad range of use-cases
```go
import (
"fmt"
"log"
"github.com/docker/docker/pkg/reexec"
"github.com/docker/libnetwork"
"github.com/docker/libnetwork/config"
"github.com/docker/libnetwork/netlabel"
"github.com/docker/libnetwork/options"
)
func main() {
if reexec.Init() {
return
@ -34,7 +45,7 @@ func main() {
// Create a network for containers to join.
// NewNetwork accepts Variadic optional arguments that libnetwork and Drivers can use.
network, err := controller.NewNetwork(networkType, "network1")
network, err := controller.NewNetwork(networkType, "network1", "")
if err != nil {
log.Fatalf("controller.NewNetwork: %s", err)
}

135
vendor/github.com/docker/libnetwork/ipamutils/utils.go generated vendored Normal file
View File

@ -0,0 +1,135 @@
// Package ipamutils provides utility functions for ipam management
package ipamutils
import (
"fmt"
"net"
"sync"
)
var (
// PredefinedLocalScopeDefaultNetworks contains a list of 31 IPv4 private networks with host size 16 and 12
// (172.17-31.x.x/16, 192.168.x.x/20) which do not overlap with the networks in `PredefinedGlobalScopeDefaultNetworks`
PredefinedLocalScopeDefaultNetworks []*net.IPNet
// PredefinedGlobalScopeDefaultNetworks contains a list of 64K IPv4 private networks with host size 8
// (10.x.x.x/24) which do not overlap with the networks in `PredefinedLocalScopeDefaultNetworks`
PredefinedGlobalScopeDefaultNetworks []*net.IPNet
mutex sync.Mutex
localScopeDefaultNetworks = []*NetworkToSplit{{"172.17.0.0/16", 16}, {"172.18.0.0/16", 16}, {"172.19.0.0/16", 16},
{"172.20.0.0/14", 16}, {"172.24.0.0/14", 16}, {"172.28.0.0/14", 16},
{"192.168.0.0/16", 20}}
globalScopeDefaultNetworks = []*NetworkToSplit{{"10.0.0.0/8", 24}}
)
// NetworkToSplit represent a network that has to be split in chunks with mask length Size.
// Each subnet in the set is derived from the Base pool. Base is to be passed
// in CIDR format.
// Example: a Base "10.10.0.0/16 with Size 24 will define the set of 256
// 10.10.[0-255].0/24 address pools
type NetworkToSplit struct {
Base string `json:"base"`
Size int `json:"size"`
}
func init() {
var err error
if PredefinedGlobalScopeDefaultNetworks, err = splitNetworks(globalScopeDefaultNetworks); err != nil {
//we are going to panic in case of error as we should never get into this state
panic("InitAddressPools failed to initialize the global scope default address pool")
}
if PredefinedLocalScopeDefaultNetworks, err = splitNetworks(localScopeDefaultNetworks); err != nil {
//we are going to panic in case of error as we should never get into this state
panic("InitAddressPools failed to initialize the local scope default address pool")
}
}
// configDefaultNetworks configures local as well global default pool based on input
func configDefaultNetworks(defaultAddressPool []*NetworkToSplit, result *[]*net.IPNet) error {
mutex.Lock()
defer mutex.Unlock()
defaultNetworks, err := splitNetworks(defaultAddressPool)
if err != nil {
return err
}
*result = defaultNetworks
return nil
}
// GetGlobalScopeDefaultNetworks returns PredefinedGlobalScopeDefaultNetworks
func GetGlobalScopeDefaultNetworks() []*net.IPNet {
mutex.Lock()
defer mutex.Unlock()
return PredefinedGlobalScopeDefaultNetworks
}
// GetLocalScopeDefaultNetworks returns PredefinedLocalScopeDefaultNetworks
func GetLocalScopeDefaultNetworks() []*net.IPNet {
mutex.Lock()
defer mutex.Unlock()
return PredefinedLocalScopeDefaultNetworks
}
// ConfigGlobalScopeDefaultNetworks configures global default pool.
// Ideally this will be called from SwarmKit as part of swarm init
func ConfigGlobalScopeDefaultNetworks(defaultAddressPool []*NetworkToSplit) error {
if defaultAddressPool == nil {
defaultAddressPool = globalScopeDefaultNetworks
}
return configDefaultNetworks(defaultAddressPool, &PredefinedGlobalScopeDefaultNetworks)
}
// ConfigLocalScopeDefaultNetworks configures local default pool.
// Ideally this will be called during libnetwork init
func ConfigLocalScopeDefaultNetworks(defaultAddressPool []*NetworkToSplit) error {
if defaultAddressPool == nil {
return nil
}
return configDefaultNetworks(defaultAddressPool, &PredefinedLocalScopeDefaultNetworks)
}
// splitNetworks takes a slice of networks, split them accordingly and returns them
func splitNetworks(list []*NetworkToSplit) ([]*net.IPNet, error) {
localPools := make([]*net.IPNet, 0, len(list))
for _, p := range list {
_, b, err := net.ParseCIDR(p.Base)
if err != nil {
return nil, fmt.Errorf("invalid base pool %q: %v", p.Base, err)
}
ones, _ := b.Mask.Size()
if p.Size <= 0 || p.Size < ones {
return nil, fmt.Errorf("invalid pools size: %d", p.Size)
}
localPools = append(localPools, splitNetwork(p.Size, b)...)
}
return localPools, nil
}
func splitNetwork(size int, base *net.IPNet) []*net.IPNet {
one, bits := base.Mask.Size()
mask := net.CIDRMask(size, bits)
n := 1 << uint(size-one)
s := uint(bits - size)
list := make([]*net.IPNet, 0, n)
for i := 0; i < n; i++ {
ip := copyIP(base.IP)
addIntToIP(ip, uint(i<<s))
list = append(list, &net.IPNet{IP: ip, Mask: mask})
}
return list
}
func copyIP(from net.IP) net.IP {
ip := make([]byte, len(from))
copy(ip, from)
return ip
}
func addIntToIP(array net.IP, ordinal uint) {
for i := len(array) - 1; i >= 0; i-- {
array[i] |= (byte)(ordinal & 0xff)
ordinal >>= 8
}
}

View File

@ -14,13 +14,6 @@ import (
"github.com/docker/libnetwork/types"
)
// constants for the IP address type
const (
IP = iota // IPv4 and IPv6
IPv4
IPv6
)
var (
// ErrNetworkOverlapsWithNameservers preformatted error
ErrNetworkOverlapsWithNameservers = errors.New("requested network overlaps with nameserver")
@ -177,10 +170,10 @@ func ReverseIP(IP string) string {
return strings.Join(reverseIP, ".")
}
// ParseAlias parses and validates the specified string as a alias format (name:alias)
// ParseAlias parses and validates the specified string as an alias format (name:alias)
func ParseAlias(val string) (string, string, error) {
if val == "" {
return "", "", fmt.Errorf("empty string specified for alias")
return "", "", errors.New("empty string specified for alias")
}
arr := strings.Split(val, ":")
if len(arr) > 2 {

View File

@ -0,0 +1,23 @@
package netutils
import (
"net"
"github.com/docker/libnetwork/types"
)
// ElectInterfaceAddresses looks for an interface on the OS with the specified name
// and returns returns all its IPv4 and IPv6 addresses in CIDR notation.
// If a failure in retrieving the addresses or no IPv4 address is found, an error is returned.
// If the interface does not exist, it chooses from a predefined
// list the first IPv4 address which does not conflict with other
// interfaces on the system.
func ElectInterfaceAddresses(name string) ([]*net.IPNet, []*net.IPNet, error) {
return nil, nil, types.NotImplementedErrorf("not supported on freebsd")
}
// FindAvailableNetwork returns a network from the passed list which does not
// overlap with existing interfaces in the system
func FindAvailableNetwork(list []*net.IPNet) (*net.IPNet, error) {
return nil, types.NotImplementedErrorf("not supported on freebsd")
}

View File

@ -4,24 +4,31 @@
package netutils
import (
"fmt"
"net"
"strings"
"github.com/docker/libnetwork/ipamutils"
"github.com/docker/libnetwork/ns"
"github.com/docker/libnetwork/osl"
"github.com/docker/libnetwork/resolvconf"
"github.com/docker/libnetwork/types"
"github.com/vishvananda/netlink"
)
var (
networkGetRoutesFct = netlink.RouteList
networkGetRoutesFct func(netlink.Link, int) ([]netlink.Route, error)
)
// CheckRouteOverlaps checks whether the passed network overlaps with any existing routes
func CheckRouteOverlaps(toCheck *net.IPNet) error {
if networkGetRoutesFct == nil {
networkGetRoutesFct = ns.NlHandle().RouteList
}
networks, err := networkGetRoutesFct(nil, netlink.FAMILY_V4)
if err != nil {
return err
}
for _, network := range networks {
if network.Dst != nil && NetworkOverlaps(toCheck, network.Dst) {
return ErrNetworkOverlaps
@ -33,13 +40,18 @@ func CheckRouteOverlaps(toCheck *net.IPNet) error {
// GenerateIfaceName returns an interface name using the passed in
// prefix and the length of random bytes. The api ensures that the
// there are is no interface which exists with that name.
func GenerateIfaceName(prefix string, len int) (string, error) {
func GenerateIfaceName(nlh *netlink.Handle, prefix string, len int) (string, error) {
linkByName := netlink.LinkByName
if nlh != nil {
linkByName = nlh.LinkByName
}
for i := 0; i < 3; i++ {
name, err := GenerateRandomName(prefix, len)
if err != nil {
continue
}
if _, err := netlink.LinkByName(name); err != nil {
_, err = linkByName(name)
if err != nil {
if strings.Contains(err.Error(), "not found") {
return name, nil
}
@ -48,3 +60,67 @@ func GenerateIfaceName(prefix string, len int) (string, error) {
}
return "", types.InternalErrorf("could not generate interface name")
}
// ElectInterfaceAddresses looks for an interface on the OS with the
// specified name and returns returns all its IPv4 and IPv6 addresses in CIDR notation.
// If a failure in retrieving the addresses or no IPv4 address is found, an error is returned.
// If the interface does not exist, it chooses from a predefined
// list the first IPv4 address which does not conflict with other
// interfaces on the system.
func ElectInterfaceAddresses(name string) ([]*net.IPNet, []*net.IPNet, error) {
var (
v4Nets []*net.IPNet
v6Nets []*net.IPNet
)
defer osl.InitOSContext()()
link, _ := ns.NlHandle().LinkByName(name)
if link != nil {
v4addr, err := ns.NlHandle().AddrList(link, netlink.FAMILY_V4)
if err != nil {
return nil, nil, err
}
v6addr, err := ns.NlHandle().AddrList(link, netlink.FAMILY_V6)
if err != nil {
return nil, nil, err
}
for _, nlAddr := range v4addr {
v4Nets = append(v4Nets, nlAddr.IPNet)
}
for _, nlAddr := range v6addr {
v6Nets = append(v6Nets, nlAddr.IPNet)
}
}
if link == nil || len(v4Nets) == 0 {
// Choose from predefined local scope networks
v4Net, err := FindAvailableNetwork(ipamutils.PredefinedLocalScopeDefaultNetworks)
if err != nil {
return nil, nil, err
}
v4Nets = append(v4Nets, v4Net)
}
return v4Nets, v6Nets, nil
}
// FindAvailableNetwork returns a network from the passed list which does not
// overlap with existing interfaces in the system
func FindAvailableNetwork(list []*net.IPNet) (*net.IPNet, error) {
// We don't check for an error here, because we don't really care if we
// can't read /etc/resolv.conf. So instead we skip the append if resolvConf
// is nil. It either doesn't exist, or we can't read it for some reason.
var nameservers []string
if rc, err := resolvconf.Get(); err == nil {
nameservers = resolvconf.GetNameserversAsCIDR(rc.Content)
}
for _, nw := range list {
if err := CheckNameserverOverlaps(nameservers, nw); err == nil {
if err := CheckRouteOverlaps(nw); err == nil {
return nw, nil
}
}
}
return nil, fmt.Errorf("no available network")
}

View File

@ -0,0 +1,25 @@
package netutils
import (
"net"
"github.com/docker/libnetwork/types"
)
// ElectInterfaceAddresses looks for an interface on the OS with the specified name
// and returns returns all its IPv4 and IPv6 addresses in CIDR notation.
// If a failure in retrieving the addresses or no IPv4 address is found, an error is returned.
// If the interface does not exist, it chooses from a predefined
// list the first IPv4 address which does not conflict with other
// interfaces on the system.
func ElectInterfaceAddresses(name string) ([]*net.IPNet, []*net.IPNet, error) {
return nil, nil, types.NotImplementedErrorf("not supported on windows")
}
// FindAvailableNetwork returns a network from the passed list which does not
// overlap with existing interfaces in the system
// TODO : Use appropriate windows APIs to identify non-overlapping subnets
func FindAvailableNetwork(list []*net.IPNet) (*net.IPNet, error) {
return nil, nil
}

140
vendor/github.com/docker/libnetwork/ns/init_linux.go generated vendored Normal file
View File

@ -0,0 +1,140 @@
package ns
import (
"fmt"
"os"
"os/exec"
"strings"
"sync"
"syscall"
"time"
"github.com/sirupsen/logrus"
"github.com/vishvananda/netlink"
"github.com/vishvananda/netns"
)
var (
initNs netns.NsHandle
initNl *netlink.Handle
initOnce sync.Once
// NetlinkSocketsTimeout represents the default timeout duration for the sockets
NetlinkSocketsTimeout = 3 * time.Second
)
// Init initializes a new network namespace
func Init() {
var err error
initNs, err = netns.Get()
if err != nil {
logrus.Errorf("could not get initial namespace: %v", err)
}
initNl, err = netlink.NewHandle(getSupportedNlFamilies()...)
if err != nil {
logrus.Errorf("could not create netlink handle on initial namespace: %v", err)
}
err = initNl.SetSocketTimeout(NetlinkSocketsTimeout)
if err != nil {
logrus.Warnf("Failed to set the timeout on the default netlink handle sockets: %v", err)
}
}
// SetNamespace sets the initial namespace handler
func SetNamespace() error {
initOnce.Do(Init)
if err := netns.Set(initNs); err != nil {
linkInfo, linkErr := getLink()
if linkErr != nil {
linkInfo = linkErr.Error()
}
return fmt.Errorf("failed to set to initial namespace, %v, initns fd %d: %v", linkInfo, initNs, err)
}
return nil
}
// ParseHandlerInt transforms the namespace handler into an integer
func ParseHandlerInt() int {
return int(getHandler())
}
// GetHandler returns the namespace handler
func getHandler() netns.NsHandle {
initOnce.Do(Init)
return initNs
}
func getLink() (string, error) {
return os.Readlink(fmt.Sprintf("/proc/%d/task/%d/ns/net", os.Getpid(), syscall.Gettid()))
}
// NlHandle returns the netlink handler
func NlHandle() *netlink.Handle {
initOnce.Do(Init)
return initNl
}
func getSupportedNlFamilies() []int {
fams := []int{syscall.NETLINK_ROUTE}
// NETLINK_XFRM test
if err := loadXfrmModules(); err != nil {
if checkXfrmSocket() != nil {
logrus.Warnf("Could not load necessary modules for IPSEC rules: %v", err)
} else {
fams = append(fams, syscall.NETLINK_XFRM)
}
} else {
fams = append(fams, syscall.NETLINK_XFRM)
}
// NETLINK_NETFILTER test
if err := loadNfConntrackModules(); err != nil {
if checkNfSocket() != nil {
logrus.Warnf("Could not load necessary modules for Conntrack: %v", err)
} else {
fams = append(fams, syscall.NETLINK_NETFILTER)
}
} else {
fams = append(fams, syscall.NETLINK_NETFILTER)
}
return fams
}
func loadXfrmModules() error {
if out, err := exec.Command("modprobe", "-va", "xfrm_user").CombinedOutput(); err != nil {
return fmt.Errorf("Running modprobe xfrm_user failed with message: `%s`, error: %v", strings.TrimSpace(string(out)), err)
}
if out, err := exec.Command("modprobe", "-va", "xfrm_algo").CombinedOutput(); err != nil {
return fmt.Errorf("Running modprobe xfrm_algo failed with message: `%s`, error: %v", strings.TrimSpace(string(out)), err)
}
return nil
}
// API check on required xfrm modules (xfrm_user, xfrm_algo)
func checkXfrmSocket() error {
fd, err := syscall.Socket(syscall.AF_NETLINK, syscall.SOCK_RAW, syscall.NETLINK_XFRM)
if err != nil {
return err
}
syscall.Close(fd)
return nil
}
func loadNfConntrackModules() error {
if out, err := exec.Command("modprobe", "-va", "nf_conntrack").CombinedOutput(); err != nil {
return fmt.Errorf("Running modprobe nf_conntrack failed with message: `%s`, error: %v", strings.TrimSpace(string(out)), err)
}
if out, err := exec.Command("modprobe", "-va", "nf_conntrack_netlink").CombinedOutput(); err != nil {
return fmt.Errorf("Running modprobe nf_conntrack_netlink failed with message: `%s`, error: %v", strings.TrimSpace(string(out)), err)
}
return nil
}
// API check on required nf_conntrack* modules (nf_conntrack, nf_conntrack_netlink)
func checkNfSocket() error {
fd, err := syscall.Socket(syscall.AF_NETLINK, syscall.SOCK_RAW, syscall.NETLINK_NETFILTER)
if err != nil {
return err
}
syscall.Close(fd)
return nil
}

View File

@ -0,0 +1,3 @@
package ns
// File is present so that go build ./... is closer to working on Windows from repo root.

View File

@ -0,0 +1,4 @@
package osl
// IfaceOption is a function option type to set interface options
type IfaceOption func()

View File

@ -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
}

View File

@ -0,0 +1,4 @@
package osl
// IfaceOption is a function option type to set interface options
type IfaceOption func()

View File

@ -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
}

View File

@ -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)
}
}
}

View File

@ -0,0 +1,7 @@
// +build !linux
package kernel
// ApplyOSTweaks applies the configuration values passed as arguments
func ApplyOSTweaks(osConfig map[string]*OSValue) {
}

View File

@ -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)
}
}
}

View File

@ -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) {
}

View File

@ -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) {
}

View File

@ -0,0 +1,4 @@
package osl
// NeighOption is a function option type to set neighbor options
type NeighOption func()

194
vendor/github.com/docker/libnetwork/osl/neigh_linux.go generated vendored Normal file
View File

@ -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
}

View File

@ -0,0 +1,4 @@
package osl
// NeighOption is a function option type to set neighbor options
type NeighOption func()

View File

@ -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
}
}

203
vendor/github.com/docker/libnetwork/osl/route_linux.go generated vendored Normal file
View File

@ -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
}

187
vendor/github.com/docker/libnetwork/osl/sandbox.go generated vendored Normal file
View File

@ -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)
}

View File

@ -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) {
}

View File

@ -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 ""
}

View File

@ -4,10 +4,14 @@ import (
"regexp"
)
// IPLocalhost is a regex patter for localhost IP address range.
// IPLocalhost is a regex pattern for IPv4 or IPv6 loopback range.
const IPLocalhost = `((127\.([0-9]{1,3}\.){2}[0-9]{1,3})|(::1)$)`
// IPv4Localhost is a regex pattern for IPv4 localhost address range.
const IPv4Localhost = `(127\.([0-9]{1,3}\.){2}[0-9]{1,3})`
var localhostIPRegexp = regexp.MustCompile(IPLocalhost)
var localhostIPv4Regexp = regexp.MustCompile(IPv4Localhost)
// IsLocalhost returns true if ip matches the localhost IP regular expression.
// Used for determining if nameserver settings are being passed which are
@ -15,3 +19,8 @@ var localhostIPRegexp = regexp.MustCompile(IPLocalhost)
func IsLocalhost(ip string) bool {
return localhostIPRegexp.MatchString(ip)
}
// IsIPv4Localhost returns true if ip matches the IPv4 localhost regular expression.
func IsIPv4Localhost(ip string) bool {
return localhostIPv4Regexp.MatchString(ip)
}

View File

@ -8,10 +8,15 @@ import (
"strings"
"sync"
"github.com/Sirupsen/logrus"
"github.com/docker/docker/pkg/ioutils"
"github.com/docker/libnetwork/netutils"
"github.com/docker/libnetwork/resolvconf/dns"
"github.com/docker/libnetwork/types"
"github.com/sirupsen/logrus"
)
const (
// DefaultResolvConf points to the default file used for dns configuration on a linux machine
DefaultResolvConf = "/etc/resolv.conf"
)
var (
@ -25,7 +30,7 @@ var (
// -- e.g. other link-local types -- either won't work in containers or are unnecessary.
// For readability and sufficiency for Docker purposes this seemed more reasonable than a
// 1000+ character regexp with exact and complete IPv6 validation
ipv6Address = `([0-9A-Fa-f]{0,4}:){2,7}([0-9A-Fa-f]{0,4})`
ipv6Address = `([0-9A-Fa-f]{0,4}:){2,7}([0-9A-Fa-f]{0,4})(%\w+)?`
localhostNSRegexp = regexp.MustCompile(`(?m)^nameserver\s+` + dns.IPLocalhost + `\s*\n*`)
nsIPv6Regexp = regexp.MustCompile(`(?m)^nameserver\s+` + ipv6Address + `\s*\n*`)
@ -50,15 +55,7 @@ type File struct {
// Get returns the contents of /etc/resolv.conf and its hash
func Get() (*File, error) {
resolv, err := ioutil.ReadFile("/etc/resolv.conf")
if err != nil {
return nil, err
}
hash, err := ioutils.HashData(bytes.NewReader(resolv))
if err != nil {
return nil, err
}
return &File{Content: resolv, Hash: hash}, nil
return GetSpecific(DefaultResolvConf)
}
// GetSpecific returns the contents of the user specified resolv.conf file and its hash
@ -122,11 +119,11 @@ func FilterResolvDNS(resolvConf []byte, ipv6Enabled bool) (*File, error) {
}
// if the resulting resolvConf has no more nameservers defined, add appropriate
// default DNS servers for IPv4 and (optionally) IPv6
if len(GetNameservers(cleanedResolvConf, netutils.IP)) == 0 {
logrus.Infof("No non-localhost DNS nameservers are left in resolv.conf. Using default external servers : %v", defaultIPv4Dns)
if len(GetNameservers(cleanedResolvConf, types.IP)) == 0 {
logrus.Infof("No non-localhost DNS nameservers are left in resolv.conf. Using default external servers: %v", defaultIPv4Dns)
dns := defaultIPv4Dns
if ipv6Enabled {
logrus.Infof("IPv6 enabled; Adding default IPv6 external servers : %v", defaultIPv6Dns)
logrus.Infof("IPv6 enabled; Adding default IPv6 external servers: %v", defaultIPv6Dns)
dns = append(dns, defaultIPv6Dns...)
}
cleanedResolvConf = append(cleanedResolvConf, []byte("\n"+strings.Join(dns, "\n"))...)
@ -158,11 +155,11 @@ func GetNameservers(resolvConf []byte, kind int) []string {
nameservers := []string{}
for _, line := range getLines(resolvConf, []byte("#")) {
var ns [][]byte
if kind == netutils.IP {
if kind == types.IP {
ns = nsRegexp.FindSubmatch(line)
} else if kind == netutils.IPv4 {
} else if kind == types.IPv4 {
ns = nsIPv4Regexpmatch.FindSubmatch(line)
} else if kind == netutils.IPv6 {
} else if kind == types.IPv6 {
ns = nsIPv6Regexpmatch.FindSubmatch(line)
}
if len(ns) > 0 {
@ -177,8 +174,15 @@ func GetNameservers(resolvConf []byte, kind int) []string {
// This function's output is intended for net.ParseCIDR
func GetNameserversAsCIDR(resolvConf []byte) []string {
nameservers := []string{}
for _, nameserver := range GetNameservers(resolvConf, netutils.IP) {
nameservers = append(nameservers, nameserver+"/32")
for _, nameserver := range GetNameservers(resolvConf, types.IP) {
var address string
// If IPv6, strip zone if present
if strings.Contains(nameserver, ":") {
address = strings.Split(nameserver, "%")[0] + "/128"
} else {
address = nameserver + "/32"
}
nameservers = append(nameservers, address)
}
return nameservers
}

View File

@ -7,12 +7,35 @@ import (
"net"
"strconv"
"strings"
"github.com/ishidawataru/sctp"
)
// constants for the IP address type
const (
IP = iota // IPv4 and IPv6
IPv4
IPv6
)
// EncryptionKey is the libnetwork representation of the key distributed by the lead
// manager.
type EncryptionKey struct {
Subsystem string
Algorithm int32
Key []byte
LamportTime uint64
}
// UUID represents a globally unique ID of various resources like network and endpoint
type UUID string
// TransportPort represent a local Layer 4 endpoint
// QosPolicy represents a quality of service policy on an endpoint
type QosPolicy struct {
MaxEgressBandwidth uint64
}
// TransportPort represents a local Layer 4 endpoint
type TransportPort struct {
Proto Protocol
Port uint16
@ -58,7 +81,7 @@ func (t *TransportPort) FromString(s string) error {
return BadRequestErrorf("invalid format for transport port: %s", s)
}
// PortBinding represent a port binding between the container and the host
// PortBinding represents a port binding between the container and the host
type PortBinding struct {
Proto Protocol
IP net.IP
@ -75,6 +98,8 @@ func (p PortBinding) HostAddr() (net.Addr, error) {
return &net.UDPAddr{IP: p.HostIP, Port: int(p.HostPort)}, nil
case TCP:
return &net.TCPAddr{IP: p.HostIP, Port: int(p.HostPort)}, nil
case SCTP:
return &sctp.SCTPAddr{IP: []net.IP{p.HostIP}, Port: int(p.HostPort)}, nil
default:
return nil, ErrInvalidProtocolBinding(p.Proto.String())
}
@ -87,6 +112,8 @@ func (p PortBinding) ContainerAddr() (net.Addr, error) {
return &net.UDPAddr{IP: p.IP, Port: int(p.Port)}, nil
case TCP:
return &net.TCPAddr{IP: p.IP, Port: int(p.Port)}, nil
case SCTP:
return &sctp.SCTPAddr{IP: []net.IP{p.IP}, Port: int(p.Port)}, nil
default:
return nil, ErrInvalidProtocolBinding(p.Proto.String())
}
@ -104,21 +131,26 @@ func (p *PortBinding) GetCopy() PortBinding {
}
}
// String return the PortBinding structure in string form
// String returns the PortBinding structure in string form
func (p *PortBinding) String() string {
ret := fmt.Sprintf("%s/", p.Proto)
if p.IP != nil {
ret = fmt.Sprintf("%s%s", ret, p.IP.String())
ret += p.IP.String()
}
ret = fmt.Sprintf("%s:%d/", ret, p.Port)
if p.HostIP != nil {
ret = fmt.Sprintf("%s%s", ret, p.HostIP.String())
ret += p.HostIP.String()
}
ret = fmt.Sprintf("%s:%d", ret, p.HostPort)
return ret
}
// FromString reads the TransportPort structure from string
// FromString reads the PortBinding structure from string s.
// String s is a triple of "protocol/containerIP:port/hostIP:port"
// containerIP and hostIP can be in dotted decimal ("192.0.2.1") or IPv6 ("2001:db8::68") form.
// Zoned addresses ("169.254.0.23%eth0" or "fe80::1ff:fe23:4567:890a%eth0") are not supported.
// If string s is incorrectly formatted or the IP addresses or ports cannot be parsed, FromString
// returns an error.
func (p *PortBinding) FromString(s string) error {
ps := strings.Split(s, "/")
if len(ps) != 3 {
@ -140,21 +172,19 @@ func (p *PortBinding) FromString(s string) error {
}
func parseIPPort(s string) (net.IP, uint16, error) {
pp := strings.Split(s, ":")
if len(pp) != 2 {
return nil, 0, BadRequestErrorf("invalid format: %s", s)
}
var ip net.IP
if pp[0] != "" {
if ip = net.ParseIP(pp[0]); ip == nil {
return nil, 0, BadRequestErrorf("invalid ip: %s", pp[0])
}
}
port, err := strconv.ParseUint(pp[1], 10, 16)
hoststr, portstr, err := net.SplitHostPort(s)
if err != nil {
return nil, 0, BadRequestErrorf("invalid port: %s", pp[1])
return nil, 0, err
}
ip := net.ParseIP(hoststr)
if ip == nil {
return nil, 0, BadRequestErrorf("invalid ip: %s", hoststr)
}
port, err := strconv.ParseUint(portstr, 10, 16)
if err != nil {
return nil, 0, BadRequestErrorf("invalid port: %s", portstr)
}
return ip, uint16(port), nil
@ -212,9 +242,11 @@ const (
TCP = 6
// UDP is for the UDP ip protocol
UDP = 17
// SCTP is for the SCTP ip protocol
SCTP = 132
)
// Protocol represents a IP protocol number
// Protocol represents an IP protocol number
type Protocol uint8
func (p Protocol) String() string {
@ -225,6 +257,8 @@ func (p Protocol) String() string {
return "tcp"
case UDP:
return "udp"
case SCTP:
return "sctp"
default:
return fmt.Sprintf("%d", p)
}
@ -239,6 +273,8 @@ func ParseProtocol(s string) Protocol {
return UDP
case "tcp":
return TCP
case "sctp":
return SCTP
default:
return 0
}
@ -296,6 +332,8 @@ func CompareIPNet(a, b *net.IPNet) bool {
}
// GetMinimalIP returns the address in its shortest form
// If ip contains an IPv4-mapped IPv6 address, the 4-octet form of the IPv4 address will be returned.
// Otherwise ip is returned unchanged.
func GetMinimalIP(ip net.IP) net.IP {
if ip != nil && ip.To4() != nil {
return ip.To4()
@ -318,6 +356,12 @@ func GetMinimalIPNet(nw *net.IPNet) *net.IPNet {
return nw
}
// IsIPNetValid returns true if the ipnet is a valid network/mask
// combination. Otherwise returns false.
func IsIPNetValid(nw *net.IPNet) bool {
return nw.String() != "0.0.0.0/0"
}
var v4inV6MaskPrefix = []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
// compareIPMask checks if the passed ip and mask are semantically compatible.

50
vendor/github.com/docker/libnetwork/vendor.conf generated vendored Normal file
View File

@ -0,0 +1,50 @@
github.com/Azure/go-ansiterm d6e3b3328b783f23731bc4d058875b0371ff8109
github.com/BurntSushi/toml a368813c5e648fee92e5f6c30e3944ff9d5e8895
github.com/Microsoft/go-winio v0.4.11
github.com/Microsoft/hcsshim v0.7.3
github.com/armon/go-metrics eb0af217e5e9747e41dd5303755356b62d28e3ec
github.com/armon/go-radix e39d623f12e8e41c7b5529e9a9dd67a1e2261f80
github.com/codegangsta/cli a65b733b303f0055f8d324d805f393cd3e7a7904
github.com/containerd/continuity d3c23511c1bf5851696cba83143d9cbcd666869b
github.com/coreos/etcd v3.2.1
github.com/coreos/go-semver v0.2.0
github.com/deckarep/golang-set ef32fa3046d9f249d399f98ebaf9be944430fd1d
go.etcd.io/bbolt v1.3.1-etcd.8
github.com/docker/docker 162ba6016def672690ee4a1f3978368853a1e149
github.com/docker/go-connections 7beb39f0b969b075d1325fecb092faf27fd357b6
github.com/docker/go-events 9461782956ad83b30282bf90e31fa6a70c255ba9
github.com/docker/go-units 9e638d38cf6977a37a8ea0078f3ee75a7cdb2dd1
github.com/docker/libkv 458977154600b9f23984d9f4b82e79570b5ae12b
github.com/godbus/dbus v4.0.0
github.com/gogo/protobuf v1.0.0
github.com/gorilla/context v1.1
github.com/gorilla/mux v1.1
github.com/hashicorp/consul v0.5.2
github.com/hashicorp/go-msgpack 71c2886f5a673a35f909803f38ece5810165097b
github.com/hashicorp/go-multierror fcdddc395df1ddf4247c69bd436e84cfa0733f7e
github.com/hashicorp/memberlist 3d8438da9589e7b608a83ffac1ef8211486bcb7c
github.com/sean-/seed e2103e2c35297fb7e17febb81e49b312087a2372
github.com/hashicorp/go-sockaddr 6d291a969b86c4b633730bfc6b8b9d64c3aafed9
github.com/hashicorp/serf 598c54895cc5a7b1a24a398d635e8c0ea0959870
github.com/mattn/go-shellwords v1.0.3
github.com/miekg/dns v1.0.7
github.com/opencontainers/go-digest v1.0.0-rc1
github.com/opencontainers/image-spec v1.0.1
github.com/opencontainers/runc 69663f0bd4b60df09991c08812a60108003fa340
github.com/opencontainers/runtime-spec v1.0.1
github.com/samuel/go-zookeeper d0e0d8e11f318e000a8cc434616d69e329edc374
github.com/sirupsen/logrus v1.0.3
github.com/ugorji/go f1f1a805ed361a0e078bb537e4ea78cd37dcf065
github.com/vishvananda/netlink b2de5d10e38ecce8607e6b438b6d174f389a004e
github.com/vishvananda/netns 604eaf189ee867d8c147fafc28def2394e878d25
golang.org/x/crypto 1a580b3eff7814fc9b40602fd35256c63b50f491
golang.org/x/net 0ed95abb35c445290478a5348a7b38bb154135fd
golang.org/x/sys 37707fdb30a5b38865cfb95e5aab41707daec7fd
golang.org/x/sync fd80eb99c8f653c847d294a001bdf2a3a6f768f5
github.com/pkg/errors 839d9e913e063e28dfd0e6c7b7512793e0a48be9
github.com/ishidawataru/sctp 07191f837fedd2f13d1ec7b5f885f0f3ec54b1cb
gotest.tools v2.1.0
github.com/google/go-cmp v0.2.0