diff --git a/Makefile b/Makefile index b98424b6c0..c13836bc32 100644 --- a/Makefile +++ b/Makefile @@ -9,6 +9,7 @@ DOCKER_ENVS := \ -e DOCKER_EXECDRIVER \ -e DOCKER_GRAPHDRIVER \ -e DOCKER_STORAGE_OPTS \ + -e DOCKER_USERLANDPROXY \ -e TESTDIRS \ -e TESTFLAGS \ -e TIMEOUT diff --git a/api/client/run.go b/api/client/run.go index 12d1b24283..10cf92459d 100644 --- a/api/client/run.go +++ b/api/client/run.go @@ -9,9 +9,9 @@ import ( "github.com/Sirupsen/logrus" "github.com/docker/docker/opts" "github.com/docker/docker/pkg/promise" - "github.com/docker/docker/pkg/resolvconf/dns" "github.com/docker/docker/pkg/signal" "github.com/docker/docker/runconfig" + "github.com/docker/libnetwork/resolvconf/dns" ) func (cid *cidFile) Close() error { diff --git a/api/server/server.go b/api/server/server.go index fa591a467c..04038531f5 100644 --- a/api/server/server.go +++ b/api/server/server.go @@ -23,7 +23,6 @@ import ( "github.com/docker/docker/builder" "github.com/docker/docker/cliconfig" "github.com/docker/docker/daemon" - "github.com/docker/docker/daemon/networkdriver/bridge" "github.com/docker/docker/graph" "github.com/docker/docker/pkg/ioutils" "github.com/docker/docker/pkg/jsonmessage" @@ -36,6 +35,7 @@ import ( "github.com/docker/docker/pkg/version" "github.com/docker/docker/runconfig" "github.com/docker/docker/utils" + "github.com/docker/libnetwork/portallocator" ) type ServerConfig struct { @@ -1548,8 +1548,9 @@ func allocateDaemonPort(addr string) error { return fmt.Errorf("failed to lookup %s address in host specification", host) } + pa := portallocator.Get() for _, hostIP := range hostIPs { - if _, err := bridge.RequestPort(hostIP, "tcp", intPort); err != nil { + if _, err := pa.RequestPort(hostIP, "tcp", intPort); err != nil { return fmt.Errorf("failed to allocate daemon listening port %d (err: %v)", intPort, err) } } diff --git a/daemon/config.go b/daemon/config.go index b190269498..3599def874 100644 --- a/daemon/config.go +++ b/daemon/config.go @@ -1,8 +1,8 @@ package daemon import ( - "github.com/docker/docker/daemon/networkdriver" - "github.com/docker/docker/daemon/networkdriver/bridge" + "net" + "github.com/docker/docker/opts" flag "github.com/docker/docker/pkg/mflag" "github.com/docker/docker/runconfig" @@ -16,8 +16,9 @@ const ( // CommonConfig defines the configuration of a docker daemon which are // common across platforms. type CommonConfig struct { - AutoRestart bool - Bridge bridge.Config + AutoRestart bool + // Bridge holds bridge network specific configuration. + Bridge bridgeConfig Context map[string][]string CorsHeaders string DisableNetwork bool @@ -35,6 +36,24 @@ type CommonConfig struct { TrustKeyPath string } +// bridgeConfig stores all the bridge driver specific +// configuration. +type bridgeConfig struct { + EnableIPv6 bool + EnableIPTables bool + EnableIPForward bool + EnableIPMasq bool + EnableUserlandProxy bool + DefaultIP net.IP + Iface string + IP string + FixedCIDR string + FixedCIDRv6 string + DefaultGatewayIPv4 string + DefaultGatewayIPv6 string + InterContainerCommunication bool +} + // InstallCommonFlags adds command-line options to the top-level flag parser for // the current process. // Subsequent calls to `flag.Parse` will populate config with values parsed @@ -45,9 +64,9 @@ func (config *Config) InstallCommonFlags() { flag.StringVar(&config.Root, []string{"g", "-graph"}, defaultGraph, "Root of the Docker runtime") flag.StringVar(&config.ExecRoot, []string{"-exec-root"}, "/var/run/docker", "Root of the Docker execdriver") flag.BoolVar(&config.AutoRestart, []string{"#r", "#-restart"}, true, "--restart on the daemon has been deprecated in favor of --restart policies on docker run") - flag.BoolVar(&config.Bridge.EnableIptables, []string{"#iptables", "-iptables"}, true, "Enable addition of iptables rules") - flag.BoolVar(&config.Bridge.EnableIpForward, []string{"#ip-forward", "-ip-forward"}, true, "Enable net.ipv4.ip_forward") - flag.BoolVar(&config.Bridge.EnableIpMasq, []string{"-ip-masq"}, true, "Enable IP masquerading") + flag.BoolVar(&config.Bridge.EnableIPTables, []string{"#iptables", "-iptables"}, true, "Enable addition of iptables rules") + flag.BoolVar(&config.Bridge.EnableIPForward, []string{"#ip-forward", "-ip-forward"}, true, "Enable net.ipv4.ip_forward") + flag.BoolVar(&config.Bridge.EnableIPMasq, []string{"-ip-masq"}, true, "Enable IP masquerading") flag.BoolVar(&config.Bridge.EnableIPv6, []string{"-ipv6"}, false, "Enable IPv6 networking") flag.StringVar(&config.Bridge.IP, []string{"#bip", "-bip"}, "", "Specify network bridge IP") flag.StringVar(&config.Bridge.Iface, []string{"b", "-bridge"}, "", "Attach containers to a network bridge") @@ -61,7 +80,7 @@ func (config *Config) InstallCommonFlags() { flag.IntVar(&config.Mtu, []string{"#mtu", "-mtu"}, 0, "Set the containers network MTU") flag.BoolVar(&config.EnableCors, []string{"#api-enable-cors", "#-api-enable-cors"}, false, "Enable CORS headers in the remote API, this is deprecated by --api-cors-header") flag.StringVar(&config.CorsHeaders, []string{"-api-cors-header"}, "", "Set CORS headers in the remote API") - opts.IPVar(&config.Bridge.DefaultIp, []string{"#ip", "-ip"}, "0.0.0.0", "Default IP when binding container ports") + opts.IPVar(&config.Bridge.DefaultIP, []string{"#ip", "-ip"}, "0.0.0.0", "Default IP when binding container ports") // FIXME: why the inconsistency between "hosts" and "sockets"? opts.IPListVar(&config.Dns, []string{"#dns", "-dns"}, "DNS server to use") opts.DnsSearchListVar(&config.DnsSearch, []string{"-dns-search"}, "DNS search domains to use") @@ -71,10 +90,3 @@ func (config *Config) InstallCommonFlags() { flag.BoolVar(&config.Bridge.EnableUserlandProxy, []string{"-userland-proxy"}, true, "Use userland proxy for loopback traffic") } - -func getDefaultNetworkMtu() int { - if iface, err := networkdriver.GetDefaultRouteIface(); err == nil { - return iface.MTU - } - return defaultNetworkMtu -} diff --git a/daemon/container.go b/daemon/container.go index 55e1f890c5..7a59bec5d7 100644 --- a/daemon/container.go +++ b/daemon/container.go @@ -252,18 +252,12 @@ func (container *Container) Start() (err error) { } }() - if err := container.setupContainerDns(); err != nil { - return err - } if err := container.Mount(); err != nil { return err } if err := container.initializeNetworking(); err != nil { return err } - if err := container.updateParentsHosts(); err != nil { - return err - } container.verifyDaemonSettings() if err := container.prepareVolumes(); err != nil { return err diff --git a/daemon/container_linux.go b/daemon/container_linux.go index 58128f49d4..ee9a5092d9 100644 --- a/daemon/container_linux.go +++ b/daemon/container_linux.go @@ -3,11 +3,13 @@ package daemon import ( - "bytes" "fmt" "io/ioutil" + "net" "os" + "path" "path/filepath" + "strconv" "strings" "syscall" "time" @@ -15,20 +17,21 @@ import ( "github.com/Sirupsen/logrus" "github.com/docker/docker/daemon/execdriver" "github.com/docker/docker/daemon/network" - "github.com/docker/docker/daemon/networkdriver/bridge" "github.com/docker/docker/links" "github.com/docker/docker/nat" "github.com/docker/docker/pkg/archive" "github.com/docker/docker/pkg/directory" - "github.com/docker/docker/pkg/etchosts" "github.com/docker/docker/pkg/ioutils" - "github.com/docker/docker/pkg/resolvconf" "github.com/docker/docker/pkg/stringid" "github.com/docker/docker/pkg/ulimit" "github.com/docker/docker/runconfig" "github.com/docker/docker/utils" "github.com/docker/libcontainer/configs" "github.com/docker/libcontainer/devices" + "github.com/docker/libnetwork" + "github.com/docker/libnetwork/netlabel" + "github.com/docker/libnetwork/netutils" + "github.com/docker/libnetwork/options" ) const DefaultPathEnv = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" @@ -65,179 +68,6 @@ func killProcessDirectly(container *Container) error { return nil } -func (container *Container) setupContainerDns() error { - if container.ResolvConfPath != "" { - // check if this is an existing container that needs DNS update: - if container.UpdateDns { - // read the host's resolv.conf, get the hash and call updateResolvConf - logrus.Debugf("Check container (%s) for update to resolv.conf - UpdateDns flag was set", container.ID) - latestResolvConf, latestHash := resolvconf.GetLastModified() - - // clean container resolv.conf re: localhost nameservers and IPv6 NS (if IPv6 disabled) - updatedResolvConf, modified := resolvconf.FilterResolvDns(latestResolvConf, container.daemon.config.Bridge.EnableIPv6) - if modified { - // changes have occurred during resolv.conf localhost cleanup: generate an updated hash - newHash, err := ioutils.HashData(bytes.NewReader(updatedResolvConf)) - if err != nil { - return err - } - latestHash = newHash - } - - if err := container.updateResolvConf(updatedResolvConf, latestHash); err != nil { - return err - } - // successful update of the restarting container; set the flag off - container.UpdateDns = false - } - return nil - } - - var ( - config = container.hostConfig - daemon = container.daemon - ) - - resolvConf, err := resolvconf.Get() - if err != nil { - return err - } - container.ResolvConfPath, err = container.GetRootResourcePath("resolv.conf") - if err != nil { - return err - } - - if config.NetworkMode.IsBridge() || config.NetworkMode.IsNone() { - // check configurations for any container/daemon dns settings - if len(config.Dns) > 0 || len(daemon.config.Dns) > 0 || len(config.DnsSearch) > 0 || len(daemon.config.DnsSearch) > 0 { - var ( - dns = resolvconf.GetNameservers(resolvConf) - dnsSearch = resolvconf.GetSearchDomains(resolvConf) - ) - if len(config.Dns) > 0 { - dns = config.Dns - } else if len(daemon.config.Dns) > 0 { - dns = daemon.config.Dns - } - if len(config.DnsSearch) > 0 { - dnsSearch = config.DnsSearch - } else if len(daemon.config.DnsSearch) > 0 { - dnsSearch = daemon.config.DnsSearch - } - return resolvconf.Build(container.ResolvConfPath, dns, dnsSearch) - } - - // replace any localhost/127.*, and remove IPv6 nameservers if IPv6 disabled in daemon - resolvConf, _ = resolvconf.FilterResolvDns(resolvConf, daemon.config.Bridge.EnableIPv6) - } - //get a sha256 hash of the resolv conf at this point so we can check - //for changes when the host resolv.conf changes (e.g. network update) - resolvHash, err := ioutils.HashData(bytes.NewReader(resolvConf)) - if err != nil { - return err - } - resolvHashFile := container.ResolvConfPath + ".hash" - if err = ioutil.WriteFile(resolvHashFile, []byte(resolvHash), 0644); err != nil { - return err - } - return ioutil.WriteFile(container.ResolvConfPath, resolvConf, 0644) -} - -// called when the host's resolv.conf changes to check whether container's resolv.conf -// is unchanged by the container "user" since container start: if unchanged, the -// container's resolv.conf will be updated to match the host's new resolv.conf -func (container *Container) updateResolvConf(updatedResolvConf []byte, newResolvHash string) error { - - if container.ResolvConfPath == "" { - return nil - } - if container.Running { - //set a marker in the hostConfig to update on next start/restart - container.UpdateDns = true - return nil - } - - resolvHashFile := container.ResolvConfPath + ".hash" - - //read the container's current resolv.conf and compute the hash - resolvBytes, err := ioutil.ReadFile(container.ResolvConfPath) - if err != nil { - return err - } - curHash, err := ioutils.HashData(bytes.NewReader(resolvBytes)) - if err != nil { - return err - } - - //read the hash from the last time we wrote resolv.conf in the container - hashBytes, err := ioutil.ReadFile(resolvHashFile) - if err != nil { - if !os.IsNotExist(err) { - return err - } - // backwards compat: if no hash file exists, this container pre-existed from - // a Docker daemon that didn't contain this update feature. Given we can't know - // if the user has modified the resolv.conf since container start time, safer - // to just never update the container's resolv.conf during it's lifetime which - // we can control by setting hashBytes to an empty string - hashBytes = []byte("") - } - - //if the user has not modified the resolv.conf of the container since we wrote it last - //we will replace it with the updated resolv.conf from the host - if string(hashBytes) == curHash { - logrus.Debugf("replacing %q with updated host resolv.conf", container.ResolvConfPath) - - // for atomic updates to these files, use temporary files with os.Rename: - dir := filepath.Dir(container.ResolvConfPath) - tmpHashFile, err := ioutil.TempFile(dir, "hash") - if err != nil { - return err - } - tmpResolvFile, err := ioutil.TempFile(dir, "resolv") - if err != nil { - return err - } - - // write the updates to the temp files - if err = ioutil.WriteFile(tmpHashFile.Name(), []byte(newResolvHash), 0644); err != nil { - return err - } - if err = ioutil.WriteFile(tmpResolvFile.Name(), updatedResolvConf, 0644); err != nil { - return err - } - - // rename the temp files for atomic replace - if err = os.Rename(tmpHashFile.Name(), resolvHashFile); err != nil { - return err - } - return os.Rename(tmpResolvFile.Name(), container.ResolvConfPath) - } - return nil -} - -func (container *Container) updateParentsHosts() error { - refs := container.daemon.ContainerGraph().RefPaths(container.ID) - for _, ref := range refs { - if ref.ParentID == "0" { - continue - } - - c, err := container.daemon.Get(ref.ParentID) - if err != nil { - logrus.Error(err) - } - - if c != nil && !container.daemon.config.DisableNetwork && container.hostConfig.NetworkMode.IsPrivate() { - logrus.Debugf("Update /etc/hosts of %s for alias %s with ip %s", c.ID, ref.Name, container.NetworkSettings.IPAddress) - if err := etchosts.Update(c.HostsPath, container.NetworkSettings.IPAddress, ref.Name); err != nil { - logrus.Errorf("Failed to update /etc/hosts in parent container %s for alias %s: %v", c.ID, ref.Name, err) - } - } - } - return nil -} - func (container *Container) setupLinkedContainers() ([]string, error) { var ( env []string @@ -360,39 +190,16 @@ func getDevicesFromPath(deviceMapping runconfig.DeviceMapping) (devs []*configs. func populateCommand(c *Container, env []string) error { en := &execdriver.Network{ - Mtu: c.daemon.config.Mtu, - Interface: nil, + NamespacePath: c.NetworkSettings.SandboxKey, } parts := strings.SplitN(string(c.hostConfig.NetworkMode), ":", 2) - switch parts[0] { - case "none": - case "host": - en.HostNetworking = true - case "bridge", "": // empty string to support existing containers - if !c.Config.NetworkDisabled { - network := c.NetworkSettings - en.Interface = &execdriver.NetworkInterface{ - Gateway: network.Gateway, - Bridge: network.Bridge, - IPAddress: network.IPAddress, - IPPrefixLen: network.IPPrefixLen, - MacAddress: network.MacAddress, - LinkLocalIPv6Address: network.LinkLocalIPv6Address, - GlobalIPv6Address: network.GlobalIPv6Address, - GlobalIPv6PrefixLen: network.GlobalIPv6PrefixLen, - IPv6Gateway: network.IPv6Gateway, - HairpinMode: network.HairpinMode, - } - } - case "container": + if parts[0] == "container" { nc, err := c.getNetworkedContainer() if err != nil { return err } en.ContainerID = nc.ID - default: - return fmt.Errorf("invalid network mode: %s", c.hostConfig.NetworkMode) } ipc := &execdriver.Ipc{} @@ -537,40 +344,318 @@ func (container *Container) GetSize() (int64, int64) { return sizeRw, sizeRootfs } -func (container *Container) AllocateNetwork() error { - mode := container.hostConfig.NetworkMode - if container.Config.NetworkDisabled || !mode.IsPrivate() { +func (container *Container) buildHostnameFile() error { + hostnamePath, err := container.GetRootResourcePath("hostname") + if err != nil { + return err + } + container.HostnamePath = hostnamePath + + if container.Config.Domainname != "" { + return ioutil.WriteFile(container.HostnamePath, []byte(fmt.Sprintf("%s.%s\n", container.Config.Hostname, container.Config.Domainname)), 0644) + } + return ioutil.WriteFile(container.HostnamePath, []byte(container.Config.Hostname+"\n"), 0644) +} + +func (container *Container) buildJoinOptions() ([]libnetwork.EndpointOption, error) { + var ( + joinOptions []libnetwork.EndpointOption + err error + dns []string + dnsSearch []string + ) + + joinOptions = append(joinOptions, libnetwork.JoinOptionHostname(container.Config.Hostname), + libnetwork.JoinOptionDomainname(container.Config.Domainname)) + + if container.hostConfig.NetworkMode.IsHost() { + joinOptions = append(joinOptions, libnetwork.JoinOptionUseDefaultSandbox()) + } + + container.HostsPath, err = container.GetRootResourcePath("hosts") + if err != nil { + return nil, err + } + joinOptions = append(joinOptions, libnetwork.JoinOptionHostsPath(container.HostsPath)) + + container.ResolvConfPath, err = container.GetRootResourcePath("resolv.conf") + if err != nil { + return nil, err + } + joinOptions = append(joinOptions, libnetwork.JoinOptionResolvConfPath(container.ResolvConfPath)) + + if len(container.hostConfig.Dns) > 0 { + dns = container.hostConfig.Dns + } else if len(container.daemon.config.Dns) > 0 { + dns = container.daemon.config.Dns + } + + for _, d := range dns { + joinOptions = append(joinOptions, libnetwork.JoinOptionDNS(d)) + } + + if len(container.hostConfig.DnsSearch) > 0 { + dnsSearch = container.hostConfig.DnsSearch + } else if len(container.daemon.config.DnsSearch) > 0 { + dnsSearch = container.daemon.config.DnsSearch + } + + for _, ds := range dnsSearch { + joinOptions = append(joinOptions, libnetwork.JoinOptionDNSSearch(ds)) + } + + if container.NetworkSettings.SecondaryIPAddresses != nil { + name := container.Config.Hostname + if container.Config.Domainname != "" { + name = name + "." + container.Config.Domainname + } + + for _, a := range container.NetworkSettings.SecondaryIPAddresses { + joinOptions = append(joinOptions, libnetwork.JoinOptionExtraHost(name, a.Addr)) + } + } + + var childEndpoints, parentEndpoints []string + + children, err := container.daemon.Children(container.Name) + if err != nil { + return nil, err + } + + for linkAlias, child := range children { + _, alias := path.Split(linkAlias) + // allow access to the linked container via the alias, real name, and container hostname + aliasList := alias + " " + child.Config.Hostname + // only add the name if alias isn't equal to the name + if alias != child.Name[1:] { + aliasList = aliasList + " " + child.Name[1:] + } + joinOptions = append(joinOptions, libnetwork.JoinOptionExtraHost(aliasList, child.NetworkSettings.IPAddress)) + if child.NetworkSettings.EndpointID != "" { + childEndpoints = append(childEndpoints, child.NetworkSettings.EndpointID) + } + } + + for _, extraHost := range container.hostConfig.ExtraHosts { + // allow IPv6 addresses in extra hosts; only split on first ":" + parts := strings.SplitN(extraHost, ":", 2) + joinOptions = append(joinOptions, libnetwork.JoinOptionExtraHost(parts[0], parts[1])) + } + + refs := container.daemon.ContainerGraph().RefPaths(container.ID) + for _, ref := range refs { + if ref.ParentID == "0" { + continue + } + + c, err := container.daemon.Get(ref.ParentID) + if err != nil { + logrus.Error(err) + } + + if c != nil && !container.daemon.config.DisableNetwork && container.hostConfig.NetworkMode.IsPrivate() { + logrus.Debugf("Update /etc/hosts of %s for alias %s with ip %s", c.ID, ref.Name, container.NetworkSettings.IPAddress) + joinOptions = append(joinOptions, libnetwork.JoinOptionParentUpdate(c.NetworkSettings.EndpointID, ref.Name, container.NetworkSettings.IPAddress)) + if c.NetworkSettings.EndpointID != "" { + parentEndpoints = append(parentEndpoints, c.NetworkSettings.EndpointID) + } + } + } + + linkOptions := options.Generic{ + netlabel.GenericData: options.Generic{ + "ParentEndpoints": parentEndpoints, + "ChildEndpoints": childEndpoints, + }, + } + + joinOptions = append(joinOptions, libnetwork.JoinOptionGeneric(linkOptions)) + + return joinOptions, nil +} + +func (container *Container) buildPortMapInfo(n libnetwork.Network, ep libnetwork.Endpoint, networkSettings *network.Settings) (*network.Settings, error) { + if ep == nil { + return nil, fmt.Errorf("invalid endpoint while building port map info") + } + + if networkSettings == nil { + return nil, fmt.Errorf("invalid networksettings while building port map info") + } + + driverInfo, err := ep.DriverInfo() + if err != nil { + return nil, err + } + + if driverInfo == nil { + // It is not an error for epInfo to be nil + return networkSettings, nil + } + + if mac, ok := driverInfo[netlabel.MacAddress]; ok { + networkSettings.MacAddress = mac.(net.HardwareAddr).String() + } + + mapData, ok := driverInfo[netlabel.PortMap] + if !ok { + return networkSettings, nil + } + + if portMapping, ok := mapData.([]netutils.PortBinding); ok { + networkSettings.Ports = nat.PortMap{} + for _, pp := range portMapping { + natPort := nat.NewPort(pp.Proto.String(), strconv.Itoa(int(pp.Port))) + natBndg := nat.PortBinding{HostIp: pp.HostIP.String(), HostPort: strconv.Itoa(int(pp.HostPort))} + networkSettings.Ports[natPort] = append(networkSettings.Ports[natPort], natBndg) + } + } + + return networkSettings, nil +} + +func (container *Container) buildEndpointInfo(n libnetwork.Network, ep libnetwork.Endpoint, networkSettings *network.Settings) (*network.Settings, error) { + if ep == nil { + return nil, fmt.Errorf("invalid endpoint while building port map info") + } + + if networkSettings == nil { + return nil, fmt.Errorf("invalid networksettings while building port map info") + } + + epInfo := ep.Info() + if epInfo == nil { + // It is not an error to get an empty endpoint info + return networkSettings, nil + } + + ifaceList := epInfo.InterfaceList() + if len(ifaceList) == 0 { + return networkSettings, nil + } + + iface := ifaceList[0] + + ones, _ := iface.Address().Mask.Size() + networkSettings.IPAddress = iface.Address().IP.String() + networkSettings.IPPrefixLen = ones + + if iface.AddressIPv6().IP.To16() != nil { + onesv6, _ := iface.AddressIPv6().Mask.Size() + networkSettings.GlobalIPv6Address = iface.AddressIPv6().IP.String() + networkSettings.GlobalIPv6PrefixLen = onesv6 + } + + if len(ifaceList) == 1 { + return networkSettings, nil + } + + networkSettings.SecondaryIPAddresses = make([]network.Address, 0, len(ifaceList)-1) + networkSettings.SecondaryIPv6Addresses = make([]network.Address, 0, len(ifaceList)-1) + for _, iface := range ifaceList[1:] { + ones, _ := iface.Address().Mask.Size() + addr := network.Address{Addr: iface.Address().IP.String(), PrefixLen: ones} + networkSettings.SecondaryIPAddresses = append(networkSettings.SecondaryIPAddresses, addr) + + if iface.AddressIPv6().IP.To16() != nil { + onesv6, _ := iface.AddressIPv6().Mask.Size() + addrv6 := network.Address{Addr: iface.AddressIPv6().IP.String(), PrefixLen: onesv6} + networkSettings.SecondaryIPv6Addresses = append(networkSettings.SecondaryIPv6Addresses, addrv6) + } + } + + return networkSettings, nil +} + +func (container *Container) updateJoinInfo(ep libnetwork.Endpoint) error { + epInfo := ep.Info() + if epInfo == nil { + // It is not an error to get an empty endpoint info return nil } - var err error + container.NetworkSettings.Gateway = epInfo.Gateway().String() + if epInfo.GatewayIPv6().To16() != nil { + container.NetworkSettings.IPv6Gateway = epInfo.GatewayIPv6().String() + } - networkSettings, err := bridge.Allocate(container.ID, container.Config.MacAddress, "", "") + container.NetworkSettings.SandboxKey = epInfo.SandboxKey() + + return nil +} + +func (container *Container) updateNetworkSettings(n libnetwork.Network, ep libnetwork.Endpoint) error { + networkSettings := &network.Settings{NetworkID: n.ID(), EndpointID: ep.ID()} + + networkSettings, err := container.buildPortMapInfo(n, ep, networkSettings) if err != nil { return err } - // Error handling: At this point, the interface is allocated so we have to - // make sure that it is always released in case of error, otherwise we - // might leak resources. - - if container.Config.PortSpecs != nil { - if err = migratePortMappings(container.Config, container.hostConfig); err != nil { - bridge.Release(container.ID) - return err - } - container.Config.PortSpecs = nil - if err = container.WriteHostConfig(); err != nil { - bridge.Release(container.ID) - return err - } + networkSettings, err = container.buildEndpointInfo(n, ep, networkSettings) + if err != nil { + return err } + if container.hostConfig.NetworkMode == runconfig.NetworkMode("bridge") { + networkSettings.Bridge = container.daemon.config.Bridge.Iface + } + + container.NetworkSettings = networkSettings + return nil +} + +func (container *Container) UpdateNetwork() error { + n, err := container.daemon.netController.NetworkByID(container.NetworkSettings.NetworkID) + if err != nil { + return fmt.Errorf("error locating network id %s: %v", container.NetworkSettings.NetworkID, err) + } + + ep, err := n.EndpointByID(container.NetworkSettings.EndpointID) + if err != nil { + return fmt.Errorf("error locating endpoint id %s: %v", container.NetworkSettings.EndpointID, err) + } + + if err := ep.Leave(container.ID); err != nil { + return fmt.Errorf("endpoint leave failed: %v", err) + + } + + joinOptions, err := container.buildJoinOptions() + if err != nil { + return fmt.Errorf("Update network failed: %v", err) + } + + if _, err := ep.Join(container.ID, joinOptions...); err != nil { + return fmt.Errorf("endpoint join failed: %v", err) + } + + if err := container.updateJoinInfo(ep); err != nil { + return fmt.Errorf("Updating join info failed: %v", err) + } + + return nil +} + +func (container *Container) buildCreateEndpointOptions() ([]libnetwork.EndpointOption, error) { var ( - portSpecs = make(nat.PortSet) - bindings = make(nat.PortMap) + portSpecs = make(nat.PortSet) + bindings = make(nat.PortMap) + pbList []netutils.PortBinding + exposeList []netutils.TransportPort + createOptions []libnetwork.EndpointOption ) + if container.Config.PortSpecs != nil { + if err := migratePortMappings(container.Config, container.hostConfig); err != nil { + return nil, err + } + container.Config.PortSpecs = nil + if err := container.WriteHostConfig(); err != nil { + return nil, err + } + } + if container.Config.ExposedPorts != nil { portSpecs = container.Config.ExposedPorts } @@ -597,52 +682,99 @@ func (container *Container) AllocateNetwork() error { } nat.SortPortMap(ports, bindings) for _, port := range ports { - if err = container.allocatePort(port, bindings); err != nil { - bridge.Release(container.ID) - return err + expose := netutils.TransportPort{} + expose.Proto = netutils.ParseProtocol(port.Proto()) + expose.Port = uint16(port.Int()) + exposeList = append(exposeList, expose) + + pb := netutils.PortBinding{Port: expose.Port, Proto: expose.Proto} + binding := bindings[port] + for i := 0; i < len(binding); i++ { + pbCopy := pb.GetCopy() + pbCopy.HostPort = uint16(nat.Port(binding[i].HostPort).Int()) + pbCopy.HostIP = net.ParseIP(binding[i].HostIp) + pbList = append(pbList, pbCopy) + } + + if container.hostConfig.PublishAllPorts && len(binding) == 0 { + pbList = append(pbList, pb) } } - container.WriteHostConfig() - networkSettings.Ports = bindings - container.NetworkSettings = networkSettings + createOptions = append(createOptions, + libnetwork.CreateOptionPortMapping(pbList), + libnetwork.CreateOptionExposedPorts(exposeList)) + + if container.Config.MacAddress != "" { + mac, err := net.ParseMAC(container.Config.MacAddress) + if err != nil { + return nil, err + } + + genericOption := options.Generic{ + netlabel.MacAddress: mac, + } + + createOptions = append(createOptions, libnetwork.EndpointOptionGeneric(genericOption)) + } + + return createOptions, nil +} + +func (container *Container) AllocateNetwork() error { + mode := container.hostConfig.NetworkMode + if container.Config.NetworkDisabled || mode.IsContainer() { + return nil + } + + var err error + + n, err := container.daemon.netController.NetworkByName(string(mode)) + if err != nil { + return fmt.Errorf("error locating network with name %s: %v", string(mode), err) + } + + createOptions, err := container.buildCreateEndpointOptions() + if err != nil { + return err + } + + ep, err := n.CreateEndpoint(container.Name, createOptions...) + if err != nil { + return err + } + + if err := container.updateNetworkSettings(n, ep); err != nil { + return err + } + + joinOptions, err := container.buildJoinOptions() + if err != nil { + return err + } + + if _, err := ep.Join(container.ID, joinOptions...); err != nil { + return err + } + + if err := container.updateJoinInfo(ep); err != nil { + return fmt.Errorf("Updating join info failed: %v", err) + } + + container.WriteHostConfig() return nil } func (container *Container) initializeNetworking() error { var err error - if container.hostConfig.NetworkMode.IsHost() { - container.Config.Hostname, err = os.Hostname() - if err != nil { - return err - } - parts := strings.SplitN(container.Config.Hostname, ".", 2) - if len(parts) > 1 { - container.Config.Hostname = parts[0] - container.Config.Domainname = parts[1] - } - - content, err := ioutil.ReadFile("/etc/hosts") - if os.IsNotExist(err) { - return container.buildHostnameAndHostsFiles("") - } else if err != nil { - return err - } - - if err := container.buildHostnameFile(); err != nil { - return err - } - - hostsPath, err := container.GetRootResourcePath("hosts") - if err != nil { - return err - } - container.HostsPath = hostsPath - - return ioutil.WriteFile(container.HostsPath, content, 0644) + // Make sure NetworkMode has an acceptable value before + // initializing networking. + if container.hostConfig.NetworkMode == runconfig.NetworkMode("") { + container.hostConfig.NetworkMode = runconfig.NetworkMode("bridge") } + if container.hostConfig.NetworkMode.IsContainer() { // we need to get the hosts files from the container to join nc, err := container.getNetworkedContainer() @@ -656,14 +788,30 @@ func (container *Container) initializeNetworking() error { container.Config.Domainname = nc.Config.Domainname return nil } + if container.daemon.config.DisableNetwork { container.Config.NetworkDisabled = true - return container.buildHostnameAndHostsFiles("127.0.1.1") } + + if container.hostConfig.NetworkMode.IsHost() { + container.Config.Hostname, err = os.Hostname() + if err != nil { + return err + } + + parts := strings.SplitN(container.Config.Hostname, ".", 2) + if len(parts) > 1 { + container.Config.Hostname = parts[0] + container.Config.Domainname = parts[1] + } + + } + if err := container.AllocateNetwork(); err != nil { return err } - return container.buildHostnameAndHostsFiles(container.NetworkSettings.IPAddress) + + return container.buildHostnameFile() } // Make sure the config is compatible with the current kernel @@ -701,62 +849,6 @@ func (container *Container) ExportRw() (archive.Archive, error) { nil } -func (container *Container) buildHostnameFile() error { - hostnamePath, err := container.GetRootResourcePath("hostname") - if err != nil { - return err - } - container.HostnamePath = hostnamePath - - if container.Config.Domainname != "" { - return ioutil.WriteFile(container.HostnamePath, []byte(fmt.Sprintf("%s.%s\n", container.Config.Hostname, container.Config.Domainname)), 0644) - } - return ioutil.WriteFile(container.HostnamePath, []byte(container.Config.Hostname+"\n"), 0644) -} - -func (container *Container) buildHostsFiles(IP string) error { - - hostsPath, err := container.GetRootResourcePath("hosts") - if err != nil { - return err - } - container.HostsPath = hostsPath - - var extraContent []etchosts.Record - - children, err := container.daemon.Children(container.Name) - if err != nil { - return err - } - - for linkAlias, child := range children { - _, alias := filepath.Split(linkAlias) - // allow access to the linked container via the alias, real name, and container hostname - aliasList := alias + " " + child.Config.Hostname - // only add the name if alias isn't equal to the name - if alias != child.Name[1:] { - aliasList = aliasList + " " + child.Name[1:] - } - extraContent = append(extraContent, etchosts.Record{Hosts: aliasList, IP: child.NetworkSettings.IPAddress}) - } - - for _, extraHost := range container.hostConfig.ExtraHosts { - // allow IPv6 addresses in extra hosts; only split on first ":" - parts := strings.SplitN(extraHost, ":", 2) - extraContent = append(extraContent, etchosts.Record{Hosts: parts[0], IP: parts[1]}) - } - - return etchosts.Build(container.HostsPath, IP, container.Config.Hostname, container.Config.Domainname, extraContent) -} - -func (container *Container) buildHostnameAndHostsFiles(IP string) error { - if err := container.buildHostnameFile(); err != nil { - return err - } - - return container.buildHostsFiles(IP) -} - func (container *Container) getIpcContainer() (*Container, error) { containerID := container.hostConfig.IpcMode.Container() c, err := container.daemon.Get(containerID) @@ -795,23 +887,6 @@ func (container *Container) setupWorkingDirectory() error { return nil } -func (container *Container) allocatePort(port nat.Port, bindings nat.PortMap) error { - binding := bindings[port] - if container.hostConfig.PublishAllPorts && len(binding) == 0 { - binding = append(binding, nat.PortBinding{}) - } - - for i := 0; i < len(binding); i++ { - b, err := bridge.AllocatePort(container.ID, port, binding[i]) - if err != nil { - return err - } - binding[i] = b - } - bindings[port] = binding - return nil -} - func (container *Container) getNetworkedContainer() (*Container, error) { parts := strings.SplitN(string(container.hostConfig.NetworkMode), ":", 2) switch parts[0] { @@ -836,36 +911,33 @@ func (container *Container) getNetworkedContainer() (*Container, error) { } func (container *Container) ReleaseNetwork() { - if container.Config.NetworkDisabled || !container.hostConfig.NetworkMode.IsPrivate() { + if container.hostConfig.NetworkMode.IsContainer() { return } - bridge.Release(container.ID) + + n, err := container.daemon.netController.NetworkByID(container.NetworkSettings.NetworkID) + if err != nil { + logrus.Errorf("error locating network id %s: %v", container.NetworkSettings.NetworkID, err) + return + } + + ep, err := n.EndpointByID(container.NetworkSettings.EndpointID) + if err != nil { + logrus.Errorf("error locating endpoint id %s: %v", container.NetworkSettings.EndpointID, err) + return + } + + if err := ep.Leave(container.ID); err != nil { + logrus.Errorf("leaving endpoint failed: %v", err) + } + + if err := ep.Delete(); err != nil { + logrus.Errorf("deleting endpoint failed: %v", err) + } + container.NetworkSettings = &network.Settings{} } -func (container *Container) RestoreNetwork() error { - mode := container.hostConfig.NetworkMode - // Don't attempt a restore if we previously didn't allocate networking. - // This might be a legacy container with no network allocated, in which case the - // allocation will happen once and for all at start. - if !container.isNetworkAllocated() || container.Config.NetworkDisabled || !mode.IsPrivate() { - return nil - } - - // Re-allocate the interface with the same IP and MAC address. - if _, err := bridge.Allocate(container.ID, container.NetworkSettings.MacAddress, container.NetworkSettings.IPAddress, ""); err != nil { - return err - } - - // Re-allocate any previously allocated ports. - for port := range container.NetworkSettings.Ports { - if err := container.allocatePort(port, container.NetworkSettings.Ports); err != nil { - return err - } - } - return nil -} - func disableAllActiveLinks(container *Container) { if container.activeLinks != nil { for _, link := range container.activeLinks { @@ -878,6 +950,10 @@ func (container *Container) DisableLink(name string) { if container.activeLinks != nil { if link, exists := container.activeLinks[name]; exists { link.Disable() + delete(container.activeLinks, name) + if err := container.UpdateNetwork(); err != nil { + logrus.Debugf("Could not update network to remove link: %v", err) + } } else { logrus.Debugf("Could not find active link for %s", name) } diff --git a/daemon/daemon.go b/daemon/daemon.go index 465213517f..96c4e9c736 100644 --- a/daemon/daemon.go +++ b/daemon/daemon.go @@ -1,10 +1,10 @@ package daemon import ( - "bytes" "fmt" "io" "io/ioutil" + "net" "os" "path" "path/filepath" @@ -15,6 +15,9 @@ import ( "time" "github.com/docker/libcontainer/label" + "github.com/docker/libnetwork" + "github.com/docker/libnetwork/netlabel" + "github.com/docker/libnetwork/options" "github.com/Sirupsen/logrus" "github.com/docker/docker/api" @@ -26,7 +29,6 @@ import ( _ "github.com/docker/docker/daemon/graphdriver/vfs" "github.com/docker/docker/daemon/logger" "github.com/docker/docker/daemon/network" - "github.com/docker/docker/daemon/networkdriver/bridge" "github.com/docker/docker/graph" "github.com/docker/docker/image" "github.com/docker/docker/pkg/archive" @@ -37,7 +39,6 @@ import ( "github.com/docker/docker/pkg/namesgenerator" "github.com/docker/docker/pkg/parsers" "github.com/docker/docker/pkg/parsers/kernel" - "github.com/docker/docker/pkg/resolvconf" "github.com/docker/docker/pkg/stringid" "github.com/docker/docker/pkg/sysinfo" "github.com/docker/docker/pkg/truncindex" @@ -46,8 +47,6 @@ import ( "github.com/docker/docker/trust" "github.com/docker/docker/utils" "github.com/docker/docker/volumes" - - "github.com/go-fsnotify/fsnotify" ) var ( @@ -109,6 +108,7 @@ type Daemon struct { defaultLogConfig runconfig.LogConfig RegistryService *registry.Service EventsService *events.Events + netController libnetwork.NetworkController } // Get looks for a container using the provided information, which could be @@ -349,61 +349,6 @@ func (daemon *Daemon) restore() error { return nil } -// set up the watch on the host's /etc/resolv.conf so that we can update container's -// live resolv.conf when the network changes on the host -func (daemon *Daemon) setupResolvconfWatcher() error { - - watcher, err := fsnotify.NewWatcher() - if err != nil { - return err - } - - //this goroutine listens for the events on the watch we add - //on the resolv.conf file on the host - go func() { - for { - select { - case event := <-watcher.Events: - if event.Name == "/etc/resolv.conf" && - (event.Op&(fsnotify.Write|fsnotify.Create) != 0) { - // verify a real change happened before we go further--a file write may have happened - // without an actual change to the file - updatedResolvConf, newResolvConfHash, err := resolvconf.GetIfChanged() - if err != nil { - logrus.Debugf("Error retrieving updated host resolv.conf: %v", err) - } else if updatedResolvConf != nil { - // because the new host resolv.conf might have localhost nameservers.. - updatedResolvConf, modified := resolvconf.FilterResolvDns(updatedResolvConf, daemon.config.Bridge.EnableIPv6) - if modified { - // changes have occurred during localhost cleanup: generate an updated hash - newHash, err := ioutils.HashData(bytes.NewReader(updatedResolvConf)) - if err != nil { - logrus.Debugf("Error generating hash of new resolv.conf: %v", err) - } else { - newResolvConfHash = newHash - } - } - logrus.Debug("host network resolv.conf changed--walking container list for updates") - contList := daemon.containers.List() - for _, container := range contList { - if err := container.updateResolvConf(updatedResolvConf, newResolvConfHash); err != nil { - logrus.Debugf("Error on resolv.conf update check for container ID: %s: %v", container.ID, err) - } - } - } - } - case err := <-watcher.Errors: - logrus.Debugf("host resolv.conf notify error: %v", err) - } - } - }() - - if err := watcher.Add("/etc"); err != nil { - return err - } - return nil -} - func (daemon *Daemon) checkDeprecatedExpose(config *runconfig.Config) bool { if config != nil { if config.PortSpecs != nil { @@ -727,18 +672,15 @@ func (daemon *Daemon) RegisterLinks(container *Container, hostConfig *runconfig. } func NewDaemon(config *Config, registryService *registry.Service) (daemon *Daemon, err error) { - if config.Mtu == 0 { - config.Mtu = getDefaultNetworkMtu() - } // Check for mutually incompatible config options if config.Bridge.Iface != "" && config.Bridge.IP != "" { return nil, fmt.Errorf("You specified -b & --bip, mutually exclusive options. Please specify only one.") } - if !config.Bridge.EnableIptables && !config.Bridge.InterContainerCommunication { + if !config.Bridge.EnableIPTables && !config.Bridge.InterContainerCommunication { return nil, fmt.Errorf("You specified --iptables=false with --icc=false. ICC uses iptables to function. Please set --icc or --iptables to true.") } - if !config.Bridge.EnableIptables && config.Bridge.EnableIpMasq { - config.Bridge.EnableIpMasq = false + if !config.Bridge.EnableIPTables && config.Bridge.EnableIPMasq { + config.Bridge.EnableIPMasq = false } config.DisableNetwork = config.Bridge.Iface == disableNetworkBridge @@ -882,8 +824,9 @@ func NewDaemon(config *Config, registryService *registry.Service) (daemon *Daemo } if !config.DisableNetwork { - if err := bridge.InitDriver(&config.Bridge); err != nil { - return nil, fmt.Errorf("Error initializing Bridge: %v", err) + d.netController, err = initNetworkController(config) + if err != nil { + return nil, fmt.Errorf("Error initializing network controller: %v", err) } } @@ -942,12 +885,97 @@ func NewDaemon(config *Config, registryService *registry.Service) (daemon *Daemo return nil, err } - // set up filesystem watch on resolv.conf for network changes - if err := d.setupResolvconfWatcher(); err != nil { - return nil, err + return d, nil +} + +func initNetworkController(config *Config) (libnetwork.NetworkController, error) { + controller, err := libnetwork.New() + if err != nil { + return nil, fmt.Errorf("error obtaining controller instance: %v", err) } - return d, nil + // Initialize default driver "null" + + if err := controller.ConfigureNetworkDriver("null", options.Generic{}); err != nil { + return nil, fmt.Errorf("Error initializing null driver: %v", err) + } + + // Initialize default network on "null" + if _, err := controller.NewNetwork("null", "none"); err != nil { + return nil, fmt.Errorf("Error creating default \"null\" network: %v", err) + } + + // Initialize default driver "host" + if err := controller.ConfigureNetworkDriver("host", options.Generic{}); err != nil { + return nil, fmt.Errorf("Error initializing host driver: %v", err) + } + + // Initialize default network on "host" + if _, err := controller.NewNetwork("host", "host"); err != nil { + return nil, fmt.Errorf("Error creating default \"host\" network: %v", err) + } + + // Initialize default driver "bridge" + option := options.Generic{ + "EnableIPForwarding": config.Bridge.EnableIPForward} + + if err := controller.ConfigureNetworkDriver("bridge", options.Generic{netlabel.GenericData: option}); err != nil { + return nil, fmt.Errorf("Error initializing bridge driver: %v", err) + } + + netOption := options.Generic{ + "BridgeName": config.Bridge.Iface, + "Mtu": config.Mtu, + "EnableIPTables": config.Bridge.EnableIPTables, + "EnableIPMasquerade": config.Bridge.EnableIPMasq, + "EnableICC": config.Bridge.InterContainerCommunication, + "EnableUserlandProxy": config.Bridge.EnableUserlandProxy, + } + + if config.Bridge.IP != "" { + ip, bipNet, err := net.ParseCIDR(config.Bridge.IP) + if err != nil { + return nil, err + } + + bipNet.IP = ip + netOption["AddressIPv4"] = bipNet + } + + if config.Bridge.FixedCIDR != "" { + _, fCIDR, err := net.ParseCIDR(config.Bridge.FixedCIDR) + if err != nil { + return nil, err + } + + netOption["FixedCIDR"] = fCIDR + } + + if config.Bridge.FixedCIDRv6 != "" { + _, fCIDRv6, err := net.ParseCIDR(config.Bridge.FixedCIDRv6) + if err != nil { + return nil, err + } + + netOption["FixedCIDRv6"] = fCIDRv6 + } + + // --ip processing + if config.Bridge.DefaultIP != nil { + netOption["DefaultBindingIP"] = config.Bridge.DefaultIP + } + + // Initialize default network on "bridge" with the same name + _, err = controller.NewNetwork("bridge", "bridge", + libnetwork.NetworkOptionGeneric(options.Generic{ + netlabel.GenericData: netOption, + netlabel.EnableIPv6: config.Bridge.EnableIPv6, + })) + if err != nil { + return nil, fmt.Errorf("Error creating default \"bridge\" network: %v", err) + } + + return controller, nil } func (daemon *Daemon) Shutdown() error { diff --git a/daemon/delete.go b/daemon/delete.go index 464193b283..f82f612ae3 100644 --- a/daemon/delete.go +++ b/daemon/delete.go @@ -35,13 +35,14 @@ func (daemon *Daemon) ContainerRm(name string, config *ContainerRmConfig) error } parentContainer, _ := daemon.Get(pe.ID()) + if err := daemon.ContainerGraph().Delete(name); err != nil { + return err + } + if parentContainer != nil { parentContainer.DisableLink(n) } - if err := daemon.ContainerGraph().Delete(name); err != nil { - return err - } return nil } diff --git a/daemon/execdriver/driver.go b/daemon/execdriver/driver.go index 554ad82c1b..eca77e921e 100644 --- a/daemon/execdriver/driver.go +++ b/daemon/execdriver/driver.go @@ -72,6 +72,7 @@ type Network struct { Interface *NetworkInterface `json:"interface"` // if interface is nil then networking is disabled Mtu int `json:"mtu"` ContainerID string `json:"container_id"` // id of the container to join network. + NamespacePath string `json:"namespace_path"` HostNetworking bool `json:"host_networking"` } diff --git a/daemon/execdriver/lxc/driver.go b/daemon/execdriver/lxc/driver.go index 49db608743..4b5730a3f4 100644 --- a/daemon/execdriver/lxc/driver.go +++ b/daemon/execdriver/lxc/driver.go @@ -12,6 +12,7 @@ import ( "os/exec" "path" "path/filepath" + "runtime" "strconv" "strings" "sync" @@ -30,6 +31,7 @@ import ( "github.com/docker/libcontainer/system" "github.com/docker/libcontainer/user" "github.com/kr/pty" + "github.com/vishvananda/netns" ) const DriverName = "lxc" @@ -80,6 +82,41 @@ func (d *driver) Name() string { return fmt.Sprintf("%s-%s", DriverName, version) } +func setupNetNs(nsPath string) (*os.Process, error) { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + origns, err := netns.Get() + if err != nil { + return nil, err + } + defer origns.Close() + + f, err := os.OpenFile(nsPath, os.O_RDONLY, 0) + if err != nil { + return nil, fmt.Errorf("failed to get network namespace %q: %v", nsPath, err) + } + defer f.Close() + + nsFD := f.Fd() + if err := netns.Set(netns.NsHandle(nsFD)); err != nil { + return nil, fmt.Errorf("failed to set network namespace %q: %v", nsPath, err) + } + defer netns.Set(origns) + + cmd := exec.Command("/bin/sh", "-c", "while true; do sleep 1; done") + if err := cmd.Start(); err != nil { + return nil, fmt.Errorf("failed to start netns process: %v", err) + } + + return cmd.Process, nil +} + +func killNetNsProc(proc *os.Process) { + proc.Kill() + proc.Wait() +} + func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (execdriver.ExitStatus, error) { var ( term execdriver.Terminal @@ -87,6 +124,10 @@ func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba dataPath = d.containerDir(c.ID) ) + if c.Network.NamespacePath == "" && c.Network.ContainerID == "" { + return execdriver.ExitStatus{ExitCode: -1}, fmt.Errorf("empty namespace path for non-container network") + } + container, err := d.createContainer(c) if err != nil { return execdriver.ExitStatus{ExitCode: -1}, err @@ -136,10 +177,20 @@ func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba params = append(params, "-F") } + proc := &os.Process{} if c.Network.ContainerID != "" { params = append(params, "--share-net", c.Network.ContainerID, ) + } else { + proc, err = setupNetNs(c.Network.NamespacePath) + if err != nil { + return execdriver.ExitStatus{ExitCode: -1}, err + } + + pidStr := fmt.Sprintf("%d", proc.Pid) + params = append(params, + "--share-net", pidStr) } if c.Ipc != nil { if c.Ipc.ContainerID != "" { @@ -157,15 +208,6 @@ func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba "--", c.InitPath, ) - if c.Network.Interface != nil { - params = append(params, - "-g", c.Network.Interface.Gateway, - "-i", fmt.Sprintf("%s/%d", c.Network.Interface.IPAddress, c.Network.Interface.IPPrefixLen), - ) - } - params = append(params, - "-mtu", strconv.Itoa(c.Network.Mtu), - ) if c.ProcessConfig.User != "" { params = append(params, "-u", c.ProcessConfig.User) @@ -214,10 +256,12 @@ func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba c.ProcessConfig.Args = append([]string{name}, arg...) if err := createDeviceNodes(c.Rootfs, c.AutoCreatedDevices); err != nil { + killNetNsProc(proc) return execdriver.ExitStatus{ExitCode: -1}, err } if err := c.ProcessConfig.Start(); err != nil { + killNetNsProc(proc) return execdriver.ExitStatus{ExitCode: -1}, err } @@ -245,8 +289,10 @@ func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba // Poll lxc for RUNNING status pid, err := d.waitForStart(c, waitLock) if err != nil { + killNetNsProc(proc) return terminate(err) } + killNetNsProc(proc) cgroupPaths, err := cgroupPaths(c.ID) if err != nil { diff --git a/daemon/execdriver/lxc/lxc_template.go b/daemon/execdriver/lxc/lxc_template.go index 2f88808a08..6bb50e6ab1 100644 --- a/daemon/execdriver/lxc/lxc_template.go +++ b/daemon/execdriver/lxc/lxc_template.go @@ -16,22 +16,7 @@ import ( ) const LxcTemplate = ` -{{if .Network.Interface}} -# network configuration -lxc.network.type = veth -lxc.network.link = {{.Network.Interface.Bridge}} -lxc.network.name = eth0 -lxc.network.mtu = {{.Network.Mtu}} -lxc.network.flags = up -{{else if .Network.HostNetworking}} lxc.network.type = none -{{else}} -# network is disabled (-n=false) -lxc.network.type = empty -lxc.network.flags = up -lxc.network.mtu = {{.Network.Mtu}} -{{end}} - # root filesystem {{$ROOTFS := .Rootfs}} lxc.rootfs = {{$ROOTFS}} @@ -145,6 +130,7 @@ lxc.network.ipv4.gateway = {{.Network.Interface.Gateway}} {{if .Network.Interface.MacAddress}} lxc.network.hwaddr = {{.Network.Interface.MacAddress}} {{end}} +{{end}} {{if .ProcessConfig.Env}} lxc.utsname = {{getHostname .ProcessConfig.Env}} {{end}} @@ -164,7 +150,6 @@ lxc.cap.drop = {{.}} {{end}} {{end}} {{end}} -{{end}} ` var LxcTemplateCompiled *template.Template diff --git a/daemon/execdriver/lxc/lxc_template_unit_test.go b/daemon/execdriver/lxc/lxc_template_unit_test.go index fcac6a3e57..904fa120a8 100644 --- a/daemon/execdriver/lxc/lxc_template_unit_test.go +++ b/daemon/execdriver/lxc/lxc_template_unit_test.go @@ -264,13 +264,8 @@ func TestCustomLxcConfigMisc(t *testing.T) { "lxc.cgroup.cpuset.cpus = 0,1", }, Network: &execdriver.Network{ - Mtu: 1500, - Interface: &execdriver.NetworkInterface{ - Gateway: "10.10.10.1", - IPAddress: "10.10.10.10", - IPPrefixLen: 24, - Bridge: "docker0", - }, + Mtu: 1500, + Interface: nil, }, ProcessConfig: processConfig, CapAdd: []string{"net_admin", "syslog"}, @@ -282,13 +277,6 @@ func TestCustomLxcConfigMisc(t *testing.T) { if err != nil { t.Fatal(err) } - // network - grepFile(t, p, "lxc.network.type = veth") - grepFile(t, p, "lxc.network.link = docker0") - grepFile(t, p, "lxc.network.name = eth0") - grepFile(t, p, "lxc.network.ipv4 = 10.10.10.10/24") - grepFile(t, p, "lxc.network.ipv4.gateway = 10.10.10.1") - grepFile(t, p, "lxc.network.flags = up") grepFile(t, p, "lxc.aa_profile = lxc-container-default-with-nesting") // hostname grepFile(t, p, "lxc.utsname = testhost") @@ -329,13 +317,8 @@ func TestCustomLxcConfigMiscOverride(t *testing.T) { "lxc.network.ipv4 = 172.0.0.1", }, Network: &execdriver.Network{ - Mtu: 1500, - Interface: &execdriver.NetworkInterface{ - Gateway: "10.10.10.1", - IPAddress: "10.10.10.10", - IPPrefixLen: 24, - Bridge: "docker0", - }, + Mtu: 1500, + Interface: nil, }, ProcessConfig: processConfig, CapAdd: []string{"NET_ADMIN", "SYSLOG"}, @@ -346,13 +329,6 @@ func TestCustomLxcConfigMiscOverride(t *testing.T) { if err != nil { t.Fatal(err) } - // network - grepFile(t, p, "lxc.network.type = veth") - grepFile(t, p, "lxc.network.link = docker0") - grepFile(t, p, "lxc.network.name = eth0") - grepFile(t, p, "lxc.network.ipv4 = 172.0.0.1") - grepFile(t, p, "lxc.network.ipv4.gateway = 10.10.10.1") - grepFile(t, p, "lxc.network.flags = up") // hostname grepFile(t, p, "lxc.utsname = testhost") diff --git a/daemon/execdriver/native/create.go b/daemon/execdriver/native/create.go index ff0da9a276..6d2a1e8c45 100644 --- a/daemon/execdriver/native/create.go +++ b/daemon/execdriver/native/create.go @@ -89,40 +89,6 @@ func generateIfaceName() (string, error) { } func (d *driver) createNetwork(container *configs.Config, c *execdriver.Command) error { - if c.Network.HostNetworking { - container.Namespaces.Remove(configs.NEWNET) - return nil - } - - container.Networks = []*configs.Network{ - { - Type: "loopback", - }, - } - - iName, err := generateIfaceName() - if err != nil { - return err - } - if c.Network.Interface != nil { - vethNetwork := configs.Network{ - Name: "eth0", - HostInterfaceName: iName, - Mtu: c.Network.Mtu, - Address: fmt.Sprintf("%s/%d", c.Network.Interface.IPAddress, c.Network.Interface.IPPrefixLen), - MacAddress: c.Network.Interface.MacAddress, - Gateway: c.Network.Interface.Gateway, - Type: "veth", - Bridge: c.Network.Interface.Bridge, - HairpinMode: c.Network.Interface.HairpinMode, - } - if c.Network.Interface.GlobalIPv6Address != "" { - vethNetwork.IPv6Address = fmt.Sprintf("%s/%d", c.Network.Interface.GlobalIPv6Address, c.Network.Interface.GlobalIPv6PrefixLen) - vethNetwork.IPv6Gateway = c.Network.Interface.IPv6Gateway - } - container.Networks = append(container.Networks, &vethNetwork) - } - if c.Network.ContainerID != "" { d.Lock() active := d.activeContainers[c.Network.ContainerID] @@ -138,8 +104,14 @@ func (d *driver) createNetwork(container *configs.Config, c *execdriver.Command) } container.Namespaces.Add(configs.NEWNET, state.NamespacePaths[configs.NEWNET]) + return nil } + if c.Network.NamespacePath == "" { + return fmt.Errorf("network namespace path is empty") + } + + container.Namespaces.Add(configs.NEWNET, c.Network.NamespacePath) return nil } diff --git a/daemon/network/settings.go b/daemon/network/settings.go index 91d61160a6..ca60ff1980 100644 --- a/daemon/network/settings.go +++ b/daemon/network/settings.go @@ -2,18 +2,28 @@ package network import "github.com/docker/docker/nat" +type Address struct { + Addr string + PrefixLen int +} + type Settings struct { - IPAddress string - IPPrefixLen int - MacAddress string - LinkLocalIPv6Address string - LinkLocalIPv6PrefixLen int + Bridge string + EndpointID string + Gateway string GlobalIPv6Address string GlobalIPv6PrefixLen int - Gateway string + HairpinMode bool + IPAddress string + IPPrefixLen int IPv6Gateway string - Bridge string + LinkLocalIPv6Address string + LinkLocalIPv6PrefixLen int + MacAddress string + NetworkID string PortMapping map[string]map[string]string // Deprecated Ports nat.PortMap - HairpinMode bool + SandboxKey string + SecondaryIPAddresses []Address + SecondaryIPv6Addresses []Address } diff --git a/daemon/networkdriver/bridge/driver.go b/daemon/networkdriver/bridge/driver.go deleted file mode 100644 index 3449f4a1ef..0000000000 --- a/daemon/networkdriver/bridge/driver.go +++ /dev/null @@ -1,811 +0,0 @@ -package bridge - -import ( - "encoding/hex" - "errors" - "fmt" - "io/ioutil" - "net" - "os" - "os/exec" - "path/filepath" - "strconv" - "strings" - "sync" - - "github.com/Sirupsen/logrus" - "github.com/docker/docker/daemon/network" - "github.com/docker/docker/daemon/networkdriver" - "github.com/docker/docker/daemon/networkdriver/ipallocator" - "github.com/docker/docker/daemon/networkdriver/portmapper" - "github.com/docker/docker/nat" - "github.com/docker/docker/pkg/iptables" - "github.com/docker/docker/pkg/parsers/kernel" - "github.com/docker/docker/pkg/resolvconf" - "github.com/docker/libcontainer/netlink" -) - -const ( - DefaultNetworkBridge = "docker0" - MaxAllocatedPortAttempts = 10 -) - -// Network interface represents the networking stack of a container -type networkInterface struct { - IP net.IP - IPv6 net.IP - PortMappings []net.Addr // There are mappings to the host interfaces -} - -type ifaces struct { - c map[string]*networkInterface - sync.Mutex -} - -func (i *ifaces) Set(key string, n *networkInterface) { - i.Lock() - i.c[key] = n - i.Unlock() -} - -func (i *ifaces) Get(key string) *networkInterface { - i.Lock() - res := i.c[key] - i.Unlock() - return res -} - -var ( - addrs = []string{ - // Here we don't follow the convention of using the 1st IP of the range for the gateway. - // This is to use the same gateway IPs as the /24 ranges, which predate the /16 ranges. - // In theory this shouldn't matter - in practice there's bound to be a few scripts relying - // on the internal addressing or other things like that. - // They shouldn't, but hey, let's not break them unless we really have to. - "172.17.42.1/16", // Don't use 172.16.0.0/16, it conflicts with EC2 DNS 172.16.0.23 - "10.0.42.1/16", // Don't even try using the entire /8, that's too intrusive - "10.1.42.1/16", - "10.42.42.1/16", - "172.16.42.1/24", - "172.16.43.1/24", - "172.16.44.1/24", - "10.0.42.1/24", - "10.0.43.1/24", - "192.168.42.1/24", - "192.168.43.1/24", - "192.168.44.1/24", - } - - bridgeIface string - bridgeIPv4Network *net.IPNet - gatewayIPv4 net.IP - bridgeIPv6Addr net.IP - globalIPv6Network *net.IPNet - gatewayIPv6 net.IP - portMapper *portmapper.PortMapper - once sync.Once - hairpinMode bool - - defaultBindingIP = net.ParseIP("0.0.0.0") - currentInterfaces = ifaces{c: make(map[string]*networkInterface)} - ipAllocator = ipallocator.New() -) - -func initPortMapper() { - once.Do(func() { - portMapper = portmapper.New() - }) -} - -type Config struct { - EnableIPv6 bool - EnableIptables bool - EnableIpForward bool - EnableIpMasq bool - EnableUserlandProxy bool - DefaultIp net.IP - Iface string - IP string - FixedCIDR string - FixedCIDRv6 string - DefaultGatewayIPv4 string - DefaultGatewayIPv6 string - InterContainerCommunication bool -} - -func InitDriver(config *Config) error { - var ( - networkv4 *net.IPNet - networkv6 *net.IPNet - addrv4 net.Addr - addrsv6 []net.Addr - bridgeIPv6 = "fe80::1/64" - ) - - // try to modprobe bridge first - // see gh#12177 - if out, err := exec.Command("modprobe", "-va", "bridge", "nf_nat", "br_netfilter").Output(); err != nil { - logrus.Warnf("Running modprobe bridge nf_nat failed with message: %s, error: %v", out, err) - } - - initPortMapper() - - if config.DefaultIp != nil { - defaultBindingIP = config.DefaultIp - } - - hairpinMode = !config.EnableUserlandProxy - - bridgeIface = config.Iface - usingDefaultBridge := false - if bridgeIface == "" { - usingDefaultBridge = true - bridgeIface = DefaultNetworkBridge - } - - addrv4, addrsv6, err := networkdriver.GetIfaceAddr(bridgeIface) - - if err != nil { - // No Bridge existent, create one - // If we're not using the default bridge, fail without trying to create it - if !usingDefaultBridge { - return err - } - - logrus.Info("Bridge interface not found, trying to create it") - - // If the iface is not found, try to create it - if err := configureBridge(config.IP, bridgeIPv6, config.EnableIPv6); err != nil { - logrus.Errorf("Could not configure Bridge: %s", err) - return err - } - - addrv4, addrsv6, err = networkdriver.GetIfaceAddr(bridgeIface) - if err != nil { - return err - } - - if config.FixedCIDRv6 != "" { - // Setting route to global IPv6 subnet - logrus.Infof("Adding route to IPv6 network %q via device %q", config.FixedCIDRv6, bridgeIface) - if err := netlink.AddRoute(config.FixedCIDRv6, "", "", bridgeIface); err != nil { - logrus.Fatalf("Could not add route to IPv6 network %q via device %q", config.FixedCIDRv6, bridgeIface) - } - } - } else { - // Bridge exists already, getting info... - // Validate that the bridge ip matches the ip specified by BridgeIP - if config.IP != "" { - networkv4 = addrv4.(*net.IPNet) - bip, _, err := net.ParseCIDR(config.IP) - if err != nil { - return err - } - if !networkv4.IP.Equal(bip) { - return fmt.Errorf("Bridge ip (%s) does not match existing bridge configuration %s", networkv4.IP, bip) - } - } - - // A bridge might exist but not have any IPv6 addr associated with it yet - // (for example, an existing Docker installation that has only been used - // with IPv4 and docker0 already is set up) In that case, we can perform - // the bridge init for IPv6 here, else we will error out below if --ipv6=true - if len(addrsv6) == 0 && config.EnableIPv6 { - if err := setupIPv6Bridge(bridgeIPv6); err != nil { - return err - } - // Recheck addresses now that IPv6 is setup on the bridge - addrv4, addrsv6, err = networkdriver.GetIfaceAddr(bridgeIface) - if err != nil { - return err - } - } - - // TODO: Check if route to config.FixedCIDRv6 is set - } - - if config.EnableIPv6 { - bip6, _, err := net.ParseCIDR(bridgeIPv6) - if err != nil { - return err - } - found := false - for _, addrv6 := range addrsv6 { - networkv6 = addrv6.(*net.IPNet) - if networkv6.IP.Equal(bip6) { - found = true - break - } - } - if !found { - return fmt.Errorf("Bridge IPv6 does not match existing bridge configuration %s", bip6) - } - } - - networkv4 = addrv4.(*net.IPNet) - - if config.EnableIPv6 { - if len(addrsv6) == 0 { - return errors.New("IPv6 enabled but no IPv6 detected") - } - bridgeIPv6Addr = networkv6.IP - } - - if config.EnableIptables { - if err := iptables.FirewalldInit(); err != nil { - logrus.Debugf("Error initializing firewalld: %v", err) - } - } - - // Configure iptables for link support - if config.EnableIptables { - if err := setupIPTables(addrv4, config.InterContainerCommunication, config.EnableIpMasq); err != nil { - logrus.Errorf("Error configuring iptables: %s", err) - return err - } - // call this on Firewalld reload - iptables.OnReloaded(func() { setupIPTables(addrv4, config.InterContainerCommunication, config.EnableIpMasq) }) - } - - if config.EnableIpForward { - // Enable IPv4 forwarding - if err := ioutil.WriteFile("/proc/sys/net/ipv4/ip_forward", []byte{'1', '\n'}, 0644); err != nil { - logrus.Warnf("Unable to enable IPv4 forwarding: %v", err) - } - - if config.FixedCIDRv6 != "" { - // Enable IPv6 forwarding - if err := ioutil.WriteFile("/proc/sys/net/ipv6/conf/default/forwarding", []byte{'1', '\n'}, 0644); err != nil { - logrus.Warnf("Unable to enable IPv6 default forwarding: %v", err) - } - if err := ioutil.WriteFile("/proc/sys/net/ipv6/conf/all/forwarding", []byte{'1', '\n'}, 0644); err != nil { - logrus.Warnf("Unable to enable IPv6 all forwarding: %v", err) - } - } - } - - if hairpinMode { - // Enable loopback adresses routing - sysPath := filepath.Join("/proc/sys/net/ipv4/conf", bridgeIface, "route_localnet") - if err := ioutil.WriteFile(sysPath, []byte{'1', '\n'}, 0644); err != nil { - logrus.Warnf("Unable to enable local routing for hairpin mode: %v", err) - } - } - - // We can always try removing the iptables - if err := iptables.RemoveExistingChain("DOCKER", iptables.Nat); err != nil { - return err - } - - if config.EnableIptables { - _, err := iptables.NewChain("DOCKER", bridgeIface, iptables.Nat, hairpinMode) - if err != nil { - return err - } - // call this on Firewalld reload - iptables.OnReloaded(func() { iptables.NewChain("DOCKER", bridgeIface, iptables.Nat, hairpinMode) }) - chain, err := iptables.NewChain("DOCKER", bridgeIface, iptables.Filter, hairpinMode) - if err != nil { - return err - } - // call this on Firewalld reload - iptables.OnReloaded(func() { iptables.NewChain("DOCKER", bridgeIface, iptables.Filter, hairpinMode) }) - - portMapper.SetIptablesChain(chain) - } - - bridgeIPv4Network = networkv4 - if config.FixedCIDR != "" { - _, subnet, err := net.ParseCIDR(config.FixedCIDR) - if err != nil { - return err - } - logrus.Debugf("Subnet: %v", subnet) - if err := ipAllocator.RegisterSubnet(bridgeIPv4Network, subnet); err != nil { - logrus.Errorf("Error registering subnet for IPv4 bridge network: %s", err) - return err - } - } - - gateway, err := requestDefaultGateway(config.DefaultGatewayIPv4, bridgeIPv4Network) - if err != nil { - return err - } - gatewayIPv4 = gateway - - if config.FixedCIDRv6 != "" { - _, subnet, err := net.ParseCIDR(config.FixedCIDRv6) - if err != nil { - return err - } - logrus.Debugf("Subnet: %v", subnet) - if err := ipAllocator.RegisterSubnet(subnet, subnet); err != nil { - logrus.Errorf("Error registering subnet for IPv6 bridge network: %s", err) - return err - } - globalIPv6Network = subnet - - gateway, err := requestDefaultGateway(config.DefaultGatewayIPv6, globalIPv6Network) - if err != nil { - return err - } - gatewayIPv6 = gateway - } - - // Block BridgeIP in IP allocator - ipAllocator.RequestIP(bridgeIPv4Network, bridgeIPv4Network.IP) - - if config.EnableIptables { - iptables.OnReloaded(portMapper.ReMapAll) // call this on Firewalld reload - } - - return nil -} - -func setupIPTables(addr net.Addr, icc, ipmasq bool) error { - // Enable NAT - - if ipmasq { - natArgs := []string{"-s", addr.String(), "!", "-o", bridgeIface, "-j", "MASQUERADE"} - - if !iptables.Exists(iptables.Nat, "POSTROUTING", natArgs...) { - if output, err := iptables.Raw(append([]string{ - "-t", string(iptables.Nat), "-I", "POSTROUTING"}, natArgs...)...); err != nil { - return fmt.Errorf("Unable to enable network bridge NAT: %s", err) - } else if len(output) != 0 { - return iptables.ChainError{Chain: "POSTROUTING", Output: output} - } - } - } - - var ( - args = []string{"-i", bridgeIface, "-o", bridgeIface, "-j"} - acceptArgs = append(args, "ACCEPT") - dropArgs = append(args, "DROP") - ) - - if !icc { - iptables.Raw(append([]string{"-D", "FORWARD"}, acceptArgs...)...) - - if !iptables.Exists(iptables.Filter, "FORWARD", dropArgs...) { - logrus.Debugf("Disable inter-container communication") - if output, err := iptables.Raw(append([]string{"-A", "FORWARD"}, dropArgs...)...); err != nil { - return fmt.Errorf("Unable to prevent intercontainer communication: %s", err) - } else if len(output) != 0 { - return fmt.Errorf("Error disabling intercontainer communication: %s", output) - } - } - } else { - iptables.Raw(append([]string{"-D", "FORWARD"}, dropArgs...)...) - - if !iptables.Exists(iptables.Filter, "FORWARD", acceptArgs...) { - logrus.Debugf("Enable inter-container communication") - if output, err := iptables.Raw(append([]string{"-A", "FORWARD"}, acceptArgs...)...); err != nil { - return fmt.Errorf("Unable to allow intercontainer communication: %s", err) - } else if len(output) != 0 { - return fmt.Errorf("Error enabling intercontainer communication: %s", output) - } - } - } - - // In hairpin mode, masquerade traffic from localhost - if hairpinMode { - masqueradeArgs := []string{"-t", "nat", "-m", "addrtype", "--src-type", "LOCAL", "-o", bridgeIface, "-j", "MASQUERADE"} - if !iptables.Exists(iptables.Filter, "POSTROUTING", masqueradeArgs...) { - if output, err := iptables.Raw(append([]string{"-I", "POSTROUTING"}, masqueradeArgs...)...); err != nil { - return fmt.Errorf("Unable to masquerade local traffic: %s", err) - } else if len(output) != 0 { - return fmt.Errorf("Error iptables masquerade local traffic: %s", output) - } - } - } - - // Accept all non-intercontainer outgoing packets - outgoingArgs := []string{"-i", bridgeIface, "!", "-o", bridgeIface, "-j", "ACCEPT"} - if !iptables.Exists(iptables.Filter, "FORWARD", outgoingArgs...) { - if output, err := iptables.Raw(append([]string{"-I", "FORWARD"}, outgoingArgs...)...); err != nil { - return fmt.Errorf("Unable to allow outgoing packets: %s", err) - } else if len(output) != 0 { - return iptables.ChainError{Chain: "FORWARD outgoing", Output: output} - } - } - - // Accept incoming packets for existing connections - existingArgs := []string{"-o", bridgeIface, "-m", "conntrack", "--ctstate", "RELATED,ESTABLISHED", "-j", "ACCEPT"} - - if !iptables.Exists(iptables.Filter, "FORWARD", existingArgs...) { - if output, err := iptables.Raw(append([]string{"-I", "FORWARD"}, existingArgs...)...); err != nil { - return fmt.Errorf("Unable to allow incoming packets: %s", err) - } else if len(output) != 0 { - return iptables.ChainError{Chain: "FORWARD incoming", Output: output} - } - } - return nil -} - -func RequestPort(ip net.IP, proto string, port int) (int, error) { - initPortMapper() - return portMapper.Allocator.RequestPort(ip, proto, port) -} - -// configureBridge attempts to create and configure a network bridge interface named `bridgeIface` on the host -// If bridgeIP is empty, it will try to find a non-conflicting IP from the Docker-specified private ranges -// If the bridge `bridgeIface` already exists, it will only perform the IP address association with the existing -// bridge (fixes issue #8444) -// If an address which doesn't conflict with existing interfaces can't be found, an error is returned. -func configureBridge(bridgeIP string, bridgeIPv6 string, enableIPv6 bool) error { - nameservers := []string{} - resolvConf, _ := resolvconf.Get() - // 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. - if resolvConf != nil { - nameservers = append(nameservers, resolvconf.GetNameserversAsCIDR(resolvConf)...) - } - - var ifaceAddr string - if len(bridgeIP) != 0 { - _, _, err := net.ParseCIDR(bridgeIP) - if err != nil { - return err - } - ifaceAddr = bridgeIP - } else { - for _, addr := range addrs { - _, dockerNetwork, err := net.ParseCIDR(addr) - if err != nil { - return err - } - if err := networkdriver.CheckNameserverOverlaps(nameservers, dockerNetwork); err == nil { - if err := networkdriver.CheckRouteOverlaps(dockerNetwork); err == nil { - ifaceAddr = addr - break - } else { - logrus.Debugf("%s %s", addr, err) - } - } - } - } - - if ifaceAddr == "" { - return fmt.Errorf("Could not find a free IP address range for interface '%s'. Please configure its address manually and run 'docker -b %s'", bridgeIface, bridgeIface) - } - logrus.Debugf("Creating bridge %s with network %s", bridgeIface, ifaceAddr) - - if err := createBridgeIface(bridgeIface); err != nil { - // The bridge may already exist, therefore we can ignore an "exists" error - if !os.IsExist(err) { - return err - } - } - - iface, err := net.InterfaceByName(bridgeIface) - if err != nil { - return err - } - - ipAddr, ipNet, err := net.ParseCIDR(ifaceAddr) - if err != nil { - return err - } - - if err := netlink.NetworkLinkAddIp(iface, ipAddr, ipNet); err != nil { - return fmt.Errorf("Unable to add private network: %s", err) - } - - if enableIPv6 { - if err := setupIPv6Bridge(bridgeIPv6); err != nil { - return err - } - } - - if err := netlink.NetworkLinkUp(iface); err != nil { - return fmt.Errorf("Unable to start network bridge: %s", err) - } - return nil -} - -func setupIPv6Bridge(bridgeIPv6 string) error { - - iface, err := net.InterfaceByName(bridgeIface) - if err != nil { - return err - } - // Enable IPv6 on the bridge - procFile := "/proc/sys/net/ipv6/conf/" + iface.Name + "/disable_ipv6" - if err := ioutil.WriteFile(procFile, []byte{'0', '\n'}, 0644); err != nil { - return fmt.Errorf("Unable to enable IPv6 addresses on bridge: %v", err) - } - - ipAddr6, ipNet6, err := net.ParseCIDR(bridgeIPv6) - if err != nil { - return fmt.Errorf("Unable to parse bridge IPv6 address: %q, error: %v", bridgeIPv6, err) - } - - if err := netlink.NetworkLinkAddIp(iface, ipAddr6, ipNet6); err != nil { - return fmt.Errorf("Unable to add private IPv6 network: %v", err) - } - - return nil -} - -func requestDefaultGateway(requestedGateway string, network *net.IPNet) (gateway net.IP, err error) { - if requestedGateway != "" { - gateway = net.ParseIP(requestedGateway) - - if gateway == nil { - return nil, fmt.Errorf("Bad parameter: invalid gateway ip %s", requestedGateway) - } - - if !network.Contains(gateway) { - return nil, fmt.Errorf("Gateway ip %s must be part of the network %s", requestedGateway, network.String()) - } - - ipAllocator.RequestIP(network, gateway) - } - - return gateway, nil -} - -func createBridgeIface(name string) error { - kv, err := kernel.GetKernelVersion() - // Only set the bridge's mac address if the kernel version is > 3.3 - // before that it was not supported - setBridgeMacAddr := err == nil && (kv.Kernel >= 3 && kv.Major >= 3) - logrus.Debugf("setting bridge mac address = %v", setBridgeMacAddr) - return netlink.CreateBridge(name, setBridgeMacAddr) -} - -// Generate a IEEE802 compliant MAC address from the given IP address. -// -// The generator is guaranteed to be consistent: the same IP will always yield the same -// MAC address. This is to avoid ARP cache issues. -func generateMacAddr(ip net.IP) net.HardwareAddr { - hw := make(net.HardwareAddr, 6) - - // The first byte of the MAC address has to comply with these rules: - // 1. Unicast: Set the least-significant bit to 0. - // 2. Address is locally administered: Set the second-least-significant bit (U/L) to 1. - // 3. As "small" as possible: The veth address has to be "smaller" than the bridge address. - hw[0] = 0x02 - - // The first 24 bits of the MAC represent the Organizationally Unique Identifier (OUI). - // Since this address is locally administered, we can do whatever we want as long as - // it doesn't conflict with other addresses. - hw[1] = 0x42 - - // Insert the IP address into the last 32 bits of the MAC address. - // This is a simple way to guarantee the address will be consistent and unique. - copy(hw[2:], ip.To4()) - - return hw -} - -func linkLocalIPv6FromMac(mac string) (string, error) { - hx := strings.Replace(mac, ":", "", -1) - hw, err := hex.DecodeString(hx) - if err != nil { - return "", errors.New("Could not parse MAC address " + mac) - } - - hw[0] ^= 0x2 - - return fmt.Sprintf("fe80::%x%x:%xff:fe%x:%x%x/64", hw[0], hw[1], hw[2], hw[3], hw[4], hw[5]), nil -} - -// Allocate a network interface -func Allocate(id, requestedMac, requestedIP, requestedIPv6 string) (*network.Settings, error) { - var ( - ip net.IP - mac net.HardwareAddr - err error - globalIPv6 net.IP - defaultGWIPv4 net.IP - defaultGWIPv6 net.IP - ) - - ip, err = ipAllocator.RequestIP(bridgeIPv4Network, net.ParseIP(requestedIP)) - if err != nil { - return nil, err - } - - // If no explicit mac address was given, generate one from the IP address. - if mac, err = net.ParseMAC(requestedMac); err != nil { - mac = generateMacAddr(ip) - } - - if globalIPv6Network != nil { - // If globalIPv6Network Size is at least a /80 subnet generate IPv6 address from MAC address - netmaskOnes, _ := globalIPv6Network.Mask.Size() - ipv6 := net.ParseIP(requestedIPv6) - if ipv6 == nil && netmaskOnes <= 80 { - ipv6 = make(net.IP, len(globalIPv6Network.IP)) - copy(ipv6, globalIPv6Network.IP) - for i, h := range mac { - ipv6[i+10] = h - } - } - - globalIPv6, err = ipAllocator.RequestIP(globalIPv6Network, ipv6) - if err != nil { - logrus.Errorf("Allocator: RequestIP v6: %v", err) - return nil, err - } - logrus.Infof("Allocated IPv6 %s", globalIPv6) - } - - maskSize, _ := bridgeIPv4Network.Mask.Size() - - if gatewayIPv4 != nil { - defaultGWIPv4 = gatewayIPv4 - } else { - defaultGWIPv4 = bridgeIPv4Network.IP - } - - if gatewayIPv6 != nil { - defaultGWIPv6 = gatewayIPv6 - } else { - defaultGWIPv6 = bridgeIPv6Addr - } - - // If linklocal IPv6 - localIPv6Net, err := linkLocalIPv6FromMac(mac.String()) - if err != nil { - return nil, err - } - localIPv6, _, _ := net.ParseCIDR(localIPv6Net) - - networkSettings := &network.Settings{ - IPAddress: ip.String(), - Gateway: defaultGWIPv4.String(), - MacAddress: mac.String(), - Bridge: bridgeIface, - IPPrefixLen: maskSize, - LinkLocalIPv6Address: localIPv6.String(), - HairpinMode: hairpinMode, - } - - if globalIPv6Network != nil { - networkSettings.GlobalIPv6Address = globalIPv6.String() - maskV6Size, _ := globalIPv6Network.Mask.Size() - networkSettings.GlobalIPv6PrefixLen = maskV6Size - networkSettings.IPv6Gateway = defaultGWIPv6.String() - } - - currentInterfaces.Set(id, &networkInterface{ - IP: ip, - IPv6: globalIPv6, - }) - - return networkSettings, nil -} - -// Release an interface for a select ip -func Release(id string) { - var containerInterface = currentInterfaces.Get(id) - - if containerInterface == nil { - logrus.Warnf("No network information to release for %s", id) - return - } - - for _, nat := range containerInterface.PortMappings { - if err := portMapper.Unmap(nat); err != nil { - logrus.Infof("Unable to unmap port %s: %s", nat, err) - } - } - - if err := ipAllocator.ReleaseIP(bridgeIPv4Network, containerInterface.IP); err != nil { - logrus.Infof("Unable to release IPv4 %s", err) - } - if globalIPv6Network != nil { - if err := ipAllocator.ReleaseIP(globalIPv6Network, containerInterface.IPv6); err != nil { - logrus.Infof("Unable to release IPv6 %s", err) - } - } -} - -// Allocate an external port and map it to the interface -func AllocatePort(id string, port nat.Port, binding nat.PortBinding) (nat.PortBinding, error) { - var ( - ip = defaultBindingIP - proto = port.Proto() - containerPort = port.Int() - network = currentInterfaces.Get(id) - ) - - if binding.HostIp != "" { - ip = net.ParseIP(binding.HostIp) - if ip == nil { - return nat.PortBinding{}, fmt.Errorf("Bad parameter: invalid host ip %s", binding.HostIp) - } - } - - // host ip, proto, and host port - var container net.Addr - switch proto { - case "tcp": - container = &net.TCPAddr{IP: network.IP, Port: containerPort} - case "udp": - container = &net.UDPAddr{IP: network.IP, Port: containerPort} - default: - return nat.PortBinding{}, fmt.Errorf("unsupported address type %s", proto) - } - - // - // Try up to 10 times to get a port that's not already allocated. - // - // In the event of failure to bind, return the error that portmapper.Map - // yields. - // - - var ( - host net.Addr - err error - ) - hostPort, err := nat.ParsePort(binding.HostPort) - if err != nil { - return nat.PortBinding{}, err - } - for i := 0; i < MaxAllocatedPortAttempts; i++ { - if host, err = portMapper.Map(container, ip, hostPort, !hairpinMode); err == nil { - break - } - // There is no point in immediately retrying to map an explicitly - // chosen port. - if hostPort != 0 { - logrus.Warnf("Failed to allocate and map port %d: %s", hostPort, err) - break - } - logrus.Warnf("Failed to allocate and map port: %s, retry: %d", err, i+1) - } - - if err != nil { - return nat.PortBinding{}, err - } - - network.PortMappings = append(network.PortMappings, host) - - switch netAddr := host.(type) { - case *net.TCPAddr: - return nat.PortBinding{HostIp: netAddr.IP.String(), HostPort: strconv.Itoa(netAddr.Port)}, nil - case *net.UDPAddr: - return nat.PortBinding{HostIp: netAddr.IP.String(), HostPort: strconv.Itoa(netAddr.Port)}, nil - default: - return nat.PortBinding{}, fmt.Errorf("unsupported address type %T", netAddr) - } -} - -//TODO: should it return something more than just an error? -func LinkContainers(action, parentIP, childIP string, ports []nat.Port, ignoreErrors bool) error { - var nfAction iptables.Action - - switch action { - case "-A": - nfAction = iptables.Append - case "-I": - nfAction = iptables.Insert - case "-D": - nfAction = iptables.Delete - default: - return fmt.Errorf("Invalid action '%s' specified", action) - } - - ip1 := net.ParseIP(parentIP) - if ip1 == nil { - return fmt.Errorf("Parent IP '%s' is invalid", parentIP) - } - ip2 := net.ParseIP(childIP) - if ip2 == nil { - return fmt.Errorf("Child IP '%s' is invalid", childIP) - } - - chain := iptables.Chain{Name: "DOCKER", Bridge: bridgeIface} - for _, port := range ports { - if err := chain.Link(nfAction, ip1, ip2, port.Int(), port.Proto()); !ignoreErrors && err != nil { - return err - } - } - return nil -} diff --git a/daemon/networkdriver/bridge/driver_test.go b/daemon/networkdriver/bridge/driver_test.go deleted file mode 100644 index c82acb86a2..0000000000 --- a/daemon/networkdriver/bridge/driver_test.go +++ /dev/null @@ -1,193 +0,0 @@ -package bridge - -import ( - "fmt" - "net" - "strconv" - "testing" - - "github.com/docker/docker/daemon/network" - "github.com/docker/docker/daemon/networkdriver/portmapper" - "github.com/docker/docker/nat" - "github.com/docker/docker/pkg/iptables" -) - -func init() { - // reset the new proxy command for mocking out the userland proxy in tests - portmapper.NewProxy = portmapper.NewMockProxyCommand -} - -func findFreePort(t *testing.T) string { - l, err := net.Listen("tcp", ":0") - if err != nil { - t.Fatal("Failed to find a free port") - } - defer l.Close() - - result, err := net.ResolveTCPAddr("tcp", l.Addr().String()) - if err != nil { - t.Fatal("Failed to resolve address to identify free port") - } - return strconv.Itoa(result.Port) -} - -func TestAllocatePortDetection(t *testing.T) { - freePort := findFreePort(t) - - if err := InitDriver(new(Config)); err != nil { - t.Fatal("Failed to initialize network driver") - } - - // Allocate interface - if _, err := Allocate("container_id", "", "", ""); err != nil { - t.Fatal("Failed to allocate network interface") - } - - port := nat.Port(freePort + "/tcp") - binding := nat.PortBinding{HostIp: "127.0.0.1", HostPort: freePort} - - // Allocate same port twice, expect failure on second call - if _, err := AllocatePort("container_id", port, binding); err != nil { - t.Fatal("Failed to find a free port to allocate") - } - if _, err := AllocatePort("container_id", port, binding); err == nil { - t.Fatal("Duplicate port allocation granted by AllocatePort") - } -} - -func TestHostnameFormatChecking(t *testing.T) { - freePort := findFreePort(t) - - if err := InitDriver(new(Config)); err != nil { - t.Fatal("Failed to initialize network driver") - } - - // Allocate interface - if _, err := Allocate("container_id", "", "", ""); err != nil { - t.Fatal("Failed to allocate network interface") - } - - port := nat.Port(freePort + "/tcp") - binding := nat.PortBinding{HostIp: "localhost", HostPort: freePort} - - if _, err := AllocatePort("container_id", port, binding); err == nil { - t.Fatal("Failed to check invalid HostIP") - } -} - -func newInterfaceAllocation(t *testing.T, globalIPv6 *net.IPNet, requestedMac, requestedIP, requestedIPv6 string, expectFail bool) *network.Settings { - // set IPv6 global if given - if globalIPv6 != nil { - globalIPv6Network = globalIPv6 - } - - networkSettings, err := Allocate("container_id", requestedMac, requestedIP, requestedIPv6) - if err == nil && expectFail { - t.Fatal("Doesn't fail to allocate network interface") - } else if err != nil && !expectFail { - t.Fatal("Failed to allocate network interface") - - } - - if globalIPv6 != nil { - // check for bug #11427 - if globalIPv6Network.IP.String() != globalIPv6.IP.String() { - t.Fatal("globalIPv6Network was modified during allocation") - } - // clean up IPv6 global - globalIPv6Network = nil - } - - return networkSettings -} - -func TestIPv6InterfaceAllocationAutoNetmaskGt80(t *testing.T) { - _, subnet, _ := net.ParseCIDR("2001:db8:1234:1234:1234::/81") - networkSettings := newInterfaceAllocation(t, subnet, "", "", "", false) - - // ensure low manually assigend global ip - ip := net.ParseIP(networkSettings.GlobalIPv6Address) - _, subnet, _ = net.ParseCIDR(fmt.Sprintf("%s/%d", subnet.IP.String(), 120)) - if !subnet.Contains(ip) { - t.Fatalf("Error ip %s not in subnet %s", ip.String(), subnet.String()) - } -} - -func TestIPv6InterfaceAllocationAutoNetmaskLe80(t *testing.T) { - _, subnet, _ := net.ParseCIDR("2001:db8:1234:1234:1234::/80") - networkSettings := newInterfaceAllocation(t, subnet, "ab:cd:ab:cd:ab:cd", "", "", false) - - // ensure global ip with mac - ip := net.ParseIP(networkSettings.GlobalIPv6Address) - expectedIP := net.ParseIP("2001:db8:1234:1234:1234:abcd:abcd:abcd") - if ip.String() != expectedIP.String() { - t.Fatalf("Error ip %s should be %s", ip.String(), expectedIP.String()) - } - - // ensure link local format - ip = net.ParseIP(networkSettings.LinkLocalIPv6Address) - expectedIP = net.ParseIP("fe80::a9cd:abff:fecd:abcd") - if ip.String() != expectedIP.String() { - t.Fatalf("Error ip %s should be %s", ip.String(), expectedIP.String()) - } - -} - -func TestIPv6InterfaceAllocationRequest(t *testing.T) { - _, subnet, _ := net.ParseCIDR("2001:db8:1234:1234:1234::/80") - expectedIP := "2001:db8:1234:1234:1234::1328" - - networkSettings := newInterfaceAllocation(t, subnet, "", "", expectedIP, false) - - // ensure global ip with mac - ip := net.ParseIP(networkSettings.GlobalIPv6Address) - if ip.String() != expectedIP { - t.Fatalf("Error ip %s should be %s", ip.String(), expectedIP) - } - - // retry -> fails for duplicated address - _ = newInterfaceAllocation(t, subnet, "", "", expectedIP, true) -} - -func TestMacAddrGeneration(t *testing.T) { - ip := net.ParseIP("192.168.0.1") - mac := generateMacAddr(ip).String() - - // Should be consistent. - if generateMacAddr(ip).String() != mac { - t.Fatal("Inconsistent MAC address") - } - - // Should be unique. - ip2 := net.ParseIP("192.168.0.2") - if generateMacAddr(ip2).String() == mac { - t.Fatal("Non-unique MAC address") - } -} - -func TestLinkContainers(t *testing.T) { - // Init driver - if err := InitDriver(new(Config)); err != nil { - t.Fatal("Failed to initialize network driver") - } - - // Allocate interface - if _, err := Allocate("container_id", "", "", ""); err != nil { - t.Fatal("Failed to allocate network interface") - } - - bridgeIface = "lo" - if _, err := iptables.NewChain("DOCKER", bridgeIface, iptables.Filter, false); err != nil { - t.Fatal(err) - } - - if err := LinkContainers("-I", "172.17.0.1", "172.17.0.2", []nat.Port{nat.Port("1234")}, false); err != nil { - t.Fatal("LinkContainers failed") - } - - // flush rules - if _, err := iptables.Raw([]string{"-F", "DOCKER"}...); err != nil { - t.Fatal(err) - } - -} diff --git a/daemon/networkdriver/network.go b/daemon/networkdriver/network.go deleted file mode 100644 index 8dda789d2f..0000000000 --- a/daemon/networkdriver/network.go +++ /dev/null @@ -1,10 +0,0 @@ -package networkdriver - -import ( - "errors" -) - -var ( - ErrNetworkOverlapsWithNameservers = errors.New("requested network overlaps with nameserver") - ErrNetworkOverlaps = errors.New("requested network overlaps with existing network") -) diff --git a/daemon/networkdriver/portmapper/mapper_test.go b/daemon/networkdriver/portmapper/mapper_test.go deleted file mode 100644 index b908db2811..0000000000 --- a/daemon/networkdriver/portmapper/mapper_test.go +++ /dev/null @@ -1,149 +0,0 @@ -package portmapper - -import ( - "net" - "testing" - - "github.com/docker/docker/pkg/iptables" -) - -func init() { - // override this func to mock out the proxy server - NewProxy = NewMockProxyCommand -} - -func TestSetIptablesChain(t *testing.T) { - pm := New() - - c := &iptables.Chain{ - Name: "TEST", - Bridge: "192.168.1.1", - } - - if pm.chain != nil { - t.Fatal("chain should be nil at init") - } - - pm.SetIptablesChain(c) - if pm.chain == nil { - t.Fatal("chain should not be nil after set") - } -} - -func TestMapPorts(t *testing.T) { - pm := New() - dstIp1 := net.ParseIP("192.168.0.1") - dstIp2 := net.ParseIP("192.168.0.2") - dstAddr1 := &net.TCPAddr{IP: dstIp1, Port: 80} - dstAddr2 := &net.TCPAddr{IP: dstIp2, Port: 80} - - srcAddr1 := &net.TCPAddr{Port: 1080, IP: net.ParseIP("172.16.0.1")} - srcAddr2 := &net.TCPAddr{Port: 1080, IP: net.ParseIP("172.16.0.2")} - - addrEqual := func(addr1, addr2 net.Addr) bool { - return (addr1.Network() == addr2.Network()) && (addr1.String() == addr2.String()) - } - - if host, err := pm.Map(srcAddr1, dstIp1, 80, true); err != nil { - t.Fatalf("Failed to allocate port: %s", err) - } else if !addrEqual(dstAddr1, host) { - t.Fatalf("Incorrect mapping result: expected %s:%s, got %s:%s", - dstAddr1.String(), dstAddr1.Network(), host.String(), host.Network()) - } - - if _, err := pm.Map(srcAddr1, dstIp1, 80, true); err == nil { - t.Fatalf("Port is in use - mapping should have failed") - } - - if _, err := pm.Map(srcAddr2, dstIp1, 80, true); err == nil { - t.Fatalf("Port is in use - mapping should have failed") - } - - if _, err := pm.Map(srcAddr2, dstIp2, 80, true); err != nil { - t.Fatalf("Failed to allocate port: %s", err) - } - - if pm.Unmap(dstAddr1) != nil { - t.Fatalf("Failed to release port") - } - - if pm.Unmap(dstAddr2) != nil { - t.Fatalf("Failed to release port") - } - - if pm.Unmap(dstAddr2) == nil { - t.Fatalf("Port already released, but no error reported") - } -} - -func TestGetUDPKey(t *testing.T) { - addr := &net.UDPAddr{IP: net.ParseIP("192.168.1.5"), Port: 53} - - key := getKey(addr) - - if expected := "192.168.1.5:53/udp"; key != expected { - t.Fatalf("expected key %s got %s", expected, key) - } -} - -func TestGetTCPKey(t *testing.T) { - addr := &net.TCPAddr{IP: net.ParseIP("192.168.1.5"), Port: 80} - - key := getKey(addr) - - if expected := "192.168.1.5:80/tcp"; key != expected { - t.Fatalf("expected key %s got %s", expected, key) - } -} - -func TestGetUDPIPAndPort(t *testing.T) { - addr := &net.UDPAddr{IP: net.ParseIP("192.168.1.5"), Port: 53} - - ip, port := getIPAndPort(addr) - if expected := "192.168.1.5"; ip.String() != expected { - t.Fatalf("expected ip %s got %s", expected, ip) - } - - if ep := 53; port != ep { - t.Fatalf("expected port %d got %d", ep, port) - } -} - -func TestMapAllPortsSingleInterface(t *testing.T) { - pm := New() - dstIp1 := net.ParseIP("0.0.0.0") - srcAddr1 := &net.TCPAddr{Port: 1080, IP: net.ParseIP("172.16.0.1")} - - hosts := []net.Addr{} - var host net.Addr - var err error - - defer func() { - for _, val := range hosts { - pm.Unmap(val) - } - }() - - for i := 0; i < 10; i++ { - start, end := pm.Allocator.Begin, pm.Allocator.End - for i := start; i < end; i++ { - if host, err = pm.Map(srcAddr1, dstIp1, 0, true); err != nil { - t.Fatal(err) - } - - hosts = append(hosts, host) - } - - if _, err := pm.Map(srcAddr1, dstIp1, start, true); err == nil { - t.Fatalf("Port %d should be bound but is not", start) - } - - for _, val := range hosts { - if err := pm.Unmap(val); err != nil { - t.Fatal(err) - } - } - - hosts = []net.Addr{} - } -} diff --git a/daemon/networkdriver/utils.go b/daemon/networkdriver/utils.go deleted file mode 100644 index 9f0c88cd5e..0000000000 --- a/daemon/networkdriver/utils.go +++ /dev/null @@ -1,118 +0,0 @@ -package networkdriver - -import ( - "errors" - "fmt" - "net" - - "github.com/docker/libcontainer/netlink" -) - -var ( - networkGetRoutesFct = netlink.NetworkGetRoutes - ErrNoDefaultRoute = errors.New("no default route") -) - -func CheckNameserverOverlaps(nameservers []string, toCheck *net.IPNet) error { - if len(nameservers) > 0 { - for _, ns := range nameservers { - _, nsNetwork, err := net.ParseCIDR(ns) - if err != nil { - return err - } - if NetworkOverlaps(toCheck, nsNetwork) { - return ErrNetworkOverlapsWithNameservers - } - } - } - return nil -} - -func CheckRouteOverlaps(toCheck *net.IPNet) error { - networks, err := networkGetRoutesFct() - if err != nil { - return err - } - - for _, network := range networks { - if network.IPNet != nil && NetworkOverlaps(toCheck, network.IPNet) { - return ErrNetworkOverlaps - } - } - return nil -} - -// Detects overlap between one IPNet and another -func NetworkOverlaps(netX *net.IPNet, netY *net.IPNet) bool { - if len(netX.IP) == len(netY.IP) { - if firstIP, _ := NetworkRange(netX); netY.Contains(firstIP) { - return true - } - if firstIP, _ := NetworkRange(netY); netX.Contains(firstIP) { - return true - } - } - return false -} - -// Calculates the first and last IP addresses in an IPNet -func NetworkRange(network *net.IPNet) (net.IP, net.IP) { - var netIP net.IP - if network.IP.To4() != nil { - netIP = network.IP.To4() - } else if network.IP.To16() != nil { - netIP = network.IP.To16() - } else { - return nil, nil - } - - lastIP := make([]byte, len(netIP), len(netIP)) - - for i := 0; i < len(netIP); i++ { - lastIP[i] = netIP[i] | ^network.Mask[i] - } - return netIP.Mask(network.Mask), net.IP(lastIP) -} - -// Return the first IPv4 address and slice of IPv6 addresses for the specified network interface -func GetIfaceAddr(name string) (net.Addr, []net.Addr, error) { - iface, err := net.InterfaceByName(name) - if err != nil { - return nil, nil, err - } - addrs, err := iface.Addrs() - if err != nil { - return nil, nil, err - } - var addrs4 []net.Addr - var addrs6 []net.Addr - for _, addr := range addrs { - ip := (addr.(*net.IPNet)).IP - if ip4 := ip.To4(); ip4 != nil { - addrs4 = append(addrs4, addr) - } else if ip6 := ip.To16(); len(ip6) == net.IPv6len { - addrs6 = append(addrs6, addr) - } - } - switch { - case len(addrs4) == 0: - return nil, nil, fmt.Errorf("Interface %v has no IPv4 addresses", name) - case len(addrs4) > 1: - fmt.Printf("Interface %v has more than 1 IPv4 address. Defaulting to using %v\n", - name, (addrs4[0].(*net.IPNet)).IP) - } - return addrs4[0], addrs6, nil -} - -func GetDefaultRouteIface() (*net.Interface, error) { - rs, err := networkGetRoutesFct() - if err != nil { - return nil, fmt.Errorf("unable to get routes: %v", err) - } - for _, r := range rs { - if r.Default { - return r.Iface, nil - } - } - return nil, ErrNoDefaultRoute -} diff --git a/hack/vendor.sh b/hack/vendor.sh index 13c9a32fcf..5288b6634d 100755 --- a/hack/vendor.sh +++ b/hack/vendor.sh @@ -46,7 +46,6 @@ clone() { clone git github.com/Sirupsen/logrus v0.7.3 # logrus is a common dependency among multiple deps clone git github.com/docker/libtrust 230dfd18c232 clone git github.com/go-check/check 64131543e7896d5bcc6bd5a76287eb75ea96c673 -clone git github.com/go-fsnotify/fsnotify v1.2.0 clone git github.com/gorilla/context 14f550f51a clone git github.com/gorilla/mux e444e69cbd clone git github.com/kr/pty 5cf931ef8f @@ -55,6 +54,11 @@ clone git github.com/tchap/go-patricia v2.1.0 clone hg code.google.com/p/go.net 84a4013f96e0 clone hg code.google.com/p/gosqlite 74691fb6f837 +#get libnetwork packages +clone git github.com/docker/libnetwork v0.2 +clone git github.com/vishvananda/netns 008d17ae001344769b031375bdb38a86219154c6 +clone git github.com/vishvananda/netlink 8eb64238879fed52fd51c5b30ad20b928fb4c36c + # get distribution packages clone git github.com/docker/distribution d957768537c5af40e4f4cd96871f7b2bde9e2923 mv src/github.com/docker/distribution/digest tmp-digest diff --git a/integration-cli/docker_cli_daemon_test.go b/integration-cli/docker_cli_daemon_test.go index 2097ad00a0..2a1ce918a5 100644 --- a/integration-cli/docker_cli_daemon_test.go +++ b/integration-cli/docker_cli_daemon_test.go @@ -15,6 +15,7 @@ import ( "strings" "time" + "github.com/docker/libnetwork/iptables" "github.com/docker/libtrust" "github.com/go-check/check" ) @@ -721,13 +722,49 @@ func (s *DockerDaemonSuite) TestDaemonICCLinkExpose(c *check.C) { c.Assert(matched, check.Equals, true, check.Commentf("iptables output should have contained %q, but was %q", regex, out)) - _, err = d.Cmd("run", "-d", "--expose", "4567", "--name", "icc1", "busybox", "nc", "-l", "-p", "4567") - c.Assert(err, check.IsNil) + out, err = d.Cmd("run", "-d", "--expose", "4567", "--name", "icc1", "busybox", "nc", "-l", "-p", "4567") + c.Assert(err, check.IsNil, check.Commentf(out)) out, err = d.Cmd("run", "--link", "icc1:icc1", "busybox", "nc", "icc1", "4567") c.Assert(err, check.IsNil, check.Commentf(out)) } +func (s *DockerDaemonSuite) TestDaemonLinksIpTablesRulesWhenLinkAndUnlink(c *check.C) { + bridgeName := "external-bridge" + bridgeIp := "192.169.1.1/24" + + out, err := createInterface(c, "bridge", bridgeName, bridgeIp) + c.Assert(err, check.IsNil, check.Commentf(out)) + defer deleteInterface(c, bridgeName) + + args := []string{"--bridge", bridgeName, "--icc=false"} + err = s.d.StartWithBusybox(args...) + c.Assert(err, check.IsNil) + defer s.d.Restart() + + _, err = s.d.Cmd("run", "-d", "--name", "child", "--publish", "8080:80", "busybox", "top") + c.Assert(err, check.IsNil) + _, err = s.d.Cmd("run", "-d", "--name", "parent", "--link", "child:http", "busybox", "top") + c.Assert(err, check.IsNil) + + childIP := s.d.findContainerIP("child") + parentIP := s.d.findContainerIP("parent") + + sourceRule := []string{"-i", bridgeName, "-o", bridgeName, "-p", "tcp", "-s", childIP, "--sport", "80", "-d", parentIP, "-j", "ACCEPT"} + destinationRule := []string{"-i", bridgeName, "-o", bridgeName, "-p", "tcp", "-s", parentIP, "--dport", "80", "-d", childIP, "-j", "ACCEPT"} + if !iptables.Exists("filter", "DOCKER", sourceRule...) || !iptables.Exists("filter", "DOCKER", destinationRule...) { + c.Fatal("Iptables rules not found") + } + + s.d.Cmd("rm", "--link", "parent/http") + if iptables.Exists("filter", "DOCKER", sourceRule...) || iptables.Exists("filter", "DOCKER", destinationRule...) { + c.Fatal("Iptables rules should be removed when unlink") + } + + s.d.Cmd("kill", "child") + s.d.Cmd("kill", "parent") +} + func (s *DockerDaemonSuite) TestDaemonUlimitDefaults(c *check.C) { testRequires(c, NativeExecDriver) diff --git a/integration-cli/docker_cli_links_test.go b/integration-cli/docker_cli_links_test.go index 80c89dcd84..1f7432a8bf 100644 --- a/integration-cli/docker_cli_links_test.go +++ b/integration-cli/docker_cli_links_test.go @@ -10,7 +10,6 @@ import ( "strings" "time" - "github.com/docker/docker/pkg/iptables" "github.com/go-check/check" ) @@ -110,31 +109,6 @@ func (s *DockerSuite) TestLinksPingLinkedContainersAfterRename(c *check.C) { } -func (s *DockerSuite) TestLinksIpTablesRulesWhenLinkAndUnlink(c *check.C) { - testRequires(c, SameHostDaemon) - - dockerCmd(c, "run", "-d", "--name", "child", "--publish", "8080:80", "busybox", "top") - dockerCmd(c, "run", "-d", "--name", "parent", "--link", "child:http", "busybox", "top") - - childIP := findContainerIP(c, "child") - parentIP := findContainerIP(c, "parent") - - sourceRule := []string{"-i", "docker0", "-o", "docker0", "-p", "tcp", "-s", childIP, "--sport", "80", "-d", parentIP, "-j", "ACCEPT"} - destinationRule := []string{"-i", "docker0", "-o", "docker0", "-p", "tcp", "-s", parentIP, "--dport", "80", "-d", childIP, "-j", "ACCEPT"} - if !iptables.Exists("filter", "DOCKER", sourceRule...) || !iptables.Exists("filter", "DOCKER", destinationRule...) { - c.Fatal("Iptables rules not found") - } - - dockerCmd(c, "rm", "--link", "parent/http") - if iptables.Exists("filter", "DOCKER", sourceRule...) || iptables.Exists("filter", "DOCKER", destinationRule...) { - c.Fatal("Iptables rules should be removed when unlink") - } - - dockerCmd(c, "kill", "child") - dockerCmd(c, "kill", "parent") - -} - func (s *DockerSuite) TestLinksInspectLinksStarted(c *check.C) { var ( expected = map[string]struct{}{"/container1:/testinspectlink/alias1": {}, "/container2:/testinspectlink/alias2": {}} diff --git a/integration-cli/docker_cli_run_test.go b/integration-cli/docker_cli_run_test.go index 86d7a5e1cc..bfb50e418e 100644 --- a/integration-cli/docker_cli_run_test.go +++ b/integration-cli/docker_cli_run_test.go @@ -19,7 +19,7 @@ import ( "time" "github.com/docker/docker/nat" - "github.com/docker/docker/pkg/resolvconf" + "github.com/docker/libnetwork/resolvconf" "github.com/go-check/check" ) @@ -1459,14 +1459,11 @@ func (s *DockerSuite) TestRunDnsOptionsBasedOnHostResolvConf(c *check.C) { } } -// Test the file watch notifier on docker host's /etc/resolv.conf -// A go-routine is responsible for auto-updating containers which are -// stopped and have an unmodified copy of resolv.conf, as well as -// marking running containers as requiring an update on next restart -func (s *DockerSuite) TestRunResolvconfUpdater(c *check.C) { - // Because overlay doesn't support inotify properly, we need to skip - // this test if the docker daemon has Storage Driver == overlay - testRequires(c, SameHostDaemon, NotOverlay) +// Test if container resolv.conf gets updated the next time it restarts +// if host /etc/resolv.conf has changed. This only applies if the container +// uses the host's /etc/resolv.conf and does not have any dns options provided. +func (s *DockerSuite) TestRunResolvconfUpdate(c *check.C) { + testRequires(c, SameHostDaemon) tmpResolvConf := []byte("search pommesfrites.fr\nnameserver 12.34.56.78") tmpLocalhostResolvConf := []byte("nameserver 127.0.0.1") @@ -1492,7 +1489,7 @@ func (s *DockerSuite) TestRunResolvconfUpdater(c *check.C) { } }() - //1. test that a non-running container gets an updated resolv.conf + //1. test that a restarting container gets an updated resolv.conf cmd = exec.Command(dockerBinary, "run", "--name='first'", "busybox", "true") if _, err := runCommand(cmd); err != nil { c.Fatal(err) @@ -1508,17 +1505,26 @@ func (s *DockerSuite) TestRunResolvconfUpdater(c *check.C) { c.Fatal(err) } - time.Sleep(time.Second / 2) + // start the container again to pickup changes + cmd = exec.Command(dockerBinary, "start", "first") + if out, err := runCommand(cmd); err != nil { + c.Fatalf("Errored out %s, \nerror: %v", string(out), err) + } + // check for update in container containerResolv, err := readContainerFile(containerID1, "resolv.conf") if err != nil { c.Fatal(err) } if !bytes.Equal(containerResolv, bytesResolvConf) { - c.Fatalf("Stopped container does not have updated resolv.conf; expected %q, got %q", tmpResolvConf, string(containerResolv)) + c.Fatalf("Restarted container does not have updated resolv.conf; expected %q, got %q", tmpResolvConf, string(containerResolv)) } - //2. test that a non-running container does not receive resolv.conf updates + /* //make a change to resolv.conf (in this case replacing our tmp copy with orig copy) + if err := ioutil.WriteFile("/etc/resolv.conf", resolvConfSystem, 0644); err != nil { + c.Fatal(err) + } */ + //2. test that a restarting container does not receive resolv.conf updates // if it modified the container copy of the starting point resolv.conf cmd = exec.Command(dockerBinary, "run", "--name='second'", "busybox", "sh", "-c", "echo 'search mylittlepony.com' >>/etc/resolv.conf") if _, err = runCommand(cmd); err != nil { @@ -1528,24 +1534,26 @@ func (s *DockerSuite) TestRunResolvconfUpdater(c *check.C) { if err != nil { c.Fatal(err) } - containerResolvHashBefore, err := readContainerFile(containerID2, "resolv.conf.hash") - if err != nil { - c.Fatal(err) - } //make a change to resolv.conf (in this case replacing our tmp copy with orig copy) if err := ioutil.WriteFile("/etc/resolv.conf", resolvConfSystem, 0644); err != nil { c.Fatal(err) } - time.Sleep(time.Second / 2) - containerResolvHashAfter, err := readContainerFile(containerID2, "resolv.conf.hash") + // start the container again + cmd = exec.Command(dockerBinary, "start", "second") + if out, err := runCommand(cmd); err != nil { + c.Fatalf("Errored out %s, \nerror: %v", string(out), err) + } + + // check for update in container + containerResolv, err = readContainerFile(containerID2, "resolv.conf") if err != nil { c.Fatal(err) } - if !bytes.Equal(containerResolvHashBefore, containerResolvHashAfter) { - c.Fatalf("Stopped container with modified resolv.conf should not have been updated; expected hash: %v, new hash: %v", containerResolvHashBefore, containerResolvHashAfter) + if bytes.Equal(containerResolv, resolvConfSystem) { + c.Fatalf("Restarting a container after container updated resolv.conf should not pick up host changes; expected %q, got %q", string(containerResolv), string(resolvConfSystem)) } //3. test that a running container's resolv.conf is not modified while running @@ -1556,26 +1564,19 @@ func (s *DockerSuite) TestRunResolvconfUpdater(c *check.C) { } runningContainerID := strings.TrimSpace(out) - containerResolvHashBefore, err = readContainerFile(runningContainerID, "resolv.conf.hash") - if err != nil { - c.Fatal(err) - } - // replace resolv.conf if err := ioutil.WriteFile("/etc/resolv.conf", bytesResolvConf, 0644); err != nil { c.Fatal(err) } - // make sure the updater has time to run to validate we really aren't - // getting updated - time.Sleep(time.Second / 2) - containerResolvHashAfter, err = readContainerFile(runningContainerID, "resolv.conf.hash") + // check for update in container + containerResolv, err = readContainerFile(runningContainerID, "resolv.conf") if err != nil { c.Fatal(err) } - if !bytes.Equal(containerResolvHashBefore, containerResolvHashAfter) { - c.Fatalf("Running container's resolv.conf should not be updated; expected hash: %v, new hash: %v", containerResolvHashBefore, containerResolvHashAfter) + if bytes.Equal(containerResolv, bytesResolvConf) { + c.Fatalf("Running container should not have updated resolv.conf; expected %q, got %q", string(resolvConfSystem), string(containerResolv)) } //4. test that a running container's resolv.conf is updated upon restart @@ -1591,7 +1592,7 @@ func (s *DockerSuite) TestRunResolvconfUpdater(c *check.C) { c.Fatal(err) } if !bytes.Equal(containerResolv, bytesResolvConf) { - c.Fatalf("Restarted container should have updated resolv.conf; expected %q, got %q", tmpResolvConf, string(containerResolv)) + c.Fatalf("Restarted container should have updated resolv.conf; expected %q, got %q", string(bytesResolvConf), string(containerResolv)) } //5. test that additions of a localhost resolver are cleaned from @@ -1603,7 +1604,12 @@ func (s *DockerSuite) TestRunResolvconfUpdater(c *check.C) { c.Fatal(err) } - time.Sleep(time.Second / 2) + // start the container again to pickup changes + cmd = exec.Command(dockerBinary, "start", "first") + if out, err := runCommand(cmd); err != nil { + c.Fatalf("Errored out %s, \nerror: %v", string(out), err) + } + // our first exited container ID should have been updated, but with default DNS // after the cleanup of resolv.conf found only a localhost nameserver: containerResolv, err = readContainerFile(containerID1, "resolv.conf") @@ -1645,7 +1651,12 @@ func (s *DockerSuite) TestRunResolvconfUpdater(c *check.C) { c.Fatal(err) } - time.Sleep(time.Second / 2) + // start the container again to pickup changes + cmd = exec.Command(dockerBinary, "start", "third") + if out, err := runCommand(cmd); err != nil { + c.Fatalf("Errored out %s, \nerror: %v", string(out), err) + } + // check for update in container containerResolv, err = readContainerFile(containerID3, "resolv.conf") if err != nil { diff --git a/links/links.go b/links/links.go index 58fec95f56..a756c8b0e5 100644 --- a/links/links.go +++ b/links/links.go @@ -5,9 +5,7 @@ import ( "path" "strings" - "github.com/docker/docker/daemon/networkdriver/bridge" "github.com/docker/docker/nat" - "github.com/docker/docker/pkg/iptables" ) type Link struct { @@ -140,26 +138,10 @@ func (l *Link) getDefaultPort() *nat.Port { } func (l *Link) Enable() error { - // -A == iptables append flag - if err := l.toggle("-A", false); err != nil { - return err - } - // call this on Firewalld reload - iptables.OnReloaded(func() { l.toggle("-A", false) }) l.IsEnabled = true return nil } func (l *Link) Disable() { - // We do not care about errors here because the link may not - // exist in iptables - // -D == iptables delete flag - l.toggle("-D", true) - // call this on Firewalld reload - iptables.OnReloaded(func() { l.toggle("-D", true) }) l.IsEnabled = false } - -func (l *Link) toggle(action string, ignoreErrors bool) error { - return bridge.LinkContainers(action, l.ParentIP, l.ChildIP, l.Ports, ignoreErrors) -} diff --git a/runconfig/hostconfig.go b/runconfig/hostconfig.go index 9fdb6e0fd5..1418dea4a8 100644 --- a/runconfig/hostconfig.go +++ b/runconfig/hostconfig.go @@ -18,7 +18,7 @@ type NetworkMode string // IsPrivate indicates whether container use it's private network stack func (n NetworkMode) IsPrivate() bool { - return !(n.IsHost() || n.IsContainer() || n.IsNone()) + return !(n.IsHost() || n.IsContainer()) } func (n NetworkMode) IsBridge() bool { diff --git a/vendor/src/github.com/docker/libnetwork/.gitignore b/vendor/src/github.com/docker/libnetwork/.gitignore new file mode 100644 index 0000000000..c03c9653ab --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/.gitignore @@ -0,0 +1,33 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test +*.prof + +# Coverage +*.tmp +*.coverprofile + +# IDE files +.project + +libnetwork-build.created diff --git a/vendor/src/github.com/docker/libnetwork/Godeps/Godeps.json b/vendor/src/github.com/docker/libnetwork/Godeps/Godeps.json new file mode 100644 index 0000000000..c28930943b --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/Godeps/Godeps.json @@ -0,0 +1,85 @@ +{ + "ImportPath": "github.com/docker/libnetwork", + "GoVersion": "go1.4.1", + "Packages": [ + "./..." + ], + "Deps": [ + { + "ImportPath": "github.com/Sirupsen/logrus", + "Comment": "v0.6.4-12-g467d9d5", + "Rev": "467d9d55c2d2c17248441a8fc661561161f40d5e" + }, + { + "ImportPath": "github.com/docker/docker/pkg/homedir", + "Comment": "v1.4.1-3479-ga9172f5", + "Rev": "a9172f572e13086859c652e2d581950e910d63d4" + }, + { + "ImportPath": "github.com/docker/docker/pkg/ioutils", + "Comment": "v1.4.1-3479-ga9172f5", + "Rev": "a9172f572e13086859c652e2d581950e910d63d4" + }, + { + "ImportPath": "github.com/docker/docker/pkg/mflag", + "Comment": "v1.4.1-3479-ga9172f5", + "Rev": "a9172f572e13086859c652e2d581950e910d63d4" + }, + { + "ImportPath": "github.com/docker/docker/pkg/parsers", + "Comment": "v1.4.1-3479-ga9172f5", + "Rev": "a9172f572e13086859c652e2d581950e910d63d4" + }, + { + "ImportPath": "github.com/docker/docker/pkg/plugins", + "Comment": "v1.4.1-3479-ga9172f5", + "Rev": "a9172f572e13086859c652e2d581950e910d63d4" + }, + { + "ImportPath": "github.com/docker/docker/pkg/proxy", + "Comment": "v1.4.1-3479-ga9172f5", + "Rev": "a9172f572e13086859c652e2d581950e910d63d4" + }, + { + "ImportPath": "github.com/docker/docker/pkg/reexec", + "Comment": "v1.4.1-3479-ga9172f5", + "Rev": "a9172f572e13086859c652e2d581950e910d63d4" + }, + { + "ImportPath": "github.com/docker/docker/pkg/stringid", + "Comment": "v1.4.1-3479-ga9172f5", + "Rev": "a9172f572e13086859c652e2d581950e910d63d4" + }, + { + "ImportPath": "github.com/docker/docker/pkg/term", + "Comment": "v1.4.1-3479-ga9172f5", + "Rev": "a9172f572e13086859c652e2d581950e910d63d4" + }, + { + "ImportPath": "github.com/docker/libcontainer/user", + "Comment": "v1.4.0-495-g3e66118", + "Rev": "3e661186ba24f259d3860f067df052c7f6904bee" + }, + { + "ImportPath": "github.com/godbus/dbus", + "Comment": "v2-3-g4160802", + "Rev": "41608027bdce7bfa8959d653a00b954591220e67" + }, + { + "ImportPath": "github.com/gorilla/context", + "Rev": "215affda49addc4c8ef7e2534915df2c8c35c6cd" + }, + { + "ImportPath": "github.com/gorilla/mux", + "Rev": "8096f47503459bcc74d1f4c487b7e6e42e5746b5" + }, + { + "ImportPath": "github.com/vishvananda/netlink", + "Rev": "8eb64238879fed52fd51c5b30ad20b928fb4c36c" + }, + { + "ImportPath": "github.com/vishvananda/netns", + "Rev": "008d17ae001344769b031375bdb38a86219154c6" + } + ] +} diff --git a/vendor/src/github.com/docker/libnetwork/Godeps/Readme b/vendor/src/github.com/docker/libnetwork/Godeps/Readme new file mode 100644 index 0000000000..4cdaa53d56 --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/Godeps/Readme @@ -0,0 +1,5 @@ +This directory tree is generated automatically by godep. + +Please do not edit. + +See https://github.com/tools/godep for more information. diff --git a/vendor/src/github.com/docker/libnetwork/LICENSE b/vendor/src/github.com/docker/libnetwork/LICENSE new file mode 100644 index 0000000000..e06d208186 --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/LICENSE @@ -0,0 +1,202 @@ +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/vendor/src/github.com/docker/libnetwork/MAINTAINERS b/vendor/src/github.com/docker/libnetwork/MAINTAINERS new file mode 100644 index 0000000000..398fd6de37 --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/MAINTAINERS @@ -0,0 +1,4 @@ +Alexandr Morozov (@LK4D4) +Arnaud Porterie (@icecrime) +Madhu Venugopal (@mavenugo) +Jana Radhakrishnan (@mrjana) diff --git a/vendor/src/github.com/docker/libnetwork/Makefile b/vendor/src/github.com/docker/libnetwork/Makefile new file mode 100644 index 0000000000..892d827dbf --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/Makefile @@ -0,0 +1,78 @@ +.PHONY: all all-local build build-local check check-code check-format run-tests check-local install-deps coveralls circle-ci +SHELL=/bin/bash +build_image=libnetwork-build +dockerargs = --privileged -v $(shell pwd):/go/src/github.com/docker/libnetwork -w /go/src/github.com/docker/libnetwork +container_env = -e "INSIDECONTAINER=-incontainer=true" +docker = docker run --rm ${dockerargs} ${container_env} ${build_image} +ciargs = -e "COVERALLS_TOKEN=$$COVERALLS_TOKEN" -e "INSIDECONTAINER=-incontainer=true" +cidocker = docker run ${ciargs} ${dockerargs} golang:1.4 + +all: ${build_image}.created + ${docker} make all-local + +all-local: check-local build-local + +${build_image}.created: + docker run --name=libnetworkbuild -v $(shell pwd):/go/src/github.com/docker/libnetwork -w /go/src/github.com/docker/libnetwork golang:1.4 make install-deps + docker commit libnetworkbuild ${build_image} + docker rm libnetworkbuild + touch ${build_image}.created + +build: ${build_image}.created + ${docker} make build-local + +build-local: + $(shell which godep) go build ./... + +check: ${build_image}.created + ${docker} make check-local + +check-code: + @echo "Checking code... " + test -z "$$(golint ./... | tee /dev/stderr)" + go vet ./... + @echo "Done checking code" + +check-format: + @echo "Checking format... " + test -z "$$(goimports -l . | grep -v Godeps/_workspace/src/ | tee /dev/stderr)" + @echo "Done checking format" + +run-tests: + @echo "Running tests... " + @echo "mode: count" > coverage.coverprofile + @for dir in $$(find . -maxdepth 10 -not -path './.git*' -not -path '*/_*' -type d); do \ + if ls $$dir/*.go &> /dev/null; then \ + pushd . &> /dev/null ; \ + cd $$dir ; \ + $(shell which godep) go test ${INSIDECONTAINER} -test.parallel 3 -test.v -covermode=count -coverprofile=./profile.tmp ; \ + ret=$$? ;\ + if [ $$ret -ne 0 ]; then exit $$ret; fi ;\ + popd &> /dev/null; \ + if [ -f $$dir/profile.tmp ]; then \ + cat $$dir/profile.tmp | tail -n +2 >> coverage.coverprofile ; \ + rm $$dir/profile.tmp ; \ + fi ; \ + fi ; \ + done + @echo "Done running tests" + +check-local: check-format check-code run-tests + +install-deps: + apt-get update && apt-get -y install iptables + go get github.com/tools/godep + go get github.com/golang/lint/golint + go get golang.org/x/tools/cmd/vet + go get golang.org/x/tools/cmd/goimports + go get golang.org/x/tools/cmd/cover + go get github.com/mattn/goveralls + +coveralls: + -@goveralls -service circleci -coverprofile=coverage.coverprofile -repotoken $$COVERALLS_TOKEN + +# CircleCI's Docker fails when cleaning up using the --rm flag +# The following target is a workaround for this + +circle-ci: + @${cidocker} make install-deps check-local coveralls diff --git a/vendor/src/github.com/docker/libnetwork/README.md b/vendor/src/github.com/docker/libnetwork/README.md new file mode 100644 index 0000000000..e51eba1569 --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/README.md @@ -0,0 +1,86 @@ +# 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) + +Libnetwork provides a native Go implementation for connecting containers + +The goal of libnetwork is to deliver a robust Container Network Model that provides a consistent programming interface and the required network abstractions for applications. + +**NOTE**: libnetwork project is under heavy development and is not ready for general use. + +#### Design +Please refer to the [design](docs/design.md) for more information. + +#### Using libnetwork + +There are many networking solutions available to suit a broad range of use-cases. libnetwork uses a driver / plugin model to support all of these solutions while abstracting the complexity of the driver implementations by exposing a simple and consistent Network Model to users. + + +```go + // Create a new controller instance + controller := libnetwork.New() + + // Select and configure the network driver + networkType := "bridge" + + driverOptions := options.Generic{} + genericOption := make(map[string]interface{}) + genericOption[netlabel.GenericData] = driverOptions + err := controller.ConfigureNetworkDriver(networkType, genericOption) + if err != nil { + return + } + + // Create a network for containers to join. + // NewNetwork accepts Variadic optional arguments that libnetwork and Drivers can make of + network, err := controller.NewNetwork(networkType, "network1") + if err != nil { + return + } + + // For each new container: allocate IP and interfaces. The returned network + // settings will be used for container infos (inspect and such), as well as + // iptables rules for port publishing. This info is contained or accessible + // from the returned endpoint. + ep, err := network.CreateEndpoint("Endpoint1") + if err != nil { + return + } + + // A container can join the endpoint by providing the container ID to the join + // api which returns the sandbox key which can be used to access the sandbox + // created for the container during join. + // Join acceps Variadic arguments which will be made use of by libnetwork and Drivers + _, err = ep.Join("container1", + libnetwork.JoinOptionHostname("test"), + libnetwork.JoinOptionDomainname("docker.io")) + if err != nil { + return + } + + // libentwork client can check the endpoint's operational data via the Info() API + epInfo, err := ep.DriverInfo() + mapData, ok := epInfo[netlabel.PortMap] + if ok { + portMapping, ok := mapData.([]netutils.PortBinding) + if ok { + fmt.Printf("Current port mapping for endpoint %s: %v", ep.Name(), portMapping) + } + } + +``` +#### Current Status +Please watch this space for updates on the progress. + +Currently libnetwork is nothing more than an attempt to modularize the Docker platform's networking subsystem by moving it into libnetwork as a library. + +## Future +Please refer to [roadmap](ROADMAP.md) for more information. + +## Contributing + +Want to hack on libnetwork? [Docker's contributions guidelines](https://github.com/docker/docker/blob/master/CONTRIBUTING.md) apply. + +## Copyright and license +Code and documentation copyright 2015 Docker, inc. Code released under the Apache 2.0 license. Docs released under Creative commons. + diff --git a/vendor/src/github.com/docker/libnetwork/ROADMAP.md b/vendor/src/github.com/docker/libnetwork/ROADMAP.md new file mode 100644 index 0000000000..af8964559b --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/ROADMAP.md @@ -0,0 +1,29 @@ +# Roadmap + +Libnetwork is a young project and is still being defined. +This document defines the high-level goals of the project and defines the release-relationship to the Docker Platform. + +* [Goals](#goals) +* [Project Planning](#project-planning): release-relationship to the Docker Platform. + +## Long-term Goal + +libnetwork project will follow Docker and Linux philosophy of delivering small, highly modular and composable tools that works well independently. +libnetwork aims to satisfy that composable need for Networking in Containers. + +## Short-term Goals + +- Modularize the networking logic in Docker Engine and libcontainer in to a single, reusable library +- Replace the networking subsystem of Docker Engine, with libnetwork +- Define a flexible model that allows local and remote drivers to provide networking to containers +- Provide a stand-alone tool "dnet" for managing and testing libnetwork + +## Project Planning + +Libnetwork versions do not map 1:1 with Docker Platform releases. +Milestones and Project Pages are used to define the set of features that are included in each release. + +| Platform Version | Libnetwork Version | Planning | +|------------------|--------------------|----------| +| Docker 1.7 | [0.3](https://github.com/docker/libnetwork/milestones/0.3) | [Project Page](https://github.com/docker/libnetwork/wiki/Docker-1.7-Project-Page) | +| Docker 1.8 | [1.0](https://github.com/docker/libnetwork/milestones/1.0) | [Project Page](https://github.com/docker/libnetwork/wiki/Docker-1.8-Project-Page) | diff --git a/vendor/src/github.com/docker/libnetwork/api/api.go b/vendor/src/github.com/docker/libnetwork/api/api.go new file mode 100644 index 0000000000..0eebf4d197 --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/api/api.go @@ -0,0 +1,481 @@ +package api + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + + "github.com/docker/libnetwork" + "github.com/gorilla/mux" +) + +var ( + successResponse = responseStatus{Status: "Success", StatusCode: http.StatusOK} + createdResponse = responseStatus{Status: "Created", StatusCode: http.StatusCreated} + mismatchResponse = responseStatus{Status: "Body/URI parameter mismatch", StatusCode: http.StatusBadRequest} +) + +const ( + urlNwName = "name" + urlNwID = "id" + urlEpName = "endpoint-name" + urlEpID = "endpoint-id" + urlCnID = "container-id" +) + +// NewHTTPHandler creates and initialize the HTTP handler to serve the requests for libnetwork +func NewHTTPHandler(c libnetwork.NetworkController) func(w http.ResponseWriter, req *http.Request) { + h := &httpHandler{c: c} + h.initRouter() + return h.handleRequest +} + +type responseStatus struct { + Status string + StatusCode int +} + +func (r *responseStatus) isOK() bool { + return r.StatusCode == http.StatusOK || r.StatusCode == http.StatusCreated +} + +type processor func(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) + +type httpHandler struct { + c libnetwork.NetworkController + r *mux.Router +} + +func (h *httpHandler) handleRequest(w http.ResponseWriter, req *http.Request) { + // Make sure the service is there + if h.c == nil { + http.Error(w, "NetworkController is not available", http.StatusServiceUnavailable) + return + } + + // Get handler from router and execute it + h.r.ServeHTTP(w, req) +} + +func (h *httpHandler) initRouter() { + m := map[string]map[string]processor{ + "GET": { + "/networks": procGetNetworks, + "/networks/name/{" + urlNwName + ":.*}": procGetNetwork, + "/networks/id/{" + urlNwID + ":.*}": procGetNetwork, + "/networks/name/{" + urlNwName + ":.*}/endpoints/": procGetEndpoints, + "/networks/id/{" + urlNwID + ":.*}/endpoints/": procGetEndpoints, + "/networks/name/{" + urlNwName + ":.*}/endpoints/name/{" + urlEpName + ":.*}": procGetEndpoint, + "/networks/id/{" + urlNwID + ":.*}/endpoints/name/{" + urlEpName + ":.*}": procGetEndpoint, + "/networks/name/{" + urlNwName + ":.*}/endpoints/id/{" + urlEpID + ":.*}": procGetEndpoint, + "/networks/id/{" + urlNwID + ":.*}/endpoints/id/{" + urlEpID + ":.*}": procGetEndpoint, + }, + "POST": { + "/networks/name/{" + urlNwName + ":.*}": procCreateNetwork, + "/networks/name/{" + urlNwName + ":.*}/endpoint/name/{" + urlEpName + ":.*}": procCreateEndpoint, + "/networks/name/{" + urlNwName + ":.*}/endpoint/name/{" + urlEpName + ":.*}/container/{" + urlCnID + ":.*}": procJoinEndpoint, + }, + "DELETE": { + "/networks/name/{" + urlNwName + ":.*}": procDeleteNetwork, + "/networks/id/{" + urlNwID + ":.*}": procDeleteNetwork, + "/networks/name/{" + urlNwName + ":.*}/endpoints/name/{" + urlEpName + ":.*}": procDeleteEndpoint, + "/networks/name/{" + urlNwName + ":.*}/endpoints/id/{" + urlEpID + ":.*}": procDeleteEndpoint, + "/networks/id/{" + urlNwID + ":.*}/endpoints/name/{" + urlEpName + ":.*}": procDeleteEndpoint, + "/networks/id/{" + urlNwID + ":.*}/endpoints/id/{" + urlEpID + ":.*}": procDeleteEndpoint, + "/networks/name/{" + urlNwName + ":.*}/endpoint/name/{" + urlEpName + ":.*}/container/{" + urlCnID + ":.*}": procLeaveEndpoint, + "/networks/name/{" + urlNwName + ":.*}/endpoint/id/{" + urlEpID + ":.*}/container/{" + urlCnID + ":.*}": procLeaveEndpoint, + "/networks/id/{" + urlNwID + ":.*}/endpoint/name/{" + urlEpName + ":.*}/container/{" + urlCnID + ":.*}": procLeaveEndpoint, + "/networks/id/{" + urlNwID + ":.*}/endpoint/id/{" + urlEpID + ":.*}/container/{" + urlCnID + ":.*}": procLeaveEndpoint, + }, + } + + h.r = mux.NewRouter() + for method, routes := range m { + for route, fct := range routes { + f := makeHandler(h.c, fct) + h.r.Path(route).Methods(method).HandlerFunc(f) + } + } +} + +func makeHandler(ctrl libnetwork.NetworkController, fct processor) http.HandlerFunc { + return func(w http.ResponseWriter, req *http.Request) { + var ( + body []byte + err error + ) + if req.Body != nil { + body, err = ioutil.ReadAll(req.Body) + if err != nil { + http.Error(w, "Invalid body: "+err.Error(), http.StatusBadRequest) + return + } + } + + res, rsp := fct(ctrl, mux.Vars(req), body) + if !rsp.isOK() { + http.Error(w, rsp.Status, rsp.StatusCode) + return + } + if res != nil { + writeJSON(w, rsp.StatusCode, res) + } + } +} + +/***************** + Resource Builders +******************/ + +func buildNetworkResource(nw libnetwork.Network) *networkResource { + r := &networkResource{} + if nw != nil { + r.Name = nw.Name() + r.ID = nw.ID() + r.Type = nw.Type() + epl := nw.Endpoints() + r.Endpoints = make([]*endpointResource, 0, len(epl)) + for _, e := range epl { + epr := buildEndpointResource(e) + r.Endpoints = append(r.Endpoints, epr) + } + } + return r +} + +func buildEndpointResource(ep libnetwork.Endpoint) *endpointResource { + r := &endpointResource{} + if ep != nil { + r.Name = ep.Name() + r.ID = ep.ID() + r.Network = ep.Network() + } + return r +} + +/************** + Options Parser +***************/ + +func (ej *endpointJoin) parseOptions() []libnetwork.EndpointOption { + var setFctList []libnetwork.EndpointOption + if ej.HostName != "" { + setFctList = append(setFctList, libnetwork.JoinOptionHostname(ej.HostName)) + } + if ej.DomainName != "" { + setFctList = append(setFctList, libnetwork.JoinOptionDomainname(ej.DomainName)) + } + if ej.HostsPath != "" { + setFctList = append(setFctList, libnetwork.JoinOptionHostsPath(ej.HostsPath)) + } + if ej.ResolvConfPath != "" { + setFctList = append(setFctList, libnetwork.JoinOptionResolvConfPath(ej.ResolvConfPath)) + } + if ej.UseDefaultSandbox { + setFctList = append(setFctList, libnetwork.JoinOptionUseDefaultSandbox()) + } + if ej.DNS != nil { + for _, d := range ej.DNS { + setFctList = append(setFctList, libnetwork.JoinOptionDNS(d)) + } + } + if ej.ExtraHosts != nil { + for _, e := range ej.ExtraHosts { + setFctList = append(setFctList, libnetwork.JoinOptionExtraHost(e.Name, e.Address)) + } + } + if ej.ParentUpdates != nil { + for _, p := range ej.ParentUpdates { + setFctList = append(setFctList, libnetwork.JoinOptionParentUpdate(p.EndpointID, p.Name, p.Address)) + } + } + return setFctList +} + +/****************** + Process functions +*******************/ + +/*************************** + NetworkController interface +****************************/ +func procCreateNetwork(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { + var create networkCreate + + err := json.Unmarshal(body, &create) + if err != nil { + return "", &responseStatus{Status: "Invalid body: " + err.Error(), StatusCode: http.StatusBadRequest} + } + + name := vars[urlNwName] + if name != create.Name { + return "", &mismatchResponse + } + + nw, err := c.NewNetwork(create.NetworkType, name, nil) + if err != nil { + return "", convertNetworkError(err) + } + + return nw.ID(), &createdResponse +} + +func procGetNetwork(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { + t, by := detectNetworkTarget(vars) + nw, errRsp := findNetwork(c, t, by) + if !errRsp.isOK() { + return nil, errRsp + } + return buildNetworkResource(nw), &successResponse +} + +func procGetNetworks(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { + var list []*networkResource + for _, nw := range c.Networks() { + nwr := buildNetworkResource(nw) + list = append(list, nwr) + } + return list, &successResponse +} + +/****************** + Network interface +*******************/ +func procCreateEndpoint(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { + var ec endpointCreate + + err := json.Unmarshal(body, &ec) + if err != nil { + return "", &responseStatus{Status: "Invalid body: " + err.Error(), StatusCode: http.StatusBadRequest} + } + + epn := vars[urlEpName] + if ec.Name != epn { + return "", &mismatchResponse + } + + nwT, nwBy := detectNetworkTarget(vars) + n, errRsp := findNetwork(c, nwT, nwBy) + if !errRsp.isOK() { + return "", errRsp + } + + if ec.NetworkID != n.ID() { + return "", &mismatchResponse + } + + var setFctList []libnetwork.EndpointOption + if ec.ExposedPorts != nil { + setFctList = append(setFctList, libnetwork.CreateOptionExposedPorts(ec.ExposedPorts)) + } + if ec.PortMapping != nil { + setFctList = append(setFctList, libnetwork.CreateOptionPortMapping(ec.PortMapping)) + } + + ep, err := n.CreateEndpoint(epn, setFctList...) + if err != nil { + return "", convertNetworkError(err) + } + + return ep.ID(), &createdResponse +} + +func procGetEndpoint(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { + nwT, nwBy := detectNetworkTarget(vars) + epT, epBy := detectEndpointTarget(vars) + + ep, errRsp := findEndpoint(c, nwT, epT, nwBy, epBy) + if !errRsp.isOK() { + return nil, errRsp + } + + return buildEndpointResource(ep), &successResponse +} + +func procGetEndpoints(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { + target, by := detectNetworkTarget(vars) + + nw, errRsp := findNetwork(c, target, by) + if !errRsp.isOK() { + return nil, errRsp + } + + var list []*endpointResource + for _, ep := range nw.Endpoints() { + epr := buildEndpointResource(ep) + list = append(list, epr) + } + + return list, &successResponse +} + +func procDeleteNetwork(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { + target, by := detectNetworkTarget(vars) + + nw, errRsp := findNetwork(c, target, by) + if !errRsp.isOK() { + return nil, errRsp + } + + err := nw.Delete() + if err != nil { + return nil, convertNetworkError(err) + } + + return nil, &successResponse +} + +/****************** + Endpoint interface +*******************/ +func procJoinEndpoint(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { + var ej endpointJoin + err := json.Unmarshal(body, &ej) + if err != nil { + return nil, &responseStatus{Status: "Invalid body: " + err.Error(), StatusCode: http.StatusBadRequest} + } + + cid := vars[urlCnID] + if ej.ContainerID != cid { + return "", &mismatchResponse + } + + nwT, nwBy := detectNetworkTarget(vars) + epT, epBy := detectEndpointTarget(vars) + + ep, errRsp := findEndpoint(c, nwT, epT, nwBy, epBy) + if !errRsp.isOK() { + return nil, errRsp + } + + cd, err := ep.Join(ej.ContainerID, ej.parseOptions()...) + if err != nil { + return nil, convertNetworkError(err) + } + return cd, &successResponse +} + +func procLeaveEndpoint(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { + nwT, nwBy := detectNetworkTarget(vars) + epT, epBy := detectEndpointTarget(vars) + + ep, errRsp := findEndpoint(c, nwT, epT, nwBy, epBy) + if !errRsp.isOK() { + return nil, errRsp + } + + err := ep.Leave(vars[urlCnID]) + if err != nil { + return nil, convertNetworkError(err) + } + + return nil, &successResponse +} + +func procDeleteEndpoint(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { + nwT, nwBy := detectNetworkTarget(vars) + epT, epBy := detectEndpointTarget(vars) + + ep, errRsp := findEndpoint(c, nwT, epT, nwBy, epBy) + if !errRsp.isOK() { + return nil, errRsp + } + + err := ep.Delete() + if err != nil { + return nil, convertNetworkError(err) + } + + return nil, &successResponse +} + +/*********** + Utilities +************/ +const ( + byID = iota + byName +) + +func detectNetworkTarget(vars map[string]string) (string, int) { + if target, ok := vars[urlNwName]; ok { + return target, byName + } + if target, ok := vars[urlNwID]; ok { + return target, byID + } + // vars are populated from the URL, following cannot happen + panic("Missing URL variable parameter for network") +} + +func detectEndpointTarget(vars map[string]string) (string, int) { + if target, ok := vars[urlEpName]; ok { + return target, byName + } + if target, ok := vars[urlEpID]; ok { + return target, byID + } + // vars are populated from the URL, following cannot happen + panic("Missing URL variable parameter for endpoint") +} + +func findNetwork(c libnetwork.NetworkController, s string, by int) (libnetwork.Network, *responseStatus) { + var ( + nw libnetwork.Network + err error + ) + switch by { + case byID: + nw, err = c.NetworkByID(s) + case byName: + nw, err = c.NetworkByName(s) + default: + panic(fmt.Sprintf("unexpected selector for network search: %d", by)) + } + if err != nil { + if err == libnetwork.ErrNoSuchNetwork { + return nil, &responseStatus{Status: "Resource not found: Network", StatusCode: http.StatusNotFound} + } + return nil, &responseStatus{Status: err.Error(), StatusCode: http.StatusBadRequest} + } + return nw, &successResponse +} + +func findEndpoint(c libnetwork.NetworkController, ns, es string, nwBy, epBy int) (libnetwork.Endpoint, *responseStatus) { + nw, errRsp := findNetwork(c, ns, nwBy) + if !errRsp.isOK() { + return nil, errRsp + } + var ( + err error + ep libnetwork.Endpoint + ) + switch epBy { + case byID: + ep, err = nw.EndpointByID(es) + case byName: + ep, err = nw.EndpointByName(es) + default: + panic(fmt.Sprintf("unexpected selector for endpoint search: %d", epBy)) + } + if err != nil { + if err == libnetwork.ErrNoSuchEndpoint { + return nil, &responseStatus{Status: "Resource not found: Endpoint", StatusCode: http.StatusNotFound} + } + return nil, &responseStatus{Status: err.Error(), StatusCode: http.StatusBadRequest} + } + return ep, &successResponse +} + +func convertNetworkError(err error) *responseStatus { + // No real libnetwork error => http error code conversion for now. + // Will came in later when new interface for libnetwork error is vailable + return &responseStatus{Status: err.Error(), StatusCode: http.StatusBadRequest} +} + +func writeJSON(w http.ResponseWriter, code int, v interface{}) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(code) + return json.NewEncoder(w).Encode(v) +} diff --git a/vendor/src/github.com/docker/libnetwork/api/api_test.go b/vendor/src/github.com/docker/libnetwork/api/api_test.go new file mode 100644 index 0000000000..5065379ccf --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/api/api_test.go @@ -0,0 +1,1306 @@ +package api + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + "os" + "runtime" + "testing" + + "github.com/docker/docker/pkg/reexec" + "github.com/docker/libnetwork" + "github.com/docker/libnetwork/netlabel" + "github.com/docker/libnetwork/netutils" + "github.com/docker/libnetwork/options" +) + +const ( + bridgeNetType = "bridge" + bridgeName = "docker0" +) + +func getEmptyGenericOption() map[string]interface{} { + genericOption := make(map[string]interface{}) + genericOption[netlabel.GenericData] = options.Generic{} + return genericOption +} + +func i2s(i interface{}) string { + s, ok := i.(string) + if !ok { + panic(fmt.Sprintf("Failed i2s for %v", i)) + } + return s +} + +func i2e(i interface{}) *endpointResource { + s, ok := i.(*endpointResource) + if !ok { + panic(fmt.Sprintf("Failed i2e for %v", i)) + } + return s +} + +func i2c(i interface{}) *libnetwork.ContainerData { + s, ok := i.(*libnetwork.ContainerData) + if !ok { + panic(fmt.Sprintf("Failed i2c for %v", i)) + } + return s +} + +func i2eL(i interface{}) []*endpointResource { + s, ok := i.([]*endpointResource) + if !ok { + panic(fmt.Sprintf("Failed i2eL for %v", i)) + } + return s +} + +func i2n(i interface{}) *networkResource { + s, ok := i.(*networkResource) + if !ok { + panic(fmt.Sprintf("Failed i2n for %v", i)) + } + return s +} + +func i2nL(i interface{}) []*networkResource { + s, ok := i.([]*networkResource) + if !ok { + panic(fmt.Sprintf("Failed i2nL for %v", i)) + } + return s +} + +func TestMain(m *testing.M) { + if reexec.Init() { + return + } + os.Exit(m.Run()) +} + +func TestJoinOptionParser(t *testing.T) { + hn := "host1" + dn := "docker.com" + hp := "/etc/hosts" + rc := "/etc/resolv.conf" + dnss := []string{"8.8.8.8", "172.28.34.5"} + ehs := []endpointExtraHost{endpointExtraHost{Name: "extra1", Address: "172.28.9.1"}, endpointExtraHost{Name: "extra2", Address: "172.28.9.2"}} + pus := []endpointParentUpdate{endpointParentUpdate{EndpointID: "abc123def456", Name: "serv1", Address: "172.28.30.123"}} + + ej := endpointJoin{ + HostName: hn, + DomainName: dn, + HostsPath: hp, + ResolvConfPath: rc, + DNS: dnss, + ExtraHosts: ehs, + ParentUpdates: pus, + UseDefaultSandbox: true, + } + + if len(ej.parseOptions()) != 10 { + t.Fatalf("Failed to generate all libnetwork.EndpointJoinOption methods libnetwork.EndpointJoinOption method") + } + +} + +func TestJson(t *testing.T) { + nc := networkCreate{Name: "mynet", NetworkType: bridgeNetType} + b, err := json.Marshal(nc) + if err != nil { + t.Fatal(err) + } + + var ncp networkCreate + err = json.Unmarshal(b, &ncp) + if err != nil { + t.Fatal(err) + } + + if nc.Name != ncp.Name || nc.NetworkType != ncp.NetworkType { + t.Fatalf("Incorrect networkCreate after json encoding/deconding: %v", ncp) + } + + ec := endpointCreate{Name: "mioEp", NetworkID: "0xabcde"} + b, err = json.Marshal(ec) + if err != nil { + t.Fatal(err) + } + + var ecp endpointCreate + err = json.Unmarshal(b, &ecp) + if err != nil { + t.Fatal(err) + } + + if ec.Name != ecp.Name || ec.NetworkID != ecp.NetworkID { + t.Fatalf("Incorrect endpointCreate after json encoding/deconding: %v", ecp) + } + + jl := endpointJoin{ContainerID: "abcdef456789"} + b, err = json.Marshal(jl) + if err != nil { + t.Fatal(err) + } + + var jld endpointJoin + err = json.Unmarshal(b, &jld) + if err != nil { + t.Fatal(err) + } + + if jl.ContainerID != jld.ContainerID { + t.Fatalf("Incorrect endpointJoin after json encoding/deconding: %v", ecp) + } +} + +func TestCreateDeleteNetwork(t *testing.T) { + defer netutils.SetupTestNetNS(t)() + + c, err := libnetwork.New() + if err != nil { + t.Fatal(err) + } + err = c.ConfigureNetworkDriver(bridgeNetType, nil) + if err != nil { + t.Fatal(err) + } + + badBody, err := json.Marshal("bad body") + if err != nil { + t.Fatal(err) + } + + goodVars := map[string]string{urlNwName: "myNet"} + _, errRsp := procCreateNetwork(c, goodVars, badBody) + if errRsp == &createdResponse { + t.Fatalf("Expected to fail but succeeded") + } + + incompleteBody, err := json.Marshal(networkCreate{Name: "myNet"}) + if err != nil { + t.Fatal(err) + } + + _, errRsp = procCreateNetwork(c, goodVars, incompleteBody) + if errRsp == &createdResponse { + t.Fatalf("Expected to fail but succeeded") + } + if errRsp.StatusCode != http.StatusBadRequest { + t.Fatalf("Expected StatusBadRequest status code, got: %v", errRsp.StatusCode) + } + + ops := make(map[string]interface{}) + ops[netlabel.GenericData] = options.Generic{} + nc := networkCreate{Name: "myNet", NetworkType: bridgeNetType, Options: ops} + goodBody, err := json.Marshal(nc) + if err != nil { + t.Fatal(err) + } + + badVars := map[string]string{urlNwName: ""} + _, errRsp = procCreateNetwork(c, badVars, goodBody) + if errRsp == &createdResponse { + t.Fatalf("Expected to fail but succeeded") + } + if errRsp.StatusCode != http.StatusBadRequest { + t.Fatalf("Expected StatusBadRequest status code, got: %v", errRsp.StatusCode) + } + + badVars[urlNwName] = "badNetworkName" + _, errRsp = procCreateNetwork(c, badVars, goodBody) + if errRsp == &createdResponse { + t.Fatalf("Expected to fail but succeeded") + } + if errRsp.StatusCode != http.StatusBadRequest { + t.Fatalf("Expected StatusBadRequest status code, got: %v", errRsp.StatusCode) + } + + _, errRsp = procCreateNetwork(c, goodVars, goodBody) + if errRsp != &createdResponse { + t.Fatalf("Unexepected failure: %v", errRsp) + } + + _, errRsp = procDeleteNetwork(c, badVars, nil) + if errRsp == &successResponse { + t.Fatalf("Expected to fail but succeeded") + } + + badVars[urlNwName] = "" + _, errRsp = procDeleteNetwork(c, badVars, nil) + if errRsp == &successResponse { + t.Fatalf("Expected to fail but succeeded") + } + + _, errRsp = procDeleteNetwork(c, goodVars, nil) + if errRsp != &successResponse { + t.Fatalf("Unexepected failure: %v", errRsp) + } +} + +func TestGetNetworksAndEndpoints(t *testing.T) { + defer netutils.SetupTestNetNS(t)() + + c, err := libnetwork.New() + if err != nil { + t.Fatal(err) + } + err = c.ConfigureNetworkDriver(bridgeNetType, nil) + if err != nil { + t.Fatal(err) + } + + nc := networkCreate{Name: "sh", NetworkType: bridgeNetType} + body, err := json.Marshal(nc) + if err != nil { + t.Fatal(err) + } + + vars := map[string]string{urlNwName: "sh"} + inid, errRsp := procCreateNetwork(c, vars, body) + if errRsp != &createdResponse { + t.Fatalf("Unexepected failure: %v", errRsp) + } + nid, ok := inid.(string) + if !ok { + t.FailNow() + } + + ec1 := endpointCreate{ + Name: "ep1", + NetworkID: string(nid), + ExposedPorts: []netutils.TransportPort{ + netutils.TransportPort{Proto: netutils.TCP, Port: uint16(5000)}, + netutils.TransportPort{Proto: netutils.UDP, Port: uint16(400)}, + netutils.TransportPort{Proto: netutils.TCP, Port: uint16(600)}, + }, + PortMapping: []netutils.PortBinding{ + netutils.PortBinding{Proto: netutils.TCP, Port: uint16(230), HostPort: uint16(23000)}, + netutils.PortBinding{Proto: netutils.UDP, Port: uint16(200), HostPort: uint16(22000)}, + netutils.PortBinding{Proto: netutils.TCP, Port: uint16(120), HostPort: uint16(12000)}, + }, + } + b1, err := json.Marshal(ec1) + if err != nil { + t.Fatal(err) + } + ec2 := endpointCreate{Name: "ep2", NetworkID: nid} + b2, err := json.Marshal(ec2) + if err != nil { + t.Fatal(err) + } + + vars[urlEpName] = "ep1" + ieid1, errRsp := procCreateEndpoint(c, vars, b1) + if errRsp != &createdResponse { + t.Fatalf("Unexepected failure: %v", errRsp) + } + eid1 := i2s(ieid1) + vars[urlEpName] = "ep2" + ieid2, errRsp := procCreateEndpoint(c, vars, b2) + if errRsp != &createdResponse { + t.Fatalf("Unexepected failure: %v", errRsp) + } + eid2 := i2s(ieid2) + + vars[urlNwName] = "" + vars[urlEpName] = "ep1" + _, errRsp = procGetEndpoint(c, vars, nil) + if errRsp == &successResponse { + t.Fatalf("Expected failure but succeeded: %v", errRsp) + } + if errRsp.StatusCode != http.StatusBadRequest { + t.Fatalf("Expected to fail with http.StatusBadRequest, but got: %d", errRsp.StatusCode) + } + + vars = make(map[string]string) + vars[urlNwName] = "sh" + vars[urlEpID] = "" + _, errRsp = procGetEndpoint(c, vars, nil) + if errRsp == &successResponse { + t.Fatalf("Expected failure but succeeded: %v", errRsp) + } + if errRsp.StatusCode != http.StatusBadRequest { + t.Fatalf("Expected to fail with http.StatusBadRequest, but got: %d", errRsp.StatusCode) + } + + vars = make(map[string]string) + vars[urlNwID] = "" + vars[urlEpID] = eid1 + _, errRsp = procGetEndpoint(c, vars, nil) + if errRsp == &successResponse { + t.Fatalf("Expected failure but succeeded: %v", errRsp) + } + if errRsp.StatusCode != http.StatusBadRequest { + t.Fatalf("Expected to fail with http.StatusBadRequest, but got: %d", errRsp.StatusCode) + } + + // nw by name and ep by id + vars[urlNwName] = "sh" + i1, errRsp := procGetEndpoint(c, vars, nil) + if errRsp != &successResponse { + t.Fatalf("Unexepected failure: %v", errRsp) + } + // nw by name and ep by name + delete(vars, urlEpID) + vars[urlEpName] = "ep1" + i2, errRsp := procGetEndpoint(c, vars, nil) + if errRsp != &successResponse { + t.Fatalf("Unexepected failure: %v", errRsp) + } + // nw by id and ep by name + delete(vars, urlNwName) + vars[urlNwID] = nid + i3, errRsp := procGetEndpoint(c, vars, nil) + if errRsp != &successResponse { + t.Fatalf("Unexepected failure: %v", errRsp) + } + // nw by id and ep by id + delete(vars, urlEpName) + vars[urlEpID] = eid1 + i4, errRsp := procGetEndpoint(c, vars, nil) + if errRsp != &successResponse { + t.Fatalf("Unexepected failure: %v", errRsp) + } + + id1 := i2e(i1).ID + if id1 != i2e(i2).ID || id1 != i2e(i3).ID || id1 != i2e(i4).ID { + t.Fatalf("Endpoints retireved via different query parameters differ: %v, %v, %v, %v", i1, i2, i3, i4) + } + + vars[urlNwName] = "" + _, errRsp = procGetEndpoints(c, vars, nil) + if errRsp == &successResponse { + t.Fatalf("Expected failure, got: %v", errRsp) + } + + delete(vars, urlNwName) + vars[urlNwID] = "fakeID" + _, errRsp = procGetEndpoints(c, vars, nil) + if errRsp == &successResponse { + t.Fatalf("Expected failure, got: %v", errRsp) + } + + vars[urlNwID] = nid + _, errRsp = procGetEndpoints(c, vars, nil) + if errRsp != &successResponse { + t.Fatalf("Unexepected failure: %v", errRsp) + } + + vars[urlNwName] = "sh" + iepList, errRsp := procGetEndpoints(c, vars, nil) + if errRsp != &successResponse { + t.Fatalf("Unexepected failure: %v", errRsp) + } + epList := i2eL(iepList) + if len(epList) != 2 { + t.Fatalf("Did not return the expected number (2) of endpoint resources: %d", len(epList)) + } + if "sh" != epList[0].Network || "sh" != epList[1].Network { + t.Fatalf("Did not find expected network name in endpoint resources") + } + + vars = make(map[string]string) + vars[urlNwName] = "" + _, errRsp = procGetNetwork(c, vars, nil) + if errRsp == &successResponse { + t.Fatalf("Exepected failure, got: %v", errRsp) + } + vars[urlNwName] = "shhhhh" + _, errRsp = procGetNetwork(c, vars, nil) + if errRsp == &successResponse { + t.Fatalf("Exepected failure, got: %v", errRsp) + } + vars[urlNwName] = "sh" + inr1, errRsp := procGetNetwork(c, vars, nil) + if errRsp != &successResponse { + t.Fatalf("Unexepected failure: %v", errRsp) + } + nr1 := i2n(inr1) + + delete(vars, urlNwName) + vars[urlNwID] = "cacca" + _, errRsp = procGetNetwork(c, vars, nil) + if errRsp == &successResponse { + t.Fatalf("Unexepected failure: %v", errRsp) + } + vars[urlNwID] = nid + inr2, errRsp := procGetNetwork(c, vars, nil) + if errRsp != &successResponse { + t.Fatalf("procgetNetworkByName() != procgetNetworkById(), %v vs %v", inr1, inr2) + } + nr2 := i2n(inr2) + if nr1.Name != nr2.Name || nr1.Type != nr2.Type || nr1.ID != nr2.ID || len(nr1.Endpoints) != len(nr2.Endpoints) { + t.Fatalf("Get by name and Get failure: %v", errRsp) + } + + if len(nr1.Endpoints) != 2 { + t.Fatalf("Did not find the expected number (2) of endpoint resources in the network resource: %d", len(nr1.Endpoints)) + } + for _, er := range nr1.Endpoints { + if er.ID != eid1 && er.ID != eid2 { + t.Fatalf("Did not find the expected endpoint resources in the network resource: %v", nr1.Endpoints) + } + } + + iList, errRsp := procGetNetworks(c, nil, nil) + if errRsp != &successResponse { + t.Fatalf("Unexepected failure: %v", errRsp) + } + netList := i2nL(iList) + if len(netList) != 1 { + t.Fatalf("Did not return the expected number of network resources") + } + if nid != netList[0].ID { + t.Fatalf("Did not find expected network %s: %v", nid, netList) + } + + _, errRsp = procDeleteNetwork(c, vars, nil) + if errRsp == &successResponse { + t.Fatalf("Exepected failure, got: %v", errRsp) + } + + vars[urlEpName] = "ep1" + _, errRsp = procDeleteEndpoint(c, vars, nil) + if errRsp != &successResponse { + t.Fatalf("Unexepected failure: %v", errRsp) + } + iepList, errRsp = procGetEndpoints(c, vars, nil) + if errRsp != &successResponse { + t.Fatalf("Unexepected failure: %v", errRsp) + } + epList = i2eL(iepList) + if len(epList) != 1 { + t.Fatalf("Did not return the expected number (1) of endpoint resources: %d", len(epList)) + } + + vars[urlEpName] = "ep2" + _, errRsp = procDeleteEndpoint(c, vars, nil) + if errRsp != &successResponse { + t.Fatalf("Unexepected failure: %v", errRsp) + } + iepList, errRsp = procGetEndpoints(c, vars, nil) + if errRsp != &successResponse { + t.Fatalf("Unexepected failure: %v", errRsp) + } + epList = i2eL(iepList) + if len(epList) != 0 { + t.Fatalf("Did not return the expected number (0) of endpoint resources: %d", len(epList)) + } + + _, errRsp = procDeleteNetwork(c, vars, nil) + if errRsp != &successResponse { + t.Fatalf("Unexepected failure: %v", errRsp) + } + + iList, errRsp = procGetNetworks(c, nil, nil) + if errRsp != &successResponse { + t.Fatalf("Unexepected failure: %v", errRsp) + } + netList = i2nL(iList) + if len(netList) != 0 { + t.Fatalf("Did not return the expected number of network resources") + } +} + +func TestFindNetworkUtil(t *testing.T) { + defer netutils.SetupTestNetNS(t)() + + c, err := libnetwork.New() + if err != nil { + t.Fatal(err) + } + err = c.ConfigureNetworkDriver(bridgeNetType, nil) + if err != nil { + t.Fatal(err) + } + + nw, err := c.NewNetwork(bridgeNetType, "network", nil) + if err != nil { + t.Fatal(err) + } + nid := nw.ID() + + defer checkPanic(t) + findNetwork(c, "", -1) + + _, errRsp := findNetwork(c, "", byName) + if errRsp == &successResponse { + t.Fatalf("Expected to fail but succeeded") + } + if errRsp.StatusCode != http.StatusBadRequest { + t.Fatalf("Expected %d, but got: %d", http.StatusBadRequest, errRsp.StatusCode) + } + + n, errRsp := findNetwork(c, nid, byID) + if errRsp != &successResponse { + t.Fatalf("Unexpected failure: %v", errRsp) + } + if n == nil { + t.Fatalf("Unexpected nil libnetwork.Network") + } + if nid != n.ID() { + t.Fatalf("Incorrect libnetwork.Network resource. It has different id: %v", n) + } + if "network" != n.Name() { + t.Fatalf("Incorrect libnetwork.Network resource. It has different name: %v", n) + } + + n, errRsp = findNetwork(c, "network", byName) + if errRsp != &successResponse { + t.Fatalf("Unexpected failure: %v", errRsp) + } + if n == nil { + t.Fatalf("Unexpected nil libnetwork.Network") + } + if nid != n.ID() { + t.Fatalf("Incorrect libnetwork.Network resource. It has different id: %v", n) + } + if "network" != n.Name() { + t.Fatalf("Incorrect libnetwork.Network resource. It has different name: %v", n) + } + + n.Delete() + + _, errRsp = findNetwork(c, nid, byID) + if errRsp == &successResponse { + t.Fatalf("Expected to fail but succeeded") + } + if errRsp.StatusCode != http.StatusNotFound { + t.Fatalf("Expected %d, but got: %d", http.StatusNotFound, errRsp.StatusCode) + } + + _, errRsp = findNetwork(c, "network", byName) + if errRsp == &successResponse { + t.Fatalf("Expected to fail but succeeded") + } + if errRsp.StatusCode != http.StatusNotFound { + t.Fatalf("Expected %d, but got: %d", http.StatusNotFound, errRsp.StatusCode) + } +} + +func TestCreateDeleteEndpoints(t *testing.T) { + defer netutils.SetupTestNetNS(t)() + + c, err := libnetwork.New() + if err != nil { + t.Fatal(err) + } + err = c.ConfigureNetworkDriver(bridgeNetType, nil) + if err != nil { + t.Fatal(err) + } + + nc := networkCreate{Name: "firstNet", NetworkType: bridgeNetType} + body, err := json.Marshal(nc) + if err != nil { + t.Fatal(err) + } + + vars := map[string]string{urlNwName: "firstNet"} + i, errRsp := procCreateNetwork(c, vars, body) + if errRsp != &createdResponse { + t.Fatalf("Unexepected failure: %v", errRsp) + } + nid := i2s(i) + + vbad, err := json.Marshal("bad endppint create data") + if err != nil { + t.Fatal(err) + } + + vars[urlEpName] = "ep1" + _, errRsp = procCreateEndpoint(c, vars, vbad) + if errRsp == &createdResponse { + t.Fatalf("Expected to fail but succeeded") + } + + bad, err := json.Marshal(endpointCreate{Name: "ep1", NetworkID: "123456"}) + if err != nil { + t.Fatal(err) + } + _, errRsp = procCreateEndpoint(c, vars, bad) + if errRsp == &createdResponse { + t.Fatalf("Expected to fail but succeeded") + } + + soso, err := json.Marshal(endpointCreate{Name: "ep11", NetworkID: nid}) + if err != nil { + t.Fatal(err) + } + _, errRsp = procCreateEndpoint(c, vars, soso) + if errRsp != &mismatchResponse { + t.Fatalf("Expected to fail with \"mismatchResponse\", but got: %v", errRsp) + } + + bla, err := json.Marshal(endpointCreate{Name: "", NetworkID: nid}) + if err != nil { + t.Fatal(err) + } + vars[urlNwName] = "firstNet" + vars[urlEpName] = "" + _, errRsp = procCreateEndpoint(c, vars, bla) + if errRsp == &createdResponse { + t.Fatalf("Expected to fail but succeeded: %v", errRsp) + } + + b, err := json.Marshal(endpointCreate{Name: "firstEp", NetworkID: nid}) + if err != nil { + t.Fatal(err) + } + + vars[urlNwName] = "secondNet" + vars[urlEpName] = "firstEp" + _, errRsp = procCreateEndpoint(c, vars, b) + if errRsp == &createdResponse { + t.Fatalf("Expected to fail but succeeded") + } + + vars[urlNwName] = "firstNet" + vars[urlEpName] = "ep1" + _, errRsp = procCreateEndpoint(c, vars, b) + if errRsp != &mismatchResponse { + t.Fatalf("Expected to fail with \"mismatchResponse\", but got: %v", errRsp) + } + + vars = make(map[string]string) + _, errRsp = procCreateEndpoint(c, vars, b) + if errRsp == &successResponse { + t.Fatalf("Expected failure but succeeded: %v", errRsp) + } + + vars[urlNwName] = "firstNet" + _, errRsp = procCreateEndpoint(c, vars, b) + if errRsp == &successResponse { + t.Fatalf("Expected failure but succeeded: %v", errRsp) + } + + vars[urlEpName] = "firstEp" + i, errRsp = procCreateEndpoint(c, vars, b) + if errRsp != &createdResponse { + t.Fatalf("Unexepected failure: %v", errRsp) + } + eid := i2s(i) + + _, errRsp = findEndpoint(c, "myNet", "firstEp", byName, byName) + if errRsp == &successResponse { + t.Fatalf("Expected failure but succeeded: %v", errRsp) + } + + ep0, errRsp := findEndpoint(c, nid, "firstEp", byID, byName) + if errRsp != &successResponse { + t.Fatalf("Unexepected failure: %v", errRsp) + } + + ep1, errRsp := findEndpoint(c, "firstNet", "firstEp", byName, byName) + if errRsp != &successResponse { + t.Fatalf("Unexepected failure: %v", errRsp) + } + + ep2, errRsp := findEndpoint(c, nid, eid, byID, byID) + if errRsp != &successResponse { + t.Fatalf("Unexepected failure: %v", errRsp) + } + + ep3, errRsp := findEndpoint(c, "firstNet", eid, byName, byID) + if errRsp != &successResponse { + t.Fatalf("Unexepected failure: %v", errRsp) + } + + if ep0 != ep1 || ep0 != ep2 || ep0 != ep3 { + t.Fatalf("Diffenrent queries returned different endpoints") + } + + vars = make(map[string]string) + vars[urlNwName] = "" + vars[urlEpName] = "ep1" + _, errRsp = procDeleteEndpoint(c, vars, nil) + if errRsp == &successResponse { + t.Fatalf("Expected failure, got: %v", errRsp) + } + + vars[urlNwName] = "firstNet" + vars[urlEpName] = "" + _, errRsp = procDeleteEndpoint(c, vars, nil) + if errRsp == &successResponse { + t.Fatalf("Expected failure, got: %v", errRsp) + } + + vars[urlEpName] = "ep2" + _, errRsp = procDeleteEndpoint(c, vars, nil) + if errRsp == &successResponse { + t.Fatalf("Expected failure, got: %v", errRsp) + } + + vars[urlEpName] = "firstEp" + _, errRsp = procDeleteEndpoint(c, vars, nil) + if errRsp != &successResponse { + t.Fatalf("Unexepected failure: %v", errRsp) + } + + _, errRsp = findEndpoint(c, "firstNet", "firstEp", byName, byName) + if errRsp == &successResponse { + t.Fatalf("Expected failure, got: %v", errRsp) + } +} + +func TestJoinLeave(t *testing.T) { + defer netutils.SetupTestNetNS(t)() + + c, err := libnetwork.New() + if err != nil { + t.Fatal(err) + } + err = c.ConfigureNetworkDriver(bridgeNetType, nil) + if err != nil { + t.Fatal(err) + } + + nb, err := json.Marshal(networkCreate{Name: "network", NetworkType: bridgeNetType}) + if err != nil { + t.Fatal(err) + } + vars := map[string]string{urlNwName: "network"} + i, errRsp := procCreateNetwork(c, vars, nb) + if errRsp != &createdResponse { + t.Fatalf("Unexepected failure: %v", errRsp) + } + nid := i2s(i) + + vars[urlEpName] = "epoint" + eb, err := json.Marshal(endpointCreate{Name: "epoint", NetworkID: nid}) + if err != nil { + t.Fatal(err) + } + _, errRsp = procCreateEndpoint(c, vars, eb) + if errRsp != &createdResponse { + t.Fatalf("Unexepected failure: %v", errRsp) + } + + vbad, err := json.Marshal("bad data") + if err != nil { + t.Fatal(err) + } + _, errRsp = procJoinEndpoint(c, vars, vbad) + if errRsp == &successResponse { + t.Fatalf("Expected failure, got: %v", errRsp) + } + + bad, err := json.Marshal(endpointJoin{}) + if err != nil { + t.Fatal(err) + } + _, errRsp = procJoinEndpoint(c, vars, bad) + if errRsp == &successResponse { + t.Fatalf("Expected failure, got: %v", errRsp) + } + + cid := "abcdefghi" + jl := endpointJoin{ContainerID: cid} + jlb, err := json.Marshal(jl) + if err != nil { + t.Fatal(err) + } + + vars = make(map[string]string) + vars[urlNwName] = "" + vars[urlEpName] = "" + vars[urlCnID] = cid + _, errRsp = procJoinEndpoint(c, vars, jlb) + if errRsp == &successResponse { + t.Fatalf("Expected failure, got: %v", errRsp) + } + + vars[urlNwName] = "network1" + vars[urlEpName] = "" + _, errRsp = procJoinEndpoint(c, vars, jlb) + if errRsp == &successResponse { + t.Fatalf("Expected failure, got: %v", errRsp) + } + + vars[urlNwName] = "network" + vars[urlEpName] = "endpoint" + _, errRsp = procJoinEndpoint(c, vars, jlb) + if errRsp == &successResponse { + t.Fatalf("Expected failure, got: %v", errRsp) + } + + vars[urlEpName] = "epoint" + delete(vars, urlCnID) + _, errRsp = procJoinEndpoint(c, vars, jlb) + if errRsp == &successResponse { + t.Fatalf("Expected failure, got: %v", errRsp) + } + + vars[urlCnID] = "who?" + _, errRsp = procJoinEndpoint(c, vars, jlb) + if errRsp == &successResponse { + t.Fatalf("Expected failure, got: %v", errRsp) + } + + vars[urlCnID] = cid + cdi, errRsp := procJoinEndpoint(c, vars, jlb) + if errRsp != &successResponse { + t.Fatalf("Unexpected failure, got: %v", errRsp) + } + cd := i2c(cdi) + if cd.SandboxKey == "" { + t.Fatalf("Empty sandbox key") + } + _, errRsp = procDeleteEndpoint(c, vars, nil) + if errRsp == &successResponse { + t.Fatalf("Expected failure, got: %v", errRsp) + } + + vars[urlNwName] = "network2" + _, errRsp = procLeaveEndpoint(c, vars, vbad) + if errRsp == &successResponse { + t.Fatalf("Expected failure, got: %v", errRsp) + } + _, errRsp = procLeaveEndpoint(c, vars, bad) + if errRsp == &successResponse { + t.Fatalf("Expected failure, got: %v", errRsp) + } + _, errRsp = procLeaveEndpoint(c, vars, jlb) + if errRsp == &successResponse { + t.Fatalf("Expected failure, got: %v", errRsp) + } + vars = make(map[string]string) + vars[urlNwName] = "" + vars[urlEpName] = "" + _, errRsp = procLeaveEndpoint(c, vars, jlb) + if errRsp == &successResponse { + t.Fatalf("Expected failure, got: %v", errRsp) + } + vars[urlNwName] = "network" + vars[urlEpName] = "" + _, errRsp = procLeaveEndpoint(c, vars, jlb) + if errRsp == &successResponse { + t.Fatalf("Expected failure, got: %v", errRsp) + } + vars[urlEpName] = "2epoint" + _, errRsp = procLeaveEndpoint(c, vars, jlb) + if errRsp == &successResponse { + t.Fatalf("Expected failure, got: %v", errRsp) + } + vars[urlEpName] = "epoint" + vars[urlCnID] = "who" + _, errRsp = procLeaveEndpoint(c, vars, jlb) + if errRsp == &successResponse { + t.Fatalf("Expected failure, got: %v", errRsp) + } + + delete(vars, urlCnID) + _, errRsp = procLeaveEndpoint(c, vars, jlb) + if errRsp == &successResponse { + t.Fatalf("Expected failure, got: %v", errRsp) + } + + vars[urlCnID] = cid + _, errRsp = procLeaveEndpoint(c, vars, jlb) + if errRsp != &successResponse { + t.Fatalf("Unexepected failure: %v", errRsp) + } + + _, errRsp = procLeaveEndpoint(c, vars, jlb) + if errRsp == &successResponse { + t.Fatalf("Expected failure, got: %v", errRsp) + } + + _, errRsp = procDeleteEndpoint(c, vars, nil) + if errRsp != &successResponse { + t.Fatalf("Unexepected failure: %v", errRsp) + } +} + +func TestFindEndpointUtil(t *testing.T) { + defer netutils.SetupTestNetNS(t)() + + c, err := libnetwork.New() + if err != nil { + t.Fatal(err) + } + err = c.ConfigureNetworkDriver(bridgeNetType, nil) + if err != nil { + t.Fatal(err) + } + + nw, err := c.NewNetwork(bridgeNetType, "second", nil) + if err != nil { + t.Fatal(err) + } + nid := nw.ID() + + ep, err := nw.CreateEndpoint("secondEp", nil) + if err != nil { + t.Fatal(err) + } + eid := ep.ID() + + defer checkPanic(t) + findEndpoint(c, nid, "", byID, -1) + + _, errRsp := findEndpoint(c, nid, "", byID, byName) + if errRsp == &successResponse { + t.Fatalf("Expected failure, but got: %v", errRsp) + } + if errRsp.StatusCode != http.StatusBadRequest { + t.Fatalf("Expected %d, but got: %d", http.StatusBadRequest, errRsp.StatusCode) + } + + ep0, errRsp := findEndpoint(c, nid, "secondEp", byID, byName) + if errRsp != &successResponse { + t.Fatalf("Unexepected failure: %v", errRsp) + } + + ep1, errRsp := findEndpoint(c, "second", "secondEp", byName, byName) + if errRsp != &successResponse { + t.Fatalf("Unexepected failure: %v", errRsp) + } + + ep2, errRsp := findEndpoint(c, nid, eid, byID, byID) + if errRsp != &successResponse { + t.Fatalf("Unexepected failure: %v", errRsp) + } + + ep3, errRsp := findEndpoint(c, "second", eid, byName, byID) + if errRsp != &successResponse { + t.Fatalf("Unexepected failure: %v", errRsp) + } + + if ep0 != ep1 || ep0 != ep2 || ep0 != ep3 { + t.Fatalf("Diffenrent queries returned different endpoints") + } + + ep.Delete() + + _, errRsp = findEndpoint(c, nid, "secondEp", byID, byName) + if errRsp == &successResponse { + t.Fatalf("Expected failure, but got: %v", errRsp) + } + if errRsp.StatusCode != http.StatusNotFound { + t.Fatalf("Expected %d, but got: %d", http.StatusNotFound, errRsp.StatusCode) + } + + _, errRsp = findEndpoint(c, "second", "secondEp", byName, byName) + if errRsp == &successResponse { + t.Fatalf("Expected failure, but got: %v", errRsp) + } + if errRsp.StatusCode != http.StatusNotFound { + t.Fatalf("Expected %d, but got: %d", http.StatusNotFound, errRsp.StatusCode) + } + + _, errRsp = findEndpoint(c, nid, eid, byID, byID) + if errRsp == &successResponse { + t.Fatalf("Expected failure, but got: %v", errRsp) + } + if errRsp.StatusCode != http.StatusNotFound { + t.Fatalf("Expected %d, but got: %d", http.StatusNotFound, errRsp.StatusCode) + } + + _, errRsp = findEndpoint(c, "second", eid, byName, byID) + if errRsp == &successResponse { + t.Fatalf("Expected failure, but got: %v", errRsp) + } + if errRsp.StatusCode != http.StatusNotFound { + t.Fatalf("Expected %d, but got: %d", http.StatusNotFound, errRsp.StatusCode) + } +} + +func checkPanic(t *testing.T) { + if r := recover(); r != nil { + if _, ok := r.(runtime.Error); ok { + panic(r) + } + } else { + t.Fatalf("Expected to panic, but suceeded") + } +} + +func TestDetectNetworkTargetPanic(t *testing.T) { + defer checkPanic(t) + vars := make(map[string]string) + detectNetworkTarget(vars) +} + +func TestDetectEndpointTargetPanic(t *testing.T) { + defer checkPanic(t) + vars := make(map[string]string) + detectEndpointTarget(vars) +} + +func TestResponseStatus(t *testing.T) { + list := []int{ + http.StatusBadGateway, + http.StatusBadRequest, + http.StatusConflict, + http.StatusContinue, + http.StatusExpectationFailed, + http.StatusForbidden, + http.StatusFound, + http.StatusGatewayTimeout, + http.StatusGone, + http.StatusHTTPVersionNotSupported, + http.StatusInternalServerError, + http.StatusLengthRequired, + http.StatusMethodNotAllowed, + http.StatusMovedPermanently, + http.StatusMultipleChoices, + http.StatusNoContent, + http.StatusNonAuthoritativeInfo, + http.StatusNotAcceptable, + http.StatusNotFound, + http.StatusNotModified, + http.StatusPartialContent, + http.StatusPaymentRequired, + http.StatusPreconditionFailed, + http.StatusProxyAuthRequired, + http.StatusRequestEntityTooLarge, + http.StatusRequestTimeout, + http.StatusRequestURITooLong, + http.StatusRequestedRangeNotSatisfiable, + http.StatusResetContent, + http.StatusServiceUnavailable, + http.StatusSwitchingProtocols, + http.StatusTemporaryRedirect, + http.StatusUnauthorized, + http.StatusUnsupportedMediaType, + http.StatusUseProxy, + } + for _, c := range list { + r := responseStatus{StatusCode: c} + if r.isOK() { + t.Fatalf("isOK() returned true for code% d", c) + } + } + + r := responseStatus{StatusCode: http.StatusOK} + if !r.isOK() { + t.Fatalf("isOK() failed") + } + + r = responseStatus{StatusCode: http.StatusCreated} + if !r.isOK() { + t.Fatalf("isOK() failed") + } +} + +// Local structs for end to end testing of api.go +type localReader struct { + data []byte + beBad bool +} + +func newLocalReader(data []byte) *localReader { + lr := &localReader{data: make([]byte, len(data))} + copy(lr.data, data) + return lr +} + +func (l *localReader) Read(p []byte) (n int, err error) { + if l.beBad { + return 0, errors.New("I am a bad reader") + } + if p == nil { + return -1, fmt.Errorf("nil buffer passed") + } + if l.data == nil || len(l.data) == 0 { + return 0, io.EOF + } + copy(p[:], l.data[:]) + return len(l.data), io.EOF +} + +type localResponseWriter struct { + body []byte + statusCode int +} + +func newWriter() *localResponseWriter { + return &localResponseWriter{} +} + +func (f *localResponseWriter) Header() http.Header { + return make(map[string][]string, 0) +} + +func (f *localResponseWriter) Write(data []byte) (int, error) { + if data == nil { + return -1, fmt.Errorf("nil data passed") + } + + f.body = make([]byte, len(data)) + copy(f.body, data) + + return len(f.body), nil +} + +func (f *localResponseWriter) WriteHeader(c int) { + f.statusCode = c +} + +func TestwriteJSON(t *testing.T) { + testCode := 55 + testData, err := json.Marshal("test data") + if err != nil { + t.Fatal(err) + } + + rsp := newWriter() + writeJSON(rsp, testCode, testData) + if rsp.statusCode != testCode { + t.Fatalf("writeJSON() failed to set the status code. Expected %d. Got %d", testCode, rsp.statusCode) + } + if !bytes.Equal(testData, rsp.body) { + t.Fatalf("writeJSON() failed to set the body. Expected %s. Got %s", testData, rsp.body) + } + +} + +func TestHttpHandlerUninit(t *testing.T) { + defer netutils.SetupTestNetNS(t)() + + c, err := libnetwork.New() + if err != nil { + t.Fatal(err) + } + + h := &httpHandler{c: c} + h.initRouter() + if h.r == nil { + t.Fatalf("initRouter() did not initialize the router") + } + + rsp := newWriter() + req, err := http.NewRequest("GET", "/networks", nil) + if err != nil { + t.Fatal(err) + } + + handleRequest := NewHTTPHandler(nil) + handleRequest(rsp, req) + if rsp.statusCode != http.StatusServiceUnavailable { + t.Fatalf("Expected (%d). Got (%d): %s", http.StatusServiceUnavailable, rsp.statusCode, rsp.body) + } + + handleRequest = NewHTTPHandler(c) + + handleRequest(rsp, req) + if rsp.statusCode != http.StatusOK { + t.Fatalf("Unexpectded failure: (%d): %s", rsp.statusCode, rsp.body) + } + + n, err := c.NewNetwork(bridgeNetType, "onenet", nil) + if err != nil { + t.Fatal(err) + } + nwr := buildNetworkResource(n) + expected, err := json.Marshal([]networkResource{*nwr}) + if err != nil { + t.Fatal(err) + } + + handleRequest(rsp, req) + if rsp.statusCode != http.StatusOK { + t.Fatalf("Unexpectded failure: (%d): %s", rsp.statusCode, rsp.body) + } + if len(rsp.body) == 0 { + t.Fatalf("Empty list of networks") + } + if bytes.Equal(rsp.body, expected) { + t.Fatalf("Incorrect list of networks in response's body") + } +} + +func TestHttpHandlerBadBody(t *testing.T) { + defer netutils.SetupTestNetNS(t)() + + rsp := newWriter() + + c, err := libnetwork.New() + if err != nil { + t.Fatal(err) + } + handleRequest := NewHTTPHandler(c) + + req, err := http.NewRequest("POST", "/networks/name/zero-network", &localReader{beBad: true}) + if err != nil { + t.Fatal(err) + } + handleRequest(rsp, req) + if rsp.statusCode != http.StatusBadRequest { + t.Fatalf("Unexpected status code. Expected (%d). Got (%d): %s.", http.StatusBadRequest, rsp.statusCode, string(rsp.body)) + } + + body := []byte{} + lr := newLocalReader(body) + req, err = http.NewRequest("POST", "/networks/name/zero-network", lr) + if err != nil { + t.Fatal(err) + } + handleRequest(rsp, req) + if rsp.statusCode != http.StatusBadRequest { + t.Fatalf("Unexpected status code. Expected (%d). Got (%d): %s.", http.StatusBadRequest, rsp.statusCode, string(rsp.body)) + } +} + +func TestHttpHandlerGood(t *testing.T) { + defer netutils.SetupTestNetNS(t)() + + rsp := newWriter() + + c, err := libnetwork.New() + if err != nil { + t.Fatal(err) + } + handleRequest := NewHTTPHandler(c) + + nc := networkCreate{Name: "zero-network", NetworkType: bridgeNetType} + body, err := json.Marshal(nc) + if err != nil { + t.Fatal(err) + } + + lr := newLocalReader(body) + req, err := http.NewRequest("POST", "/networks/name/zero-network", lr) + if err != nil { + t.Fatal(err) + } + handleRequest(rsp, req) + if rsp.statusCode != http.StatusCreated { + t.Fatalf("Unexpectded status code. Expected (%d). Got (%d): %s.", http.StatusCreated, rsp.statusCode, string(rsp.body)) + } + if len(rsp.body) == 0 { + t.Fatalf("Empty response body") + } + + var id string + err = json.Unmarshal(rsp.body, &id) + if err != nil { + t.Fatal(err) + } + + req, err = http.NewRequest("GET", "/networks/id/"+id, nil) + if err != nil { + t.Fatal(err) + } + handleRequest(rsp, req) + if rsp.statusCode != http.StatusOK { + t.Fatalf("Unexpectded failure: (%d): %s", rsp.statusCode, rsp.body) + } + + var nwr networkResource + err = json.Unmarshal(rsp.body, &nwr) + if err != nil { + t.Fatal(err) + } + if nwr.Name != "zero-network" || id != nwr.ID { + t.Fatalf("Incongruent resource found") + } +} diff --git a/vendor/src/github.com/docker/libnetwork/api/types.go b/vendor/src/github.com/docker/libnetwork/api/types.go new file mode 100644 index 0000000000..1a0ac85562 --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/api/types.go @@ -0,0 +1,68 @@ +package api + +import "github.com/docker/libnetwork/netutils" + +/*********** + Resources +************/ + +// networkResource is the body of the "get network" http response message +type networkResource struct { + Name string + ID string + Type string + Endpoints []*endpointResource +} + +// endpointResource is the body of the "get endpoint" http response message +type endpointResource struct { + Name string + ID string + Network string +} + +/*********** + Body types + ************/ + +// networkCreate is the expected body of the "create network" http request message +type networkCreate struct { + Name string + NetworkType string + Options map[string]interface{} +} + +// endpointCreate represents the body of the "create endpoint" http request message +type endpointCreate struct { + Name string + NetworkID string + ExposedPorts []netutils.TransportPort + PortMapping []netutils.PortBinding +} + +// endpointJoin represents the expected body of the "join endpoint" or "leave endpoint" http request messages +type endpointJoin struct { + ContainerID string + HostName string + DomainName string + HostsPath string + ResolvConfPath string + DNS []string + ExtraHosts []endpointExtraHost + ParentUpdates []endpointParentUpdate + UseDefaultSandbox bool +} + +// EndpointExtraHost represents the extra host object +type endpointExtraHost struct { + Name string + Address string +} + +// EndpointParentUpdate is the object carrying the information about the +// endpoint parent that needs to be updated +type endpointParentUpdate struct { + EndpointID string + Name string + Address string +} diff --git a/vendor/src/github.com/docker/libnetwork/circle.yml b/vendor/src/github.com/docker/libnetwork/circle.yml new file mode 100644 index 0000000000..d02f6a92ed --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/circle.yml @@ -0,0 +1,12 @@ +machine: + services: + - docker + +dependencies: + override: + - echo "Nothing to install" + +test: + override: + - make circle-ci + diff --git a/vendor/src/github.com/docker/libnetwork/client/client.go b/vendor/src/github.com/docker/libnetwork/client/client.go new file mode 100644 index 0000000000..64c6f7da53 --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/client/client.go @@ -0,0 +1,105 @@ +package client + +import ( + "fmt" + "io" + "io/ioutil" + "reflect" + "strings" + + flag "github.com/docker/docker/pkg/mflag" +) + +// CallFunc provides environment specific call utility to invoke backend functions from UI +type CallFunc func(string, string, interface{}, map[string][]string) (io.ReadCloser, int, error) + +// NetworkCli is the UI object for network subcmds +type NetworkCli struct { + out io.Writer + err io.Writer + call CallFunc +} + +// NewNetworkCli is a convenient function to create a NetworkCli object +func NewNetworkCli(out, err io.Writer, call CallFunc) *NetworkCli { + return &NetworkCli{ + out: out, + err: err, + call: call, + } +} + +// getMethod is Borrowed from Docker UI which uses reflection to identify the UI Handler +func (cli *NetworkCli) getMethod(args ...string) (func(string, ...string) error, bool) { + camelArgs := make([]string, len(args)) + for i, s := range args { + if len(s) == 0 { + return nil, false + } + camelArgs[i] = strings.ToUpper(s[:1]) + strings.ToLower(s[1:]) + } + methodName := "Cmd" + strings.Join(camelArgs, "") + method := reflect.ValueOf(cli).MethodByName(methodName) + if !method.IsValid() { + return nil, false + } + return method.Interface().(func(string, ...string) error), true +} + +// Cmd is borrowed from Docker UI and acts as the entry point for network UI commands. +// network UI commands are designed to be invoked from multiple parent chains +func (cli *NetworkCli) Cmd(chain string, args ...string) error { + if len(args) > 1 { + method, exists := cli.getMethod(args[:2]...) + if exists { + return method(chain+" "+args[0], args[2:]...) + } + } + if len(args) > 0 { + method, exists := cli.getMethod(args[0]) + if !exists { + return fmt.Errorf("%s: '%s' is not a %s command. See '%s --help'.\n", chain, args[0], chain, chain) + } + return method(chain, args[1:]...) + } + flag.Usage() + return nil +} + +// Subcmd is borrowed from Docker UI and performs the same function of configuring the subCmds +func (cli *NetworkCli) Subcmd(chain, name, signature, description string, exitOnError bool) *flag.FlagSet { + var errorHandling flag.ErrorHandling + if exitOnError { + errorHandling = flag.ExitOnError + } else { + errorHandling = flag.ContinueOnError + } + flags := flag.NewFlagSet(name, errorHandling) + flags.Usage = func() { + options := "" + if signature != "" { + signature = " " + signature + } + if flags.FlagCountUndeprecated() > 0 { + options = " [OPTIONS]" + } + fmt.Fprintf(cli.out, "\nUsage: %s %s%s%s\n\n%s\n\n", chain, name, options, signature, description) + flags.SetOutput(cli.out) + flags.PrintDefaults() + } + return flags +} + +func readBody(stream io.ReadCloser, statusCode int, err error) ([]byte, int, error) { + if stream != nil { + defer stream.Close() + } + if err != nil { + return nil, statusCode, err + } + body, err := ioutil.ReadAll(stream) + if err != nil { + return nil, -1, err + } + return body, statusCode, nil +} diff --git a/vendor/src/github.com/docker/libnetwork/client/client_test.go b/vendor/src/github.com/docker/libnetwork/client/client_test.go new file mode 100644 index 0000000000..fbb5ba240a --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/client/client_test.go @@ -0,0 +1,154 @@ +package client + +import ( + "bytes" + "io" + "testing" + + _ "github.com/docker/libnetwork/netutils" +) + +// nopCloser is used to provide a dummy CallFunc for Cmd() +type nopCloser struct { + io.Reader +} + +func (nopCloser) Close() error { return nil } + +func TestClientDummyCommand(t *testing.T) { + var out, errOut bytes.Buffer + cFunc := func(method, path string, data interface{}, headers map[string][]string) (io.ReadCloser, int, error) { + return nopCloser{bytes.NewBufferString("")}, 200, nil + } + cli := NewNetworkCli(&out, &errOut, cFunc) + + err := cli.Cmd("docker", "dummy") + if err == nil { + t.Fatalf("Incorrect Command must fail") + } +} + +func TestClientNetworkInvalidCommand(t *testing.T) { + var out, errOut bytes.Buffer + cFunc := func(method, path string, data interface{}, headers map[string][]string) (io.ReadCloser, int, error) { + return nopCloser{bytes.NewBufferString("")}, 200, nil + } + cli := NewNetworkCli(&out, &errOut, cFunc) + + err := cli.Cmd("docker", "network", "invalid") + if err == nil { + t.Fatalf("Passing invalid commands must fail") + } +} + +func TestClientNetworkCreate(t *testing.T) { + var out, errOut bytes.Buffer + cFunc := func(method, path string, data interface{}, headers map[string][]string) (io.ReadCloser, int, error) { + return nopCloser{bytes.NewBufferString("")}, 200, nil + } + cli := NewNetworkCli(&out, &errOut, cFunc) + + err := cli.Cmd("docker", "network", "create", "test") + if err != nil { + t.Fatal(err.Error()) + } +} + +func TestClientNetworkCreateWithDriver(t *testing.T) { + var out, errOut bytes.Buffer + cFunc := func(method, path string, data interface{}, headers map[string][]string) (io.ReadCloser, int, error) { + return nopCloser{bytes.NewBufferString("")}, 200, nil + } + cli := NewNetworkCli(&out, &errOut, cFunc) + + err := cli.Cmd("docker", "network", "create", "-f=dummy", "test") + if err == nil { + t.Fatalf("Passing incorrect flags to the create command must fail") + } + + err = cli.Cmd("docker", "network", "create", "-d=dummy", "test") + if err != nil { + t.Fatalf(err.Error()) + } +} + +func TestClientNetworkRm(t *testing.T) { + var out, errOut bytes.Buffer + cFunc := func(method, path string, data interface{}, headers map[string][]string) (io.ReadCloser, int, error) { + return nopCloser{bytes.NewBufferString("")}, 200, nil + } + cli := NewNetworkCli(&out, &errOut, cFunc) + + err := cli.Cmd("docker", "network", "rm", "test") + if err != nil { + t.Fatal(err.Error()) + } +} + +func TestClientNetworkLs(t *testing.T) { + var out, errOut bytes.Buffer + networks := "db,web,test" + cFunc := func(method, path string, data interface{}, headers map[string][]string) (io.ReadCloser, int, error) { + return nopCloser{bytes.NewBufferString(networks)}, 200, nil + } + cli := NewNetworkCli(&out, &errOut, cFunc) + + err := cli.Cmd("docker", "network", "ls") + if err != nil { + t.Fatal(err.Error()) + } + if out.String() != networks { + t.Fatal("Network List command fail to return the intended list") + } +} + +func TestClientNetworkInfo(t *testing.T) { + var out, errOut bytes.Buffer + info := "dummy info" + cFunc := func(method, path string, data interface{}, headers map[string][]string) (io.ReadCloser, int, error) { + return nopCloser{bytes.NewBufferString(info)}, 200, nil + } + cli := NewNetworkCli(&out, &errOut, cFunc) + + err := cli.Cmd("docker", "network", "info", "test") + if err != nil { + t.Fatal(err.Error()) + } + if out.String() != info { + t.Fatal("Network List command fail to return the intended list") + } +} + +// Docker Flag processing in flag.go uses os.Exit() frequently, even for --help +// TODO : Handle the --help test-case in the IT when CLI is available +/* +func TestClientNetworkCreateHelp(t *testing.T) { + var out, errOut bytes.Buffer + cFunc := func(method, path string, data interface{}, headers map[string][]string) (io.ReadCloser, int, error) { + return nil, 0, nil + } + cli := NewNetworkCli(&out, &errOut, cFunc) + + err := cli.Cmd("docker", "network", "create", "--help") + if err != nil { + t.Fatalf(err.Error()) + } +} +*/ + +// Docker flag processing in flag.go uses os.Exit(1) for incorrect parameter case. +// TODO : Handle the missing argument case in the IT when CLI is available +/* +func TestClientNetworkCreateMissingArgument(t *testing.T) { + var out, errOut bytes.Buffer + cFunc := func(method, path string, data interface{}, headers map[string][]string) (io.ReadCloser, int, error) { + return nil, 0, nil + } + cli := NewNetworkCli(&out, &errOut, cFunc) + + err := cli.Cmd("docker", "network", "create") + if err != nil { + t.Fatal(err.Error()) + } +} +*/ diff --git a/vendor/src/github.com/docker/libnetwork/client/network.go b/vendor/src/github.com/docker/libnetwork/client/network.go new file mode 100644 index 0000000000..d9effab8c4 --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/client/network.go @@ -0,0 +1,132 @@ +package client + +import ( + "bytes" + "fmt" + "io" + + flag "github.com/docker/docker/pkg/mflag" +) + +const ( + nullNetType = "null" +) + +type command struct { + name string + description string +} + +var ( + networkCommands = []command{ + {"create", "Create a network"}, + {"rm", "Remove a network"}, + {"ls", "List all networks"}, + {"info", "Display information of a network"}, + } +) + +// CmdNetwork handles the root Network UI +func (cli *NetworkCli) CmdNetwork(chain string, args ...string) error { + cmd := cli.Subcmd(chain, "network", "COMMAND [OPTIONS] [arg...]", networkUsage(chain), false) + cmd.Require(flag.Min, 1) + err := cmd.ParseFlags(args, true) + if err == nil { + cmd.Usage() + return fmt.Errorf("Invalid command : %v", args) + } + return err +} + +// CmdNetworkCreate handles Network Create UI +func (cli *NetworkCli) CmdNetworkCreate(chain string, args ...string) error { + cmd := cli.Subcmd(chain, "create", "NETWORK-NAME", "Creates a new network with a name specified by the user", false) + flDriver := cmd.String([]string{"d", "-driver"}, "null", "Driver to manage the Network") + cmd.Require(flag.Min, 1) + err := cmd.ParseFlags(args, true) + if err != nil { + return err + } + if *flDriver == "" { + *flDriver = nullNetType + } + + nc := networkCreate{Name: cmd.Arg(0), NetworkType: *flDriver} + + obj, _, err := readBody(cli.call("POST", "/networks/name/"+cmd.Arg(0), nc, nil)) + if err != nil { + fmt.Fprintf(cli.err, "%s", err.Error()) + return err + } + if _, err := io.Copy(cli.out, bytes.NewReader(obj)); err != nil { + return err + } + return nil +} + +// CmdNetworkRm handles Network Delete UI +func (cli *NetworkCli) CmdNetworkRm(chain string, args ...string) error { + cmd := cli.Subcmd(chain, "rm", "NETWORK-NAME", "Deletes a network", false) + cmd.Require(flag.Min, 1) + err := cmd.ParseFlags(args, true) + if err != nil { + return err + } + obj, _, err := readBody(cli.call("DELETE", "/networks/name/"+cmd.Arg(0), nil, nil)) + if err != nil { + fmt.Fprintf(cli.err, "%s", err.Error()) + return err + } + if _, err := io.Copy(cli.out, bytes.NewReader(obj)); err != nil { + return err + } + return nil +} + +// CmdNetworkLs handles Network List UI +func (cli *NetworkCli) CmdNetworkLs(chain string, args ...string) error { + cmd := cli.Subcmd(chain, "ls", "", "Lists all the networks created by the user", false) + err := cmd.ParseFlags(args, true) + if err != nil { + return err + } + obj, _, err := readBody(cli.call("GET", "/networks", nil, nil)) + if err != nil { + fmt.Fprintf(cli.err, "%s", err.Error()) + return err + } + if _, err := io.Copy(cli.out, bytes.NewReader(obj)); err != nil { + return err + } + return nil +} + +// CmdNetworkInfo handles Network Info UI +func (cli *NetworkCli) CmdNetworkInfo(chain string, args ...string) error { + cmd := cli.Subcmd(chain, "info", "NETWORK-NAME", "Displays detailed information on a network", false) + cmd.Require(flag.Min, 1) + err := cmd.ParseFlags(args, true) + if err != nil { + return err + } + obj, _, err := readBody(cli.call("GET", "/networks/name/"+cmd.Arg(0), nil, nil)) + if err != nil { + fmt.Fprintf(cli.err, "%s", err.Error()) + return err + } + if _, err := io.Copy(cli.out, bytes.NewReader(obj)); err != nil { + return err + } + return nil +} + +func networkUsage(chain string) string { + help := "Commands:\n" + + for _, cmd := range networkCommands { + help += fmt.Sprintf(" %-10.10s%s\n", cmd.name, cmd.description) + } + + help += fmt.Sprintf("\nRun '%s network COMMAND --help' for more information on a command.", chain) + return help +} diff --git a/vendor/src/github.com/docker/libnetwork/client/types.go b/vendor/src/github.com/docker/libnetwork/client/types.go new file mode 100644 index 0000000000..27d17b9506 --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/client/types.go @@ -0,0 +1,34 @@ +package client + +import "github.com/docker/libnetwork/sandbox" + +/*********** + Resources +************/ + +// networkResource is the body of the "get network" http response message +type networkResource struct { + Name string + ID string + Type string + Endpoints []*endpointResource +} + +// endpointResource is the body of the "get endpoint" http response message +type endpointResource struct { + Name string + ID string + Network string + Info sandbox.Info +} + +/*********** + Body types + ************/ + +// networkCreate is the expected body of the "create network" http request message +type networkCreate struct { + Name string + NetworkType string + Options map[string]interface{} +} diff --git a/vendor/src/github.com/docker/libnetwork/cmd/dnet/dnet.go b/vendor/src/github.com/docker/libnetwork/cmd/dnet/dnet.go new file mode 100644 index 0000000000..547ec312e6 --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/cmd/dnet/dnet.go @@ -0,0 +1,206 @@ +package main + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "net/http" + "os" + "strings" + + flag "github.com/docker/docker/pkg/mflag" + "github.com/docker/docker/pkg/parsers" + + "github.com/Sirupsen/logrus" + "github.com/docker/docker/pkg/term" + "github.com/docker/libnetwork" + "github.com/docker/libnetwork/api" + "github.com/docker/libnetwork/client" + "github.com/gorilla/mux" +) + +var ( + // DefaultHTTPHost is used if only port is provided to -H flag e.g. docker -d -H tcp://:8080 + DefaultHTTPHost = "127.0.0.1" + // DefaultHTTPPort is the default http port used by dnet + DefaultHTTPPort = 2385 + // DefaultUnixSocket exported + DefaultUnixSocket = "/var/run/dnet.sock" +) + +func main() { + _, stdout, stderr := term.StdStreams() + logrus.SetOutput(stderr) + + err := dnetCommand(stdout, stderr) + if err != nil { + os.Exit(1) + } +} + +func dnetCommand(stdout, stderr io.Writer) error { + flag.Parse() + + if *flHelp { + flag.Usage() + return nil + } + + if *flLogLevel != "" { + lvl, err := logrus.ParseLevel(*flLogLevel) + if err != nil { + fmt.Fprintf(stderr, "Unable to parse logging level: %s\n", *flLogLevel) + return err + } + logrus.SetLevel(lvl) + } else { + logrus.SetLevel(logrus.InfoLevel) + } + + if *flDebug { + logrus.SetLevel(logrus.DebugLevel) + } + + if *flHost == "" { + defaultHost := os.Getenv("DNET_HOST") + if defaultHost == "" { + // TODO : Add UDS support + defaultHost = fmt.Sprintf("tcp://%s:%d", DefaultHTTPHost, DefaultHTTPPort) + } + *flHost = defaultHost + } + + dc, err := newDnetConnection(*flHost) + if err != nil { + if *flDaemon { + logrus.Error(err) + } else { + fmt.Fprint(stderr, err) + } + return err + } + + if *flDaemon { + err := dc.dnetDaemon() + if err != nil { + logrus.Errorf("dnet Daemon exited with an error : %v", err) + } + return err + } + + cli := client.NewNetworkCli(stdout, stderr, dc.httpCall) + if err := cli.Cmd("dnet", flag.Args()...); err != nil { + fmt.Fprintln(stderr, err) + return err + } + return nil +} + +type dnetConnection struct { + // proto holds the client protocol i.e. unix. + proto string + // addr holds the client address. + addr string +} + +func (d *dnetConnection) dnetDaemon() error { + controller, err := libnetwork.New() + if err != nil { + fmt.Println("Error starting dnetDaemon :", err) + return err + } + httpHandler := api.NewHTTPHandler(controller) + r := mux.NewRouter().StrictSlash(false) + post := r.PathPrefix("/networks").Subrouter() + post.Methods("GET").HandlerFunc(httpHandler) + post.Methods("PUT", "POST").HandlerFunc(httpHandler) + post.Methods("DELETE").HandlerFunc(httpHandler) + return http.ListenAndServe(d.addr, r) +} + +func newDnetConnection(val string) (*dnetConnection, error) { + url, err := parsers.ParseHost(DefaultHTTPHost, DefaultUnixSocket, val) + if err != nil { + return nil, err + } + protoAddrParts := strings.SplitN(url, "://", 2) + if len(protoAddrParts) != 2 { + return nil, fmt.Errorf("bad format, expected tcp://ADDR") + } + if strings.ToLower(protoAddrParts[0]) != "tcp" { + return nil, fmt.Errorf("dnet currently only supports tcp transport") + } + + return &dnetConnection{protoAddrParts[0], protoAddrParts[1]}, nil +} + +func (d *dnetConnection) httpCall(method, path string, data interface{}, headers map[string][]string) (io.ReadCloser, int, error) { + var in io.Reader + in, err := encodeData(data) + if err != nil { + return nil, -1, err + } + + req, err := http.NewRequest(method, fmt.Sprintf("%s", path), in) + if err != nil { + return nil, -1, err + } + + setupRequestHeaders(method, data, req, headers) + + req.URL.Host = d.addr + req.URL.Scheme = "http" + + httpClient := &http.Client{} + resp, err := httpClient.Do(req) + statusCode := -1 + if resp != nil { + statusCode = resp.StatusCode + } + if err != nil { + return nil, statusCode, fmt.Errorf("An error occurred trying to connect: %v", err) + } + + if statusCode < 200 || statusCode >= 400 { + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, statusCode, err + } + return nil, statusCode, fmt.Errorf("Error response from daemon: %s", bytes.TrimSpace(body)) + } + + return resp.Body, statusCode, nil +} + +func setupRequestHeaders(method string, data interface{}, req *http.Request, headers map[string][]string) { + if data != nil { + if headers == nil { + headers = make(map[string][]string) + } + headers["Content-Type"] = []string{"application/json"} + } + + expectedPayload := (method == "POST" || method == "PUT") + + if expectedPayload && req.Header.Get("Content-Type") == "" { + req.Header.Set("Content-Type", "text/plain") + } + + if headers != nil { + for k, v := range headers { + req.Header[k] = v + } + } +} + +func encodeData(data interface{}) (*bytes.Buffer, error) { + params := bytes.NewBuffer(nil) + if data != nil { + if err := json.NewEncoder(params).Encode(data); err != nil { + return nil, err + } + } + return params, nil +} diff --git a/vendor/src/github.com/docker/libnetwork/cmd/dnet/dnet_test.go b/vendor/src/github.com/docker/libnetwork/cmd/dnet/dnet_test.go new file mode 100644 index 0000000000..b8466f1a70 --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/cmd/dnet/dnet_test.go @@ -0,0 +1,132 @@ +package main + +import ( + "fmt" + "io/ioutil" + "os" + "testing" + "time" + + "github.com/docker/libnetwork/netutils" +) + +const dnetCommandName = "dnet" + +var origStdOut = os.Stdout + +func TestDnetDaemonCustom(t *testing.T) { + if !netutils.IsRunningInContainer() { + t.Skip("This test must run inside a container ") + } + customPort := 4567 + doneChan := make(chan bool) + go func() { + args := []string{dnetCommandName, "-d", fmt.Sprintf("-H=:%d", customPort)} + executeDnetCommand(t, args, true) + doneChan <- true + }() + + select { + case <-doneChan: + t.Fatal("dnet Daemon is not supposed to exit") + case <-time.After(3 * time.Second): + args := []string{dnetCommandName, "-d=false", fmt.Sprintf("-H=:%d", customPort), "-D", "network", "ls"} + executeDnetCommand(t, args, true) + } +} + +func TestDnetDaemonInvalidCustom(t *testing.T) { + if !netutils.IsRunningInContainer() { + t.Skip("This test must run inside a container ") + } + customPort := 4668 + doneChan := make(chan bool) + go func() { + args := []string{dnetCommandName, "-d=true", fmt.Sprintf("-H=:%d", customPort)} + executeDnetCommand(t, args, true) + doneChan <- true + }() + + select { + case <-doneChan: + t.Fatal("dnet Daemon is not supposed to exit") + case <-time.After(3 * time.Second): + args := []string{dnetCommandName, "-d=false", "-H=:6669", "-D", "network", "ls"} + executeDnetCommand(t, args, false) + } +} + +func TestDnetDaemonInvalidParams(t *testing.T) { + if !netutils.IsRunningInContainer() { + t.Skip("This test must run inside a container ") + } + args := []string{dnetCommandName, "-d=false", "-H=tcp:/127.0.0.1:8080"} + executeDnetCommand(t, args, false) + + args = []string{dnetCommandName, "-d=false", "-H=unix://var/run/dnet.sock"} + executeDnetCommand(t, args, false) + + args = []string{dnetCommandName, "-d=false", "-H=", "-l=invalid"} + executeDnetCommand(t, args, false) + + args = []string{dnetCommandName, "-d=false", "-H=", "-l=error", "invalid"} + executeDnetCommand(t, args, false) +} + +func TestDnetDefaultsWithFlags(t *testing.T) { + if !netutils.IsRunningInContainer() { + t.Skip("This test must run inside a container ") + } + doneChan := make(chan bool) + go func() { + args := []string{dnetCommandName, "-d=true", "-H=", "-l=error"} + executeDnetCommand(t, args, true) + doneChan <- true + }() + + select { + case <-doneChan: + t.Fatal("dnet Daemon is not supposed to exit") + case <-time.After(3 * time.Second): + args := []string{dnetCommandName, "-d=false", "network", "create", "-d=null", "test"} + executeDnetCommand(t, args, true) + + args = []string{dnetCommandName, "-d=false", "-D", "network", "ls"} + executeDnetCommand(t, args, true) + } +} + +func TestDnetMain(t *testing.T) { + if !netutils.IsRunningInContainer() { + t.Skip("This test must run inside a container ") + } + customPort := 4568 + doneChan := make(chan bool) + go func() { + args := []string{dnetCommandName, "-d=true", "-h=false", fmt.Sprintf("-H=:%d", customPort)} + os.Args = args + main() + doneChan <- true + }() + select { + case <-doneChan: + t.Fatal("dnet Daemon is not supposed to exit") + case <-time.After(2 * time.Second): + } +} + +func executeDnetCommand(t *testing.T, args []string, shouldSucced bool) { + _, w, _ := os.Pipe() + os.Stdout = w + + os.Args = args + err := dnetCommand(ioutil.Discard, ioutil.Discard) + if shouldSucced && err != nil { + os.Stdout = origStdOut + t.Fatalf("cli [%v] must succeed, but failed with an error : %v", args, err) + } else if !shouldSucced && err == nil { + os.Stdout = origStdOut + t.Fatalf("cli [%v] must fail, but succeeded with an error : %v", args, err) + } + os.Stdout = origStdOut +} diff --git a/vendor/src/github.com/docker/libnetwork/cmd/dnet/flags.go b/vendor/src/github.com/docker/libnetwork/cmd/dnet/flags.go new file mode 100644 index 0000000000..468f18dc09 --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/cmd/dnet/flags.go @@ -0,0 +1,49 @@ +package main + +import ( + "fmt" + "os" + + flag "github.com/docker/docker/pkg/mflag" +) + +type command struct { + name string + description string +} + +type byName []command + +var ( + flDaemon = flag.Bool([]string{"d", "-daemon"}, false, "Enable daemon mode") + flHost = flag.String([]string{"H", "-Host"}, "", "Daemon socket to connect to") + flLogLevel = flag.String([]string{"l", "-log-level"}, "info", "Set the logging level") + flDebug = flag.Bool([]string{"D", "-debug"}, false, "Enable debug mode") + flHelp = flag.Bool([]string{"h", "-help"}, false, "Print usage") + + dnetCommands = []command{ + {"network", "Network management commands"}, + } +) + +func init() { + flag.Usage = func() { + fmt.Fprint(os.Stdout, "Usage: dnet [OPTIONS] COMMAND [arg...]\n\nA self-sufficient runtime for container networking.\n\nOptions:\n") + + flag.CommandLine.SetOutput(os.Stdout) + flag.PrintDefaults() + + help := "\nCommands:\n" + + for _, cmd := range dnetCommands { + help += fmt.Sprintf(" %-10.10s%s\n", cmd.name, cmd.description) + } + + help += "\nRun 'dnet COMMAND --help' for more information on a command." + fmt.Fprintf(os.Stdout, "%s\n", help) + } +} + +func printUsage() { + fmt.Println("Usage: dnet network ") +} diff --git a/vendor/src/github.com/docker/libnetwork/cmd/readme_test/readme.go b/vendor/src/github.com/docker/libnetwork/cmd/readme_test/readme.go new file mode 100644 index 0000000000..ce4d8fedf8 --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/cmd/readme_test/readme.go @@ -0,0 +1,66 @@ +package main + +import ( + "fmt" + + "github.com/docker/libnetwork" + "github.com/docker/libnetwork/netlabel" + "github.com/docker/libnetwork/netutils" + "github.com/docker/libnetwork/options" +) + +func main() { + // Create a new controller instance + controller, err := libnetwork.New() + if err != nil { + return + } + + // Select and configure the network driver + networkType := "bridge" + + driverOptions := options.Generic{} + genericOption := make(map[string]interface{}) + genericOption[netlabel.GenericData] = driverOptions + err = controller.ConfigureNetworkDriver(networkType, genericOption) + if err != nil { + return + } + + // Create a network for containers to join. + // NewNetwork accepts Variadic optional arguments that libnetwork and Drivers can make of + network, err := controller.NewNetwork(networkType, "network1") + if err != nil { + return + } + + // For each new container: allocate IP and interfaces. The returned network + // settings will be used for container infos (inspect and such), as well as + // iptables rules for port publishing. This info is contained or accessible + // from the returned endpoint. + ep, err := network.CreateEndpoint("Endpoint1") + if err != nil { + return + } + + // A container can join the endpoint by providing the container ID to the join + // api which returns the sandbox key which can be used to access the sandbox + // created for the container during join. + // Join acceps Variadic arguments which will be made use of by libnetwork and Drivers + _, err = ep.Join("container1", + libnetwork.JoinOptionHostname("test"), + libnetwork.JoinOptionDomainname("docker.io")) + if err != nil { + return + } + + // libentwork client can check the endpoint's operational data via the Info() API + epInfo, err := ep.DriverInfo() + mapData, ok := epInfo[netlabel.PortMap] + if ok { + portMapping, ok := mapData.([]netutils.PortBinding) + if ok { + fmt.Printf("Current port mapping for endpoint %s: %v", ep.Name(), portMapping) + } + } +} diff --git a/vendor/src/github.com/docker/libnetwork/cmd/test/main.go b/vendor/src/github.com/docker/libnetwork/cmd/test/main.go new file mode 100644 index 0000000000..d944654bff --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/cmd/test/main.go @@ -0,0 +1,28 @@ +package main + +import ( + "fmt" + "log" + "net" + + "github.com/docker/libnetwork" + "github.com/docker/libnetwork/options" +) + +func main() { + ip, net, _ := net.ParseCIDR("192.168.100.1/24") + net.IP = ip + + options := options.Generic{"AddressIPv4": net} + controller, err := libnetwork.New() + if err != nil { + log.Fatal(err) + } + netType := "bridge" + err = controller.ConfigureNetworkDriver(netType, options) + netw, err := controller.NewNetwork(netType, "dummy") + if err != nil { + log.Fatal(err) + } + fmt.Printf("Network=%#v\n", netw) +} diff --git a/vendor/src/github.com/docker/libnetwork/controller.go b/vendor/src/github.com/docker/libnetwork/controller.go new file mode 100644 index 0000000000..a64d6e526b --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/controller.go @@ -0,0 +1,298 @@ +/* +Package libnetwork provides the basic functionality and extension points to +create network namespaces and allocate interfaces for containers to use. + + // Create a new controller instance + controller, _err := libnetwork.New() + + // Select and configure the network driver + networkType := "bridge" + + driverOptions := options.Generic{} + genericOption := make(map[string]interface{}) + genericOption[netlabel.GenericData] = driverOptions + err := controller.ConfigureNetworkDriver(networkType, genericOption) + if err != nil { + return + } + + // Create a network for containers to join. + // NewNetwork accepts Variadic optional arguments that libnetwork and Drivers can make of + network, err := controller.NewNetwork(networkType, "network1") + if err != nil { + return + } + + // For each new container: allocate IP and interfaces. The returned network + // settings will be used for container infos (inspect and such), as well as + // iptables rules for port publishing. This info is contained or accessible + // from the returned endpoint. + ep, err := network.CreateEndpoint("Endpoint1") + if err != nil { + return + } + + // A container can join the endpoint by providing the container ID to the join + // api which returns the sandbox key which can be used to access the sandbox + // created for the container during join. + // Join acceps Variadic arguments which will be made use of by libnetwork and Drivers + _, err = ep.Join("container1", + libnetwork.JoinOptionHostname("test"), + libnetwork.JoinOptionDomainname("docker.io")) + if err != nil { + return + } +*/ +package libnetwork + +import ( + "sync" + + "github.com/docker/docker/pkg/plugins" + "github.com/docker/docker/pkg/stringid" + "github.com/docker/libnetwork/driverapi" + "github.com/docker/libnetwork/sandbox" + "github.com/docker/libnetwork/types" +) + +// NetworkController provides the interface for controller instance which manages +// networks. +type NetworkController interface { + // ConfigureNetworkDriver applies the passed options to the driver instance for the specified network type + ConfigureNetworkDriver(networkType string, options map[string]interface{}) error + + // Create a new network. The options parameter carries network specific options. + // Labels support will be added in the near future. + NewNetwork(networkType, name string, options ...NetworkOption) (Network, error) + + // Networks returns the list of Network(s) managed by this controller. + Networks() []Network + + // WalkNetworks uses the provided function to walk the Network(s) managed by this controller. + WalkNetworks(walker NetworkWalker) + + // NetworkByName returns the Network which has the passed name. If not found, the error ErrNoSuchNetwork is returned. + NetworkByName(name string) (Network, error) + + // NetworkByID returns the Network which has the passed id. If not found, the error ErrNoSuchNetwork is returned. + NetworkByID(id string) (Network, error) +} + +// NetworkWalker is a client provided function which will be used to walk the Networks. +// When the function returns true, the walk will stop. +type NetworkWalker func(nw Network) bool + +type sandboxData struct { + sandbox sandbox.Sandbox + refCnt int +} + +type networkTable map[types.UUID]*network +type endpointTable map[types.UUID]*endpoint +type sandboxTable map[string]*sandboxData + +type controller struct { + networks networkTable + drivers driverTable + sandboxes sandboxTable + sync.Mutex +} + +// New creates a new instance of network controller. +func New() (NetworkController, error) { + c := &controller{ + networks: networkTable{}, + sandboxes: sandboxTable{}, + drivers: driverTable{}} + if err := initDrivers(c); err != nil { + return nil, err + } + return c, nil +} + +func (c *controller) ConfigureNetworkDriver(networkType string, options map[string]interface{}) error { + c.Lock() + d, ok := c.drivers[networkType] + c.Unlock() + if !ok { + return NetworkTypeError(networkType) + } + return d.Config(options) +} + +func (c *controller) RegisterDriver(networkType string, driver driverapi.Driver) error { + c.Lock() + defer c.Unlock() + if _, ok := c.drivers[networkType]; ok { + return driverapi.ErrActiveRegistration(networkType) + } + c.drivers[networkType] = driver + return nil +} + +// NewNetwork creates a new network of the specified network type. The options +// are network specific and modeled in a generic way. +func (c *controller) NewNetwork(networkType, name string, options ...NetworkOption) (Network, error) { + if name == "" { + return nil, ErrInvalidName + } + // Check if a driver for the specified network type is available + c.Lock() + d, ok := c.drivers[networkType] + c.Unlock() + if !ok { + var err error + d, err = c.loadDriver(networkType) + if err != nil { + return nil, err + } + } + + // Check if a network already exists with the specified network name + c.Lock() + for _, n := range c.networks { + if n.name == name { + c.Unlock() + return nil, NetworkNameError(name) + } + } + c.Unlock() + + // Construct the network object + network := &network{ + name: name, + id: types.UUID(stringid.GenerateRandomID()), + ctrlr: c, + driver: d, + endpoints: endpointTable{}, + } + + network.processOptions(options...) + // Create the network + if err := d.CreateNetwork(network.id, network.generic); err != nil { + return nil, err + } + + // Store the network handler in controller + c.Lock() + c.networks[network.id] = network + c.Unlock() + + return network, nil +} + +func (c *controller) Networks() []Network { + c.Lock() + defer c.Unlock() + + list := make([]Network, 0, len(c.networks)) + for _, n := range c.networks { + list = append(list, n) + } + + return list +} + +func (c *controller) WalkNetworks(walker NetworkWalker) { + for _, n := range c.Networks() { + if walker(n) { + return + } + } +} + +func (c *controller) NetworkByName(name string) (Network, error) { + if name == "" { + return nil, ErrInvalidName + } + var n Network + + s := func(current Network) bool { + if current.Name() == name { + n = current + return true + } + return false + } + + c.WalkNetworks(s) + + if n == nil { + return nil, ErrNoSuchNetwork + } + + return n, nil +} + +func (c *controller) NetworkByID(id string) (Network, error) { + if id == "" { + return nil, ErrInvalidID + } + c.Lock() + defer c.Unlock() + if n, ok := c.networks[types.UUID(id)]; ok { + return n, nil + } + return nil, ErrNoSuchNetwork +} + +func (c *controller) sandboxAdd(key string, create bool) (sandbox.Sandbox, error) { + c.Lock() + defer c.Unlock() + + sData, ok := c.sandboxes[key] + if !ok { + sb, err := sandbox.NewSandbox(key, create) + if err != nil { + return nil, err + } + + sData = &sandboxData{sandbox: sb, refCnt: 1} + c.sandboxes[key] = sData + return sData.sandbox, nil + } + + sData.refCnt++ + return sData.sandbox, nil +} + +func (c *controller) sandboxRm(key string) { + c.Lock() + defer c.Unlock() + + sData := c.sandboxes[key] + sData.refCnt-- + + if sData.refCnt == 0 { + sData.sandbox.Destroy() + delete(c.sandboxes, key) + } +} + +func (c *controller) sandboxGet(key string) sandbox.Sandbox { + c.Lock() + defer c.Unlock() + + sData, ok := c.sandboxes[key] + if !ok { + return nil + } + + return sData.sandbox +} + +func (c *controller) loadDriver(networkType string) (driverapi.Driver, error) { + // Plugins pkg performs lazy loading of plugins that acts as remote drivers. + // As per the design, this Get call will result in remote driver discovery if there is a corresponding plugin available. + _, err := plugins.Get(networkType, driverapi.NetworkPluginEndpointType) + if err != nil { + return nil, err + } + c.Lock() + defer c.Unlock() + d, ok := c.drivers[networkType] + if !ok { + return nil, ErrInvalidNetworkDriver + } + return d, nil +} diff --git a/vendor/src/github.com/docker/libnetwork/docs/bridge.md b/vendor/src/github.com/docker/libnetwork/docs/bridge.md new file mode 100644 index 0000000000..4633ce8c50 --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/docs/bridge.md @@ -0,0 +1,13 @@ +Bridge Driver +============= + +The bridge driver is an implementation that uses Linux Bridging and iptables to provide connectvity for containers +It creates a single bridge, called `docker0` by default, and attaches a `veth pair` between the bridge and every endpoint. + +## Configuration + +The bridge driver supports configuration through the Docker Daemon flags. + +## Usage + +This driver is supported for the default "bridge" network only and it cannot be used for any other networks. diff --git a/vendor/src/github.com/docker/libnetwork/docs/design.md b/vendor/src/github.com/docker/libnetwork/docs/design.md new file mode 100644 index 0000000000..33dcc3d651 --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/docs/design.md @@ -0,0 +1,148 @@ +Design +====== + +The vision and goals of libnetwork are highlighted in [roadmap](../ROADMAP.md). +This document describes how libnetwork has been designed in order to acheive this. +Requirements for individual releases can be found on the [Project Page](https://github.com/docker/libnetwork/wiki) + +Many of the design decisions are inspired by the learnings from the Docker networking design as of Docker v1.6. +Please refer to this [Docker v1.6 Design](https://github.com/docker/libnetwork/blob/docs/legacy.md) document for more information on networking design as of Docker v1.6. + +## Goal + +libnetwork project will follow Docker and Linux philosophy of developing small, highly modular and composable tools that works well independently. +Libnetwork aims to satisfy that composable need for Networking in Containers. + +## The Container Network Model + +Libnetwork implements Container Network Model (CNM) which formalizes the steps required to provide networking for containers while providing an abstraction that can be used to support multiple network drivers. The CNM is built on 3 main components. + +**Sandbox** + +A Sandbox contains the configuration of a container's network stack. +This includes management of the container's interfaces, routing table and DNS settings. +An implementation of a Sandbox could be a Linux Network Namespace, a FreeBSD Jail or other similar concept. +A Sandbox may contain *many* endpoints from *multiple* networks + +**Endpoint** + +An Endpoint joins a Sandbox to a Network. +An implementation of an Endpoint could be a `veth` pair, an Open vSwitch internal port or similar. +An Endpoint can belong to *only one* network but may only belong to *one* Sandbox + +**Network** + +A Network is a group of Endpoints that are able to communicate with each-other directly. +An implementation of a Network could be a Linux bridge, a VLAN etc... +Networks consist of *many* endpoints + +## CNM Objects + +**NetworkController** +`NetworkController` object provides the entry-point into libnetwork that exposes simple APIs for the users (such as Docker Engine) to allocate and manage Networks. libnetwork supports multiple active drivers (both inbuilt and remote). `NetworkController` allows user to bind a particular driver to a given network. + +**Driver** +`Driver` is not an user visible object, but drivers provides the actual implementation that makes network work. `NetworkController` however provides an API to configure any specific driver with driver-specific options/labels that is transparent to libnetwork, but can be handled by the drivers directly. Drivers can be both inbuilt (such as Bridge, Host, None & overlay) and remote (from plugin providers) to satisfy various usecases & deployment scenarios. At this point, the Driver owns a network and is responsible for managing the network (including IPAM, etc.). This can be improved in the future by having multiple drivers participating in handling various network management functionalities. + +**Network** +`Network` object is an implementation of the `CNM : Network` as defined above. `NetworkController` provides APIs to create and manage `Network` object. Whenever a `Network` is created or updated, the corresponding `Driver` will be notified of the event. LibNetwork treats `Network` object at an abstract level to provide connectivity between a group of end-points that belong to the same network and isolate from the rest. The Driver performs the actual work of providing the required connectivity and isolation. The connectivity can be within the same host or across multiple-hosts. Hence `Network` has a global scope within a cluster. + +**Endpoint** +`Endpoint` represents a Service Endpoint. It provides the connectivity for services exposed by a container in a network with other services provided by other containers in the network. `Network` object provides APIs to create and manage endpoint. An endpoint can be attached to only one network. `Endpoint` creation calls are made to the corresponding `Driver` which is responsible for allocating resources for the corresponding `Sandbox`. Since Endpoint represents a Service and not necessarily a particular container, `Endpoint` has a global scope within a cluster as well. + +**Sandbox** +`Sandbox` object represents container's network configuration such as ip-address, mac-address, routes, DNS entries. A `Sandbox` object is created when the user requests to create an endpoint on a network. The `Driver` that handles the `Network` is responsible to allocate the required network resources (such as ip-address) and pass the info called `SandboxInfo` back to libnetwork. libnetwork will make use of OS specific constructs (example: netns for Linux) to populate the network configuration into the containers that is represented by the `Sandbox`. A `Sandbox` can have multiple endpoints attached to different networks. Since `Sandbox` is associated with a particular container in a given host, it has a local scope that represents the Host that the Container belong to. + +**CNM Attributes** + +***Options*** +`Options` provides a generic and flexible mechanism to pass `Driver` specific configuration option from the user to the `Driver` directly. `Options` are just key-value pairs of data with `key` represented by a string and `value` represented by a generic object (such as golang `interface{}`). Libnetwork will operate on the `Options` ONLY if the `key` matches any of the well-known `Label` defined in the `net-labels` package. `Options` also encompasses `Labels` as explained below. `Options` are generally NOT end-user visible (in UI), while `Labels` are. + +***Labels*** +`Labels` are very similar to `Options` & infact they are just a subset of `Options`. `Labels` are typically end-user visible and are represented in the UI explicitely using the `--labels` option. They are passed from the UI to the `Driver` so that `Driver` can make use of it and perform any `Driver` specific operation (such as a subnet to allocate IP-Addresses from in a Network). + +## CNM Lifecycle + +Consumers of the CNM, like Docker for example, interact through the CNM Objects and its APIs to network the containers that they manage. + +0. `Drivers` registers with `NetworkController`. Build-in drivers registers inside of LibNetwork, while remote Drivers registers with LibNetwork via Plugin mechanism. (*plugin-mechanism is WIP*). Each `driver` handles a particular `networkType`. + +1. `NetworkController` object is created using `libnetwork.New()` API to manage the allocation of Networks and optionally configure a `Driver` with driver specific `Options`. + +2. `Network` is created using the controller's `NewNetwork()` API by providing a `name` and `networkType`. `networkType` parameter helps to choose a corresponding `Driver` and binds the created `Network` to that `Driver`. From this point, any operation on `Network` will be handled by that `Driver`. + +3. `controller.NewNetwork()` API also takes in optional `options` parameter which carries Driver-specific options and `Labels`, which the Drivers can make use for its purpose. + +4. `network.CreateEndpoint()` can be called to create a new Endpoint in a given network. This API also accepts optional `options` parameter which drivers can make use of. These 'options' carry both well-known labels and driver-specific labels. Drivers will in turn be called with `driver.CreateEndpoint` and it can choose to reserve IPv4/IPv6 addresses when an `Endpoint` is created in a `Network`. The `Driver` will assign these addresses using `InterfaceInfo` interface defined in the `driverapi`. The IP/IPv6 are needed to complete the endpoint as service definition along with the ports the endpoint exposes since essentially a service endpoint is nothing but a network address and the port number that the application container is listening on. + +5. `endpoint.Join()` can be used to attach a container to a `Endpoint`. The Join operation will create a `Sandbox` if it doesnt exist already for that container. The Drivers can make use of the Sandbox Key to identify multiple endpoints attached to a same container. This API also accepts optional `options` parameter which drivers can make use of. + * Though it is not a direct design issue of LibNetwork, it is highly encouraged to have users like `Docker` to call the endpoint.Join() during Container's `Start()` lifecycle that is invoked *before* the container is made operational. As part of Docker integration, this will be taken care of. + * one of a FAQ on endpoint join() API is that, why do we need an API to create an Endpoint and another to join the endpoint. + - The answer is based on the fact that Endpoint represents a Service which may or may not be backed by a Container. When an Endpoint is created, it will have its resources reserved so that any container can get attached to the endpoint later and get a consistent networking behaviour. + +6. `endpoint.Leave()` can be invoked when a container is stopped. The `Driver` can cleanup the states that it allocated during the `Join()` call. LibNetwork will delete the `Sandbox` when the last referencing endpoint leaves the network. But LibNetwork keeps hold of the IP addresses as long as the endpoint is still present and will be reused when the container(or any container) joins again. This ensures that the container's resources are reused when they are Stopped and Started again. + +7. `endpoint.Delete()` is used to delete an endpoint from a network. This results in deleting an endpoint and cleaning up the cached `sandbox.Info`. + +8. `network.Delete()` is used to delete a network. LibNetwork will not allow the delete to proceed if there are any existing endpoints attached to the Network. + + +## Implementation Details + +### Networks & Endpoints + +LibNetwork's Network and Endpoint APIs are primiarly for managing the corresponding Objects and book-keeping them to provide a level of abstraction as required by the CNM. It delegates the actual implementation to the drivers which realizes the functionality as promised in the CNM. For more information on these details, please see [the drivers section](#Drivers) + +### Sandbox + +Libnetwork provides a framework to implement of a Sandbox in multiple Operating Systems. Currently we have implemented Sandbox for Linux using `namespace_linux.go` and `configure_linux.go` in `sandbox` package +This creates a Network Namespace for each sandbox which is uniquely identified by a path on the host filesystem. +Netlink calls are used to move interfaces from the global namespace to the Sandbox namespace. +Netlink is also used to manage the routing table in the namespace. + +## Drivers + +## API + +Drivers are essentially an extension of libnetwork and provides the actual implementation for all of the LibNetwork APIs defined above. Hence there is an 1-1 correspondance for all the `Network` and `Endpoint` APIs, which includes : +* `driver.Config` +* `driver.CreateNetwork` +* `driver.DeleteNetwork` +* `driver.CreateEndpoint` +* `driver.DeleteEndpoint` +* `driver.Join` +* `driver.Leave` + +These Driver facing APIs makes use of unique identifiers (`networkid`,`endpointid`,...) instead of names (as seen in user-facing APIs). + +The APIs are still work in progress and there can be changes to these based on the driver requirements especially when it comes to Multi-host networking. + +## Implementations + +Libnetwork includes the following driver packages: + +- null +- bridge +- overlay +- remote + +### Null + +The null driver is a `noop` implementation of the driver API, used only in cases where no networking is desired. This is to provide backward compatibility to the Docker's `--net=none` option. + +### Bridge + +The `bridge` driver provides a Linux-specific bridging implementation based on the Linux Bridge. +For more details, please [see the Bridge Driver documentation](bridge.md) + +### Overlay + +The `overlay` driver implements networking that can span multiple hosts using overlay network encapsulations such as VXLAN. +For more details on its design, please see the [Overlay Driver Design](overlay.md) + +### Remote + +The `remote` package does not provide a driver, but provides a means of supporting drivers over a remote transport. +This allows a driver to be written in a language of your choice. +For further details, please see the [Remote Driver Design](remote.md) + diff --git a/vendor/src/github.com/docker/libnetwork/docs/legacy.md b/vendor/src/github.com/docker/libnetwork/docs/legacy.md new file mode 100644 index 0000000000..7a19dcdff9 --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/docs/legacy.md @@ -0,0 +1,15 @@ + +This document provides a TLD&R version of https://docs.docker.com/v1.6/articles/networking/. +If more interested in detailed operational design, please refer to this link. + +## Docker Networking design as of Docker v1.6 + +Prior to libnetwork, Docker Networking was handled in both Docker Engine and libcontainer. +Docker Engine makes use of the Bridge Driver to provide single-host networking solution with the help of linux bridge and IPTables. +Docker Engine provides simple configurations such as `--link`, `--expose`,... to enable container connectivity within the same host by abstracting away networking configuration completely from the Containers. +For external connectivity, it relied upon NAT & Port-mapping + +Docker Engine was responsible for providing the configuration for the container's networking stack. + +Libcontainer would then use this information to create the necessary networking devices and move them in to a network namespace. +This namespace would then be used when the container is started. diff --git a/vendor/src/github.com/docker/libnetwork/docs/overlay.md b/vendor/src/github.com/docker/libnetwork/docs/overlay.md new file mode 100644 index 0000000000..ec48618c22 --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/docs/overlay.md @@ -0,0 +1,6 @@ +Overlay Driver +============== + +## Configuration + +## Usage diff --git a/vendor/src/github.com/docker/libnetwork/docs/remote.md b/vendor/src/github.com/docker/libnetwork/docs/remote.md new file mode 100644 index 0000000000..c34a1cd0b0 --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/docs/remote.md @@ -0,0 +1,18 @@ +Remote Drivers +============== + +The remote driver package provides the integration point for dynamically-registered drivers. + +## LibNetwork Integration + +When LibNetwork initialises the `Remote` package with the `Init()` function, it passes a `DriverCallback` as a parameter, which implements the `RegisterDriver()`. The Remote Driver package can use this interface to register any of the `Dynamic` Drivers/Plugins with LibNetwork's `NetworkController`. + +This design ensures that the implementation details (TBD) of Dynamic Driver Registration mechanism is completely owned by the inbuilt-Remote driver, and it doesn't expose any of the driver layer to the North of LibNetwork (none of the LibNetwork client APIs are impacted). + +## Implementation + +The actual implementation of how the Inbuilt Remote Driver registers with the Dynamic Driver is Work-In-Progress. But, the Design Goal is to Honor the bigger goals of LibNetwork by keeping it Highly modular and make sure that LibNetwork is fully composable in nature. + +## Usage + +The In-Built Remote Driver follows all the rules of any other In-Built Driver and has exactly the same Driver APIs exposed. LibNetwork will also support driver-specific `options` and User-supplied `Labels` which the Dynamic Drivers can make use for its operations. diff --git a/vendor/src/github.com/docker/libnetwork/driverapi/driverapi.go b/vendor/src/github.com/docker/libnetwork/driverapi/driverapi.go new file mode 100644 index 0000000000..35467dafda --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/driverapi/driverapi.go @@ -0,0 +1,139 @@ +package driverapi + +import ( + "errors" + "fmt" + "net" + + "github.com/docker/libnetwork/types" +) + +var ( + // ErrEndpointExists is returned if more than one endpoint is added to the network + ErrEndpointExists = errors.New("Endpoint already exists (Only one endpoint allowed)") + // ErrNoNetwork is returned if no network with the specified id exists + ErrNoNetwork = errors.New("No network exists") + // ErrNoEndpoint is returned if no endpoint with the specified id exists + ErrNoEndpoint = errors.New("No endpoint exists") + // ErrNotImplemented is returned when a Driver has not implemented an API yet + ErrNotImplemented = errors.New("The API is not implemented yet") +) + +// NetworkPluginEndpointType represents the Endpoint Type used by Plugin system +const NetworkPluginEndpointType = "NetworkDriver" + +// Driver is an interface that every plugin driver needs to implement. +type Driver interface { + // Push driver specific config to the driver + Config(options map[string]interface{}) error + + // CreateNetwork invokes the driver method to create a network passing + // the network id and network specific config. The config mechanism will + // eventually be replaced with labels which are yet to be introduced. + CreateNetwork(nid types.UUID, options map[string]interface{}) error + + // DeleteNetwork invokes the driver method to delete network passing + // the network id. + DeleteNetwork(nid types.UUID) error + + // CreateEndpoint invokes the driver method to create an endpoint + // passing the network id, endpoint id endpoint information and driver + // specific config. The endpoint information can be either consumed by + // the driver or populated by the driver. The config mechanism will + // eventually be replaced with labels which are yet to be introduced. + CreateEndpoint(nid, eid types.UUID, epInfo EndpointInfo, options map[string]interface{}) error + + // DeleteEndpoint invokes the driver method to delete an endpoint + // passing the network id and endpoint id. + DeleteEndpoint(nid, eid types.UUID) error + + // EndpointOperInfo retrieves from the driver the operational data related to the specified endpoint + EndpointOperInfo(nid, eid types.UUID) (map[string]interface{}, error) + + // Join method is invoked when a Sandbox is attached to an endpoint. + Join(nid, eid types.UUID, sboxKey string, jinfo JoinInfo, options map[string]interface{}) error + + // Leave method is invoked when a Sandbox detaches from an endpoint. + Leave(nid, eid types.UUID) error + + // Type returns the the type of this driver, the network type this driver manages + Type() string +} + +// EndpointInfo provides a go interface to fetch or populate endpoint assigned network resources. +type EndpointInfo interface { + // Interfaces returns a list of interfaces bound to the endpoint. + // If the list is not empty the driver is only expected to consume the interfaces. + // It is an error to try to add interfaces to a non-empty list. + // If the list is empty the driver is expected to populate with 0 or more interfaces. + Interfaces() []InterfaceInfo + + // AddInterface is used by the driver to add an interface to the interface list. + // This method will return an error if the driver attempts to add interfaces + // if the Interfaces() method returned a non-empty list. + // ID field need only have significance within the endpoint so it can be a simple + // monotonically increasing number + AddInterface(ID int, mac net.HardwareAddr, ipv4 net.IPNet, ipv6 net.IPNet) error +} + +// InterfaceInfo provides a go interface for drivers to retrive +// network information to interface resources. +type InterfaceInfo interface { + // MacAddress returns the MAC address. + MacAddress() net.HardwareAddr + + // Address returns the IPv4 address. + Address() net.IPNet + + // AddressIPv6 returns the IPv6 address. + AddressIPv6() net.IPNet + + // ID returns the numerical id of the interface and has significance only within + // the endpoint. + ID() int +} + +// InterfaceNameInfo provides a go interface for the drivers to assign names +// to interfaces. +type InterfaceNameInfo interface { + // SetNames method assigns the srcName and dstName for the interface. + SetNames(srcName, dstName string) error + + // ID returns the numerical id that was assigned to the interface by the driver + // CreateEndpoint. + ID() int +} + +// JoinInfo represents a set of resources that the driver has the ability to provide during +// join time. +type JoinInfo interface { + // InterfaceNames returns a list of InterfaceNameInfo go interface to facilitate + // setting the names for the interfaces. + InterfaceNames() []InterfaceNameInfo + + // SetGateway sets the default IPv4 gateway when a container joins the endpoint. + SetGateway(net.IP) error + + // SetGatewayIPv6 sets the default IPv6 gateway when a container joins the endpoint. + SetGatewayIPv6(net.IP) error + + // SetHostsPath sets the overriding /etc/hosts path to use for the container. + SetHostsPath(string) error + + // SetResolvConfPath sets the overriding /etc/resolv.conf path to use for the container. + SetResolvConfPath(string) error +} + +// ErrActiveRegistration represents an error when a driver is registered to a networkType that is previously registered +type ErrActiveRegistration string + +// Error interface for ErrActiveRegistration +func (ar ErrActiveRegistration) Error() string { + return fmt.Sprintf("Driver already registered for type %q", string(ar)) +} + +// DriverCallback provides a Callback interface for Drivers into LibNetwork +type DriverCallback interface { + // RegisterDriver provides a way for Remote drivers to dynamically register new NetworkType and associate with a driver instance + RegisterDriver(name string, driver Driver) error +} diff --git a/vendor/src/github.com/docker/libnetwork/drivers.go b/vendor/src/github.com/docker/libnetwork/drivers.go new file mode 100644 index 0000000000..130f7ab343 --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/drivers.go @@ -0,0 +1,25 @@ +package libnetwork + +import ( + "github.com/docker/libnetwork/driverapi" + "github.com/docker/libnetwork/drivers/bridge" + "github.com/docker/libnetwork/drivers/host" + "github.com/docker/libnetwork/drivers/null" + "github.com/docker/libnetwork/drivers/remote" +) + +type driverTable map[string]driverapi.Driver + +func initDrivers(dc driverapi.DriverCallback) error { + for _, fn := range [](func(driverapi.DriverCallback) error){ + bridge.Init, + host.Init, + null.Init, + remote.Init, + } { + if err := fn(dc); err != nil { + return err + } + } + return nil +} diff --git a/vendor/src/github.com/docker/libnetwork/drivers/bridge/bridge.go b/vendor/src/github.com/docker/libnetwork/drivers/bridge/bridge.go new file mode 100644 index 0000000000..8e52188e00 --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/drivers/bridge/bridge.go @@ -0,0 +1,928 @@ +package bridge + +import ( + "errors" + "net" + "strings" + "sync" + + "github.com/docker/libnetwork/driverapi" + "github.com/docker/libnetwork/ipallocator" + "github.com/docker/libnetwork/netlabel" + "github.com/docker/libnetwork/netutils" + "github.com/docker/libnetwork/options" + "github.com/docker/libnetwork/portmapper" + "github.com/docker/libnetwork/sandbox" + "github.com/docker/libnetwork/types" + "github.com/vishvananda/netlink" +) + +const ( + networkType = "bridge" + vethPrefix = "veth" + vethLen = 7 + containerVeth = "eth0" + maxAllocatePortAttempts = 10 + ifaceID = 1 +) + +var ( + ipAllocator *ipallocator.IPAllocator + portMapper *portmapper.PortMapper +) + +// Configuration info for the "bridge" driver. +type Configuration struct { + EnableIPForwarding bool +} + +// NetworkConfiguration for network specific configuration +type NetworkConfiguration struct { + BridgeName string + AddressIPv4 *net.IPNet + FixedCIDR *net.IPNet + FixedCIDRv6 *net.IPNet + EnableIPv6 bool + EnableIPTables bool + EnableIPMasquerade bool + EnableICC bool + Mtu int + DefaultGatewayIPv4 net.IP + DefaultGatewayIPv6 net.IP + DefaultBindingIP net.IP + AllowNonDefaultBridge bool + EnableUserlandProxy bool +} + +// EndpointConfiguration represents the user specified configuration for the sandbox endpoint +type EndpointConfiguration struct { + MacAddress net.HardwareAddr + PortBindings []netutils.PortBinding + ExposedPorts []netutils.TransportPort +} + +// ContainerConfiguration represents the user specified configuration for a container +type ContainerConfiguration struct { + ParentEndpoints []string + ChildEndpoints []string +} + +type bridgeEndpoint struct { + id types.UUID + intf *sandbox.Interface + macAddress net.HardwareAddr + config *EndpointConfiguration // User specified parameters + containerConfig *ContainerConfiguration + portMapping []netutils.PortBinding // Operation port bindings +} + +type bridgeNetwork struct { + id types.UUID + bridge *bridgeInterface // The bridge's L3 interface + config *NetworkConfiguration + endpoints map[types.UUID]*bridgeEndpoint // key: endpoint id + sync.Mutex +} + +type driver struct { + config *Configuration + network *bridgeNetwork + sync.Mutex +} + +func init() { + ipAllocator = ipallocator.New() + portMapper = portmapper.New() +} + +// New constructs a new bridge driver +func newDriver() driverapi.Driver { + return &driver{} +} + +// Init registers a new instance of bridge driver +func Init(dc driverapi.DriverCallback) error { + return dc.RegisterDriver(networkType, newDriver()) +} + +// Validate performs a static validation on the network configuration parameters. +// Whatever can be assessed a priori before attempting any programming. +func (c *NetworkConfiguration) Validate() error { + if c.Mtu < 0 { + return ErrInvalidMtu + } + + // If bridge v4 subnet is specified + if c.AddressIPv4 != nil { + // If Container restricted subnet is specified, it must be a subset of bridge subnet + if c.FixedCIDR != nil { + // Check Network address + if !c.AddressIPv4.Contains(c.FixedCIDR.IP) { + return ErrInvalidContainerSubnet + } + // Check it is effectively a subset + brNetLen, _ := c.AddressIPv4.Mask.Size() + cnNetLen, _ := c.FixedCIDR.Mask.Size() + if brNetLen > cnNetLen { + return ErrInvalidContainerSubnet + } + } + // If default gw is specified, it must be part of bridge subnet + if c.DefaultGatewayIPv4 != nil { + if !c.AddressIPv4.Contains(c.DefaultGatewayIPv4) { + return ErrInvalidGateway + } + } + } + + // If default v6 gw is specified, FixedCIDRv6 must be specified and gw must belong to FixedCIDRv6 subnet + if c.EnableIPv6 && c.DefaultGatewayIPv6 != nil { + if c.FixedCIDRv6 == nil || !c.FixedCIDRv6.Contains(c.DefaultGatewayIPv6) { + return ErrInvalidGateway + } + } + + return nil +} + +func (n *bridgeNetwork) getEndpoint(eid types.UUID) (*bridgeEndpoint, error) { + n.Lock() + defer n.Unlock() + + if eid == "" { + return nil, InvalidEndpointIDError(eid) + } + + if ep, ok := n.endpoints[eid]; ok { + return ep, nil + } + + return nil, nil +} + +func (d *driver) Config(option map[string]interface{}) error { + var config *Configuration + + d.Lock() + defer d.Unlock() + + if d.config != nil { + return ErrConfigExists + } + + genericData, ok := option[netlabel.GenericData] + if ok && genericData != nil { + switch opt := genericData.(type) { + case options.Generic: + opaqueConfig, err := options.GenerateFromModel(opt, &Configuration{}) + if err != nil { + return err + } + config = opaqueConfig.(*Configuration) + case *Configuration: + config = opt + default: + return ErrInvalidDriverConfig + } + + d.config = config + } else { + config = &Configuration{} + } + + if config.EnableIPForwarding { + return setupIPForwarding(config) + } + + return nil +} + +func (d *driver) getNetwork(id types.UUID) (*bridgeNetwork, error) { + // Just a dummy function to return the only network managed by Bridge driver. + // But this API makes the caller code unchanged when we move to support multiple networks. + d.Lock() + defer d.Unlock() + return d.network, nil +} + +func parseNetworkOptions(option options.Generic) (*NetworkConfiguration, error) { + var config *NetworkConfiguration + + genericData, ok := option[netlabel.GenericData] + if ok && genericData != nil { + switch opt := genericData.(type) { + case options.Generic: + opaqueConfig, err := options.GenerateFromModel(opt, &NetworkConfiguration{}) + if err != nil { + return nil, err + } + config = opaqueConfig.(*NetworkConfiguration) + case *NetworkConfiguration: + config = opt + default: + return nil, ErrInvalidNetworkConfig + } + + if err := config.Validate(); err != nil { + return nil, err + } + } else { + config = &NetworkConfiguration{} + } + + if _, ok := option[netlabel.EnableIPv6]; ok { + config.EnableIPv6 = option[netlabel.EnableIPv6].(bool) + } + + return config, nil +} + +// Create a new network using bridge plugin +func (d *driver) CreateNetwork(id types.UUID, option map[string]interface{}) error { + var err error + + // Driver must be configured + d.Lock() + + // Sanity checks + if d.network != nil { + d.Unlock() + return ErrNetworkExists + } + + // Create and set network handler in driver + d.network = &bridgeNetwork{id: id, endpoints: make(map[types.UUID]*bridgeEndpoint)} + network := d.network + d.Unlock() + + // On failure make sure to reset driver network handler to nil + defer func() { + if err != nil { + d.Lock() + d.network = nil + d.Unlock() + } + }() + + config, err := parseNetworkOptions(option) + if err != nil { + return err + } + network.config = config + + // Create or retrieve the bridge L3 interface + bridgeIface := newInterface(config) + network.bridge = bridgeIface + + // Prepare the bridge setup configuration + bridgeSetup := newBridgeSetup(config, bridgeIface) + + // If the bridge interface doesn't exist, we need to start the setup steps + // by creating a new device and assigning it an IPv4 address. + bridgeAlreadyExists := bridgeIface.exists() + if !bridgeAlreadyExists { + bridgeSetup.queueStep(setupDevice) + } + + // Even if a bridge exists try to setup IPv4. + bridgeSetup.queueStep(setupBridgeIPv4) + + // Conditionally queue setup steps depending on configuration values. + for _, step := range []struct { + Condition bool + Fn setupStep + }{ + // Enable IPv6 on the bridge if required. We do this even for a + // previously existing bridge, as it may be here from a previous + // installation where IPv6 wasn't supported yet and needs to be + // assigned an IPv6 link-local address. + {config.EnableIPv6, setupBridgeIPv6}, + + // We ensure that the bridge has the expectedIPv4 and IPv6 addresses in + // the case of a previously existing device. + {bridgeAlreadyExists, setupVerifyAndReconcile}, + + // Setup the bridge to allocate containers IPv4 addresses in the + // specified subnet. + {config.FixedCIDR != nil, setupFixedCIDRv4}, + + // Setup the bridge to allocate containers global IPv6 addresses in the + // specified subnet. + {config.FixedCIDRv6 != nil, setupFixedCIDRv6}, + + // Setup Loopback Adresses Routing + {!config.EnableUserlandProxy, setupLoopbackAdressesRouting}, + + // Setup IPTables. + {config.EnableIPTables, setupIPTables}, + + // Setup DefaultGatewayIPv4 + {config.DefaultGatewayIPv4 != nil, setupGatewayIPv4}, + + // Setup DefaultGatewayIPv6 + {config.DefaultGatewayIPv6 != nil, setupGatewayIPv6}, + } { + if step.Condition { + bridgeSetup.queueStep(step.Fn) + } + } + + // Block bridge IP from being allocated. + bridgeSetup.queueStep(allocateBridgeIP) + // Apply the prepared list of steps, and abort at the first error. + bridgeSetup.queueStep(setupDeviceUp) + if err = bridgeSetup.apply(); err != nil { + return err + } + + return nil +} + +func (d *driver) DeleteNetwork(nid types.UUID) error { + var err error + + // Get network handler and remove it from driver + d.Lock() + n := d.network + d.network = nil + d.Unlock() + + // On failure set network handler back in driver, but + // only if is not already taken over by some other thread + defer func() { + if err != nil { + d.Lock() + if d.network == nil { + d.network = n + } + d.Unlock() + } + }() + + // Sanity check + if n == nil { + err = driverapi.ErrNoNetwork + return err + } + + // Cannot remove network if endpoints are still present + if len(n.endpoints) != 0 { + err = ActiveEndpointsError(n.id) + return err + } + + // Programming + err = netlink.LinkDel(n.bridge.Link) + + return err +} + +func (d *driver) CreateEndpoint(nid, eid types.UUID, epInfo driverapi.EndpointInfo, epOptions map[string]interface{}) error { + var ( + ipv6Addr *net.IPNet + err error + ) + + if epInfo == nil { + return errors.New("invalid endpoint info passed") + } + + if len(epInfo.Interfaces()) != 0 { + return errors.New("non empty interface list passed to bridge(local) driver") + } + + // Get the network handler and make sure it exists + d.Lock() + n := d.network + config := n.config + d.Unlock() + if n == nil { + return driverapi.ErrNoNetwork + } + + // Sanity check + n.Lock() + if n.id != nid { + n.Unlock() + return InvalidNetworkIDError(nid) + } + n.Unlock() + + // Check if endpoint id is good and retrieve correspondent endpoint + ep, err := n.getEndpoint(eid) + if err != nil { + return err + } + + // Endpoint with that id exists either on desired or other sandbox + if ep != nil { + return driverapi.ErrEndpointExists + } + + // Try to convert the options to endpoint configuration + epConfig, err := parseEndpointOptions(epOptions) + if err != nil { + return err + } + + // Create and add the endpoint + n.Lock() + endpoint := &bridgeEndpoint{id: eid, config: epConfig} + n.endpoints[eid] = endpoint + n.Unlock() + + // On failure make sure to remove the endpoint + defer func() { + if err != nil { + n.Lock() + delete(n.endpoints, eid) + n.Unlock() + } + }() + + // Generate a name for what will be the host side pipe interface + name1, err := generateIfaceName() + if err != nil { + return err + } + + // Generate a name for what will be the sandbox side pipe interface + name2, err := generateIfaceName() + if err != nil { + return err + } + + // Generate and add the interface pipe host <-> sandbox + veth := &netlink.Veth{ + LinkAttrs: netlink.LinkAttrs{Name: name1, TxQLen: 0}, + PeerName: name2} + if err = netlink.LinkAdd(veth); err != nil { + return err + } + + // Get the host side pipe interface handler + host, err := netlink.LinkByName(name1) + if err != nil { + return err + } + defer func() { + if err != nil { + netlink.LinkDel(host) + } + }() + + // Get the sandbox side pipe interface handler + sbox, err := netlink.LinkByName(name2) + if err != nil { + return err + } + defer func() { + if err != nil { + netlink.LinkDel(sbox) + } + }() + + // Set the sbox's MAC. If specified, use the one configured by user, otherwise use a random one + mac := electMacAddress(epConfig) + err = netlink.LinkSetHardwareAddr(sbox, mac) + if err != nil { + return err + } + endpoint.macAddress = mac + + // Add bridge inherited attributes to pipe interfaces + if config.Mtu != 0 { + err = netlink.LinkSetMTU(host, config.Mtu) + if err != nil { + return err + } + err = netlink.LinkSetMTU(sbox, config.Mtu) + if err != nil { + return err + } + } + + // Attach host side pipe interface into the bridge + if err = netlink.LinkSetMaster(host, + &netlink.Bridge{LinkAttrs: netlink.LinkAttrs{Name: config.BridgeName}}); err != nil { + return err + } + + // v4 address for the sandbox side pipe interface + ip4, err := ipAllocator.RequestIP(n.bridge.bridgeIPv4, nil) + if err != nil { + return err + } + ipv4Addr := &net.IPNet{IP: ip4, Mask: n.bridge.bridgeIPv4.Mask} + + // v6 address for the sandbox side pipe interface + ipv6Addr = &net.IPNet{} + if config.EnableIPv6 { + var ip6 net.IP + + network := n.bridge.bridgeIPv6 + if config.FixedCIDRv6 != nil { + network = config.FixedCIDRv6 + } + + ones, _ := network.Mask.Size() + if ones <= 80 { + ip6 = make(net.IP, len(network.IP)) + copy(ip6, network.IP) + for i, h := range mac { + ip6[i+10] = h + } + } + + ip6, err := ipAllocator.RequestIP(network, ip6) + if err != nil { + return err + } + + ipv6Addr = &net.IPNet{IP: ip6, Mask: network.Mask} + } + + // Create the sandbox side pipe interface + intf := &sandbox.Interface{} + intf.SrcName = name2 + intf.DstName = containerVeth + intf.Address = ipv4Addr + + if config.EnableIPv6 { + intf.AddressIPv6 = ipv6Addr + } + + // Store the interface in endpoint, this is needed for cleanup on DeleteEndpoint() + endpoint.intf = intf + + err = epInfo.AddInterface(ifaceID, endpoint.macAddress, *ipv4Addr, *ipv6Addr) + if err != nil { + return err + } + + // Program any required port mapping and store them in the endpoint + endpoint.portMapping, err = allocatePorts(epConfig, intf, config.DefaultBindingIP, config.EnableUserlandProxy) + if err != nil { + return err + } + + return nil +} + +func (d *driver) DeleteEndpoint(nid, eid types.UUID) error { + var err error + + // Get the network handler and make sure it exists + d.Lock() + n := d.network + config := n.config + d.Unlock() + if n == nil { + return driverapi.ErrNoNetwork + } + + // Sanity Check + n.Lock() + if n.id != nid { + n.Unlock() + return InvalidNetworkIDError(nid) + } + n.Unlock() + + // Check endpoint id and if an endpoint is actually there + ep, err := n.getEndpoint(eid) + if err != nil { + return err + } + if ep == nil { + return EndpointNotFoundError(eid) + } + + // Remove it + n.Lock() + delete(n.endpoints, eid) + n.Unlock() + + // On failure make sure to set back ep in n.endpoints, but only + // if it hasn't been taken over already by some other thread. + defer func() { + if err != nil { + n.Lock() + if _, ok := n.endpoints[eid]; !ok { + n.endpoints[eid] = ep + } + n.Unlock() + } + }() + + // Remove port mappings. Do not stop endpoint delete on unmap failure + releasePorts(ep) + + // Release the v4 address allocated to this endpoint's sandbox interface + err = ipAllocator.ReleaseIP(n.bridge.bridgeIPv4, ep.intf.Address.IP) + if err != nil { + return err + } + + // Release the v6 address allocated to this endpoint's sandbox interface + if config.EnableIPv6 { + err := ipAllocator.ReleaseIP(n.bridge.bridgeIPv6, ep.intf.AddressIPv6.IP) + if err != nil { + return err + } + } + + // Try removal of link. Discard error: link pair might have + // already been deleted by sandbox delete. + link, err := netlink.LinkByName(ep.intf.SrcName) + if err == nil { + netlink.LinkDel(link) + } + + return nil +} + +func (d *driver) EndpointOperInfo(nid, eid types.UUID) (map[string]interface{}, error) { + // Get the network handler and make sure it exists + d.Lock() + n := d.network + d.Unlock() + if n == nil { + return nil, driverapi.ErrNoNetwork + } + + // Sanity check + n.Lock() + if n.id != nid { + n.Unlock() + return nil, InvalidNetworkIDError(nid) + } + n.Unlock() + + // Check if endpoint id is good and retrieve correspondent endpoint + ep, err := n.getEndpoint(eid) + if err != nil { + return nil, err + } + if ep == nil { + return nil, driverapi.ErrNoEndpoint + } + + m := make(map[string]interface{}) + + if ep.portMapping != nil { + // Return a copy of the operational data + pmc := make([]netutils.PortBinding, 0, len(ep.portMapping)) + for _, pm := range ep.portMapping { + pmc = append(pmc, pm.GetCopy()) + } + m[netlabel.PortMap] = pmc + } + + if len(ep.macAddress) != 0 { + m[netlabel.MacAddress] = ep.macAddress + } + + return m, nil +} + +// Join method is invoked when a Sandbox is attached to an endpoint. +func (d *driver) Join(nid, eid types.UUID, sboxKey string, jinfo driverapi.JoinInfo, options map[string]interface{}) error { + network, err := d.getNetwork(nid) + if err != nil { + return err + } + + endpoint, err := network.getEndpoint(eid) + if err != nil { + return err + } + + if endpoint == nil { + return EndpointNotFoundError(eid) + } + + for _, iNames := range jinfo.InterfaceNames() { + // Make sure to set names on the correct interface ID. + if iNames.ID() == ifaceID { + err = iNames.SetNames(endpoint.intf.SrcName, endpoint.intf.DstName) + if err != nil { + return err + } + } + } + + err = jinfo.SetGateway(network.bridge.gatewayIPv4) + if err != nil { + return err + } + + err = jinfo.SetGatewayIPv6(network.bridge.gatewayIPv6) + if err != nil { + return err + } + + if !network.config.EnableICC { + return d.link(network, endpoint, options, true) + } + + return nil +} + +// Leave method is invoked when a Sandbox detaches from an endpoint. +func (d *driver) Leave(nid, eid types.UUID) error { + network, err := d.getNetwork(nid) + if err != nil { + return err + } + + endpoint, err := network.getEndpoint(eid) + if err != nil { + return err + } + + if endpoint == nil { + return EndpointNotFoundError(eid) + } + + if !network.config.EnableICC { + return d.link(network, endpoint, nil, false) + } + + return nil +} + +func (d *driver) link(network *bridgeNetwork, endpoint *bridgeEndpoint, options map[string]interface{}, enable bool) error { + var ( + cc *ContainerConfiguration + err error + ) + + if enable { + cc, err = parseContainerOptions(options) + if err != nil { + return err + } + } else { + cc = endpoint.containerConfig + } + + if cc == nil { + return nil + } + + if endpoint.config != nil && endpoint.config.ExposedPorts != nil { + for _, p := range cc.ParentEndpoints { + var parentEndpoint *bridgeEndpoint + parentEndpoint, err = network.getEndpoint(types.UUID(p)) + if err != nil { + return err + } + if parentEndpoint == nil { + err = InvalidEndpointIDError(p) + return err + } + + l := newLink(parentEndpoint.intf.Address.IP.String(), + endpoint.intf.Address.IP.String(), + endpoint.config.ExposedPorts, network.config.BridgeName) + if enable { + err = l.Enable() + if err != nil { + return err + } + defer func() { + if err != nil { + l.Disable() + } + }() + } else { + l.Disable() + } + } + } + + for _, c := range cc.ChildEndpoints { + var childEndpoint *bridgeEndpoint + childEndpoint, err = network.getEndpoint(types.UUID(c)) + if err != nil { + return err + } + if childEndpoint == nil { + err = InvalidEndpointIDError(c) + return err + } + if childEndpoint.config == nil || childEndpoint.config.ExposedPorts == nil { + continue + } + + l := newLink(endpoint.intf.Address.IP.String(), + childEndpoint.intf.Address.IP.String(), + childEndpoint.config.ExposedPorts, network.config.BridgeName) + if enable { + err = l.Enable() + if err != nil { + return err + } + defer func() { + if err != nil { + l.Disable() + } + }() + } else { + l.Disable() + } + } + + if enable { + endpoint.containerConfig = cc + } + + return nil +} + +func (d *driver) Type() string { + return networkType +} + +func parseEndpointOptions(epOptions map[string]interface{}) (*EndpointConfiguration, error) { + if epOptions == nil { + return nil, nil + } + + ec := &EndpointConfiguration{} + + if opt, ok := epOptions[netlabel.MacAddress]; ok { + if mac, ok := opt.(net.HardwareAddr); ok { + ec.MacAddress = mac + } else { + return nil, ErrInvalidEndpointConfig + } + } + + if opt, ok := epOptions[netlabel.PortMap]; ok { + if bs, ok := opt.([]netutils.PortBinding); ok { + ec.PortBindings = bs + } else { + return nil, ErrInvalidEndpointConfig + } + } + + if opt, ok := epOptions[netlabel.ExposedPorts]; ok { + if ports, ok := opt.([]netutils.TransportPort); ok { + ec.ExposedPorts = ports + } else { + return nil, ErrInvalidEndpointConfig + } + } + + return ec, nil +} + +func parseContainerOptions(cOptions map[string]interface{}) (*ContainerConfiguration, error) { + if cOptions == nil { + return nil, nil + } + genericData := cOptions[netlabel.GenericData] + if genericData == nil { + return nil, nil + } + switch opt := genericData.(type) { + case options.Generic: + opaqueConfig, err := options.GenerateFromModel(opt, &ContainerConfiguration{}) + if err != nil { + return nil, err + } + return opaqueConfig.(*ContainerConfiguration), nil + case *ContainerConfiguration: + return opt, nil + default: + return nil, nil + } +} + +func electMacAddress(epConfig *EndpointConfiguration) net.HardwareAddr { + if epConfig != nil && epConfig.MacAddress != nil { + return epConfig.MacAddress + } + return netutils.GenerateRandomMAC() +} + +// Generates a name to be used for a virtual ethernet +// interface. The name is constructed by 'veth' appended +// by a randomly generated hex value. (example: veth0f60e2c) +func generateIfaceName() (string, error) { + for i := 0; i < 3; i++ { + name, err := netutils.GenerateRandomName(vethPrefix, vethLen) + if err != nil { + continue + } + if _, err := net.InterfaceByName(name); err != nil { + if strings.Contains(err.Error(), "no such") { + return name, nil + } + return "", err + } + } + return "", ErrIfaceName +} diff --git a/vendor/src/github.com/docker/libnetwork/drivers/bridge/bridge_test.go b/vendor/src/github.com/docker/libnetwork/drivers/bridge/bridge_test.go new file mode 100644 index 0000000000..d11ea8104f --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/drivers/bridge/bridge_test.go @@ -0,0 +1,531 @@ +package bridge + +import ( + "bytes" + "fmt" + "net" + "regexp" + "testing" + + "github.com/docker/libnetwork/driverapi" + "github.com/docker/libnetwork/iptables" + "github.com/docker/libnetwork/netlabel" + "github.com/docker/libnetwork/netutils" + "github.com/vishvananda/netlink" +) + +func TestCreateFullOptions(t *testing.T) { + defer netutils.SetupTestNetNS(t)() + d := newDriver() + + config := &Configuration{ + EnableIPForwarding: true, + } + + netConfig := &NetworkConfiguration{ + BridgeName: DefaultBridgeName, + EnableIPv6: true, + FixedCIDR: bridgeNetworks[0], + EnableIPTables: true, + } + _, netConfig.FixedCIDRv6, _ = net.ParseCIDR("2001:db8::/48") + genericOption := make(map[string]interface{}) + genericOption[netlabel.GenericData] = config + + if err := d.Config(genericOption); err != nil { + t.Fatalf("Failed to setup driver config: %v", err) + } + + netOption := make(map[string]interface{}) + netOption[netlabel.GenericData] = netConfig + + err := d.CreateNetwork("dummy", netOption) + if err != nil { + t.Fatalf("Failed to create bridge: %v", err) + } +} + +func TestCreate(t *testing.T) { + defer netutils.SetupTestNetNS(t)() + d := newDriver() + + config := &NetworkConfiguration{BridgeName: DefaultBridgeName} + genericOption := make(map[string]interface{}) + genericOption[netlabel.GenericData] = config + + if err := d.CreateNetwork("dummy", genericOption); err != nil { + t.Fatalf("Failed to create bridge: %v", err) + } +} + +func TestCreateFail(t *testing.T) { + defer netutils.SetupTestNetNS(t)() + d := newDriver() + + config := &NetworkConfiguration{BridgeName: "dummy0"} + genericOption := make(map[string]interface{}) + genericOption[netlabel.GenericData] = config + + if err := d.CreateNetwork("dummy", genericOption); err == nil { + t.Fatal("Bridge creation was expected to fail") + } +} + +type testInterface struct { + id int + mac net.HardwareAddr + addr net.IPNet + addrv6 net.IPNet + srcName string + dstName string +} + +type testEndpoint struct { + ifaces []*testInterface + gw net.IP + gw6 net.IP + hostsPath string + resolvConfPath string +} + +func (te *testEndpoint) Interfaces() []driverapi.InterfaceInfo { + iList := make([]driverapi.InterfaceInfo, len(te.ifaces)) + + for i, iface := range te.ifaces { + iList[i] = iface + } + + return iList +} + +func (te *testEndpoint) AddInterface(id int, mac net.HardwareAddr, ipv4 net.IPNet, ipv6 net.IPNet) error { + iface := &testInterface{id: id, addr: ipv4, addrv6: ipv6} + te.ifaces = append(te.ifaces, iface) + return nil +} + +func (i *testInterface) ID() int { + return i.id +} + +func (i *testInterface) MacAddress() net.HardwareAddr { + return i.mac +} + +func (i *testInterface) Address() net.IPNet { + return i.addr +} + +func (i *testInterface) AddressIPv6() net.IPNet { + return i.addrv6 +} + +func (i *testInterface) SetNames(srcName string, dstName string) error { + i.srcName = srcName + i.dstName = dstName + return nil +} + +func (te *testEndpoint) InterfaceNames() []driverapi.InterfaceNameInfo { + iList := make([]driverapi.InterfaceNameInfo, len(te.ifaces)) + + for i, iface := range te.ifaces { + iList[i] = iface + } + + return iList +} + +func (te *testEndpoint) SetGateway(gw net.IP) error { + te.gw = gw + return nil +} + +func (te *testEndpoint) SetGatewayIPv6(gw6 net.IP) error { + te.gw6 = gw6 + return nil +} + +func (te *testEndpoint) SetHostsPath(path string) error { + te.hostsPath = path + return nil +} + +func (te *testEndpoint) SetResolvConfPath(path string) error { + te.resolvConfPath = path + return nil +} + +func TestQueryEndpointInfo(t *testing.T) { + testQueryEndpointInfo(t, true) +} + +func TestQueryEndpointInfoHairpin(t *testing.T) { + testQueryEndpointInfo(t, false) +} + +func testQueryEndpointInfo(t *testing.T, ulPxyEnabled bool) { + defer netutils.SetupTestNetNS(t)() + d := newDriver() + dd, _ := d.(*driver) + + config := &NetworkConfiguration{ + BridgeName: DefaultBridgeName, + EnableIPTables: true, + EnableICC: false, + EnableUserlandProxy: ulPxyEnabled, + } + genericOption := make(map[string]interface{}) + genericOption[netlabel.GenericData] = config + + err := d.CreateNetwork("net1", genericOption) + if err != nil { + t.Fatalf("Failed to create bridge: %v", err) + } + + portMappings := getPortMapping() + epOptions := make(map[string]interface{}) + epOptions[netlabel.PortMap] = portMappings + + te := &testEndpoint{ifaces: []*testInterface{}} + err = d.CreateEndpoint("net1", "ep1", te, epOptions) + if err != nil { + t.Fatalf("Failed to create an endpoint : %s", err.Error()) + } + + ep, _ := dd.network.endpoints["ep1"] + data, err := d.EndpointOperInfo(dd.network.id, ep.id) + if err != nil { + t.Fatalf("Failed to ask for endpoint operational data: %v", err) + } + pmd, ok := data[netlabel.PortMap] + if !ok { + t.Fatalf("Endpoint operational data does not contain port mapping data") + } + pm, ok := pmd.([]netutils.PortBinding) + if !ok { + t.Fatalf("Unexpected format for port mapping in endpoint operational data") + } + if len(ep.portMapping) != len(pm) { + t.Fatalf("Incomplete data for port mapping in endpoint operational data") + } + for i, pb := range ep.portMapping { + if !pb.Equal(&pm[i]) { + t.Fatalf("Unexpected data for port mapping in endpoint operational data") + } + } + + // Cleanup as host ports are there + err = releasePorts(ep) + if err != nil { + t.Fatalf("Failed to release mapped ports: %v", err) + } +} + +func TestCreateLinkWithOptions(t *testing.T) { + defer netutils.SetupTestNetNS(t)() + d := newDriver() + + config := &NetworkConfiguration{BridgeName: DefaultBridgeName} + netOptions := make(map[string]interface{}) + netOptions[netlabel.GenericData] = config + + err := d.CreateNetwork("net1", netOptions) + if err != nil { + t.Fatalf("Failed to create bridge: %v", err) + } + + mac := net.HardwareAddr([]byte{0x1e, 0x67, 0x66, 0x44, 0x55, 0x66}) + epOptions := make(map[string]interface{}) + epOptions[netlabel.MacAddress] = mac + + te := &testEndpoint{ifaces: []*testInterface{}} + err = d.CreateEndpoint("net1", "ep", te, epOptions) + if err != nil { + t.Fatalf("Failed to create an endpoint: %s", err.Error()) + } + + err = d.Join("net1", "ep", "sbox", te, nil) + if err != nil { + t.Fatalf("Failed to join the endpoint: %v", err) + } + + ifaceName := te.ifaces[0].srcName + veth, err := netlink.LinkByName(ifaceName) + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(mac, veth.Attrs().HardwareAddr) { + t.Fatalf("Failed to parse and program endpoint configuration") + } +} + +func getExposedPorts() []netutils.TransportPort { + return []netutils.TransportPort{ + netutils.TransportPort{Proto: netutils.TCP, Port: uint16(5000)}, + netutils.TransportPort{Proto: netutils.UDP, Port: uint16(400)}, + netutils.TransportPort{Proto: netutils.TCP, Port: uint16(600)}, + } +} + +func getPortMapping() []netutils.PortBinding { + return []netutils.PortBinding{ + netutils.PortBinding{Proto: netutils.TCP, Port: uint16(230), HostPort: uint16(23000)}, + netutils.PortBinding{Proto: netutils.UDP, Port: uint16(200), HostPort: uint16(22000)}, + netutils.PortBinding{Proto: netutils.TCP, Port: uint16(120), HostPort: uint16(12000)}, + } +} + +func TestLinkContainers(t *testing.T) { + defer netutils.SetupTestNetNS(t)() + + d := newDriver() + + config := &NetworkConfiguration{ + BridgeName: DefaultBridgeName, + EnableIPTables: true, + EnableICC: false, + } + genericOption := make(map[string]interface{}) + genericOption[netlabel.GenericData] = config + + err := d.CreateNetwork("net1", genericOption) + if err != nil { + t.Fatalf("Failed to create bridge: %v", err) + } + + exposedPorts := getExposedPorts() + epOptions := make(map[string]interface{}) + epOptions[netlabel.ExposedPorts] = exposedPorts + + te1 := &testEndpoint{ifaces: []*testInterface{}} + err = d.CreateEndpoint("net1", "ep1", te1, epOptions) + if err != nil { + t.Fatalf("Failed to create an endpoint : %s", err.Error()) + } + + addr1 := te1.ifaces[0].addr + if addr1.IP.To4() == nil { + t.Fatalf("No Ipv4 address assigned to the endpoint: ep1") + } + + te2 := &testEndpoint{ifaces: []*testInterface{}} + err = d.CreateEndpoint("net1", "ep2", te2, nil) + if err != nil { + t.Fatalf("Failed to create an endpoint : %s", err.Error()) + } + + addr2 := te2.ifaces[0].addr + if addr2.IP.To4() == nil { + t.Fatalf("No Ipv4 address assigned to the endpoint: ep2") + } + + ce := []string{"ep1"} + cConfig := &ContainerConfiguration{ChildEndpoints: ce} + genericOption = make(map[string]interface{}) + genericOption[netlabel.GenericData] = cConfig + + err = d.Join("net1", "ep2", "", te2, genericOption) + if err != nil { + t.Fatalf("Failed to link ep1 and ep2") + } + + out, err := iptables.Raw("-L", DockerChain) + for _, pm := range exposedPorts { + regex := fmt.Sprintf("%s dpt:%d", pm.Proto.String(), pm.Port) + re := regexp.MustCompile(regex) + matches := re.FindAllString(string(out[:]), -1) + if len(matches) != 1 { + t.Fatalf("IP Tables programming failed %s", string(out[:])) + } + + regex = fmt.Sprintf("%s spt:%d", pm.Proto.String(), pm.Port) + matched, _ := regexp.MatchString(regex, string(out[:])) + if !matched { + t.Fatalf("IP Tables programming failed %s", string(out[:])) + } + } + + err = d.Leave("net1", "ep2") + if err != nil { + t.Fatalf("Failed to unlink ep1 and ep2") + } + + out, err = iptables.Raw("-L", DockerChain) + for _, pm := range exposedPorts { + regex := fmt.Sprintf("%s dpt:%d", pm.Proto.String(), pm.Port) + re := regexp.MustCompile(regex) + matches := re.FindAllString(string(out[:]), -1) + if len(matches) != 0 { + t.Fatalf("Leave should have deleted relevant IPTables rules %s", string(out[:])) + } + + regex = fmt.Sprintf("%s spt:%d", pm.Proto.String(), pm.Port) + matched, _ := regexp.MatchString(regex, string(out[:])) + if matched { + t.Fatalf("Leave should have deleted relevant IPTables rules %s", string(out[:])) + } + } + + // Error condition test with an invalid endpoint-id "ep4" + ce = []string{"ep1", "ep4"} + cConfig = &ContainerConfiguration{ChildEndpoints: ce} + genericOption = make(map[string]interface{}) + genericOption[netlabel.GenericData] = cConfig + + err = d.Join("net1", "ep2", "", te2, genericOption) + if err != nil { + out, err = iptables.Raw("-L", DockerChain) + for _, pm := range exposedPorts { + regex := fmt.Sprintf("%s dpt:%d", pm.Proto.String(), pm.Port) + re := regexp.MustCompile(regex) + matches := re.FindAllString(string(out[:]), -1) + if len(matches) != 0 { + t.Fatalf("Error handling should rollback relevant IPTables rules %s", string(out[:])) + } + + regex = fmt.Sprintf("%s spt:%d", pm.Proto.String(), pm.Port) + matched, _ := regexp.MatchString(regex, string(out[:])) + if matched { + t.Fatalf("Error handling should rollback relevant IPTables rules %s", string(out[:])) + } + } + } else { + t.Fatalf("Expected Join to fail given link conditions are not satisfied") + } +} + +func TestValidateConfig(t *testing.T) { + + // Test mtu + c := NetworkConfiguration{Mtu: -2} + err := c.Validate() + if err == nil { + t.Fatalf("Failed to detect invalid MTU number") + } + + c.Mtu = 9000 + err = c.Validate() + if err != nil { + t.Fatalf("unexpected validation error on MTU number") + } + + // Bridge network + _, network, _ := net.ParseCIDR("172.28.0.0/16") + + // Test FixedCIDR + _, containerSubnet, _ := net.ParseCIDR("172.27.0.0/16") + c = NetworkConfiguration{ + AddressIPv4: network, + FixedCIDR: containerSubnet, + } + + err = c.Validate() + if err == nil { + t.Fatalf("Failed to detect invalid FixedCIDR network") + } + + _, containerSubnet, _ = net.ParseCIDR("172.28.0.0/16") + c.FixedCIDR = containerSubnet + err = c.Validate() + if err != nil { + t.Fatalf("Unexpected validation error on FixedCIDR network") + } + + _, containerSubnet, _ = net.ParseCIDR("172.28.0.0/15") + c.FixedCIDR = containerSubnet + err = c.Validate() + if err == nil { + t.Fatalf("Failed to detect invalid FixedCIDR network") + } + + _, containerSubnet, _ = net.ParseCIDR("172.28.0.0/17") + c.FixedCIDR = containerSubnet + err = c.Validate() + if err != nil { + t.Fatalf("Unexpected validation error on FixedCIDR network") + } + + // Test v4 gw + c.DefaultGatewayIPv4 = net.ParseIP("172.27.30.234") + err = c.Validate() + if err == nil { + t.Fatalf("Failed to detect invalid default gateway") + } + + c.DefaultGatewayIPv4 = net.ParseIP("172.28.30.234") + err = c.Validate() + if err != nil { + t.Fatalf("Unexpected validation error on default gateway") + } + + // Test v6 gw + _, containerSubnet, _ = net.ParseCIDR("2001:1234:ae:b004::/64") + c = NetworkConfiguration{ + EnableIPv6: true, + FixedCIDRv6: containerSubnet, + DefaultGatewayIPv6: net.ParseIP("2001:1234:ac:b004::bad:a55"), + } + err = c.Validate() + if err == nil { + t.Fatalf("Failed to detect invalid v6 default gateway") + } + + c.DefaultGatewayIPv6 = net.ParseIP("2001:1234:ae:b004::bad:a55") + err = c.Validate() + if err != nil { + t.Fatalf("Unexpected validation error on v6 default gateway") + } + + c.FixedCIDRv6 = nil + err = c.Validate() + if err == nil { + t.Fatalf("Failed to detect invalid v6 default gateway") + } +} + +func TestSetDefaultGw(t *testing.T) { + defer netutils.SetupTestNetNS(t)() + d := newDriver() + + _, subnetv6, _ := net.ParseCIDR("2001:db8:ea9:9abc:b0c4::/80") + gw4 := bridgeNetworks[0].IP.To4() + gw4[3] = 254 + gw6 := net.ParseIP("2001:db8:ea9:9abc:b0c4::254") + + config := &NetworkConfiguration{ + BridgeName: DefaultBridgeName, + EnableIPv6: true, + FixedCIDRv6: subnetv6, + DefaultGatewayIPv4: gw4, + DefaultGatewayIPv6: gw6, + } + + genericOption := make(map[string]interface{}) + genericOption[netlabel.GenericData] = config + + err := d.CreateNetwork("dummy", genericOption) + if err != nil { + t.Fatalf("Failed to create bridge: %v", err) + } + + te := &testEndpoint{ifaces: []*testInterface{}} + err = d.CreateEndpoint("dummy", "ep", te, nil) + if err != nil { + t.Fatalf("Failed to create endpoint: %v", err) + } + + err = d.Join("dummy", "ep", "sbox", te, nil) + if err != nil { + t.Fatalf("Failed to join endpoint: %v", err) + } + + if !gw4.Equal(te.gw) { + t.Fatalf("Failed to configure default gateway. Expected %v. Found %v", gw4, te.gw) + } + + if !gw6.Equal(te.gw6) { + t.Fatalf("Failed to configure default gateway. Expected %v. Found %v", gw6, te.gw6) + } +} diff --git a/vendor/src/github.com/docker/libnetwork/drivers/bridge/error.go b/vendor/src/github.com/docker/libnetwork/drivers/bridge/error.go new file mode 100644 index 0000000000..5f149c46d9 --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/drivers/bridge/error.go @@ -0,0 +1,201 @@ +package bridge + +import ( + "errors" + "fmt" + "net" +) + +var ( + // ErrConfigExists error is returned when driver already has a config applied. + ErrConfigExists = errors.New("configuration already exists, bridge configuration can be applied only once") + + // ErrInvalidDriverConfig error is returned when Bridge Driver is passed an invalid config + ErrInvalidDriverConfig = errors.New("Invalid configuration passed to Bridge Driver") + + // ErrInvalidNetworkConfig error is returned when a network is created on a driver without valid config. + ErrInvalidNetworkConfig = errors.New("trying to create a network on a driver without valid config") + + // ErrInvalidContainerConfig error is returned when a endpoint create is attempted with an invalid configuration. + ErrInvalidContainerConfig = errors.New("Error in joining a container due to invalid configuration") + + // ErrInvalidEndpointConfig error is returned when a endpoint create is attempted with an invalid endpoint configuration. + ErrInvalidEndpointConfig = errors.New("trying to create an endpoint with an invalid endpoint configuration") + + // ErrNetworkExists error is returned when a network already exists and another network is created. + ErrNetworkExists = errors.New("network already exists, bridge can only have one network") + + // ErrIfaceName error is returned when a new name could not be generated. + ErrIfaceName = errors.New("failed to find name for new interface") + + // ErrNoIPAddr error is returned when bridge has no IPv4 address configured. + ErrNoIPAddr = errors.New("bridge has no IPv4 address configured") + + // ErrInvalidGateway is returned when the user provided default gateway (v4/v6) is not not valid. + ErrInvalidGateway = errors.New("default gateway ip must be part of the network") + + // ErrInvalidContainerSubnet is returned when the container subnet (FixedCIDR) is not valid. + ErrInvalidContainerSubnet = errors.New("container subnet must be a subset of bridge network") + + // ErrInvalidMtu is returned when the user provided MTU is not valid. + ErrInvalidMtu = errors.New("invalid MTU number") + + // ErrIPFwdCfg is returned when ip forwarding setup is invoked when the configuration + // not enabled. + ErrIPFwdCfg = errors.New("unexpected request to enable IP Forwarding") +) + +// ErrInvalidPort is returned when the container or host port specified in the port binding is not valid. +type ErrInvalidPort string + +func (ip ErrInvalidPort) Error() string { + return fmt.Sprintf("invalid transport port: %s", string(ip)) +} + +// ErrUnsupportedAddressType is returned when the specified address type is not supported. +type ErrUnsupportedAddressType string + +func (uat ErrUnsupportedAddressType) Error() string { + return fmt.Sprintf("unsupported address type: %s", string(uat)) +} + +// ErrInvalidAddressBinding is returned when the host address specified in the port binding is not valid. +type ErrInvalidAddressBinding string + +func (iab ErrInvalidAddressBinding) Error() string { + return fmt.Sprintf("invalid host address in port binding: %s", string(iab)) +} + +// ActiveEndpointsError is returned when there are +// still active endpoints in the network being deleted. +type ActiveEndpointsError string + +func (aee ActiveEndpointsError) Error() string { + return fmt.Sprintf("network %s has active endpoint", string(aee)) +} + +// InvalidNetworkIDError is returned when the passed +// network id for an existing network is not a known id. +type InvalidNetworkIDError string + +func (inie InvalidNetworkIDError) Error() string { + return fmt.Sprintf("invalid network id %s", string(inie)) +} + +// InvalidEndpointIDError is returned when the passed +// endpoint id is not valid. +type InvalidEndpointIDError string + +func (ieie InvalidEndpointIDError) Error() string { + return fmt.Sprintf("invalid endpoint id: %s", string(ieie)) +} + +// InvalidSandboxIDError is returned when the passed +// sandbox id valid. +type InvalidSandboxIDError string + +func (isie InvalidSandboxIDError) Error() string { + return fmt.Sprintf("invalid sanbox id: %s", string(isie)) +} + +// EndpointNotFoundError is returned when the no endpoint +// with the passed endpoint id is found. +type EndpointNotFoundError string + +func (enfe EndpointNotFoundError) Error() string { + return fmt.Sprintf("endpoint not found: %s", string(enfe)) +} + +// NonDefaultBridgeExistError is returned when a non-default +// bridge config is passed but it does not already exist. +type NonDefaultBridgeExistError string + +func (ndbee NonDefaultBridgeExistError) Error() string { + return fmt.Sprintf("bridge device with non default name %s must be created manually", string(ndbee)) +} + +// FixedCIDRv4Error is returned when fixed-cidrv4 configuration +// failed. +type FixedCIDRv4Error struct { + net *net.IPNet + subnet *net.IPNet + err error +} + +func (fcv4 *FixedCIDRv4Error) Error() string { + return fmt.Sprintf("setup FixedCIDRv4 failed for subnet %s in %s: %v", fcv4.subnet, fcv4.net, fcv4.err) +} + +// FixedCIDRv6Error is returned when fixed-cidrv6 configuration +// failed. +type FixedCIDRv6Error struct { + net *net.IPNet + err error +} + +func (fcv6 *FixedCIDRv6Error) Error() string { + return fmt.Sprintf("setup FixedCIDRv6 failed for subnet %s in %s: %v", fcv6.net, fcv6.net, fcv6.err) +} + +type ipTableCfgError string + +func (name ipTableCfgError) Error() string { + return fmt.Sprintf("unexpected request to set IP tables for interface: %s", string(name)) +} + +type invalidIPTablesCfgError string + +func (action invalidIPTablesCfgError) Error() string { + return fmt.Sprintf("Invalid IPTables action '%s'", string(action)) +} + +// IPv4AddrRangeError is returned when a valid IP address range couldn't be found. +type IPv4AddrRangeError string + +func (name IPv4AddrRangeError) Error() string { + return fmt.Sprintf("can't find an address range for interface %q", string(name)) +} + +// IPv4AddrAddError is returned when IPv4 address could not be added to the bridge. +type IPv4AddrAddError struct { + ip *net.IPNet + err error +} + +func (ipv4 *IPv4AddrAddError) Error() string { + return fmt.Sprintf("failed to add IPv4 address %s to bridge: %v", ipv4.ip, ipv4.err) +} + +// IPv6AddrAddError is returned when IPv6 address could not be added to the bridge. +type IPv6AddrAddError struct { + ip *net.IPNet + err error +} + +func (ipv6 *IPv6AddrAddError) Error() string { + return fmt.Sprintf("failed to add IPv6 address %s to bridge: %v", ipv6.ip, ipv6.err) +} + +// IPv4AddrNoMatchError is returned when the bridge's IPv4 address does not match configured. +type IPv4AddrNoMatchError struct { + ip net.IP + cfgIP net.IP +} + +func (ipv4 *IPv4AddrNoMatchError) Error() string { + return fmt.Sprintf("bridge IPv4 (%s) does not match requested configuration %s", ipv4.ip, ipv4.cfgIP) +} + +// IPv6AddrNoMatchError is returned when the bridge's IPv6 address does not match configured. +type IPv6AddrNoMatchError net.IPNet + +func (ipv6 *IPv6AddrNoMatchError) Error() string { + return fmt.Sprintf("bridge IPv6 addresses do not match the expected bridge configuration %s", (*net.IPNet)(ipv6).String()) +} + +// InvalidLinkIPAddrError is returned when a link is configured to a container with an invalid ip address +type InvalidLinkIPAddrError string + +func (address InvalidLinkIPAddrError) Error() string { + return fmt.Sprintf("Cannot link to a container with Invalid IP Address '%s'", string(address)) +} diff --git a/vendor/src/github.com/docker/libnetwork/drivers/bridge/interface.go b/vendor/src/github.com/docker/libnetwork/drivers/bridge/interface.go new file mode 100644 index 0000000000..215a7f48f2 --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/drivers/bridge/interface.go @@ -0,0 +1,63 @@ +package bridge + +import ( + "net" + + "github.com/vishvananda/netlink" +) + +const ( + // DefaultBridgeName is the default name for the bridge interface managed + // by the driver when unspecified by the caller. + DefaultBridgeName = "docker0" +) + +// Interface models the bridge network device. +type bridgeInterface struct { + Link netlink.Link + bridgeIPv4 *net.IPNet + bridgeIPv6 *net.IPNet + gatewayIPv4 net.IP + gatewayIPv6 net.IP +} + +// newInterface creates a new bridge interface structure. It attempts to find +// an already existing device identified by the Configuration BridgeName field, +// or the default bridge name when unspecified), but doesn't attempt to create +// one when missing +func newInterface(config *NetworkConfiguration) *bridgeInterface { + i := &bridgeInterface{} + + // Initialize the bridge name to the default if unspecified. + if config.BridgeName == "" { + config.BridgeName = DefaultBridgeName + } + + // Attempt to find an existing bridge named with the specified name. + i.Link, _ = netlink.LinkByName(config.BridgeName) + return i +} + +// exists indicates if the existing bridge interface exists on the system. +func (i *bridgeInterface) exists() bool { + return i.Link != nil +} + +// addresses returns a single IPv4 address and all IPv6 addresses for the +// bridge interface. +func (i *bridgeInterface) addresses() (netlink.Addr, []netlink.Addr, error) { + v4addr, err := netlink.AddrList(i.Link, netlink.FAMILY_V4) + if err != nil { + return netlink.Addr{}, nil, err + } + + v6addr, err := netlink.AddrList(i.Link, netlink.FAMILY_V6) + if err != nil { + return netlink.Addr{}, nil, err + } + + if len(v4addr) == 0 { + return netlink.Addr{}, v6addr, nil + } + return v4addr[0], v6addr, nil +} diff --git a/vendor/src/github.com/docker/libnetwork/drivers/bridge/interface_test.go b/vendor/src/github.com/docker/libnetwork/drivers/bridge/interface_test.go new file mode 100644 index 0000000000..07bfe9eed9 --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/drivers/bridge/interface_test.go @@ -0,0 +1,33 @@ +package bridge + +import ( + "testing" + + "github.com/docker/libnetwork/netutils" + "github.com/vishvananda/netlink" +) + +func TestInterfaceDefaultName(t *testing.T) { + defer netutils.SetupTestNetNS(t)() + + config := &NetworkConfiguration{} + if _ = newInterface(config); config.BridgeName != DefaultBridgeName { + t.Fatalf("Expected default interface name %q, got %q", DefaultBridgeName, config.BridgeName) + } +} + +func TestAddressesEmptyInterface(t *testing.T) { + defer netutils.SetupTestNetNS(t)() + + inf := newInterface(&NetworkConfiguration{}) + addrv4, addrsv6, err := inf.addresses() + if err != nil { + t.Fatalf("Failed to get addresses of default interface: %v", err) + } + if expected := (netlink.Addr{}); addrv4 != expected { + t.Fatalf("Default interface has unexpected IPv4: %s", addrv4) + } + if len(addrsv6) != 0 { + t.Fatalf("Default interface has unexpected IPv6: %v", addrsv6) + } +} diff --git a/vendor/src/github.com/docker/libnetwork/drivers/bridge/link.go b/vendor/src/github.com/docker/libnetwork/drivers/bridge/link.go new file mode 100644 index 0000000000..05a99dc0e4 --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/drivers/bridge/link.go @@ -0,0 +1,80 @@ +package bridge + +import ( + "fmt" + "net" + + log "github.com/Sirupsen/logrus" + "github.com/docker/libnetwork/iptables" + "github.com/docker/libnetwork/netutils" +) + +type link struct { + parentIP string + childIP string + ports []netutils.TransportPort + bridge string +} + +func (l *link) String() string { + return fmt.Sprintf("%s <-> %s [%v] on %s", l.parentIP, l.childIP, l.ports, l.bridge) +} + +func newLink(parentIP, childIP string, ports []netutils.TransportPort, bridge string) *link { + return &link{ + childIP: childIP, + parentIP: parentIP, + ports: ports, + bridge: bridge, + } + +} + +func (l *link) Enable() error { + // -A == iptables append flag + return linkContainers("-A", l.parentIP, l.childIP, l.ports, l.bridge, false) +} + +func (l *link) Disable() { + // -D == iptables delete flag + err := linkContainers("-D", l.parentIP, l.childIP, l.ports, l.bridge, true) + if err != nil { + log.Errorf("Error removing IPTables rules for a link %s due to %s", l.String(), err.Error()) + } + // Return proper error once we move to use a proper iptables package + // that returns typed errors +} + +func linkContainers(action, parentIP, childIP string, ports []netutils.TransportPort, bridge string, + ignoreErrors bool) error { + var nfAction iptables.Action + + switch action { + case "-A": + nfAction = iptables.Append + case "-I": + nfAction = iptables.Insert + case "-D": + nfAction = iptables.Delete + default: + return invalidIPTablesCfgError(action) + } + + ip1 := net.ParseIP(parentIP) + if ip1 == nil { + return InvalidLinkIPAddrError(parentIP) + } + ip2 := net.ParseIP(childIP) + if ip2 == nil { + return InvalidLinkIPAddrError(childIP) + } + + chain := iptables.Chain{Name: DockerChain, Bridge: bridge} + for _, port := range ports { + err := chain.Link(nfAction, ip1, ip2, int(port.Port), port.Proto.String()) + if !ignoreErrors && err != nil { + return err + } + } + return nil +} diff --git a/vendor/src/github.com/docker/libnetwork/drivers/bridge/link_test.go b/vendor/src/github.com/docker/libnetwork/drivers/bridge/link_test.go new file mode 100644 index 0000000000..c021fd63c7 --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/drivers/bridge/link_test.go @@ -0,0 +1,39 @@ +package bridge + +import ( + "testing" + + "github.com/docker/libnetwork/netutils" +) + +func getPorts() []netutils.TransportPort { + return []netutils.TransportPort{ + netutils.TransportPort{Proto: netutils.TCP, Port: uint16(5000)}, + netutils.TransportPort{Proto: netutils.UDP, Port: uint16(400)}, + netutils.TransportPort{Proto: netutils.TCP, Port: uint16(600)}, + } +} + +func TestLinkNew(t *testing.T) { + ports := getPorts() + + link := newLink("172.0.17.3", "172.0.17.2", ports, "docker0") + + if link == nil { + t.FailNow() + } + if link.parentIP != "172.0.17.3" { + t.Fail() + } + if link.childIP != "172.0.17.2" { + t.Fail() + } + for i, p := range link.ports { + if p != ports[i] { + t.Fail() + } + } + if link.bridge != "docker0" { + t.Fail() + } +} diff --git a/vendor/src/github.com/docker/libnetwork/drivers/bridge/network_test.go b/vendor/src/github.com/docker/libnetwork/drivers/bridge/network_test.go new file mode 100644 index 0000000000..abadc07e92 --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/drivers/bridge/network_test.go @@ -0,0 +1,200 @@ +package bridge + +import ( + "testing" + + "github.com/docker/libnetwork/driverapi" + "github.com/docker/libnetwork/netlabel" + "github.com/docker/libnetwork/netutils" + "github.com/vishvananda/netlink" +) + +func TestLinkCreate(t *testing.T) { + defer netutils.SetupTestNetNS(t)() + d := newDriver() + dr := d.(*driver) + + mtu := 1490 + config := &NetworkConfiguration{ + BridgeName: DefaultBridgeName, + Mtu: mtu, + EnableIPv6: true, + } + genericOption := make(map[string]interface{}) + genericOption[netlabel.GenericData] = config + + err := d.CreateNetwork("dummy", genericOption) + if err != nil { + t.Fatalf("Failed to create bridge: %v", err) + } + + te := &testEndpoint{ifaces: []*testInterface{}} + err = d.CreateEndpoint("dummy", "", te, nil) + if err != nil { + if _, ok := err.(InvalidEndpointIDError); !ok { + t.Fatalf("Failed with a wrong error :%s", err.Error()) + } + } else { + t.Fatalf("Failed to detect invalid config") + } + + // Good endpoint creation + err = d.CreateEndpoint("dummy", "ep", te, nil) + if err != nil { + t.Fatalf("Failed to create a link: %s", err.Error()) + } + + err = d.Join("dummy", "ep", "sbox", te, nil) + if err != nil { + t.Fatalf("Failed to create a link: %s", err.Error()) + } + + // Verify sbox endoint interface inherited MTU value from bridge config + sboxLnk, err := netlink.LinkByName(te.ifaces[0].srcName) + if err != nil { + t.Fatal(err) + } + if mtu != sboxLnk.Attrs().MTU { + t.Fatalf("Sandbox endpoint interface did not inherit bridge interface MTU config") + } + // TODO: if we could get peer name from (sboxLnk.(*netlink.Veth)).PeerName + // then we could check the MTU on hostLnk as well. + + te1 := &testEndpoint{ifaces: []*testInterface{}} + err = d.CreateEndpoint("dummy", "ep", te1, nil) + if err == nil { + t.Fatalf("Failed to detect duplicate endpoint id on same network") + } + + if len(te.ifaces) != 1 { + t.Fatalf("Expected exactly one interface. Instead got %d interface(s)", len(te.ifaces)) + } + + if te.ifaces[0].dstName == "" { + t.Fatal("Invalid Dstname returned") + } + + _, err = netlink.LinkByName(te.ifaces[0].srcName) + if err != nil { + t.Fatalf("Could not find source link %s: %v", te.ifaces[0].srcName, err) + } + + n := dr.network + ip := te.ifaces[0].addr.IP + if !n.bridge.bridgeIPv4.Contains(ip) { + t.Fatalf("IP %s is not a valid ip in the subnet %s", ip.String(), n.bridge.bridgeIPv4.String()) + } + + ip6 := te.ifaces[0].addrv6.IP + if !n.bridge.bridgeIPv6.Contains(ip6) { + t.Fatalf("IP %s is not a valid ip in the subnet %s", ip6.String(), bridgeIPv6.String()) + } + + if !te.gw.Equal(n.bridge.bridgeIPv4.IP) { + t.Fatalf("Invalid default gateway. Expected %s. Got %s", n.bridge.bridgeIPv4.IP.String(), + te.gw.String()) + } + + if !te.gw6.Equal(n.bridge.bridgeIPv6.IP) { + t.Fatalf("Invalid default gateway for IPv6. Expected %s. Got %s", n.bridge.bridgeIPv6.IP.String(), + te.gw6.String()) + } +} + +func TestLinkCreateTwo(t *testing.T) { + defer netutils.SetupTestNetNS(t)() + d := newDriver() + + config := &NetworkConfiguration{ + BridgeName: DefaultBridgeName, + EnableIPv6: true} + genericOption := make(map[string]interface{}) + genericOption[netlabel.GenericData] = config + + err := d.CreateNetwork("dummy", genericOption) + if err != nil { + t.Fatalf("Failed to create bridge: %v", err) + } + + te1 := &testEndpoint{ifaces: []*testInterface{}} + err = d.CreateEndpoint("dummy", "ep", te1, nil) + if err != nil { + t.Fatalf("Failed to create a link: %s", err.Error()) + } + + te2 := &testEndpoint{ifaces: []*testInterface{}} + err = d.CreateEndpoint("dummy", "ep", te2, nil) + if err != nil { + if err != driverapi.ErrEndpointExists { + t.Fatalf("Failed with a wrong error :%s", err.Error()) + } + } else { + t.Fatalf("Expected to fail while trying to add same endpoint twice") + } +} + +func TestLinkCreateNoEnableIPv6(t *testing.T) { + defer netutils.SetupTestNetNS(t)() + d := newDriver() + + config := &NetworkConfiguration{ + BridgeName: DefaultBridgeName} + genericOption := make(map[string]interface{}) + genericOption[netlabel.GenericData] = config + + err := d.CreateNetwork("dummy", genericOption) + if err != nil { + t.Fatalf("Failed to create bridge: %v", err) + } + + te := &testEndpoint{ifaces: []*testInterface{}} + err = d.CreateEndpoint("dummy", "ep", te, nil) + if err != nil { + t.Fatalf("Failed to create a link: %s", err.Error()) + } + + interfaces := te.ifaces + if interfaces[0].addrv6.IP.To16() != nil { + t.Fatalf("Expectd IPv6 address to be nil when IPv6 is not enabled. Got IPv6 = %s", interfaces[0].addrv6.String()) + } + + if te.gw6.To16() != nil { + t.Fatalf("Expected GatewayIPv6 to be nil when IPv6 is not enabled. Got GatewayIPv6 = %s", te.gw6.String()) + } +} + +func TestLinkDelete(t *testing.T) { + defer netutils.SetupTestNetNS(t)() + d := newDriver() + + config := &NetworkConfiguration{ + BridgeName: DefaultBridgeName, + EnableIPv6: true} + genericOption := make(map[string]interface{}) + genericOption[netlabel.GenericData] = config + + err := d.CreateNetwork("dummy", genericOption) + if err != nil { + t.Fatalf("Failed to create bridge: %v", err) + } + + te := &testEndpoint{ifaces: []*testInterface{}} + err = d.CreateEndpoint("dummy", "ep1", te, nil) + if err != nil { + t.Fatalf("Failed to create a link: %s", err.Error()) + } + + err = d.DeleteEndpoint("dummy", "") + if err != nil { + if _, ok := err.(InvalidEndpointIDError); !ok { + t.Fatalf("Failed with a wrong error :%s", err.Error()) + } + } else { + t.Fatalf("Failed to detect invalid config") + } + + err = d.DeleteEndpoint("dummy", "ep1") + if err != nil { + t.Fatal(err) + } +} diff --git a/vendor/src/github.com/docker/libnetwork/drivers/bridge/port_mapping.go b/vendor/src/github.com/docker/libnetwork/drivers/bridge/port_mapping.go new file mode 100644 index 0000000000..aec4283c01 --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/drivers/bridge/port_mapping.go @@ -0,0 +1,124 @@ +package bridge + +import ( + "bytes" + "errors" + "fmt" + "net" + + "github.com/Sirupsen/logrus" + "github.com/docker/libnetwork/netutils" + "github.com/docker/libnetwork/sandbox" +) + +var ( + defaultBindingIP = net.IPv4(0, 0, 0, 0) +) + +func allocatePorts(epConfig *EndpointConfiguration, intf *sandbox.Interface, reqDefBindIP net.IP, ulPxyEnabled bool) ([]netutils.PortBinding, error) { + if epConfig == nil || epConfig.PortBindings == nil { + return nil, nil + } + + defHostIP := defaultBindingIP + if reqDefBindIP != nil { + defHostIP = reqDefBindIP + } + + return allocatePortsInternal(epConfig.PortBindings, intf.Address.IP, defHostIP, ulPxyEnabled) +} + +func allocatePortsInternal(bindings []netutils.PortBinding, containerIP, defHostIP net.IP, ulPxyEnabled bool) ([]netutils.PortBinding, error) { + bs := make([]netutils.PortBinding, 0, len(bindings)) + for _, c := range bindings { + b := c.GetCopy() + if err := allocatePort(&b, containerIP, defHostIP, ulPxyEnabled); err != nil { + // On allocation failure, release previously allocated ports. On cleanup error, just log a warning message + if cuErr := releasePortsInternal(bs); cuErr != nil { + logrus.Warnf("Upon allocation failure for %v, failed to clear previously allocated port bindings: %v", b, cuErr) + } + return nil, err + } + bs = append(bs, b) + } + return bs, nil +} + +func allocatePort(bnd *netutils.PortBinding, containerIP, defHostIP net.IP, ulPxyEnabled bool) error { + var ( + host net.Addr + err error + ) + + // Store the container interface address in the operational binding + bnd.IP = containerIP + + // Adjust the host address in the operational binding + if len(bnd.HostIP) == 0 { + bnd.HostIP = defHostIP + } + + // Construct the container side transport address + container, err := bnd.ContainerAddr() + if err != nil { + return err + } + + // Try up to maxAllocatePortAttempts times to get a port that's not already allocated. + for i := 0; i < maxAllocatePortAttempts; i++ { + if host, err = portMapper.Map(container, bnd.HostIP, int(bnd.HostPort), ulPxyEnabled); err == nil { + break + } + // There is no point in immediately retrying to map an explicitly chosen port. + if bnd.HostPort != 0 { + logrus.Warnf("Failed to allocate and map port %d: %s", bnd.HostPort, err) + break + } + logrus.Warnf("Failed to allocate and map port: %s, retry: %d", err, i+1) + } + if err != nil { + return err + } + + // Save the host port (regardless it was or not specified in the binding) + switch netAddr := host.(type) { + case *net.TCPAddr: + bnd.HostPort = uint16(host.(*net.TCPAddr).Port) + return nil + case *net.UDPAddr: + bnd.HostPort = uint16(host.(*net.UDPAddr).Port) + return nil + default: + // For completeness + return ErrUnsupportedAddressType(fmt.Sprintf("%T", netAddr)) + } +} + +func releasePorts(ep *bridgeEndpoint) error { + return releasePortsInternal(ep.portMapping) +} + +func releasePortsInternal(bindings []netutils.PortBinding) error { + var errorBuf bytes.Buffer + + // Attempt to release all port bindings, do not stop on failure + for _, m := range bindings { + if err := releasePort(m); err != nil { + errorBuf.WriteString(fmt.Sprintf("\ncould not release %v because of %v", m, err)) + } + } + + if errorBuf.Len() != 0 { + return errors.New(errorBuf.String()) + } + return nil +} + +func releasePort(bnd netutils.PortBinding) error { + // Construct the host side transport address + host, err := bnd.HostAddr() + if err != nil { + return err + } + return portMapper.Unmap(host) +} diff --git a/vendor/src/github.com/docker/libnetwork/drivers/bridge/port_mapping_test.go b/vendor/src/github.com/docker/libnetwork/drivers/bridge/port_mapping_test.go new file mode 100644 index 0000000000..410827d1a3 --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/drivers/bridge/port_mapping_test.go @@ -0,0 +1,66 @@ +package bridge + +import ( + "os" + "testing" + + "github.com/docker/docker/pkg/reexec" + "github.com/docker/libnetwork/netlabel" + "github.com/docker/libnetwork/netutils" +) + +func TestMain(m *testing.M) { + if reexec.Init() { + return + } + os.Exit(m.Run()) +} + +func TestPortMappingConfig(t *testing.T) { + defer netutils.SetupTestNetNS(t)() + d := newDriver() + + binding1 := netutils.PortBinding{Proto: netutils.UDP, Port: uint16(400), HostPort: uint16(54000)} + binding2 := netutils.PortBinding{Proto: netutils.TCP, Port: uint16(500), HostPort: uint16(65000)} + portBindings := []netutils.PortBinding{binding1, binding2} + + epOptions := make(map[string]interface{}) + epOptions[netlabel.PortMap] = portBindings + + netConfig := &NetworkConfiguration{ + BridgeName: DefaultBridgeName, + EnableIPTables: true, + } + netOptions := make(map[string]interface{}) + netOptions[netlabel.GenericData] = netConfig + + err := d.CreateNetwork("dummy", netOptions) + if err != nil { + t.Fatalf("Failed to create bridge: %v", err) + } + + te := &testEndpoint{ifaces: []*testInterface{}} + err = d.CreateEndpoint("dummy", "ep1", te, epOptions) + if err != nil { + t.Fatalf("Failed to create the endpoint: %s", err.Error()) + } + + dd := d.(*driver) + ep, _ := dd.network.endpoints["ep1"] + if len(ep.portMapping) != 2 { + t.Fatalf("Failed to store the port bindings into the sandbox info. Found: %v", ep.portMapping) + } + if ep.portMapping[0].Proto != binding1.Proto || ep.portMapping[0].Port != binding1.Port || + ep.portMapping[1].Proto != binding2.Proto || ep.portMapping[1].Port != binding2.Port { + t.Fatalf("bridgeEndpoint has incorrect port mapping values") + } + if ep.portMapping[0].HostIP == nil || ep.portMapping[0].HostPort == 0 || + ep.portMapping[1].HostIP == nil || ep.portMapping[1].HostPort == 0 { + t.Fatalf("operational port mapping data not found on bridgeEndpoint") + } + + err = releasePorts(ep) + if err != nil { + t.Fatalf("Failed to release mapped ports: %v", err) + } +} diff --git a/vendor/src/github.com/docker/libnetwork/drivers/bridge/resolvconf.go b/vendor/src/github.com/docker/libnetwork/drivers/bridge/resolvconf.go new file mode 100644 index 0000000000..8861184220 --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/drivers/bridge/resolvconf.go @@ -0,0 +1,67 @@ +package bridge + +import ( + "bytes" + "io/ioutil" + "regexp" +) + +const ( + ipv4NumBlock = `(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)` + ipv4Address = `(` + ipv4NumBlock + `\.){3}` + ipv4NumBlock + + // This is not an IPv6 address verifier as it will accept a super-set of IPv6, and also + // will *not match* IPv4-Embedded IPv6 Addresses (RFC6052), but that and other variants + // -- 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})` +) + +var nsRegexp = regexp.MustCompile(`^\s*nameserver\s*((` + ipv4Address + `)|(` + ipv6Address + `))\s*$`) + +func readResolvConf() ([]byte, error) { + resolv, err := ioutil.ReadFile("/etc/resolv.conf") + if err != nil { + return nil, err + } + return resolv, nil +} + +// getLines parses input into lines and strips away comments. +func getLines(input []byte, commentMarker []byte) [][]byte { + lines := bytes.Split(input, []byte("\n")) + var output [][]byte + for _, currentLine := range lines { + var commentIndex = bytes.Index(currentLine, commentMarker) + if commentIndex == -1 { + output = append(output, currentLine) + } else { + output = append(output, currentLine[:commentIndex]) + } + } + return output +} + +// GetNameserversAsCIDR returns nameservers (if any) listed in +// /etc/resolv.conf as CIDR blocks (e.g., "1.2.3.4/32") +// This function's output is intended for net.ParseCIDR +func getNameserversAsCIDR(resolvConf []byte) []string { + nameservers := []string{} + for _, nameserver := range getNameservers(resolvConf) { + nameservers = append(nameservers, nameserver+"/32") + } + return nameservers +} + +// GetNameservers returns nameservers (if any) listed in /etc/resolv.conf +func getNameservers(resolvConf []byte) []string { + nameservers := []string{} + for _, line := range getLines(resolvConf, []byte("#")) { + var ns = nsRegexp.FindSubmatch(line) + if len(ns) > 0 { + nameservers = append(nameservers, string(ns[1])) + } + } + return nameservers +} diff --git a/vendor/src/github.com/docker/libnetwork/drivers/bridge/resolvconf_test.go b/vendor/src/github.com/docker/libnetwork/drivers/bridge/resolvconf_test.go new file mode 100644 index 0000000000..029f41c78c --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/drivers/bridge/resolvconf_test.go @@ -0,0 +1,53 @@ +package bridge + +import ( + "bytes" + "testing" +) + +func TestResolveConfRead(t *testing.T) { + b, err := readResolvConf() + if err != nil { + t.Fatalf("Failed to read resolv.conf: %v", err) + } + + if b == nil { + t.Fatal("Reading resolv.conf returned no content") + } +} + +func TestResolveConfReadLines(t *testing.T) { + commentChar := []byte("#") + + b, _ := readResolvConf() + lines := getLines(b, commentChar) + if lines == nil { + t.Fatal("Failed to read resolv.conf lines") + } + + for _, line := range lines { + if bytes.Index(line, commentChar) != -1 { + t.Fatal("Returned comment content from resolv.conf") + } + } +} + +func TestResolvConfNameserversAsCIDR(t *testing.T) { + resolvConf := `# Commented line +nameserver 1.2.3.4 + +nameserver 5.6.7.8 # Test +` + + cidrs := getNameserversAsCIDR([]byte(resolvConf)) + if expected := 2; len(cidrs) != expected { + t.Fatalf("Expected %d nameservers, got %d", expected, len(cidrs)) + } + + expected := []string{"1.2.3.4/32", "5.6.7.8/32"} + for i, exp := range expected { + if cidrs[i] != exp { + t.Fatalf("Expected nameservers %s, got %s", exp, cidrs[i]) + } + } +} diff --git a/vendor/src/github.com/docker/libnetwork/drivers/bridge/setup.go b/vendor/src/github.com/docker/libnetwork/drivers/bridge/setup.go new file mode 100644 index 0000000000..f2d0344839 --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/drivers/bridge/setup.go @@ -0,0 +1,26 @@ +package bridge + +type setupStep func(*NetworkConfiguration, *bridgeInterface) error + +type bridgeSetup struct { + config *NetworkConfiguration + bridge *bridgeInterface + steps []setupStep +} + +func newBridgeSetup(c *NetworkConfiguration, i *bridgeInterface) *bridgeSetup { + return &bridgeSetup{config: c, bridge: i} +} + +func (b *bridgeSetup) apply() error { + for _, fn := range b.steps { + if err := fn(b.config, b.bridge); err != nil { + return err + } + } + return nil +} + +func (b *bridgeSetup) queueStep(step setupStep) { + b.steps = append(b.steps, step) +} diff --git a/vendor/src/github.com/docker/libnetwork/drivers/bridge/setup_device.go b/vendor/src/github.com/docker/libnetwork/drivers/bridge/setup_device.go new file mode 100644 index 0000000000..1e0e168af4 --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/drivers/bridge/setup_device.go @@ -0,0 +1,50 @@ +package bridge + +import ( + log "github.com/Sirupsen/logrus" + "github.com/docker/docker/pkg/parsers/kernel" + "github.com/docker/libnetwork/netutils" + "github.com/vishvananda/netlink" +) + +// SetupDevice create a new bridge interface/ +func setupDevice(config *NetworkConfiguration, i *bridgeInterface) error { + // We only attempt to create the bridge when the requested device name is + // the default one. + if config.BridgeName != DefaultBridgeName && !config.AllowNonDefaultBridge { + return NonDefaultBridgeExistError(config.BridgeName) + } + + // Set the bridgeInterface netlink.Bridge. + i.Link = &netlink.Bridge{ + LinkAttrs: netlink.LinkAttrs{ + Name: config.BridgeName, + }, + } + + // Only set the bridge's MAC address if the kernel version is > 3.3, as it + // was not supported before that. + kv, err := kernel.GetKernelVersion() + if err == nil && (kv.Kernel >= 3 && kv.Major >= 3) { + i.Link.Attrs().HardwareAddr = netutils.GenerateRandomMAC() + log.Debugf("Setting bridge mac address to %s", i.Link.Attrs().HardwareAddr) + } + + // Call out to netlink to create the device. + return netlink.LinkAdd(i.Link) +} + +// SetupDeviceUp ups the given bridge interface. +func setupDeviceUp(config *NetworkConfiguration, i *bridgeInterface) error { + err := netlink.LinkSetUp(i.Link) + if err != nil { + return err + } + + // Attempt to update the bridge interface to refresh the flags status, + // ignoring any failure to do so. + if lnk, err := netlink.LinkByName(config.BridgeName); err == nil { + i.Link = lnk + } + return nil +} diff --git a/vendor/src/github.com/docker/libnetwork/drivers/bridge/setup_device_test.go b/vendor/src/github.com/docker/libnetwork/drivers/bridge/setup_device_test.go new file mode 100644 index 0000000000..499f46a6ce --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/drivers/bridge/setup_device_test.go @@ -0,0 +1,75 @@ +package bridge + +import ( + "bytes" + "net" + "testing" + + "github.com/docker/libnetwork/netutils" + "github.com/vishvananda/netlink" +) + +func TestSetupNewBridge(t *testing.T) { + defer netutils.SetupTestNetNS(t)() + + config := &NetworkConfiguration{BridgeName: DefaultBridgeName} + br := &bridgeInterface{} + + if err := setupDevice(config, br); err != nil { + t.Fatalf("Bridge creation failed: %v", err) + } + if br.Link == nil { + t.Fatal("bridgeInterface link is nil (expected valid link)") + } + if _, err := netlink.LinkByName(DefaultBridgeName); err != nil { + t.Fatalf("Failed to retrieve bridge device: %v", err) + } + if br.Link.Attrs().Flags&net.FlagUp == net.FlagUp { + t.Fatalf("bridgeInterface should be created down") + } +} + +func TestSetupNewNonDefaultBridge(t *testing.T) { + defer netutils.SetupTestNetNS(t)() + + config := &NetworkConfiguration{BridgeName: "test0"} + br := &bridgeInterface{} + + err := setupDevice(config, br) + if err == nil { + t.Fatal("Expected bridge creation failure with \"non default name\", succeeded") + } + + if _, ok := err.(NonDefaultBridgeExistError); !ok { + t.Fatalf("Did not fail with expected error. Actual error: %v", err) + } +} + +func TestSetupDeviceUp(t *testing.T) { + defer netutils.SetupTestNetNS(t)() + + config := &NetworkConfiguration{BridgeName: DefaultBridgeName} + br := &bridgeInterface{} + + if err := setupDevice(config, br); err != nil { + t.Fatalf("Bridge creation failed: %v", err) + } + if err := setupDeviceUp(config, br); err != nil { + t.Fatalf("Failed to up bridge device: %v", err) + } + + lnk, _ := netlink.LinkByName(DefaultBridgeName) + if lnk.Attrs().Flags&net.FlagUp != net.FlagUp { + t.Fatalf("bridgeInterface should be up") + } +} + +func TestGenerateRandomMAC(t *testing.T) { + defer netutils.SetupTestNetNS(t)() + + mac1 := netutils.GenerateRandomMAC() + mac2 := netutils.GenerateRandomMAC() + if bytes.Compare(mac1, mac2) == 0 { + t.Fatalf("Generated twice the same MAC address %v", mac1) + } +} diff --git a/vendor/src/github.com/docker/libnetwork/drivers/bridge/setup_fixedcidrv4.go b/vendor/src/github.com/docker/libnetwork/drivers/bridge/setup_fixedcidrv4.go new file mode 100644 index 0000000000..af6aecc7aa --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/drivers/bridge/setup_fixedcidrv4.go @@ -0,0 +1,17 @@ +package bridge + +import log "github.com/Sirupsen/logrus" + +func setupFixedCIDRv4(config *NetworkConfiguration, i *bridgeInterface) error { + addrv4, _, err := i.addresses() + if err != nil { + return err + } + + log.Debugf("Using IPv4 subnet: %v", config.FixedCIDR) + if err := ipAllocator.RegisterSubnet(addrv4.IPNet, config.FixedCIDR); err != nil { + return &FixedCIDRv4Error{subnet: config.FixedCIDR, net: addrv4.IPNet, err: err} + } + + return nil +} diff --git a/vendor/src/github.com/docker/libnetwork/drivers/bridge/setup_fixedcidrv4_test.go b/vendor/src/github.com/docker/libnetwork/drivers/bridge/setup_fixedcidrv4_test.go new file mode 100644 index 0000000000..5bb57d0c68 --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/drivers/bridge/setup_fixedcidrv4_test.go @@ -0,0 +1,62 @@ +package bridge + +import ( + "net" + "testing" + + "github.com/docker/libnetwork/netutils" +) + +func TestSetupFixedCIDRv4(t *testing.T) { + defer netutils.SetupTestNetNS(t)() + + config := &NetworkConfiguration{ + BridgeName: DefaultBridgeName, + AddressIPv4: &net.IPNet{IP: net.ParseIP("192.168.1.1"), Mask: net.CIDRMask(16, 32)}, + FixedCIDR: &net.IPNet{IP: net.ParseIP("192.168.2.0"), Mask: net.CIDRMask(24, 32)}} + br := &bridgeInterface{} + + if err := setupDevice(config, br); err != nil { + t.Fatalf("Bridge creation failed: %v", err) + } + if err := setupBridgeIPv4(config, br); err != nil { + t.Fatalf("Assign IPv4 to bridge failed: %v", err) + } + + if err := setupFixedCIDRv4(config, br); err != nil { + t.Fatalf("Failed to setup bridge FixedCIDRv4: %v", err) + } + + if ip, err := ipAllocator.RequestIP(config.FixedCIDR, nil); err != nil { + t.Fatalf("Failed to request IP to allocator: %v", err) + } else if expected := "192.168.2.1"; ip.String() != expected { + t.Fatalf("Expected allocated IP %s, got %s", expected, ip) + } +} + +func TestSetupBadFixedCIDRv4(t *testing.T) { + defer netutils.SetupTestNetNS(t)() + + config := &NetworkConfiguration{ + BridgeName: DefaultBridgeName, + AddressIPv4: &net.IPNet{IP: net.ParseIP("192.168.1.1"), Mask: net.CIDRMask(24, 32)}, + FixedCIDR: &net.IPNet{IP: net.ParseIP("192.168.2.0"), Mask: net.CIDRMask(24, 32)}} + br := &bridgeInterface{} + + if err := setupDevice(config, br); err != nil { + t.Fatalf("Bridge creation failed: %v", err) + } + if err := setupBridgeIPv4(config, br); err != nil { + t.Fatalf("Assign IPv4 to bridge failed: %v", err) + } + + err := setupFixedCIDRv4(config, br) + if err == nil { + t.Fatal("Setup bridge FixedCIDRv4 should have failed") + } + + if _, ok := err.(*FixedCIDRv4Error); !ok { + t.Fatalf("Did not fail with expected error. Actual error: %v", err) + } + +} diff --git a/vendor/src/github.com/docker/libnetwork/drivers/bridge/setup_fixedcidrv6.go b/vendor/src/github.com/docker/libnetwork/drivers/bridge/setup_fixedcidrv6.go new file mode 100644 index 0000000000..ce1718a30e --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/drivers/bridge/setup_fixedcidrv6.go @@ -0,0 +1,12 @@ +package bridge + +import log "github.com/Sirupsen/logrus" + +func setupFixedCIDRv6(config *NetworkConfiguration, i *bridgeInterface) error { + log.Debugf("Using IPv6 subnet: %v", config.FixedCIDRv6) + if err := ipAllocator.RegisterSubnet(config.FixedCIDRv6, config.FixedCIDRv6); err != nil { + return &FixedCIDRv6Error{net: config.FixedCIDRv6, err: err} + } + + return nil +} diff --git a/vendor/src/github.com/docker/libnetwork/drivers/bridge/setup_fixedcidrv6_test.go b/vendor/src/github.com/docker/libnetwork/drivers/bridge/setup_fixedcidrv6_test.go new file mode 100644 index 0000000000..a5a2c291b5 --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/drivers/bridge/setup_fixedcidrv6_test.go @@ -0,0 +1,37 @@ +package bridge + +import ( + "net" + "testing" + + "github.com/docker/libnetwork/netutils" +) + +func TestSetupFixedCIDRv6(t *testing.T) { + defer netutils.SetupTestNetNS(t)() + + config := &NetworkConfiguration{} + br := newInterface(config) + + _, config.FixedCIDRv6, _ = net.ParseCIDR("2002:db8::/48") + if err := setupDevice(config, br); err != nil { + t.Fatalf("Bridge creation failed: %v", err) + } + if err := setupBridgeIPv4(config, br); err != nil { + t.Fatalf("Assign IPv4 to bridge failed: %v", err) + } + + if err := setupBridgeIPv6(config, br); err != nil { + t.Fatalf("Assign IPv4 to bridge failed: %v", err) + } + + if err := setupFixedCIDRv6(config, br); err != nil { + t.Fatalf("Failed to setup bridge FixedCIDRv6: %v", err) + } + + if ip, err := ipAllocator.RequestIP(config.FixedCIDRv6, nil); err != nil { + t.Fatalf("Failed to request IP to allocator: %v", err) + } else if expected := "2002:db8::1"; ip.String() != expected { + t.Fatalf("Expected allocated IP %s, got %s", expected, ip) + } +} diff --git a/vendor/src/github.com/docker/libnetwork/drivers/bridge/setup_ip_forwarding.go b/vendor/src/github.com/docker/libnetwork/drivers/bridge/setup_ip_forwarding.go new file mode 100644 index 0000000000..85fddbca16 --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/drivers/bridge/setup_ip_forwarding.go @@ -0,0 +1,25 @@ +package bridge + +import ( + "fmt" + "io/ioutil" +) + +const ( + ipv4ForwardConf = "/proc/sys/net/ipv4/ip_forward" + ipv4ForwardConfPerm = 0644 +) + +func setupIPForwarding(config *Configuration) error { + // Sanity Check + if config.EnableIPForwarding == false { + return ErrIPFwdCfg + } + + // Enable IPv4 forwarding + if err := ioutil.WriteFile(ipv4ForwardConf, []byte{'1', '\n'}, ipv4ForwardConfPerm); err != nil { + return fmt.Errorf("Setup IP forwarding failed: %v", err) + } + + return nil +} diff --git a/vendor/src/github.com/docker/libnetwork/drivers/bridge/setup_ip_forwarding_test.go b/vendor/src/github.com/docker/libnetwork/drivers/bridge/setup_ip_forwarding_test.go new file mode 100644 index 0000000000..a555a39323 --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/drivers/bridge/setup_ip_forwarding_test.go @@ -0,0 +1,75 @@ +package bridge + +import ( + "bytes" + "io/ioutil" + "testing" +) + +func TestSetupIPForwarding(t *testing.T) { + // Read current setting and ensure the original value gets restored + procSetting := readCurrentIPForwardingSetting(t) + defer reconcileIPForwardingSetting(t, procSetting) + + // Disable IP Forwarding if enabled + if bytes.Compare(procSetting, []byte("1\n")) == 0 { + writeIPForwardingSetting(t, []byte{'0', '\n'}) + } + + // Create test interface with ip forwarding setting enabled + config := &Configuration{ + EnableIPForwarding: true} + + // Set IP Forwarding + if err := setupIPForwarding(config); err != nil { + t.Fatalf("Failed to setup IP forwarding: %v", err) + } + + // Read new setting + procSetting = readCurrentIPForwardingSetting(t) + if bytes.Compare(procSetting, []byte("1\n")) != 0 { + t.Fatalf("Failed to effectively setup IP forwarding") + } +} + +func TestUnexpectedSetupIPForwarding(t *testing.T) { + // Read current setting and ensure the original value gets restored + procSetting := readCurrentIPForwardingSetting(t) + defer reconcileIPForwardingSetting(t, procSetting) + + // Create test interface without ip forwarding setting enabled + config := &Configuration{ + EnableIPForwarding: false} + + // Attempt Set IP Forwarding + err := setupIPForwarding(config) + if err == nil { + t.Fatal("Setup IP forwarding was expected to fail") + } + + if err != ErrIPFwdCfg { + t.Fatalf("Setup IP forwarding failed with unexpected error: %v", err) + } +} + +func readCurrentIPForwardingSetting(t *testing.T) []byte { + procSetting, err := ioutil.ReadFile(ipv4ForwardConf) + if err != nil { + t.Fatalf("Can't execute test: Failed to read current IP forwarding setting: %v", err) + } + return procSetting +} + +func writeIPForwardingSetting(t *testing.T, chars []byte) { + err := ioutil.WriteFile(ipv4ForwardConf, chars, ipv4ForwardConfPerm) + if err != nil { + t.Fatalf("Can't execute or cleanup after test: Failed to reset IP forwarding: %v", err) + } +} + +func reconcileIPForwardingSetting(t *testing.T, original []byte) { + current := readCurrentIPForwardingSetting(t) + if bytes.Compare(original, current) != 0 { + writeIPForwardingSetting(t, original) + } +} diff --git a/vendor/src/github.com/docker/libnetwork/drivers/bridge/setup_ip_tables.go b/vendor/src/github.com/docker/libnetwork/drivers/bridge/setup_ip_tables.go new file mode 100644 index 0000000000..e74ded7421 --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/drivers/bridge/setup_ip_tables.go @@ -0,0 +1,173 @@ +package bridge + +import ( + "fmt" + "net" + + "github.com/docker/libnetwork/iptables" + "github.com/docker/libnetwork/netutils" +) + +// DockerChain: DOCKER iptable chain name +const ( + DockerChain = "DOCKER" +) + +func setupIPTables(config *NetworkConfiguration, i *bridgeInterface) error { + // Sanity check. + if config.EnableIPTables == false { + return ipTableCfgError(config.BridgeName) + } + + hairpinMode := !config.EnableUserlandProxy + + addrv4, _, err := netutils.GetIfaceAddr(config.BridgeName) + if err != nil { + return fmt.Errorf("Failed to setup IP tables, cannot acquire Interface address: %s", err.Error()) + } + if err = setupIPTablesInternal(config.BridgeName, addrv4, config.EnableICC, config.EnableIPMasquerade, hairpinMode, true); err != nil { + return fmt.Errorf("Failed to Setup IP tables: %s", err.Error()) + } + + _, err = iptables.NewChain(DockerChain, config.BridgeName, iptables.Nat, hairpinMode) + if err != nil { + return fmt.Errorf("Failed to create NAT chain: %s", err.Error()) + } + + chain, err := iptables.NewChain(DockerChain, config.BridgeName, iptables.Filter, hairpinMode) + if err != nil { + return fmt.Errorf("Failed to create FILTER chain: %s", err.Error()) + } + + portMapper.SetIptablesChain(chain) + + return nil +} + +type iptRule struct { + table iptables.Table + chain string + preArgs []string + args []string +} + +func setupIPTablesInternal(bridgeIface string, addr net.Addr, icc, ipmasq, hairpin, enable bool) error { + + var ( + address = addr.String() + natRule = iptRule{table: iptables.Nat, chain: "POSTROUTING", preArgs: []string{"-t", "nat"}, args: []string{"-s", address, "!", "-o", bridgeIface, "-j", "MASQUERADE"}} + hpNatRule = iptRule{table: iptables.Nat, chain: "POSTROUTING", preArgs: []string{"-t", "nat"}, args: []string{"-m", "addrtype", "--src-type", "LOCAL", "-o", bridgeIface, "-j", "MASQUERADE"}} + outRule = iptRule{table: iptables.Filter, chain: "FORWARD", args: []string{"-i", bridgeIface, "!", "-o", bridgeIface, "-j", "ACCEPT"}} + inRule = iptRule{table: iptables.Filter, chain: "FORWARD", args: []string{"-o", bridgeIface, "-m", "conntrack", "--ctstate", "RELATED,ESTABLISHED", "-j", "ACCEPT"}} + ) + + // Set NAT. + if ipmasq { + if err := programChainRule(natRule, "NAT", enable); err != nil { + return err + } + } + + // In hairpin mode, masquerade traffic from localhost + if hairpin { + if err := programChainRule(hpNatRule, "MASQ LOCAL HOST", enable); err != nil { + return err + } + } + + // Set Inter Container Communication. + if err := setIcc(bridgeIface, icc, enable); err != nil { + return err + } + + // Set Accept on all non-intercontainer outgoing packets. + if err := programChainRule(outRule, "ACCEPT NON_ICC OUTGOING", enable); err != nil { + return err + } + + // Set Accept on incoming packets for existing connections. + if err := programChainRule(inRule, "ACCEPT INCOMING", enable); err != nil { + return err + } + + return nil +} + +func programChainRule(rule iptRule, ruleDescr string, insert bool) error { + var ( + prefix []string + operation string + condition bool + doesExist = iptables.Exists(rule.table, rule.chain, rule.args...) + ) + + if insert { + condition = !doesExist + prefix = []string{"-I", rule.chain} + operation = "enable" + } else { + condition = doesExist + prefix = []string{"-D", rule.chain} + operation = "disable" + } + if rule.preArgs != nil { + prefix = append(rule.preArgs, prefix...) + } + + if condition { + if output, err := iptables.Raw(append(prefix, rule.args...)...); err != nil { + return fmt.Errorf("Unable to %s %s rule: %s", operation, ruleDescr, err.Error()) + } else if len(output) != 0 { + return &iptables.ChainError{Chain: rule.chain, Output: output} + } + } + + return nil +} + +func setIcc(bridgeIface string, iccEnable, insert bool) error { + var ( + table = iptables.Filter + chain = "FORWARD" + args = []string{"-i", bridgeIface, "-o", bridgeIface, "-j"} + acceptArgs = append(args, "ACCEPT") + dropArgs = append(args, "DROP") + ) + + if insert { + if !iccEnable { + iptables.Raw(append([]string{"-D", chain}, acceptArgs...)...) + + if !iptables.Exists(table, chain, dropArgs...) { + if output, err := iptables.Raw(append([]string{"-A", chain}, dropArgs...)...); err != nil { + return fmt.Errorf("Unable to prevent intercontainer communication: %s", err.Error()) + } else if len(output) != 0 { + return fmt.Errorf("Error disabling intercontainer communication: %s", output) + } + } + } else { + iptables.Raw(append([]string{"-D", chain}, dropArgs...)...) + + if !iptables.Exists(table, chain, acceptArgs...) { + if output, err := iptables.Raw(append([]string{"-A", chain}, acceptArgs...)...); err != nil { + return fmt.Errorf("Unable to allow intercontainer communication: %s", err.Error()) + } else if len(output) != 0 { + return fmt.Errorf("Error enabling intercontainer communication: %s", output) + } + } + } + } else { + // Remove any ICC rule. + if !iccEnable { + if iptables.Exists(table, chain, dropArgs...) { + iptables.Raw(append([]string{"-D", chain}, dropArgs...)...) + } + } else { + if iptables.Exists(table, chain, acceptArgs...) { + iptables.Raw(append([]string{"-D", chain}, acceptArgs...)...) + } + } + } + + return nil +} diff --git a/vendor/src/github.com/docker/libnetwork/drivers/bridge/setup_ip_tables_test.go b/vendor/src/github.com/docker/libnetwork/drivers/bridge/setup_ip_tables_test.go new file mode 100644 index 0000000000..1c73ba9d7b --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/drivers/bridge/setup_ip_tables_test.go @@ -0,0 +1,103 @@ +package bridge + +import ( + "net" + "testing" + + "github.com/docker/libnetwork/iptables" + "github.com/docker/libnetwork/netutils" +) + +const ( + iptablesTestBridgeIP = "192.168.42.1" +) + +func TestProgramIPTable(t *testing.T) { + // Create a test bridge with a basic bridge configuration (name + IPv4). + defer netutils.SetupTestNetNS(t)() + createTestBridge(getBasicTestConfig(), &bridgeInterface{}, t) + + // Store various iptables chain rules we care for. + rules := []struct { + rule iptRule + descr string + }{ + {iptRule{table: iptables.Filter, chain: "FORWARD", args: []string{"-d", "127.1.2.3", "-i", "lo", "-o", "lo", "-j", "DROP"}}, "Test Loopback"}, + {iptRule{table: iptables.Nat, chain: "POSTROUTING", preArgs: []string{"-t", "nat"}, args: []string{"-s", iptablesTestBridgeIP, "!", "-o", DefaultBridgeName, "-j", "MASQUERADE"}}, "NAT Test"}, + {iptRule{table: iptables.Filter, chain: "FORWARD", args: []string{"-i", DefaultBridgeName, "!", "-o", DefaultBridgeName, "-j", "ACCEPT"}}, "Test ACCEPT NON_ICC OUTGOING"}, + {iptRule{table: iptables.Filter, chain: "FORWARD", args: []string{"-o", DefaultBridgeName, "-m", "conntrack", "--ctstate", "RELATED,ESTABLISHED", "-j", "ACCEPT"}}, "Test ACCEPT INCOMING"}, + {iptRule{table: iptables.Filter, chain: "FORWARD", args: []string{"-i", DefaultBridgeName, "-o", DefaultBridgeName, "-j", "ACCEPT"}}, "Test enable ICC"}, + {iptRule{table: iptables.Filter, chain: "FORWARD", args: []string{"-i", DefaultBridgeName, "-o", DefaultBridgeName, "-j", "DROP"}}, "Test disable ICC"}, + } + + // Assert the chain rules' insertion and removal. + for _, c := range rules { + assertIPTableChainProgramming(c.rule, c.descr, t) + } +} + +func TestSetupIPTables(t *testing.T) { + // Create a test bridge with a basic bridge configuration (name + IPv4). + defer netutils.SetupTestNetNS(t)() + config := getBasicTestConfig() + br := &bridgeInterface{} + + createTestBridge(config, br, t) + + // Modify iptables params in base configuration and apply them. + config.EnableIPTables = true + assertBridgeConfig(config, br, t) + + config.EnableIPMasquerade = true + assertBridgeConfig(config, br, t) + + config.EnableICC = true + assertBridgeConfig(config, br, t) + + config.EnableIPMasquerade = false + assertBridgeConfig(config, br, t) +} + +func getBasicTestConfig() *NetworkConfiguration { + config := &NetworkConfiguration{ + BridgeName: DefaultBridgeName, + AddressIPv4: &net.IPNet{IP: net.ParseIP(iptablesTestBridgeIP), Mask: net.CIDRMask(16, 32)}} + return config +} + +func createTestBridge(config *NetworkConfiguration, br *bridgeInterface, t *testing.T) { + if err := setupDevice(config, br); err != nil { + t.Fatalf("Failed to create the testing Bridge: %s", err.Error()) + } + if err := setupBridgeIPv4(config, br); err != nil { + t.Fatalf("Failed to bring up the testing Bridge: %s", err.Error()) + } +} + +// Assert base function which pushes iptables chain rules on insertion and removal. +func assertIPTableChainProgramming(rule iptRule, descr string, t *testing.T) { + // Add + if err := programChainRule(rule, descr, true); err != nil { + t.Fatalf("Failed to program iptable rule %s: %s", descr, err.Error()) + } + if iptables.Exists(rule.table, rule.chain, rule.args...) == false { + t.Fatalf("Failed to effectively program iptable rule: %s", descr) + } + + // Remove + if err := programChainRule(rule, descr, false); err != nil { + t.Fatalf("Failed to remove iptable rule %s: %s", descr, err.Error()) + } + if iptables.Exists(rule.table, rule.chain, rule.args...) == true { + t.Fatalf("Failed to effectively remove iptable rule: %s", descr) + } +} + +// Assert function which pushes chains based on bridge config parameters. +func assertBridgeConfig(config *NetworkConfiguration, br *bridgeInterface, t *testing.T) { + // Attempt programming of ip tables. + err := setupIPTables(config, br) + if err != nil { + t.Fatalf("%v", err) + } +} diff --git a/vendor/src/github.com/docker/libnetwork/drivers/bridge/setup_ipv4.go b/vendor/src/github.com/docker/libnetwork/drivers/bridge/setup_ipv4.go new file mode 100644 index 0000000000..7a775a376d --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/drivers/bridge/setup_ipv4.go @@ -0,0 +1,136 @@ +package bridge + +import ( + "fmt" + "io/ioutil" + "net" + + "path/filepath" + + log "github.com/Sirupsen/logrus" + "github.com/docker/libnetwork/netutils" + "github.com/vishvananda/netlink" +) + +var bridgeNetworks []*net.IPNet + +func init() { + // Here we don't follow the convention of using the 1st IP of the range for the gateway. + // This is to use the same gateway IPs as the /24 ranges, which predate the /16 ranges. + // In theory this shouldn't matter - in practice there's bound to be a few scripts relying + // on the internal addressing or other stupid things like that. + // They shouldn't, but hey, let's not break them unless we really have to. + for _, addr := range []string{ + "172.17.42.1/16", // Don't use 172.16.0.0/16, it conflicts with EC2 DNS 172.16.0.23 + "10.0.42.1/16", // Don't even try using the entire /8, that's too intrusive + "10.1.42.1/16", + "10.42.42.1/16", + "172.16.42.1/24", + "172.16.43.1/24", + "172.16.44.1/24", + "10.0.42.1/24", + "10.0.43.1/24", + "192.168.42.1/24", + "192.168.43.1/24", + "192.168.44.1/24", + } { + ip, net, err := net.ParseCIDR(addr) + if err != nil { + log.Errorf("Failed to parse address %s", addr) + continue + } + net.IP = ip.To4() + bridgeNetworks = append(bridgeNetworks, net) + } +} + +func setupBridgeIPv4(config *NetworkConfiguration, i *bridgeInterface) error { + addrv4, _, err := i.addresses() + if err != nil { + return err + } + + // Check if we have an IP address already on the bridge. + if addrv4.IPNet != nil { + // Make sure to store bridge network and default gateway before getting out. + i.bridgeIPv4 = addrv4.IPNet + i.gatewayIPv4 = addrv4.IPNet.IP + return nil + } + + // Do not try to configure IPv4 on a non-default bridge unless you are + // specifically asked to do so. + if config.BridgeName != DefaultBridgeName && !config.AllowNonDefaultBridge { + return NonDefaultBridgeExistError(config.BridgeName) + } + + bridgeIPv4, err := electBridgeIPv4(config) + if err != nil { + return err + } + + log.Debugf("Creating bridge interface %q with network %s", config.BridgeName, bridgeIPv4) + if err := netlink.AddrAdd(i.Link, &netlink.Addr{IPNet: bridgeIPv4}); err != nil { + return &IPv4AddrAddError{ip: bridgeIPv4, err: err} + } + + // Store bridge network and default gateway + i.bridgeIPv4 = bridgeIPv4 + i.gatewayIPv4 = i.bridgeIPv4.IP + + return nil +} + +func allocateBridgeIP(config *NetworkConfiguration, i *bridgeInterface) error { + ipAllocator.RequestIP(i.bridgeIPv4, i.bridgeIPv4.IP) + return nil +} + +func electBridgeIPv4(config *NetworkConfiguration) (*net.IPNet, error) { + // Use the requested IPv4 CIDR when available. + if config.AddressIPv4 != nil { + return config.AddressIPv4, nil + } + + // 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. + nameservers := []string{} + if resolvConf, _ := readResolvConf(); resolvConf != nil { + nameservers = append(nameservers, getNameserversAsCIDR(resolvConf)...) + } + + // Try to automatically elect appropriate bridge IPv4 settings. + for _, n := range bridgeNetworks { + if err := netutils.CheckNameserverOverlaps(nameservers, n); err == nil { + if err := netutils.CheckRouteOverlaps(n); err == nil { + return n, nil + } + } + } + + return nil, IPv4AddrRangeError(config.BridgeName) +} + +func setupGatewayIPv4(config *NetworkConfiguration, i *bridgeInterface) error { + if !i.bridgeIPv4.Contains(config.DefaultGatewayIPv4) { + return ErrInvalidGateway + } + if _, err := ipAllocator.RequestIP(i.bridgeIPv4, config.DefaultGatewayIPv4); err != nil { + return err + } + + // Store requested default gateway + i.gatewayIPv4 = config.DefaultGatewayIPv4 + + return nil +} + +func setupLoopbackAdressesRouting(config *NetworkConfiguration, i *bridgeInterface) error { + // Enable loopback adresses routing + sysPath := filepath.Join("/proc/sys/net/ipv4/conf", config.BridgeName, "route_localnet") + if err := ioutil.WriteFile(sysPath, []byte{'1', '\n'}, 0644); err != nil { + return fmt.Errorf("Unable to enable local routing for hairpin mode: %v", err) + } + return nil +} diff --git a/vendor/src/github.com/docker/libnetwork/drivers/bridge/setup_ipv4_test.go b/vendor/src/github.com/docker/libnetwork/drivers/bridge/setup_ipv4_test.go new file mode 100644 index 0000000000..e311d641dc --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/drivers/bridge/setup_ipv4_test.go @@ -0,0 +1,100 @@ +package bridge + +import ( + "net" + "testing" + + "github.com/docker/libnetwork/netutils" + "github.com/vishvananda/netlink" +) + +func setupTestInterface(t *testing.T) (*NetworkConfiguration, *bridgeInterface) { + config := &NetworkConfiguration{ + BridgeName: DefaultBridgeName} + br := &bridgeInterface{} + + if err := setupDevice(config, br); err != nil { + t.Fatalf("Bridge creation failed: %v", err) + } + return config, br +} + +func TestSetupBridgeIPv4Fixed(t *testing.T) { + defer netutils.SetupTestNetNS(t)() + + ip, netw, err := net.ParseCIDR("192.168.1.1/24") + if err != nil { + t.Fatalf("Failed to parse bridge IPv4: %v", err) + } + + config, br := setupTestInterface(t) + config.AddressIPv4 = &net.IPNet{IP: ip, Mask: netw.Mask} + if err := setupBridgeIPv4(config, br); err != nil { + t.Fatalf("Failed to setup bridge IPv4: %v", err) + } + + addrsv4, err := netlink.AddrList(br.Link, netlink.FAMILY_V4) + if err != nil { + t.Fatalf("Failed to list device IPv4 addresses: %v", err) + } + + var found bool + for _, addr := range addrsv4 { + if config.AddressIPv4.String() == addr.IPNet.String() { + found = true + break + } + } + + if !found { + t.Fatalf("Bridge device does not have requested IPv4 address %v", config.AddressIPv4) + } +} + +func TestSetupBridgeIPv4Auto(t *testing.T) { + defer netutils.SetupTestNetNS(t)() + + config, br := setupTestInterface(t) + if err := setupBridgeIPv4(config, br); err != nil { + t.Fatalf("Failed to setup bridge IPv4: %v", err) + } + + addrsv4, err := netlink.AddrList(br.Link, netlink.FAMILY_V4) + if err != nil { + t.Fatalf("Failed to list device IPv4 addresses: %v", err) + } + + var found bool + for _, addr := range addrsv4 { + if bridgeNetworks[0].String() == addr.IPNet.String() { + found = true + break + } + } + + if !found { + t.Fatalf("Bridge device does not have the automatic IPv4 address %v", bridgeNetworks[0].String()) + } +} + +func TestSetupGatewayIPv4(t *testing.T) { + defer netutils.SetupTestNetNS(t)() + + ip, nw, _ := net.ParseCIDR("192.168.0.24/16") + nw.IP = ip + gw := net.ParseIP("192.168.0.254") + + config := &NetworkConfiguration{ + BridgeName: DefaultBridgeName, + DefaultGatewayIPv4: gw} + + br := &bridgeInterface{bridgeIPv4: nw} + + if err := setupGatewayIPv4(config, br); err != nil { + t.Fatalf("Set Default Gateway failed: %v", err) + } + + if !gw.Equal(br.gatewayIPv4) { + t.Fatalf("Set Default Gateway failed. Expected %v, Found %v", gw, br.gatewayIPv4) + } +} diff --git a/vendor/src/github.com/docker/libnetwork/drivers/bridge/setup_ipv6.go b/vendor/src/github.com/docker/libnetwork/drivers/bridge/setup_ipv6.go new file mode 100644 index 0000000000..81cbda1b88 --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/drivers/bridge/setup_ipv6.go @@ -0,0 +1,66 @@ +package bridge + +import ( + "fmt" + "io/ioutil" + "net" + + "github.com/vishvananda/netlink" +) + +var bridgeIPv6 *net.IPNet + +const bridgeIPv6Str = "fe80::1/64" + +func init() { + // We allow ourselves to panic in this special case because we indicate a + // failure to parse a compile-time define constant. + if ip, netw, err := net.ParseCIDR(bridgeIPv6Str); err == nil { + bridgeIPv6 = &net.IPNet{IP: ip, Mask: netw.Mask} + } else { + panic(fmt.Sprintf("Cannot parse default bridge IPv6 address %q: %v", bridgeIPv6Str, err)) + } +} + +func setupBridgeIPv6(config *NetworkConfiguration, i *bridgeInterface) error { + // Enable IPv6 on the bridge + procFile := "/proc/sys/net/ipv6/conf/" + config.BridgeName + "/disable_ipv6" + if err := ioutil.WriteFile(procFile, []byte{'0', '\n'}, 0644); err != nil { + return fmt.Errorf("Unable to enable IPv6 addresses on bridge: %v", err) + } + + _, addrsv6, err := i.addresses() + if err != nil { + return err + } + + // Add the default link local ipv6 address if it doesn't exist + if !findIPv6Address(netlink.Addr{IPNet: bridgeIPv6}, addrsv6) { + if err := netlink.AddrAdd(i.Link, &netlink.Addr{IPNet: bridgeIPv6}); err != nil { + return &IPv6AddrAddError{ip: bridgeIPv6, err: err} + } + } + + // Store bridge network and default gateway + i.bridgeIPv6 = bridgeIPv6 + i.gatewayIPv6 = i.bridgeIPv6.IP + + return nil +} + +func setupGatewayIPv6(config *NetworkConfiguration, i *bridgeInterface) error { + if config.FixedCIDRv6 == nil { + return ErrInvalidContainerSubnet + } + if !config.FixedCIDRv6.Contains(config.DefaultGatewayIPv6) { + return ErrInvalidGateway + } + if _, err := ipAllocator.RequestIP(config.FixedCIDRv6, config.DefaultGatewayIPv6); err != nil { + return err + } + + // Store requested default gateway + i.gatewayIPv6 = config.DefaultGatewayIPv6 + + return nil +} diff --git a/vendor/src/github.com/docker/libnetwork/drivers/bridge/setup_ipv6_test.go b/vendor/src/github.com/docker/libnetwork/drivers/bridge/setup_ipv6_test.go new file mode 100644 index 0000000000..cb8c17fb85 --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/drivers/bridge/setup_ipv6_test.go @@ -0,0 +1,70 @@ +package bridge + +import ( + "bytes" + "fmt" + "io/ioutil" + "net" + "testing" + + "github.com/docker/libnetwork/netutils" + "github.com/vishvananda/netlink" +) + +func TestSetupIPv6(t *testing.T) { + defer netutils.SetupTestNetNS(t)() + + config, br := setupTestInterface(t) + if err := setupBridgeIPv6(config, br); err != nil { + t.Fatalf("Failed to setup bridge IPv6: %v", err) + } + + procSetting, err := ioutil.ReadFile(fmt.Sprintf("/proc/sys/net/ipv6/conf/%s/disable_ipv6", config.BridgeName)) + if err != nil { + t.Fatalf("Failed to read disable_ipv6 kernel setting: %v", err) + } + + if expected := []byte("0\n"); bytes.Compare(expected, procSetting) != 0 { + t.Fatalf("Invalid kernel setting disable_ipv6: expected %q, got %q", string(expected), string(procSetting)) + } + + addrsv6, err := netlink.AddrList(br.Link, netlink.FAMILY_V6) + if err != nil { + t.Fatalf("Failed to list device IPv6 addresses: %v", err) + } + + var found bool + for _, addr := range addrsv6 { + if bridgeIPv6Str == addr.IPNet.String() { + found = true + break + } + } + + if !found { + t.Fatalf("Bridge device does not have requested IPv6 address %v", bridgeIPv6Str) + } + +} + +func TestSetupGatewayIPv6(t *testing.T) { + defer netutils.SetupTestNetNS(t)() + + _, nw, _ := net.ParseCIDR("2001:db8:ea9:9abc:ffff::/80") + gw := net.ParseIP("2001:db8:ea9:9abc:ffff::254") + + config := &NetworkConfiguration{ + BridgeName: DefaultBridgeName, + FixedCIDRv6: nw, + DefaultGatewayIPv6: gw} + + br := &bridgeInterface{} + + if err := setupGatewayIPv6(config, br); err != nil { + t.Fatalf("Set Default Gateway failed: %v", err) + } + + if !gw.Equal(br.gatewayIPv6) { + t.Fatalf("Set Default Gateway failed. Expected %v, Found %v", gw, br.gatewayIPv6) + } +} diff --git a/vendor/src/github.com/docker/libnetwork/drivers/bridge/setup_verify.go b/vendor/src/github.com/docker/libnetwork/drivers/bridge/setup_verify.go new file mode 100644 index 0000000000..9954ca8594 --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/drivers/bridge/setup_verify.go @@ -0,0 +1,44 @@ +package bridge + +import "github.com/vishvananda/netlink" + +func setupVerifyAndReconcile(config *NetworkConfiguration, i *bridgeInterface) error { + // Fetch a single IPv4 and a slice of IPv6 addresses from the bridge. + addrv4, addrsv6, err := i.addresses() + if err != nil { + return err + } + + // Verify that the bridge does have an IPv4 address. + if addrv4.IPNet == nil { + return ErrNoIPAddr + } + + // Verify that the bridge IPv4 address matches the requested configuration. + if config.AddressIPv4 != nil && !addrv4.IP.Equal(config.AddressIPv4.IP) { + return &IPv4AddrNoMatchError{ip: addrv4.IP, cfgIP: config.AddressIPv4.IP} + } + + // Verify that one of the bridge IPv6 addresses matches the requested + // configuration. + if config.EnableIPv6 && !findIPv6Address(netlink.Addr{IPNet: bridgeIPv6}, addrsv6) { + return (*IPv6AddrNoMatchError)(bridgeIPv6) + } + + // By this time we have either configured a new bridge with an IP address + // or made sure an existing bridge's IP matches the configuration + // Now is the time to cache these states in the bridgeInterface. + i.bridgeIPv4 = addrv4.IPNet + i.bridgeIPv6 = bridgeIPv6 + + return nil +} + +func findIPv6Address(addr netlink.Addr, addresses []netlink.Addr) bool { + for _, addrv6 := range addresses { + if addrv6.String() == addr.String() { + return true + } + } + return false +} diff --git a/vendor/src/github.com/docker/libnetwork/drivers/bridge/setup_verify_test.go b/vendor/src/github.com/docker/libnetwork/drivers/bridge/setup_verify_test.go new file mode 100644 index 0000000000..d3c79dd504 --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/drivers/bridge/setup_verify_test.go @@ -0,0 +1,110 @@ +package bridge + +import ( + "net" + "testing" + + "github.com/docker/libnetwork/netutils" + "github.com/vishvananda/netlink" +) + +func setupVerifyTest(t *testing.T) *bridgeInterface { + inf := &bridgeInterface{} + + br := netlink.Bridge{} + br.LinkAttrs.Name = "default0" + if err := netlink.LinkAdd(&br); err == nil { + inf.Link = &br + } else { + t.Fatalf("Failed to create bridge interface: %v", err) + } + + return inf +} + +func TestSetupVerify(t *testing.T) { + defer netutils.SetupTestNetNS(t)() + + addrv4 := net.IPv4(192, 168, 1, 1) + inf := setupVerifyTest(t) + config := &NetworkConfiguration{} + config.AddressIPv4 = &net.IPNet{IP: addrv4, Mask: addrv4.DefaultMask()} + + if err := netlink.AddrAdd(inf.Link, &netlink.Addr{IPNet: config.AddressIPv4}); err != nil { + t.Fatalf("Failed to assign IPv4 %s to interface: %v", config.AddressIPv4, err) + } + + if err := setupVerifyAndReconcile(config, inf); err != nil { + t.Fatalf("Address verification failed: %v", err) + } +} + +func TestSetupVerifyBad(t *testing.T) { + defer netutils.SetupTestNetNS(t)() + + addrv4 := net.IPv4(192, 168, 1, 1) + inf := setupVerifyTest(t) + config := &NetworkConfiguration{} + config.AddressIPv4 = &net.IPNet{IP: addrv4, Mask: addrv4.DefaultMask()} + + ipnet := &net.IPNet{IP: net.IPv4(192, 168, 1, 2), Mask: addrv4.DefaultMask()} + if err := netlink.AddrAdd(inf.Link, &netlink.Addr{IPNet: ipnet}); err != nil { + t.Fatalf("Failed to assign IPv4 %s to interface: %v", ipnet, err) + } + + if err := setupVerifyAndReconcile(config, inf); err == nil { + t.Fatal("Address verification was expected to fail") + } +} + +func TestSetupVerifyMissing(t *testing.T) { + defer netutils.SetupTestNetNS(t)() + + addrv4 := net.IPv4(192, 168, 1, 1) + inf := setupVerifyTest(t) + config := &NetworkConfiguration{} + config.AddressIPv4 = &net.IPNet{IP: addrv4, Mask: addrv4.DefaultMask()} + + if err := setupVerifyAndReconcile(config, inf); err == nil { + t.Fatal("Address verification was expected to fail") + } +} + +func TestSetupVerifyIPv6(t *testing.T) { + defer netutils.SetupTestNetNS(t)() + + addrv4 := net.IPv4(192, 168, 1, 1) + inf := setupVerifyTest(t) + config := &NetworkConfiguration{} + config.AddressIPv4 = &net.IPNet{IP: addrv4, Mask: addrv4.DefaultMask()} + config.EnableIPv6 = true + + if err := netlink.AddrAdd(inf.Link, &netlink.Addr{IPNet: bridgeIPv6}); err != nil { + t.Fatalf("Failed to assign IPv6 %s to interface: %v", bridgeIPv6, err) + } + if err := netlink.AddrAdd(inf.Link, &netlink.Addr{IPNet: config.AddressIPv4}); err != nil { + t.Fatalf("Failed to assign IPv4 %s to interface: %v", config.AddressIPv4, err) + } + + if err := setupVerifyAndReconcile(config, inf); err != nil { + t.Fatalf("Address verification failed: %v", err) + } +} + +func TestSetupVerifyIPv6Missing(t *testing.T) { + defer netutils.SetupTestNetNS(t)() + + addrv4 := net.IPv4(192, 168, 1, 1) + inf := setupVerifyTest(t) + config := &NetworkConfiguration{} + config.AddressIPv4 = &net.IPNet{IP: addrv4, Mask: addrv4.DefaultMask()} + config.EnableIPv6 = true + + if err := netlink.AddrAdd(inf.Link, &netlink.Addr{IPNet: config.AddressIPv4}); err != nil { + t.Fatalf("Failed to assign IPv4 %s to interface: %v", config.AddressIPv4, err) + } + + if err := setupVerifyAndReconcile(config, inf); err == nil { + t.Fatal("Address verification was expected to fail") + } +} diff --git a/vendor/src/github.com/docker/libnetwork/drivers/host/host.go b/vendor/src/github.com/docker/libnetwork/drivers/host/host.go new file mode 100644 index 0000000000..50cdad7ad5 --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/drivers/host/host.go @@ -0,0 +1,53 @@ +package host + +import ( + "github.com/docker/libnetwork/driverapi" + "github.com/docker/libnetwork/types" +) + +const networkType = "host" + +type driver struct{} + +// Init registers a new instance of host driver +func Init(dc driverapi.DriverCallback) error { + return dc.RegisterDriver(networkType, &driver{}) +} + +func (d *driver) Config(option map[string]interface{}) error { + return nil +} + +func (d *driver) CreateNetwork(id types.UUID, option map[string]interface{}) error { + return nil +} + +func (d *driver) DeleteNetwork(nid types.UUID) error { + return nil +} + +func (d *driver) CreateEndpoint(nid, eid types.UUID, epInfo driverapi.EndpointInfo, epOptions map[string]interface{}) error { + return nil +} + +func (d *driver) DeleteEndpoint(nid, eid types.UUID) error { + return nil +} + +func (d *driver) EndpointOperInfo(nid, eid types.UUID) (map[string]interface{}, error) { + return make(map[string]interface{}, 0), nil +} + +// Join method is invoked when a Sandbox is attached to an endpoint. +func (d *driver) Join(nid, eid types.UUID, sboxKey string, jinfo driverapi.JoinInfo, options map[string]interface{}) error { + return (jinfo.SetHostsPath("/etc/hosts")) +} + +// Leave method is invoked when a Sandbox detaches from an endpoint. +func (d *driver) Leave(nid, eid types.UUID) error { + return nil +} + +func (d *driver) Type() string { + return networkType +} diff --git a/vendor/src/github.com/docker/libnetwork/drivers/null/null.go b/vendor/src/github.com/docker/libnetwork/drivers/null/null.go new file mode 100644 index 0000000000..11ac469a09 --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/drivers/null/null.go @@ -0,0 +1,53 @@ +package null + +import ( + "github.com/docker/libnetwork/driverapi" + "github.com/docker/libnetwork/types" +) + +const networkType = "null" + +type driver struct{} + +// Init registers a new instance of null driver +func Init(dc driverapi.DriverCallback) error { + return dc.RegisterDriver(networkType, &driver{}) +} + +func (d *driver) Config(option map[string]interface{}) error { + return nil +} + +func (d *driver) CreateNetwork(id types.UUID, option map[string]interface{}) error { + return nil +} + +func (d *driver) DeleteNetwork(nid types.UUID) error { + return nil +} + +func (d *driver) CreateEndpoint(nid, eid types.UUID, epInfo driverapi.EndpointInfo, epOptions map[string]interface{}) error { + return nil +} + +func (d *driver) DeleteEndpoint(nid, eid types.UUID) error { + return nil +} + +func (d *driver) EndpointOperInfo(nid, eid types.UUID) (map[string]interface{}, error) { + return make(map[string]interface{}, 0), nil +} + +// Join method is invoked when a Sandbox is attached to an endpoint. +func (d *driver) Join(nid, eid types.UUID, sboxKey string, jinfo driverapi.JoinInfo, options map[string]interface{}) error { + return nil +} + +// Leave method is invoked when a Sandbox detaches from an endpoint. +func (d *driver) Leave(nid, eid types.UUID) error { + return nil +} + +func (d *driver) Type() string { + return networkType +} diff --git a/vendor/src/github.com/docker/libnetwork/drivers/remote/driver.go b/vendor/src/github.com/docker/libnetwork/drivers/remote/driver.go new file mode 100644 index 0000000000..0159fb40bf --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/drivers/remote/driver.go @@ -0,0 +1,69 @@ +package remote + +import ( + "errors" + + log "github.com/Sirupsen/logrus" + "github.com/docker/docker/pkg/plugins" + "github.com/docker/libnetwork/driverapi" + "github.com/docker/libnetwork/types" +) + +var errNoCallback = errors.New("No Callback handler registered with Driver") + +type driver struct { + endpoint *plugins.Client + networkType string +} + +// Init does the necessary work to register remote drivers +func Init(dc driverapi.DriverCallback) error { + plugins.Handle(driverapi.NetworkPluginEndpointType, func(name string, client *plugins.Client) { + + // TODO : Handhake with the Remote Plugin goes here + + newDriver := &driver{networkType: name, endpoint: client} + if err := dc.RegisterDriver(name, newDriver); err != nil { + log.Errorf("Error registering Driver for %s due to %v", name, err) + } + }) + return nil +} + +func (d *driver) Config(option map[string]interface{}) error { + return driverapi.ErrNotImplemented +} + +func (d *driver) CreateNetwork(id types.UUID, option map[string]interface{}) error { + return driverapi.ErrNotImplemented +} + +func (d *driver) DeleteNetwork(nid types.UUID) error { + return driverapi.ErrNotImplemented +} + +func (d *driver) CreateEndpoint(nid, eid types.UUID, epInfo driverapi.EndpointInfo, epOptions map[string]interface{}) error { + return driverapi.ErrNotImplemented +} + +func (d *driver) DeleteEndpoint(nid, eid types.UUID) error { + return driverapi.ErrNotImplemented +} + +func (d *driver) EndpointOperInfo(nid, eid types.UUID) (map[string]interface{}, error) { + return nil, driverapi.ErrNotImplemented +} + +// Join method is invoked when a Sandbox is attached to an endpoint. +func (d *driver) Join(nid, eid types.UUID, sboxKey string, jinfo driverapi.JoinInfo, options map[string]interface{}) error { + return driverapi.ErrNotImplemented +} + +// Leave method is invoked when a Sandbox detaches from an endpoint. +func (d *driver) Leave(nid, eid types.UUID) error { + return driverapi.ErrNotImplemented +} + +func (d *driver) Type() string { + return d.networkType +} diff --git a/vendor/src/github.com/docker/libnetwork/endpoint.go b/vendor/src/github.com/docker/libnetwork/endpoint.go new file mode 100644 index 0000000000..3ddec80ec9 --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/endpoint.go @@ -0,0 +1,724 @@ +package libnetwork + +import ( + "bytes" + "io/ioutil" + "os" + "path" + "path/filepath" + "sync" + + "github.com/Sirupsen/logrus" + "github.com/docker/docker/pkg/ioutils" + "github.com/docker/libnetwork/etchosts" + "github.com/docker/libnetwork/netlabel" + "github.com/docker/libnetwork/netutils" + "github.com/docker/libnetwork/resolvconf" + "github.com/docker/libnetwork/sandbox" + "github.com/docker/libnetwork/types" +) + +// Endpoint represents a logical connection between a network and a sandbox. +type Endpoint interface { + // A system generated id for this endpoint. + ID() string + + // Name returns the name of this endpoint. + Name() string + + // Network returns the name of the network to which this endpoint is attached. + Network() string + + // Join creates a new sandbox for the given container ID and populates the + // network resources allocated for the endpoint and joins the sandbox to + // the endpoint. It returns the sandbox key to the caller + Join(containerID string, options ...EndpointOption) (*ContainerData, error) + + // Leave removes the sandbox associated with container ID and detaches + // the network resources populated in the sandbox + Leave(containerID string, options ...EndpointOption) error + + // Return certain operational data belonging to this endpoint + Info() EndpointInfo + + // Info returns a collection of driver operational data related to this endpoint retrieved from the driver + DriverInfo() (map[string]interface{}, error) + + // Delete and detaches this endpoint from the network. + Delete() error +} + +// EndpointOption is a option setter function type used to pass varios options to Network +// and Endpoint interfaces methods. The various setter functions of type EndpointOption are +// provided by libnetwork, they look like Option[...](...) +type EndpointOption func(ep *endpoint) + +// ContainerData is a set of data returned when a container joins an endpoint. +type ContainerData struct { + SandboxKey string +} + +// These are the container configs used to customize container /etc/hosts file. +type hostsPathConfig struct { + hostName string + domainName string + hostsPath string + extraHosts []extraHost + parentUpdates []parentUpdate +} + +// These are the container configs used to customize container /etc/resolv.conf file. +type resolvConfPathConfig struct { + resolvConfPath string + dnsList []string + dnsSearchList []string +} + +type containerConfig struct { + hostsPathConfig + resolvConfPathConfig + generic map[string]interface{} + useDefaultSandBox bool +} + +type extraHost struct { + name string + IP string +} + +type parentUpdate struct { + eid string + name string + ip string +} + +type containerInfo struct { + id string + config containerConfig + data ContainerData +} + +type endpoint struct { + name string + id types.UUID + network *network + sandboxInfo *sandbox.Info + iFaces []*endpointInterface + joinInfo *endpointJoinInfo + container *containerInfo + exposedPorts []netutils.TransportPort + generic map[string]interface{} + joinLeaveDone chan struct{} + sync.Mutex +} + +const defaultPrefix = "/var/lib/docker/network/files" + +func (ep *endpoint) ID() string { + ep.Lock() + defer ep.Unlock() + + return string(ep.id) +} + +func (ep *endpoint) Name() string { + ep.Lock() + defer ep.Unlock() + + return ep.name +} + +func (ep *endpoint) Network() string { + ep.Lock() + defer ep.Unlock() + + return ep.network.name +} + +func (ep *endpoint) processOptions(options ...EndpointOption) { + ep.Lock() + defer ep.Unlock() + + for _, opt := range options { + if opt != nil { + opt(ep) + } + } +} + +func createBasePath(dir string) error { + err := os.MkdirAll(dir, 0644) + if err != nil && !os.IsExist(err) { + return err + } + + return nil +} + +func createFile(path string) error { + var f *os.File + + dir, _ := filepath.Split(path) + err := createBasePath(dir) + if err != nil { + return err + } + + f, err = os.Create(path) + if err == nil { + f.Close() + } + + return err +} + +// joinLeaveStart waits to ensure there are no joins or leaves in progress and +// marks this join/leave in progress without race +func (ep *endpoint) joinLeaveStart() { + ep.Lock() + defer ep.Unlock() + + for ep.joinLeaveDone != nil { + joinLeaveDone := ep.joinLeaveDone + ep.Unlock() + + select { + case <-joinLeaveDone: + } + + ep.Lock() + } + + ep.joinLeaveDone = make(chan struct{}) +} + +// joinLeaveEnd marks the end of this join/leave operation and +// signals the same without race to other join and leave waiters +func (ep *endpoint) joinLeaveEnd() { + ep.Lock() + defer ep.Unlock() + + if ep.joinLeaveDone != nil { + close(ep.joinLeaveDone) + ep.joinLeaveDone = nil + } +} + +func (ep *endpoint) Join(containerID string, options ...EndpointOption) (*ContainerData, error) { + var err error + + if containerID == "" { + return nil, InvalidContainerIDError(containerID) + } + + ep.joinLeaveStart() + defer ep.joinLeaveEnd() + + ep.Lock() + if ep.container != nil { + ep.Unlock() + return nil, ErrInvalidJoin + } + + ep.container = &containerInfo{ + id: containerID, + config: containerConfig{ + hostsPathConfig: hostsPathConfig{ + extraHosts: []extraHost{}, + parentUpdates: []parentUpdate{}, + }, + }} + + ep.joinInfo = &endpointJoinInfo{} + + container := ep.container + network := ep.network + epid := ep.id + joinInfo := ep.joinInfo + ifaces := ep.iFaces + + ep.Unlock() + defer func() { + ep.Lock() + if err != nil { + ep.container = nil + } + ep.Unlock() + }() + + network.Lock() + driver := network.driver + nid := network.id + ctrlr := network.ctrlr + network.Unlock() + + ep.processOptions(options...) + + sboxKey := sandbox.GenerateKey(containerID) + if container.config.useDefaultSandBox { + sboxKey = sandbox.GenerateKey("default") + } + + err = driver.Join(nid, epid, sboxKey, ep, container.config.generic) + if err != nil { + return nil, err + } + + err = ep.buildHostsFiles() + if err != nil { + return nil, err + } + + err = ep.updateParentHosts() + if err != nil { + return nil, err + } + + err = ep.setupDNS() + if err != nil { + return nil, err + } + + sb, err := ctrlr.sandboxAdd(sboxKey, !container.config.useDefaultSandBox) + if err != nil { + return nil, err + } + defer func() { + if err != nil { + ctrlr.sandboxRm(sboxKey) + } + }() + + for _, i := range ifaces { + iface := &sandbox.Interface{ + SrcName: i.srcName, + DstName: i.dstName, + Address: &i.addr, + } + if i.addrv6.IP.To16() != nil { + iface.AddressIPv6 = &i.addrv6 + } + err = sb.AddInterface(iface) + if err != nil { + return nil, err + } + } + + err = sb.SetGateway(joinInfo.gw) + if err != nil { + return nil, err + } + + err = sb.SetGatewayIPv6(joinInfo.gw6) + if err != nil { + return nil, err + } + + container.data.SandboxKey = sb.Key() + cData := container.data + + return &cData, nil +} + +func (ep *endpoint) Leave(containerID string, options ...EndpointOption) error { + var err error + + ep.joinLeaveStart() + defer ep.joinLeaveEnd() + + ep.processOptions(options...) + + ep.Lock() + container := ep.container + n := ep.network + + if container == nil || container.id == "" || + containerID == "" || container.id != containerID { + if container == nil { + err = ErrNoContainer + } else { + err = InvalidContainerIDError(containerID) + } + + ep.Unlock() + return err + } + ep.container = nil + ep.Unlock() + + n.Lock() + driver := n.driver + ctrlr := n.ctrlr + n.Unlock() + + err = driver.Leave(n.id, ep.id) + + sb := ctrlr.sandboxGet(container.data.SandboxKey) + for _, i := range sb.Interfaces() { + err = sb.RemoveInterface(i) + if err != nil { + logrus.Debugf("Remove interface failed: %v", err) + } + } + + ctrlr.sandboxRm(container.data.SandboxKey) + + return err +} + +func (ep *endpoint) Delete() error { + var err error + + ep.Lock() + epid := ep.id + name := ep.name + if ep.container != nil { + ep.Unlock() + return &ActiveContainerError{name: name, id: string(epid)} + } + + n := ep.network + ep.Unlock() + + n.Lock() + _, ok := n.endpoints[epid] + if !ok { + n.Unlock() + return &UnknownEndpointError{name: name, id: string(epid)} + } + + nid := n.id + driver := n.driver + delete(n.endpoints, epid) + n.Unlock() + defer func() { + if err != nil { + n.Lock() + n.endpoints[epid] = ep + n.Unlock() + } + }() + + err = driver.DeleteEndpoint(nid, epid) + return err +} + +func (ep *endpoint) buildHostsFiles() error { + var extraContent []etchosts.Record + + ep.Lock() + container := ep.container + joinInfo := ep.joinInfo + ifaces := ep.iFaces + ep.Unlock() + + if container == nil { + return ErrNoContainer + } + + if container.config.hostsPath == "" { + container.config.hostsPath = defaultPrefix + "/" + container.id + "/hosts" + } + + dir, _ := filepath.Split(container.config.hostsPath) + err := createBasePath(dir) + if err != nil { + return err + } + + if joinInfo != nil && joinInfo.hostsPath != "" { + content, err := ioutil.ReadFile(joinInfo.hostsPath) + if err != nil && !os.IsNotExist(err) { + return err + } + + if err == nil { + return ioutil.WriteFile(container.config.hostsPath, content, 0644) + } + } + + name := container.config.hostName + if container.config.domainName != "" { + name = name + "." + container.config.domainName + } + + for _, extraHost := range container.config.extraHosts { + extraContent = append(extraContent, + etchosts.Record{Hosts: extraHost.name, IP: extraHost.IP}) + } + + IP := "" + if len(ifaces) != 0 && ifaces[0] != nil { + IP = ifaces[0].addr.IP.String() + } + + return etchosts.Build(container.config.hostsPath, IP, container.config.hostName, + container.config.domainName, extraContent) +} + +func (ep *endpoint) updateParentHosts() error { + ep.Lock() + container := ep.container + network := ep.network + ep.Unlock() + + if container == nil { + return ErrNoContainer + } + + for _, update := range container.config.parentUpdates { + network.Lock() + pep, ok := network.endpoints[types.UUID(update.eid)] + if !ok { + network.Unlock() + continue + } + network.Unlock() + + pep.Lock() + pContainer := pep.container + pep.Unlock() + + if pContainer != nil { + if err := etchosts.Update(pContainer.config.hostsPath, update.ip, update.name); err != nil { + return err + } + } + } + + return nil +} + +func (ep *endpoint) updateDNS(resolvConf []byte) error { + ep.Lock() + container := ep.container + network := ep.network + ep.Unlock() + + if container == nil { + return ErrNoContainer + } + + oldHash := []byte{} + hashFile := container.config.resolvConfPath + ".hash" + + resolvBytes, err := ioutil.ReadFile(container.config.resolvConfPath) + if err != nil { + if !os.IsNotExist(err) { + return err + } + } else { + oldHash, err = ioutil.ReadFile(hashFile) + if err != nil { + if !os.IsNotExist(err) { + return err + } + + oldHash = []byte{} + } + } + + curHash, err := ioutils.HashData(bytes.NewReader(resolvBytes)) + if err != nil { + return err + } + + if string(oldHash) != "" && curHash != string(oldHash) { + // Seems the user has changed the container resolv.conf since the last time + // we checked so return without doing anything. + return nil + } + + // replace any localhost/127.* and remove IPv6 nameservers if IPv6 disabled. + resolvConf, _ = resolvconf.FilterResolvDNS(resolvConf, network.enableIPv6) + + newHash, err := ioutils.HashData(bytes.NewReader(resolvConf)) + if err != nil { + return err + } + + // for atomic updates to these files, use temporary files with os.Rename: + dir := path.Dir(container.config.resolvConfPath) + tmpHashFile, err := ioutil.TempFile(dir, "hash") + if err != nil { + return err + } + tmpResolvFile, err := ioutil.TempFile(dir, "resolv") + if err != nil { + return err + } + + // write the updates to the temp files + if err = ioutil.WriteFile(tmpHashFile.Name(), []byte(newHash), 0644); err != nil { + return err + } + if err = ioutil.WriteFile(tmpResolvFile.Name(), resolvConf, 0644); err != nil { + return err + } + + // rename the temp files for atomic replace + if err = os.Rename(tmpHashFile.Name(), hashFile); err != nil { + return err + } + return os.Rename(tmpResolvFile.Name(), container.config.resolvConfPath) +} + +func (ep *endpoint) setupDNS() error { + ep.Lock() + container := ep.container + ep.Unlock() + + if container == nil { + return ErrNoContainer + } + + if container.config.resolvConfPath == "" { + container.config.resolvConfPath = defaultPrefix + "/" + container.id + "/resolv.conf" + } + + dir, _ := filepath.Split(container.config.resolvConfPath) + err := createBasePath(dir) + if err != nil { + return err + } + + resolvConf, err := resolvconf.Get() + if err != nil { + return err + } + + if len(container.config.dnsList) > 0 || + len(container.config.dnsSearchList) > 0 { + var ( + dnsList = resolvconf.GetNameservers(resolvConf) + dnsSearchList = resolvconf.GetSearchDomains(resolvConf) + ) + + if len(container.config.dnsList) > 0 { + dnsList = container.config.dnsList + } + + if len(container.config.dnsSearchList) > 0 { + dnsSearchList = container.config.dnsSearchList + } + + return resolvconf.Build(container.config.resolvConfPath, dnsList, dnsSearchList) + } + + return ep.updateDNS(resolvConf) +} + +// EndpointOptionGeneric function returns an option setter for a Generic option defined +// in a Dictionary of Key-Value pair +func EndpointOptionGeneric(generic map[string]interface{}) EndpointOption { + return func(ep *endpoint) { + for k, v := range generic { + ep.generic[k] = v + } + } +} + +// JoinOptionHostname function returns an option setter for hostname option to +// be passed to endpoint Join method. +func JoinOptionHostname(name string) EndpointOption { + return func(ep *endpoint) { + ep.container.config.hostName = name + } +} + +// JoinOptionDomainname function returns an option setter for domainname option to +// be passed to endpoint Join method. +func JoinOptionDomainname(name string) EndpointOption { + return func(ep *endpoint) { + ep.container.config.domainName = name + } +} + +// JoinOptionHostsPath function returns an option setter for hostspath option to +// be passed to endpoint Join method. +func JoinOptionHostsPath(path string) EndpointOption { + return func(ep *endpoint) { + ep.container.config.hostsPath = path + } +} + +// JoinOptionExtraHost function returns an option setter for extra /etc/hosts options +// which is a name and IP as strings. +func JoinOptionExtraHost(name string, IP string) EndpointOption { + return func(ep *endpoint) { + ep.container.config.extraHosts = append(ep.container.config.extraHosts, extraHost{name: name, IP: IP}) + } +} + +// JoinOptionParentUpdate function returns an option setter for parent container +// which needs to update the IP address for the linked container. +func JoinOptionParentUpdate(eid string, name, ip string) EndpointOption { + return func(ep *endpoint) { + ep.container.config.parentUpdates = append(ep.container.config.parentUpdates, parentUpdate{eid: eid, name: name, ip: ip}) + } +} + +// JoinOptionResolvConfPath function returns an option setter for resolvconfpath option to +// be passed to endpoint Join method. +func JoinOptionResolvConfPath(path string) EndpointOption { + return func(ep *endpoint) { + ep.container.config.resolvConfPath = path + } +} + +// JoinOptionDNS function returns an option setter for dns entry option to +// be passed to endpoint Join method. +func JoinOptionDNS(dns string) EndpointOption { + return func(ep *endpoint) { + ep.container.config.dnsList = append(ep.container.config.dnsList, dns) + } +} + +// JoinOptionDNSSearch function returns an option setter for dns search entry option to +// be passed to endpoint Join method. +func JoinOptionDNSSearch(search string) EndpointOption { + return func(ep *endpoint) { + ep.container.config.dnsSearchList = append(ep.container.config.dnsSearchList, search) + } +} + +// JoinOptionUseDefaultSandbox function returns an option setter for using default sandbox to +// be passed to endpoint Join method. +func JoinOptionUseDefaultSandbox() EndpointOption { + return func(ep *endpoint) { + ep.container.config.useDefaultSandBox = true + } +} + +// CreateOptionExposedPorts function returns an option setter for the container exposed +// ports option to be passed to network.CreateEndpoint() method. +func CreateOptionExposedPorts(exposedPorts []netutils.TransportPort) EndpointOption { + return func(ep *endpoint) { + // Defensive copy + eps := make([]netutils.TransportPort, len(exposedPorts)) + copy(eps, exposedPorts) + // Store endpoint label and in generic because driver needs it + ep.exposedPorts = eps + ep.generic[netlabel.ExposedPorts] = eps + } +} + +// CreateOptionPortMapping function returns an option setter for the mapping +// ports option to be passed to network.CreateEndpoint() method. +func CreateOptionPortMapping(portBindings []netutils.PortBinding) EndpointOption { + return func(ep *endpoint) { + // Store a copy of the bindings as generic data to pass to the driver + pbs := make([]netutils.PortBinding, len(portBindings)) + copy(pbs, portBindings) + ep.generic[netlabel.PortMap] = pbs + } +} + +// JoinOptionGeneric function returns an option setter for Generic configuration +// that is not managed by libNetwork but can be used by the Drivers during the call to +// endpoint join method. Container Labels are a good example. +func JoinOptionGeneric(generic map[string]interface{}) EndpointOption { + return func(ep *endpoint) { + ep.container.config.generic = generic + } +} diff --git a/vendor/src/github.com/docker/libnetwork/endpoint_info.go b/vendor/src/github.com/docker/libnetwork/endpoint_info.go new file mode 100644 index 0000000000..5383412c0e --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/endpoint_info.go @@ -0,0 +1,215 @@ +package libnetwork + +import ( + "net" + + "github.com/docker/libnetwork/driverapi" + "github.com/docker/libnetwork/netutils" +) + +// EndpointInfo provides an interface to retrieve network resources bound to the endpoint. +type EndpointInfo interface { + // InterfaceList returns an interface list which were assigned to the endpoint + // by the driver. This can be used after the endpoint has been created. + InterfaceList() []InterfaceInfo + + // Gateway returns the IPv4 gateway assigned by the driver. + // This will only return a valid value if a container has joined the endpoint. + Gateway() net.IP + + // GatewayIPv6 returns the IPv6 gateway assigned by the driver. + // This will only return a valid value if a container has joined the endpoint. + GatewayIPv6() net.IP + + // SandboxKey returns the sanbox key for the container which has joined + // the endpoint. If there is no container joined then this will return an + // empty string. + SandboxKey() string +} + +// InterfaceInfo provides an interface to retrieve interface addresses bound to the endpoint. +type InterfaceInfo interface { + // MacAddress returns the MAC address assigned to the endpoint. + MacAddress() net.HardwareAddr + + // Address returns the IPv4 address assigned to the endpoint. + Address() net.IPNet + + // AddressIPv6 returns the IPv6 address assigned to the endpoint. + AddressIPv6() net.IPNet +} + +type endpointInterface struct { + id int + mac net.HardwareAddr + addr net.IPNet + addrv6 net.IPNet + srcName string + dstName string +} + +type endpointJoinInfo struct { + gw net.IP + gw6 net.IP + hostsPath string + resolvConfPath string +} + +func (ep *endpoint) Info() EndpointInfo { + return ep +} + +func (ep *endpoint) DriverInfo() (map[string]interface{}, error) { + ep.Lock() + network := ep.network + epid := ep.id + ep.Unlock() + + network.Lock() + driver := network.driver + nid := network.id + network.Unlock() + + return driver.EndpointOperInfo(nid, epid) +} + +func (ep *endpoint) InterfaceList() []InterfaceInfo { + ep.Lock() + defer ep.Unlock() + + iList := make([]InterfaceInfo, len(ep.iFaces)) + + for i, iface := range ep.iFaces { + iList[i] = iface + } + + return iList +} + +func (ep *endpoint) Interfaces() []driverapi.InterfaceInfo { + ep.Lock() + defer ep.Unlock() + + iList := make([]driverapi.InterfaceInfo, len(ep.iFaces)) + + for i, iface := range ep.iFaces { + iList[i] = iface + } + + return iList +} + +func (ep *endpoint) AddInterface(id int, mac net.HardwareAddr, ipv4 net.IPNet, ipv6 net.IPNet) error { + ep.Lock() + defer ep.Unlock() + + iface := &endpointInterface{ + id: id, + addr: *netutils.GetIPNetCopy(&ipv4), + addrv6: *netutils.GetIPNetCopy(&ipv6), + } + iface.mac = netutils.GetMacCopy(mac) + + ep.iFaces = append(ep.iFaces, iface) + return nil +} + +func (i *endpointInterface) ID() int { + return i.id +} + +func (i *endpointInterface) MacAddress() net.HardwareAddr { + return netutils.GetMacCopy(i.mac) +} + +func (i *endpointInterface) Address() net.IPNet { + return (*netutils.GetIPNetCopy(&i.addr)) +} + +func (i *endpointInterface) AddressIPv6() net.IPNet { + return (*netutils.GetIPNetCopy(&i.addrv6)) +} + +func (i *endpointInterface) SetNames(srcName string, dstName string) error { + i.srcName = srcName + i.dstName = dstName + return nil +} + +func (ep *endpoint) InterfaceNames() []driverapi.InterfaceNameInfo { + ep.Lock() + defer ep.Unlock() + + iList := make([]driverapi.InterfaceNameInfo, len(ep.iFaces)) + + for i, iface := range ep.iFaces { + iList[i] = iface + } + + return iList +} + +func (ep *endpoint) SandboxKey() string { + ep.Lock() + defer ep.Unlock() + + if ep.container == nil { + return "" + } + + return ep.container.data.SandboxKey +} + +func (ep *endpoint) Gateway() net.IP { + ep.Lock() + defer ep.Unlock() + + if ep.joinInfo == nil { + return net.IP{} + } + + return netutils.GetIPCopy(ep.joinInfo.gw) +} + +func (ep *endpoint) GatewayIPv6() net.IP { + ep.Lock() + defer ep.Unlock() + + if ep.joinInfo == nil { + return net.IP{} + } + + return netutils.GetIPCopy(ep.joinInfo.gw6) +} + +func (ep *endpoint) SetGateway(gw net.IP) error { + ep.Lock() + defer ep.Unlock() + + ep.joinInfo.gw = netutils.GetIPCopy(gw) + return nil +} + +func (ep *endpoint) SetGatewayIPv6(gw6 net.IP) error { + ep.Lock() + defer ep.Unlock() + + ep.joinInfo.gw6 = netutils.GetIPCopy(gw6) + return nil +} + +func (ep *endpoint) SetHostsPath(path string) error { + ep.Lock() + defer ep.Unlock() + + ep.joinInfo.hostsPath = path + return nil +} + +func (ep *endpoint) SetResolvConfPath(path string) error { + ep.Lock() + defer ep.Unlock() + + ep.joinInfo.resolvConfPath = path + return nil +} diff --git a/vendor/src/github.com/docker/libnetwork/error.go b/vendor/src/github.com/docker/libnetwork/error.go new file mode 100644 index 0000000000..088bfbc826 --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/error.go @@ -0,0 +1,98 @@ +package libnetwork + +import ( + "errors" + "fmt" +) + +var ( + // ErrNoSuchNetwork is returned when a network query finds no result + ErrNoSuchNetwork = errors.New("network not found") + // ErrNoSuchEndpoint is returned when a endpoint query finds no result + ErrNoSuchEndpoint = errors.New("endpoint not found") + // ErrNilNetworkDriver is returned if a nil network driver + // is passed to NewNetwork api. + ErrNilNetworkDriver = errors.New("nil NetworkDriver instance") + // ErrInvalidNetworkDriver is returned if an invalid driver + // instance is passed. + ErrInvalidNetworkDriver = errors.New("invalid driver bound to network") + // ErrInvalidJoin is returned if a join is attempted on an endpoint + // which already has a container joined. + ErrInvalidJoin = errors.New("a container has already joined the endpoint") + // ErrNoContainer is returned when the endpoint has no container + // attached to it. + ErrNoContainer = errors.New("no container attached to the endpoint") + // ErrInvalidID is returned when a query-by-id method is being invoked + // with an empty id parameter + ErrInvalidID = errors.New("invalid ID") + // ErrInvalidName is returned when a query-by-name or resource create method is + // invoked with an empty name parameter + ErrInvalidName = errors.New("invalid Name") +) + +// NetworkTypeError type is returned when the network type string is not +// known to libnetwork. +type NetworkTypeError string + +func (nt NetworkTypeError) Error() string { + return fmt.Sprintf("unknown driver %q", string(nt)) +} + +// NetworkNameError is returned when a network with the same name already exists. +type NetworkNameError string + +func (name NetworkNameError) Error() string { + return fmt.Sprintf("network with name %s already exists", string(name)) +} + +// UnknownNetworkError is returned when libnetwork could not find in it's database +// a network with the same name and id. +type UnknownNetworkError struct { + name string + id string +} + +func (une *UnknownNetworkError) Error() string { + return fmt.Sprintf("unknown network %s id %s", une.name, une.id) +} + +// ActiveEndpointsError is returned when a network is deleted which has active +// endpoints in it. +type ActiveEndpointsError struct { + name string + id string +} + +func (aee *ActiveEndpointsError) Error() string { + return fmt.Sprintf("network with name %s id %s has active endpoints", aee.name, aee.id) +} + +// UnknownEndpointError is returned when libnetwork could not find in it's database +// an endpoint with the same name and id. +type UnknownEndpointError struct { + name string + id string +} + +func (uee *UnknownEndpointError) Error() string { + return fmt.Sprintf("unknown endpoint %s id %s", uee.name, uee.id) +} + +// ActiveContainerError is returned when an endpoint is deleted which has active +// containers attached to it. +type ActiveContainerError struct { + name string + id string +} + +func (ace *ActiveContainerError) Error() string { + return fmt.Sprintf("endpoint with name %s id %s has active containers", ace.name, ace.id) +} + +// InvalidContainerIDError is returned when an invalid container id is passed +// in Join/Leave +type InvalidContainerIDError string + +func (id InvalidContainerIDError) Error() string { + return fmt.Sprintf("invalid container id %s", string(id)) +} diff --git a/pkg/etchosts/etchosts.go b/vendor/src/github.com/docker/libnetwork/etchosts/etchosts.go similarity index 94% rename from pkg/etchosts/etchosts.go rename to vendor/src/github.com/docker/libnetwork/etchosts/etchosts.go index bef4a480cb..88e6b63e70 100644 --- a/pkg/etchosts/etchosts.go +++ b/vendor/src/github.com/docker/libnetwork/etchosts/etchosts.go @@ -8,13 +8,13 @@ import ( "regexp" ) -// Structure for a single host record +// Record Structure for a single host record type Record struct { Hosts string IP string } -// Writes record to file and returns bytes written or error +// WriteTo writes record to file and returns bytes written or error func (r Record) WriteTo(w io.Writer) (int64, error) { n, err := fmt.Fprintf(w, "%s\t%s\n", r.IP, r.Hosts) return int64(n), err diff --git a/pkg/etchosts/etchosts_test.go b/vendor/src/github.com/docker/libnetwork/etchosts/etchosts_test.go similarity index 98% rename from pkg/etchosts/etchosts_test.go rename to vendor/src/github.com/docker/libnetwork/etchosts/etchosts_test.go index c033904c31..8c8b87c016 100644 --- a/pkg/etchosts/etchosts_test.go +++ b/vendor/src/github.com/docker/libnetwork/etchosts/etchosts_test.go @@ -5,6 +5,8 @@ import ( "io/ioutil" "os" "testing" + + _ "github.com/docker/libnetwork/netutils" ) func TestBuildDefault(t *testing.T) { diff --git a/daemon/networkdriver/ipallocator/allocator.go b/vendor/src/github.com/docker/libnetwork/ipallocator/allocator.go similarity index 79% rename from daemon/networkdriver/ipallocator/allocator.go rename to vendor/src/github.com/docker/libnetwork/ipallocator/allocator.go index 554dbdd5b1..1560099937 100644 --- a/daemon/networkdriver/ipallocator/allocator.go +++ b/vendor/src/github.com/docker/libnetwork/ipallocator/allocator.go @@ -1,3 +1,5 @@ +// Package ipallocator defines the default IP allocator. It will move out of libnetwork as an external IPAM plugin. +// This has been imported unchanged from Docker, besides additon of registration logic package ipallocator import ( @@ -7,7 +9,7 @@ import ( "sync" "github.com/Sirupsen/logrus" - "github.com/docker/docker/daemon/networkdriver" + "github.com/docker/libnetwork/netutils" ) // allocatedMap is thread-unsafe set of allocated IP @@ -19,7 +21,7 @@ type allocatedMap struct { } func newAllocatedMap(network *net.IPNet) *allocatedMap { - firstIP, lastIP := networkdriver.NetworkRange(network) + firstIP, lastIP := netutils.NetworkRange(network) begin := big.NewInt(0).Add(ipToBigInt(firstIP), big.NewInt(1)) end := big.NewInt(0).Sub(ipToBigInt(lastIP), big.NewInt(1)) @@ -34,18 +36,25 @@ func newAllocatedMap(network *net.IPNet) *allocatedMap { type networkSet map[string]*allocatedMap var ( - ErrNoAvailableIPs = errors.New("no available ip addresses on network") - ErrIPAlreadyAllocated = errors.New("ip already allocated") - ErrIPOutOfRange = errors.New("requested ip is out of range") + // ErrNoAvailableIPs preformatted error + ErrNoAvailableIPs = errors.New("no available ip addresses on network") + // ErrIPAlreadyAllocated preformatted error + ErrIPAlreadyAllocated = errors.New("ip already allocated") + // ErrIPOutOfRange preformatted error + ErrIPOutOfRange = errors.New("requested ip is out of range") + // ErrNetworkAlreadyRegistered preformatted error ErrNetworkAlreadyRegistered = errors.New("network already registered") - ErrBadSubnet = errors.New("network does not contain specified subnet") + // ErrBadSubnet preformatted error + ErrBadSubnet = errors.New("network does not contain specified subnet") ) +// IPAllocator manages the ipam type IPAllocator struct { allocatedIPs networkSet mutex sync.Mutex } +// New returns a new instance of IPAllocator func New() *IPAllocator { return &IPAllocator{networkSet{}, sync.Mutex{}} } @@ -61,18 +70,14 @@ func (a *IPAllocator) RegisterSubnet(network *net.IPNet, subnet *net.IPNet) erro if _, ok := a.allocatedIPs[key]; ok { return ErrNetworkAlreadyRegistered } - n := newAllocatedMap(network) - beginIP, endIP := networkdriver.NetworkRange(subnet) - begin := big.NewInt(0).Add(ipToBigInt(beginIP), big.NewInt(1)) - end := big.NewInt(0).Sub(ipToBigInt(endIP), big.NewInt(1)) // Check that subnet is within network - if !(begin.Cmp(n.begin) >= 0 && end.Cmp(n.end) <= 0 && begin.Cmp(end) == -1) { + beginIP, endIP := netutils.NetworkRange(subnet) + if !(network.Contains(beginIP) && network.Contains(endIP)) { return ErrBadSubnet } - n.begin.Set(begin) - n.end.Set(end) - n.last.Sub(begin, big.NewInt(1)) + + n := newAllocatedMap(subnet) a.allocatedIPs[key] = n return nil } diff --git a/daemon/networkdriver/ipallocator/allocator_test.go b/vendor/src/github.com/docker/libnetwork/ipallocator/allocator_test.go similarity index 99% rename from daemon/networkdriver/ipallocator/allocator_test.go rename to vendor/src/github.com/docker/libnetwork/ipallocator/allocator_test.go index 6c5c0e4dbc..fffe6e3389 100644 --- a/daemon/networkdriver/ipallocator/allocator_test.go +++ b/vendor/src/github.com/docker/libnetwork/ipallocator/allocator_test.go @@ -601,7 +601,7 @@ func TestRegisterBadTwice(t *testing.T) { Mask: []byte{255, 255, 255, 248}, } if err := a.RegisterSubnet(network, subnet); err != ErrNetworkAlreadyRegistered { - t.Fatalf("Expected ErrNetworkAlreadyRegistered error, got %v", err) + t.Fatalf("Expecteded ErrNetworkAlreadyRegistered error, got %v", err) } } diff --git a/pkg/iptables/firewalld.go b/vendor/src/github.com/docker/libnetwork/iptables/firewalld.go similarity index 87% rename from pkg/iptables/firewalld.go rename to vendor/src/github.com/docker/libnetwork/iptables/firewalld.go index 89f123a1ad..1227647b74 100644 --- a/pkg/iptables/firewalld.go +++ b/vendor/src/github.com/docker/libnetwork/iptables/firewalld.go @@ -8,12 +8,16 @@ import ( "github.com/godbus/dbus" ) +// IPV defines the table string type IPV string const ( - Iptables IPV = "ipv4" - Ip6tables IPV = "ipv6" - Ebtables IPV = "eb" + // Iptables point ipv4 table + Iptables IPV = "ipv4" + // IP6Tables point to ipv6 table + IP6Tables IPV = "ipv6" + // Ebtables point to bridge table + Ebtables IPV = "eb" ) const ( dbusInterface = "org.fedoraproject.FirewallD1" @@ -33,6 +37,7 @@ var ( onReloaded []*func() // callbacks when Firewalld has been reloaded ) +// FirewalldInit initializes firewalld management code. func FirewalldInit() error { var err error @@ -96,16 +101,16 @@ func signalHandler() { func dbusConnectionChanged(args []interface{}) { name := args[0].(string) - old_owner := args[1].(string) - new_owner := args[2].(string) + oldOwner := args[1].(string) + newOwner := args[2].(string) if name != dbusInterface { return } - if len(new_owner) > 0 { + if len(newOwner) > 0 { connectionEstablished() - } else if len(old_owner) > 0 { + } else if len(oldOwner) > 0 { connectionLost() } } @@ -125,7 +130,7 @@ func reloaded() { } } -// add callback +// OnReloaded add callback func OnReloaded(callback func()) { for _, pf := range onReloaded { if pf == &callback { @@ -148,7 +153,7 @@ func checkRunning() bool { return false } -// Firewalld's passthrough method simply passes args through to iptables/ip6tables +// Passthrough method simply passes args through to iptables/ip6tables func Passthrough(ipv IPV, args ...string) ([]byte, error) { var output string logrus.Debugf("Firewalld passthrough: %s, %s", ipv, args) diff --git a/pkg/iptables/firewalld_test.go b/vendor/src/github.com/docker/libnetwork/iptables/firewalld_test.go similarity index 100% rename from pkg/iptables/firewalld_test.go rename to vendor/src/github.com/docker/libnetwork/iptables/firewalld_test.go diff --git a/pkg/iptables/iptables.go b/vendor/src/github.com/docker/libnetwork/iptables/iptables.go similarity index 86% rename from pkg/iptables/iptables.go rename to vendor/src/github.com/docker/libnetwork/iptables/iptables.go index 9cf1bbfa5c..4299a7e2b6 100644 --- a/pkg/iptables/iptables.go +++ b/vendor/src/github.com/docker/libnetwork/iptables/iptables.go @@ -12,32 +12,44 @@ import ( "github.com/Sirupsen/logrus" ) +// Action signifies the iptable action. type Action string + +// Table refers to Nat, Filter or Mangle. type Table string const ( + // Append appends the rule at the end of the chain. Append Action = "-A" + // Delete deletes the rule from the chain. Delete Action = "-D" + // Insert inserts the rule at the top of the chain. Insert Action = "-I" - Nat Table = "nat" - Filter Table = "filter" - Mangle Table = "mangle" + // Nat table is used for nat translation rules. + Nat Table = "nat" + // Filter table is used for filter rules. + Filter Table = "filter" + // Mangle table is used for mangling the packet. + Mangle Table = "mangle" ) var ( iptablesPath string supportsXlock = false // used to lock iptables commands if xtables lock is not supported - bestEffortLock sync.Mutex + bestEffortLock sync.Mutex + // ErrIptablesNotFound is returned when the rule is not found. ErrIptablesNotFound = errors.New("Iptables not found") ) +// Chain defines the iptables chain. type Chain struct { Name string Bridge string Table Table } +// ChainError is returned to represent errors during ip table operation. type ChainError struct { Chain string Output []byte @@ -60,6 +72,7 @@ func initCheck() error { return nil } +// NewChain adds a new chain to ip table. func NewChain(name, bridge string, table Table, hairpinMode bool) (*Chain, error) { c := &Chain{ Name: name, @@ -117,6 +130,7 @@ func NewChain(name, bridge string, table Table, hairpinMode bool) (*Chain, error return c, nil } +// RemoveExistingChain removes existing chain from the table. func RemoveExistingChain(name string, table Table) error { c := &Chain{ Name: name, @@ -128,7 +142,7 @@ func RemoveExistingChain(name string, table Table) error { return c.Remove() } -// Add forwarding rule to 'filter' table and corresponding nat rule to 'nat' table +// Forward adds forwarding rule to 'filter' table and corresponding nat rule to 'nat' table. func (c *Chain) Forward(action Action, ip net.IP, port int, proto, destAddr string, destPort int) error { daddr := ip.String() if ip.IsUnspecified() { @@ -174,7 +188,7 @@ func (c *Chain) Forward(action Action, ip net.IP, port int, proto, destAddr stri return nil } -// Add reciprocal ACCEPT rule for two supplied IP addresses. +// Link adds reciprocal ACCEPT rule for two supplied IP addresses. // Traffic is allowed from ip1 to ip2 and vice-versa func (c *Chain) Link(action Action, ip1, ip2 net.IP, port int, proto string) error { if output, err := Raw("-t", string(Filter), string(action), c.Name, @@ -202,7 +216,7 @@ func (c *Chain) Link(action Action, ip1, ip2 net.IP, port int, proto string) err return nil } -// Add linking rule to nat/PREROUTING chain. +// Prerouting adds linking rule to nat/PREROUTING chain. func (c *Chain) Prerouting(action Action, args ...string) error { a := []string{"-t", string(Nat), string(action), "PREROUTING"} if len(args) > 0 { @@ -216,7 +230,7 @@ func (c *Chain) Prerouting(action Action, args ...string) error { return nil } -// Add linking rule to an OUTPUT chain +// Output adds linking rule to an OUTPUT chain. func (c *Chain) Output(action Action, args ...string) error { a := []string{"-t", string(c.Table), string(action), "OUTPUT"} if len(args) > 0 { @@ -230,6 +244,7 @@ func (c *Chain) Output(action Action, args ...string) error { return nil } +// Remove removes the chain. func (c *Chain) Remove() error { // Ignore errors - This could mean the chains were never set up if c.Table == Nat { @@ -245,7 +260,7 @@ func (c *Chain) Remove() error { return nil } -// Check if a rule exists +// Exists checks if a rule exists func Exists(table Table, chain string, rule ...string) bool { if string(table) == "" { table = Filter @@ -269,7 +284,7 @@ func Exists(table Table, chain string, rule ...string) bool { return strings.Contains(string(existingRules), ruleString) } -// Call 'iptables' system command, passing supplied arguments +// Raw calls 'iptables' system command, passing supplied arguments. func Raw(args ...string) ([]byte, error) { if firewalldRunning { output, err := Passthrough(Iptables, args...) diff --git a/pkg/iptables/iptables_test.go b/vendor/src/github.com/docker/libnetwork/iptables/iptables_test.go similarity index 98% rename from pkg/iptables/iptables_test.go rename to vendor/src/github.com/docker/libnetwork/iptables/iptables_test.go index cd61739121..4035b58723 100644 --- a/pkg/iptables/iptables_test.go +++ b/vendor/src/github.com/docker/libnetwork/iptables/iptables_test.go @@ -7,9 +7,11 @@ import ( "strings" "sync" "testing" + + _ "github.com/docker/libnetwork/netutils" ) -const chainName = "DOCKERTEST" +const chainName = "DOCKER-TEST" var natChain *Chain var filterChain *Chain diff --git a/vendor/src/github.com/docker/libnetwork/libnetwork_internal_test.go b/vendor/src/github.com/docker/libnetwork/libnetwork_internal_test.go new file mode 100644 index 0000000000..6a9a7fdc43 --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/libnetwork_internal_test.go @@ -0,0 +1,26 @@ +package libnetwork + +import ( + "testing" + + "github.com/docker/libnetwork/driverapi" +) + +func TestDriverRegistration(t *testing.T) { + bridgeNetType := "bridge" + c, err := New() + if err != nil { + t.Fatal(err) + } + err = c.(*controller).RegisterDriver(bridgeNetType, nil) + if err == nil { + t.Fatalf("Expecting the RegisterDriver to fail for %s", bridgeNetType) + } + if _, ok := err.(driverapi.ErrActiveRegistration); !ok { + t.Fatalf("Failed for unexpected reason: %v", err) + } + err = c.(*controller).RegisterDriver("test-dummy", nil) + if err != nil { + t.Fatalf("Test failed with an error %v", err) + } +} diff --git a/vendor/src/github.com/docker/libnetwork/libnetwork_test.go b/vendor/src/github.com/docker/libnetwork/libnetwork_test.go new file mode 100644 index 0000000000..b628190657 --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/libnetwork_test.go @@ -0,0 +1,1476 @@ +package libnetwork_test + +import ( + "bytes" + "flag" + "fmt" + "io/ioutil" + "net" + "net/http" + "net/http/httptest" + "os" + "runtime" + "strconv" + "sync" + "testing" + + log "github.com/Sirupsen/logrus" + "github.com/docker/docker/pkg/plugins" + "github.com/docker/docker/pkg/reexec" + "github.com/docker/libnetwork" + "github.com/docker/libnetwork/driverapi" + "github.com/docker/libnetwork/netlabel" + "github.com/docker/libnetwork/netutils" + "github.com/docker/libnetwork/options" + "github.com/vishvananda/netns" +) + +const ( + bridgeNetType = "bridge" + bridgeName = "docker0" +) + +func TestMain(m *testing.M) { + if reexec.Init() { + return + } + os.Exit(m.Run()) +} + +func createTestNetwork(networkType, networkName string, option options.Generic, netOption options.Generic) (libnetwork.Network, error) { + controller, err := libnetwork.New() + if err != nil { + return nil, err + } + genericOption := make(map[string]interface{}) + genericOption[netlabel.GenericData] = option + + err = controller.ConfigureNetworkDriver(networkType, genericOption) + if err != nil { + return nil, err + } + + network, err := controller.NewNetwork(networkType, networkName, + libnetwork.NetworkOptionGeneric(netOption)) + if err != nil { + return nil, err + } + + return network, nil +} + +func getEmptyGenericOption() map[string]interface{} { + genericOption := make(map[string]interface{}) + genericOption[netlabel.GenericData] = options.Generic{} + return genericOption +} + +func getPortMapping() []netutils.PortBinding { + return []netutils.PortBinding{ + netutils.PortBinding{Proto: netutils.TCP, Port: uint16(230), HostPort: uint16(23000)}, + netutils.PortBinding{Proto: netutils.UDP, Port: uint16(200), HostPort: uint16(22000)}, + netutils.PortBinding{Proto: netutils.TCP, Port: uint16(120), HostPort: uint16(12000)}, + } +} + +func TestNull(t *testing.T) { + network, err := createTestNetwork("null", "testnetwork", options.Generic{}, + options.Generic{}) + if err != nil { + t.Fatal(err) + } + + ep, err := network.CreateEndpoint("testep") + if err != nil { + t.Fatal(err) + } + + _, err = ep.Join("null_container", + libnetwork.JoinOptionHostname("test"), + libnetwork.JoinOptionDomainname("docker.io"), + libnetwork.JoinOptionExtraHost("web", "192.168.0.1")) + if err != nil { + t.Fatal(err) + } + + err = ep.Leave("null_container") + if err != nil { + t.Fatal(err) + } + + if err := ep.Delete(); err != nil { + t.Fatal(err) + } + + if err := network.Delete(); err != nil { + t.Fatal(err) + } +} + +func TestHost(t *testing.T) { + network, err := createTestNetwork("host", "testnetwork", options.Generic{}, options.Generic{}) + if err != nil { + t.Fatal(err) + } + + ep1, err := network.CreateEndpoint("testep1") + if err != nil { + t.Fatal(err) + } + + _, err = ep1.Join("host_container1", + libnetwork.JoinOptionHostname("test1"), + libnetwork.JoinOptionDomainname("docker.io"), + libnetwork.JoinOptionExtraHost("web", "192.168.0.1"), + libnetwork.JoinOptionUseDefaultSandbox()) + if err != nil { + t.Fatal(err) + } + + ep2, err := network.CreateEndpoint("testep2") + if err != nil { + t.Fatal(err) + } + + _, err = ep2.Join("host_container2", + libnetwork.JoinOptionHostname("test2"), + libnetwork.JoinOptionDomainname("docker.io"), + libnetwork.JoinOptionExtraHost("web", "192.168.0.1"), + libnetwork.JoinOptionUseDefaultSandbox()) + if err != nil { + t.Fatal(err) + } + + err = ep1.Leave("host_container1") + if err != nil { + t.Fatal(err) + } + + err = ep2.Leave("host_container2") + if err != nil { + t.Fatal(err) + } + + if err := ep1.Delete(); err != nil { + t.Fatal(err) + } + + if err := ep2.Delete(); err != nil { + t.Fatal(err) + } + + // Try to create another host endpoint and join/leave that. + ep3, err := network.CreateEndpoint("testep3") + if err != nil { + t.Fatal(err) + } + + _, err = ep3.Join("host_container3", + libnetwork.JoinOptionHostname("test3"), + libnetwork.JoinOptionDomainname("docker.io"), + libnetwork.JoinOptionExtraHost("web", "192.168.0.1"), + libnetwork.JoinOptionUseDefaultSandbox()) + if err != nil { + t.Fatal(err) + } + + err = ep3.Leave("host_container3") + if err != nil { + t.Fatal(err) + } + + if err := ep3.Delete(); err != nil { + t.Fatal(err) + } + + if err := network.Delete(); err != nil { + t.Fatal(err) + } +} + +func TestBridge(t *testing.T) { + if !netutils.IsRunningInContainer() { + defer netutils.SetupTestNetNS(t)() + } + + ip, subnet, err := net.ParseCIDR("192.168.100.1/24") + if err != nil { + t.Fatal(err) + } + subnet.IP = ip + + ip, cidr, err := net.ParseCIDR("192.168.100.2/28") + if err != nil { + t.Fatal(err) + } + cidr.IP = ip + + ip, cidrv6, err := net.ParseCIDR("fe90::1/96") + if err != nil { + t.Fatal(err) + } + cidrv6.IP = ip + + log.Debug("Adding a bridge") + option := options.Generic{ + "EnableIPForwarding": true, + } + + netOption := options.Generic{ + "BridgeName": bridgeName, + "AddressIPv4": subnet, + "FixedCIDR": cidr, + "FixedCIDRv6": cidrv6, + "EnableIPv6": true, + "EnableIPTables": true, + "EnableIPMasquerade": true, + "EnableICC": true, + "AllowNonDefaultBridge": true} + + network, err := createTestNetwork(bridgeNetType, "testnetwork", option, netOption) + if err != nil { + t.Fatal(err) + } + + ep, err := network.CreateEndpoint("testep", libnetwork.CreateOptionPortMapping(getPortMapping())) + if err != nil { + t.Fatal(err) + } + + epInfo, err := ep.DriverInfo() + if err != nil { + t.Fatal(err) + } + pmd, ok := epInfo[netlabel.PortMap] + if !ok { + t.Fatalf("Could not find expected info in endpoint data") + } + pm, ok := pmd.([]netutils.PortBinding) + if !ok { + t.Fatalf("Unexpected format for port mapping in endpoint operational data") + } + if len(pm) != 3 { + t.Fatalf("Incomplete data for port mapping in endpoint operational data: %d", len(pm)) + } + + if err := ep.Delete(); err != nil { + t.Fatal(err) + } + + if err := network.Delete(); err != nil { + t.Fatal(err) + } +} + +func TestUnknownDriver(t *testing.T) { + if !netutils.IsRunningInContainer() { + defer netutils.SetupTestNetNS(t)() + } + + _, err := createTestNetwork("unknowndriver", "testnetwork", options.Generic{}, options.Generic{}) + if err == nil { + t.Fatal("Expected to fail. But instead succeeded") + } + + if _, ok := err.(libnetwork.NetworkTypeError); !ok { + t.Fatalf("Did not fail with expected error. Actual error: %v", err) + } +} + +func TestNilRemoteDriver(t *testing.T) { + controller, err := libnetwork.New() + if err != nil { + t.Fatal(err) + } + + _, err = controller.NewNetwork("framerelay", "dummy", + libnetwork.NetworkOptionGeneric(getEmptyGenericOption())) + if err == nil { + t.Fatal("Expected to fail. But instead succeeded") + } + + if err != plugins.ErrNotFound { + t.Fatalf("Did not fail with expected error. Actual error: %v", err) + } +} + +func TestDuplicateNetwork(t *testing.T) { + if !netutils.IsRunningInContainer() { + defer netutils.SetupTestNetNS(t)() + } + + controller, err := libnetwork.New() + if err != nil { + t.Fatal(err) + } + + genericOption := make(map[string]interface{}) + genericOption[netlabel.GenericData] = options.Generic{} + + err = controller.ConfigureNetworkDriver(bridgeNetType, genericOption) + if err != nil { + t.Fatal(err) + } + + _, err = controller.NewNetwork(bridgeNetType, "testnetwork", + libnetwork.NetworkOptionGeneric(genericOption)) + if err != nil { + t.Fatal(err) + } + + _, err = controller.NewNetwork(bridgeNetType, "testnetwork") + if err == nil { + t.Fatal("Expected to fail. But instead succeeded") + } + + if _, ok := err.(libnetwork.NetworkNameError); !ok { + t.Fatalf("Did not fail with expected error. Actual error: %v", err) + } +} + +func TestNetworkName(t *testing.T) { + if !netutils.IsRunningInContainer() { + defer netutils.SetupTestNetNS(t)() + } + + _, err := createTestNetwork(bridgeNetType, "", options.Generic{}, options.Generic{}) + if err == nil { + t.Fatal("Expected to fail. But instead succeeded") + } + if err != libnetwork.ErrInvalidName { + t.Fatal("Expected to fail with ErrInvalidName error") + } + + networkName := "testnetwork" + n, err := createTestNetwork(bridgeNetType, networkName, options.Generic{}, options.Generic{}) + if err != nil { + t.Fatal(err) + } + + if n.Name() != networkName { + t.Fatalf("Expected network name %s, got %s", networkName, n.Name()) + } +} + +func TestNetworkType(t *testing.T) { + if !netutils.IsRunningInContainer() { + defer netutils.SetupTestNetNS(t)() + } + + n, err := createTestNetwork(bridgeNetType, "testnetwork", options.Generic{}, options.Generic{}) + if err != nil { + t.Fatal(err) + } + + if n.Type() != bridgeNetType { + t.Fatalf("Expected network type %s, got %s", bridgeNetType, n.Type()) + } +} + +func TestNetworkID(t *testing.T) { + if !netutils.IsRunningInContainer() { + defer netutils.SetupTestNetNS(t)() + } + + n, err := createTestNetwork(bridgeNetType, "testnetwork", options.Generic{}, options.Generic{}) + if err != nil { + t.Fatal(err) + } + + if n.ID() == "" { + t.Fatal("Expected non-empty network id") + } +} + +func TestDeleteNetworkWithActiveEndpoints(t *testing.T) { + if !netutils.IsRunningInContainer() { + defer netutils.SetupTestNetNS(t)() + } + + option := options.Generic{ + "BridgeName": bridgeName, + "AllowNonDefaultBridge": true} + + network, err := createTestNetwork(bridgeNetType, "testnetwork", options.Generic{}, option) + if err != nil { + t.Fatal(err) + } + + ep, err := network.CreateEndpoint("testep") + if err != nil { + t.Fatal(err) + } + + err = network.Delete() + if err == nil { + t.Fatal("Expected to fail. But instead succeeded") + } + + if _, ok := err.(*libnetwork.ActiveEndpointsError); !ok { + t.Fatalf("Did not fail with expected error. Actual error: %v", err) + } + + // Done testing. Now cleanup. + if err := ep.Delete(); err != nil { + t.Fatal(err) + } + + if err := network.Delete(); err != nil { + t.Fatal(err) + } +} + +func TestUnknownNetwork(t *testing.T) { + if !netutils.IsRunningInContainer() { + defer netutils.SetupTestNetNS(t)() + } + + option := options.Generic{ + "BridgeName": bridgeName, + "AllowNonDefaultBridge": true} + + network, err := createTestNetwork(bridgeNetType, "testnetwork", options.Generic{}, option) + if err != nil { + t.Fatal(err) + } + + err = network.Delete() + if err != nil { + t.Fatal(err) + } + + err = network.Delete() + if err == nil { + t.Fatal("Expected to fail. But instead succeeded") + } + + if _, ok := err.(*libnetwork.UnknownNetworkError); !ok { + t.Fatalf("Did not fail with expected error. Actual error: %v", err) + } +} + +func TestUnknownEndpoint(t *testing.T) { + if !netutils.IsRunningInContainer() { + defer netutils.SetupTestNetNS(t)() + } + + ip, subnet, err := net.ParseCIDR("192.168.100.1/24") + if err != nil { + t.Fatal(err) + } + subnet.IP = ip + + option := options.Generic{ + "BridgeName": bridgeName, + "AddressIPv4": subnet, + "AllowNonDefaultBridge": true} + + network, err := createTestNetwork(bridgeNetType, "testnetwork", options.Generic{}, option) + if err != nil { + t.Fatal(err) + } + + _, err = network.CreateEndpoint("") + if err == nil { + t.Fatal("Expected to fail. But instead succeeded") + } + if err != libnetwork.ErrInvalidName { + t.Fatal("Expected to fail with ErrInvalidName error") + } + + ep, err := network.CreateEndpoint("testep") + if err != nil { + t.Fatal(err) + } + + err = ep.Delete() + if err != nil { + t.Fatal(err) + } + + err = ep.Delete() + if err == nil { + t.Fatal("Expected to fail. But instead succeeded") + } + + if _, ok := err.(*libnetwork.UnknownEndpointError); !ok { + t.Fatalf("Did not fail with expected error. Actual error: %v", err) + } + + // Done testing. Now cleanup + if err := network.Delete(); err != nil { + t.Fatal(err) + } +} + +func TestNetworkEndpointsWalkers(t *testing.T) { + if !netutils.IsRunningInContainer() { + defer netutils.SetupTestNetNS(t)() + } + + controller, err := libnetwork.New() + if err != nil { + t.Fatal(err) + } + + err = controller.ConfigureNetworkDriver(bridgeNetType, getEmptyGenericOption()) + if err != nil { + t.Fatal(err) + } + + // Create network 1 and add 2 endpoint: ep11, ep12 + net1, err := controller.NewNetwork(bridgeNetType, "network1") + if err != nil { + t.Fatal(err) + } + ep11, err := net1.CreateEndpoint("ep11") + if err != nil { + t.Fatal(err) + } + ep12, err := net1.CreateEndpoint("ep12") + if err != nil { + t.Fatal(err) + } + + // Test list methods on net1 + epList1 := net1.Endpoints() + if len(epList1) != 2 { + t.Fatalf("Endpoints() returned wrong number of elements: %d instead of 2", len(epList1)) + } + // endpoint order is not guaranteed + for _, e := range epList1 { + if e != ep11 && e != ep12 { + t.Fatal("Endpoints() did not return all the expected elements") + } + } + + // Test Endpoint Walk method + var epName string + var epWanted libnetwork.Endpoint + wlk := func(ep libnetwork.Endpoint) bool { + if ep.Name() == epName { + epWanted = ep + return true + } + return false + } + + // Look for ep1 on network1 + epName = "ep11" + net1.WalkEndpoints(wlk) + if epWanted == nil { + t.Fatal(err) + } + if ep11 != epWanted { + t.Fatal(err) + } + + // Test Network Walk method + var netName string + var netWanted libnetwork.Network + nwWlk := func(nw libnetwork.Network) bool { + if nw.Name() == netName { + netWanted = nw + return true + } + return false + } + + // Look for network named "network1" + netName = "network1" + controller.WalkNetworks(nwWlk) + if netWanted == nil { + t.Fatal(err) + } + if net1 != netWanted { + t.Fatal(err) + } +} + +func TestControllerQuery(t *testing.T) { + if !netutils.IsRunningInContainer() { + defer netutils.SetupTestNetNS(t)() + } + + controller, err := libnetwork.New() + if err != nil { + t.Fatal(err) + } + + err = controller.ConfigureNetworkDriver(bridgeNetType, getEmptyGenericOption()) + if err != nil { + t.Fatal(err) + } + + // Create network 1 + net1, err := controller.NewNetwork(bridgeNetType, "network1") + if err != nil { + t.Fatal(err) + } + + _, err = controller.NetworkByName("") + if err == nil { + t.Fatalf("NetworkByName() succeeded with invalid target name") + } + if err != libnetwork.ErrInvalidName { + t.Fatalf("NetworkByName() failed with unexpected error: %v", err) + } + + _, err = controller.NetworkByID("") + if err == nil { + t.Fatalf("NetworkByID() succeeded with invalid target id") + } + if err != libnetwork.ErrInvalidID { + t.Fatalf("NetworkByID() failed with unexpected error: %v", err) + } + + g, err := controller.NetworkByID("network1") + if err == nil { + t.Fatalf("Unexpected success for NetworkByID(): %v", g) + } + if err != libnetwork.ErrNoSuchNetwork { + t.Fatalf("NetworkByID() failed with unexpected error: %v", err) + } + + g, err = controller.NetworkByName("network1") + if err != nil { + t.Fatalf("Unexpected failure for NetworkByName(): %v", err) + } + if g == nil { + t.Fatalf("NetworkByName() did not find the network") + } + + if g != net1 { + t.Fatalf("NetworkByName() returned the wrong network") + } + + g, err = controller.NetworkByID(net1.ID()) + if err != nil { + t.Fatalf("Unexpected failure for NetworkByID(): %v", err) + } + if net1 != g { + t.Fatalf("NetworkByID() returned unexpected element: %v", g) + } +} + +func TestNetworkQuery(t *testing.T) { + if !netutils.IsRunningInContainer() { + defer netutils.SetupTestNetNS(t)() + } + + controller, err := libnetwork.New() + if err != nil { + t.Fatal(err) + } + + err = controller.ConfigureNetworkDriver(bridgeNetType, getEmptyGenericOption()) + if err != nil { + t.Fatal(err) + } + + // Create network 1 and add 2 endpoint: ep11, ep12 + net1, err := controller.NewNetwork(bridgeNetType, "network1") + if err != nil { + t.Fatal(err) + } + ep11, err := net1.CreateEndpoint("ep11") + if err != nil { + t.Fatal(err) + } + ep12, err := net1.CreateEndpoint("ep12") + if err != nil { + t.Fatal(err) + } + + e, err := net1.EndpointByName("ep11") + if err != nil { + t.Fatal(err) + } + if ep11 != e { + t.Fatalf("EndpointByName() returned %v instead of %v", e, ep11) + } + + e, err = net1.EndpointByName("") + if err == nil { + t.Fatalf("EndpointByName() succeeded with invalid target name") + } + if err != libnetwork.ErrInvalidName { + t.Fatalf("EndpointByName() failed with unexpected error: %v", err) + } + + e, err = net1.EndpointByName("IamNotAnEndpoint") + if err == nil { + t.Fatalf("EndpointByName() succeeded with unknown target name") + } + if err != libnetwork.ErrNoSuchEndpoint { + t.Fatal(err) + } + if e != nil { + t.Fatalf("EndpointByName(): expected nil, got %v", e) + } + + e, err = net1.EndpointByID(ep12.ID()) + if err != nil { + t.Fatal(err) + } + if ep12 != e { + t.Fatalf("EndpointByID() returned %v instead of %v", e, ep12) + } + + e, err = net1.EndpointByID("") + if err == nil { + t.Fatalf("EndpointByID() succeeded with invalid target id") + } + if err != libnetwork.ErrInvalidID { + t.Fatalf("EndpointByID() failed with unexpected error: %v", err) + } +} + +const containerID = "valid_container" + +func TestEndpointJoin(t *testing.T) { + if !netutils.IsRunningInContainer() { + defer netutils.SetupTestNetNS(t)() + } + + n, err := createTestNetwork(bridgeNetType, "testnetwork", options.Generic{}, options.Generic{}) + if err != nil { + t.Fatal(err) + } + + ep, err := n.CreateEndpoint("ep1") + if err != nil { + t.Fatal(err) + } + + // Validate if ep.Info() only gives me IP address info and not names and gateway during CreateEndpoint() + info := ep.Info() + + for _, iface := range info.InterfaceList() { + if iface.Address().IP.To4() == nil { + t.Fatalf("Invalid IP address returned: %v", iface.Address()) + } + } + + if info.Gateway().To4() != nil { + t.Fatalf("Expected empty gateway for an empty endpoint. Instead found a gateway: %v", info.Gateway()) + } + + if info.SandboxKey() != "" { + t.Fatalf("Expected an empty sandbox key for an empty endpoint. Instead found a non-empty sandbox key: %s", info.SandboxKey()) + } + + _, err = ep.Join(containerID, + libnetwork.JoinOptionHostname("test"), + libnetwork.JoinOptionDomainname("docker.io"), + libnetwork.JoinOptionExtraHost("web", "192.168.0.1")) + if err != nil { + t.Fatal(err) + } + + defer func() { + err = ep.Leave(containerID) + if err != nil { + t.Fatal(err) + } + }() + + // Validate if ep.Info() only gives valid gateway and sandbox key after has container has joined. + info = ep.Info() + if info.Gateway().To4() == nil { + t.Fatalf("Expected a valid gateway for a joined endpoint. Instead found an invalid gateway: %v", info.Gateway()) + } + + if info.SandboxKey() == "" { + t.Fatalf("Expected an non-empty sandbox key for a joined endpoint. Instead found a empty sandbox key") + } +} + +func TestEndpointJoinInvalidContainerId(t *testing.T) { + if !netutils.IsRunningInContainer() { + defer netutils.SetupTestNetNS(t)() + } + + n, err := createTestNetwork(bridgeNetType, "testnetwork", options.Generic{}, options.Generic{}) + if err != nil { + t.Fatal(err) + } + + ep, err := n.CreateEndpoint("ep1") + if err != nil { + t.Fatal(err) + } + + _, err = ep.Join("") + if err == nil { + t.Fatal("Expected to fail join with empty container id string") + } + + if _, ok := err.(libnetwork.InvalidContainerIDError); !ok { + t.Fatalf("Failed for unexpected reason: %v", err) + } +} + +func TestEndpointDeleteWithActiveContainer(t *testing.T) { + if !netutils.IsRunningInContainer() { + defer netutils.SetupTestNetNS(t)() + } + + n, err := createTestNetwork(bridgeNetType, "testnetwork", options.Generic{}, options.Generic{}) + if err != nil { + t.Fatal(err) + } + + ep, err := n.CreateEndpoint("ep1") + if err != nil { + t.Fatal(err) + } + + _, err = ep.Join(containerID, + libnetwork.JoinOptionHostname("test"), + libnetwork.JoinOptionDomainname("docker.io"), + libnetwork.JoinOptionExtraHost("web", "192.168.0.1")) + if err != nil { + t.Fatal(err) + } + defer func() { + err = ep.Leave(containerID) + if err != nil { + t.Fatal(err) + } + + err = ep.Delete() + if err != nil { + t.Fatal(err) + } + }() + + err = ep.Delete() + if err == nil { + t.Fatal("Expected to fail. But instead succeeded") + } + + if _, ok := err.(*libnetwork.ActiveContainerError); !ok { + t.Fatalf("Did not fail with expected error. Actual error: %v", err) + } +} + +func TestEndpointMultipleJoins(t *testing.T) { + if !netutils.IsRunningInContainer() { + defer netutils.SetupTestNetNS(t)() + } + + n, err := createTestNetwork(bridgeNetType, "testnetwork", options.Generic{}, options.Generic{}) + if err != nil { + t.Fatal(err) + } + + ep, err := n.CreateEndpoint("ep1") + if err != nil { + t.Fatal(err) + } + + _, err = ep.Join(containerID, + libnetwork.JoinOptionHostname("test"), + libnetwork.JoinOptionDomainname("docker.io"), + libnetwork.JoinOptionExtraHost("web", "192.168.0.1")) + + if err != nil { + t.Fatal(err) + } + defer func() { + err = ep.Leave(containerID) + if err != nil { + t.Fatal(err) + } + }() + + _, err = ep.Join("container2") + if err == nil { + t.Fatal("Expected to fail multiple joins for the same endpoint") + } + + if err != libnetwork.ErrInvalidJoin { + t.Fatalf("Failed for unexpected reason: %v", err) + } +} + +func TestEndpointInvalidLeave(t *testing.T) { + if !netutils.IsRunningInContainer() { + defer netutils.SetupTestNetNS(t)() + } + + n, err := createTestNetwork(bridgeNetType, "testnetwork", options.Generic{}, options.Generic{}) + if err != nil { + t.Fatal(err) + } + + ep, err := n.CreateEndpoint("ep1") + if err != nil { + t.Fatal(err) + } + + err = ep.Leave(containerID) + if err == nil { + t.Fatal("Expected to fail leave from an endpoint which has no active join") + } + + if _, ok := err.(libnetwork.InvalidContainerIDError); !ok { + if err != libnetwork.ErrNoContainer { + t.Fatalf("Failed for unexpected reason: %v", err) + } + } + + _, err = ep.Join(containerID, + libnetwork.JoinOptionHostname("test"), + libnetwork.JoinOptionDomainname("docker.io"), + libnetwork.JoinOptionExtraHost("web", "192.168.0.1")) + + if err != nil { + t.Fatal(err) + } + defer func() { + err = ep.Leave(containerID) + if err != nil { + t.Fatal(err) + } + }() + + err = ep.Leave("") + if err == nil { + t.Fatal("Expected to fail leave with empty container id") + } + + if _, ok := err.(libnetwork.InvalidContainerIDError); !ok { + t.Fatalf("Failed for unexpected reason: %v", err) + } + + err = ep.Leave("container2") + if err == nil { + t.Fatal("Expected to fail leave with wrong container id") + } + + if _, ok := err.(libnetwork.InvalidContainerIDError); !ok { + t.Fatalf("Failed for unexpected reason: %v", err) + } + +} + +func TestEndpointUpdateParent(t *testing.T) { + if !netutils.IsRunningInContainer() { + defer netutils.SetupTestNetNS(t)() + } + + n, err := createTestNetwork("bridge", "testnetwork", options.Generic{}, options.Generic{}) + if err != nil { + t.Fatal(err) + } + + ep1, err := n.CreateEndpoint("ep1", nil) + if err != nil { + t.Fatal(err) + } + + _, err = ep1.Join(containerID, + libnetwork.JoinOptionHostname("test1"), + libnetwork.JoinOptionDomainname("docker.io"), + libnetwork.JoinOptionExtraHost("web", "192.168.0.1")) + + if err != nil { + t.Fatal(err) + } + defer func() { + err = ep1.Leave(containerID) + if err != nil { + t.Fatal(err) + } + }() + + ep2, err := n.CreateEndpoint("ep2", nil) + if err != nil { + t.Fatal(err) + } + + _, err = ep2.Join("container2", + libnetwork.JoinOptionHostname("test2"), + libnetwork.JoinOptionDomainname("docker.io"), + libnetwork.JoinOptionHostsPath("/var/lib/docker/test_network/container2/hosts"), + libnetwork.JoinOptionParentUpdate(ep1.ID(), "web", "192.168.0.2")) + + if err != nil { + t.Fatal(err) + } + + defer func() { + err = ep2.Leave("container2") + if err != nil { + t.Fatal(err) + } + }() + +} + +func TestEnableIPv6(t *testing.T) { + if !netutils.IsRunningInContainer() { + defer netutils.SetupTestNetNS(t)() + } + + tmpResolvConf := []byte("search pommesfrites.fr\nnameserver 12.34.56.78\nnameserver 2001:4860:4860::8888") + //take a copy of resolv.conf for restoring after test completes + resolvConfSystem, err := ioutil.ReadFile("/etc/resolv.conf") + if err != nil { + t.Fatal(err) + } + //cleanup + defer func() { + if err := ioutil.WriteFile("/etc/resolv.conf", resolvConfSystem, 0644); err != nil { + t.Fatal(err) + } + }() + + ip, cidrv6, err := net.ParseCIDR("fe80::1/64") + if err != nil { + t.Fatal(err) + } + cidrv6.IP = ip + + netOption := options.Generic{ + netlabel.EnableIPv6: true, + netlabel.GenericData: options.Generic{ + "FixedCIDRv6": cidrv6, + }, + } + + n, err := createTestNetwork("bridge", "testnetwork", options.Generic{}, netOption) + if err != nil { + t.Fatal(err) + } + + ep1, err := n.CreateEndpoint("ep1", nil) + if err != nil { + t.Fatal(err) + } + + if err := ioutil.WriteFile("/etc/resolv.conf", tmpResolvConf, 0644); err != nil { + t.Fatal(err) + } + + resolvConfPath := "/tmp/libnetwork_test/resolv.conf" + defer os.Remove(resolvConfPath) + + _, err = ep1.Join(containerID, + libnetwork.JoinOptionResolvConfPath(resolvConfPath)) + + if err != nil { + t.Fatal(err) + } + defer func() { + err = ep1.Leave(containerID) + if err != nil { + t.Fatal(err) + } + }() + + content, err := ioutil.ReadFile(resolvConfPath) + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(content, tmpResolvConf) { + t.Fatalf("Expected %s, Got %s", string(tmpResolvConf), string(content)) + } + + if err != nil { + t.Fatal(err) + } +} + +func TestResolvConf(t *testing.T) { + if !netutils.IsRunningInContainer() { + defer netutils.SetupTestNetNS(t)() + } + + tmpResolvConf1 := []byte("search pommesfrites.fr\nnameserver 12.34.56.78\nnameserver 2001:4860:4860::8888") + expectedResolvConf1 := []byte("search pommesfrites.fr\nnameserver 12.34.56.78\n") + tmpResolvConf2 := []byte("search pommesfrites.fr\nnameserver 112.34.56.78\nnameserver 2001:4860:4860::8888") + expectedResolvConf2 := []byte("search pommesfrites.fr\nnameserver 112.34.56.78\n") + tmpResolvConf3 := []byte("search pommesfrites.fr\nnameserver 113.34.56.78\n") + + //take a copy of resolv.conf for restoring after test completes + resolvConfSystem, err := ioutil.ReadFile("/etc/resolv.conf") + if err != nil { + t.Fatal(err) + } + //cleanup + defer func() { + if err := ioutil.WriteFile("/etc/resolv.conf", resolvConfSystem, 0644); err != nil { + t.Fatal(err) + } + }() + + n, err := createTestNetwork("bridge", "testnetwork", options.Generic{}, options.Generic{}) + if err != nil { + t.Fatal(err) + } + + ep1, err := n.CreateEndpoint("ep1", nil) + if err != nil { + t.Fatal(err) + } + + if err := ioutil.WriteFile("/etc/resolv.conf", tmpResolvConf1, 0644); err != nil { + t.Fatal(err) + } + + resolvConfPath := "/tmp/libnetwork_test/resolv.conf" + defer os.Remove(resolvConfPath) + + _, err = ep1.Join(containerID, + libnetwork.JoinOptionResolvConfPath(resolvConfPath)) + if err != nil { + t.Fatal(err) + } + defer func() { + err = ep1.Leave(containerID) + if err != nil { + t.Fatal(err) + } + }() + + content, err := ioutil.ReadFile(resolvConfPath) + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(content, expectedResolvConf1) { + t.Fatalf("Expected %s, Got %s", string(expectedResolvConf1), string(content)) + } + + err = ep1.Leave(containerID) + if err != nil { + t.Fatal(err) + } + + if err := ioutil.WriteFile("/etc/resolv.conf", tmpResolvConf2, 0644); err != nil { + t.Fatal(err) + } + + _, err = ep1.Join(containerID, + libnetwork.JoinOptionResolvConfPath(resolvConfPath)) + if err != nil { + t.Fatal(err) + } + + content, err = ioutil.ReadFile(resolvConfPath) + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(content, expectedResolvConf2) { + t.Fatalf("Expected %s, Got %s", string(expectedResolvConf2), string(content)) + } + + if err := ioutil.WriteFile(resolvConfPath, tmpResolvConf3, 0644); err != nil { + t.Fatal(err) + } + + err = ep1.Leave(containerID) + if err != nil { + t.Fatal(err) + } + + _, err = ep1.Join(containerID, + libnetwork.JoinOptionResolvConfPath(resolvConfPath)) + if err != nil { + t.Fatal(err) + } + + content, err = ioutil.ReadFile(resolvConfPath) + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(content, tmpResolvConf3) { + t.Fatalf("Expected %s, Got %s", string(tmpResolvConf3), string(content)) + } +} + +func TestInvalidRemoteDriver(t *testing.T) { + if !netutils.IsRunningInContainer() { + t.Skip("Skipping test when not running inside a Container") + } + + mux := http.NewServeMux() + server := httptest.NewServer(mux) + if server == nil { + t.Fatal("Failed to start a HTTP Server") + } + defer server.Close() + + type pluginRequest struct { + name string + } + + mux.HandleFunc("/Plugin.Activate", func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json") + fmt.Fprintln(w, `{"Implements": ["InvalidDriver"]}`) + }) + + if err := os.MkdirAll("/usr/share/docker/plugins", 0755); err != nil { + t.Fatal(err) + } + defer func() { + if err := os.RemoveAll("/usr/share/docker/plugins"); err != nil { + t.Fatal(err) + } + }() + + if err := ioutil.WriteFile("/usr/share/docker/plugins/invalid-network-driver.spec", []byte(server.URL), 0644); err != nil { + t.Fatal(err) + } + + controller, err := libnetwork.New() + if err != nil { + t.Fatal(err) + } + + _, err = controller.NewNetwork("invalid-network-driver", "dummy", + libnetwork.NetworkOptionGeneric(getEmptyGenericOption())) + if err == nil { + t.Fatal("Expected to fail. But instead succeeded") + } + + if err != plugins.ErrNotImplements { + t.Fatalf("Did not fail with expected error. Actual error: %v", err) + } +} + +func TestValidRemoteDriver(t *testing.T) { + if !netutils.IsRunningInContainer() { + t.Skip("Skipping test when not running inside a Container") + } + + mux := http.NewServeMux() + server := httptest.NewServer(mux) + if server == nil { + t.Fatal("Failed to start a HTTP Server") + } + defer server.Close() + + type pluginRequest struct { + name string + } + + mux.HandleFunc("/Plugin.Activate", func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json") + fmt.Fprintf(w, `{"Implements": ["%s"]}`, driverapi.NetworkPluginEndpointType) + }) + + if err := os.MkdirAll("/usr/share/docker/plugins", 0755); err != nil { + t.Fatal(err) + } + defer func() { + if err := os.RemoveAll("/usr/share/docker/plugins"); err != nil { + t.Fatal(err) + } + }() + + if err := ioutil.WriteFile("/usr/share/docker/plugins/valid-network-driver.spec", []byte(server.URL), 0644); err != nil { + t.Fatal(err) + } + + controller, err := libnetwork.New() + if err != nil { + t.Fatal(err) + } + + _, err = controller.NewNetwork("valid-network-driver", "dummy", + libnetwork.NetworkOptionGeneric(getEmptyGenericOption())) + if err != nil && err != driverapi.ErrNotImplemented { + t.Fatal(err) + } +} + +var ( + once sync.Once + ctrlr libnetwork.NetworkController + start = make(chan struct{}) + done = make(chan chan struct{}, numThreads-1) + origns = netns.None() + testns = netns.None() +) + +const ( + iterCnt = 25 + numThreads = 3 + first = 1 + last = numThreads + debug = false +) + +func createGlobalInstance(t *testing.T) { + var err error + defer close(start) + + origns, err = netns.Get() + if err != nil { + t.Fatal(err) + } + + if netutils.IsRunningInContainer() { + testns = origns + } else { + testns, err = netns.New() + if err != nil { + t.Fatal(err) + } + } + + ctrlr, err = libnetwork.New() + if err != nil { + t.Fatal(err) + } + + err = ctrlr.ConfigureNetworkDriver(bridgeNetType, getEmptyGenericOption()) + if err != nil { + t.Fatal("configure driver") + } + + net, err := ctrlr.NewNetwork(bridgeNetType, "network1") + if err != nil { + t.Fatal("new network") + } + + _, err = net.CreateEndpoint("ep1") + if err != nil { + t.Fatal("createendpoint") + } +} + +func debugf(format string, a ...interface{}) (int, error) { + if debug { + return fmt.Printf(format, a...) + } + + return 0, nil +} + +func parallelJoin(t *testing.T, ep libnetwork.Endpoint, thrNumber int) { + debugf("J%d.", thrNumber) + _, err := ep.Join("racing_container") + runtime.LockOSThread() + if err != nil { + if err != libnetwork.ErrNoContainer && err != libnetwork.ErrInvalidJoin { + t.Fatal(err) + } + debugf("JE%d(%v).", thrNumber, err) + } + debugf("JD%d.", thrNumber) +} + +func parallelLeave(t *testing.T, ep libnetwork.Endpoint, thrNumber int) { + debugf("L%d.", thrNumber) + err := ep.Leave("racing_container") + runtime.LockOSThread() + if err != nil { + if err != libnetwork.ErrNoContainer && err != libnetwork.ErrInvalidJoin { + t.Fatal(err) + } + debugf("LE%d(%v).", thrNumber, err) + } + debugf("LD%d.", thrNumber) +} + +func runParallelTests(t *testing.T, thrNumber int) { + var err error + + t.Parallel() + + pTest := flag.Lookup("test.parallel") + if pTest == nil { + t.Skip("Skipped because test.parallel flag not set;") + } + numParallel, err := strconv.Atoi(pTest.Value.String()) + if err != nil { + t.Fatal(err) + } + if numParallel < numThreads { + t.Skip("Skipped because t.parallel was less than ", numThreads) + } + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + if thrNumber == first { + createGlobalInstance(t) + } + + if thrNumber != first { + select { + case <-start: + } + + thrdone := make(chan struct{}) + done <- thrdone + defer close(thrdone) + + if thrNumber == last { + defer close(done) + } + + err = netns.Set(testns) + if err != nil { + t.Fatal(err) + } + } + defer netns.Set(origns) + + net, err := ctrlr.NetworkByName("network1") + if err != nil { + t.Fatal(err) + } + if net == nil { + t.Fatal("Could not find network1") + } + + ep, err := net.EndpointByName("ep1") + if err != nil { + t.Fatal(err) + } + if ep == nil { + t.Fatal("Got nil ep with no error") + } + + for i := 0; i < iterCnt; i++ { + parallelJoin(t, ep, thrNumber) + parallelLeave(t, ep, thrNumber) + } + + debugf("\n") + + if thrNumber == first { + for thrdone := range done { + select { + case <-thrdone: + } + } + + testns.Close() + err = ep.Delete() + if err != nil { + t.Fatal(err) + } + } +} + +func TestParallel1(t *testing.T) { + runParallelTests(t, 1) +} + +func TestParallel2(t *testing.T) { + runParallelTests(t, 2) +} + +func TestParallel3(t *testing.T) { + runParallelTests(t, 3) +} diff --git a/vendor/src/github.com/docker/libnetwork/netlabel/labels.go b/vendor/src/github.com/docker/libnetwork/netlabel/labels.go new file mode 100644 index 0000000000..adbabbc475 --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/netlabel/labels.go @@ -0,0 +1,18 @@ +package netlabel + +const ( + // GenericData constant that helps to identify an option as a Generic constant + GenericData = "io.docker.network.generic" + + // PortMap constant represents Port Mapping + PortMap = "io.docker.network.endpoint.portmap" + + // MacAddress constant represents Mac Address config of a Container + MacAddress = "io.docker.network.endpoint.macaddress" + + // ExposedPorts constant represents exposedports of a Container + ExposedPorts = "io.docker.network.endpoint.exposedports" + + //EnableIPv6 constant represents enabling IPV6 at network level + EnableIPv6 = "io.docker.network.enable_ipv6" +) diff --git a/vendor/src/github.com/docker/libnetwork/netutils/test_utils.go b/vendor/src/github.com/docker/libnetwork/netutils/test_utils.go new file mode 100644 index 0000000000..d0a2fab789 --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/netutils/test_utils.go @@ -0,0 +1,41 @@ +package netutils + +import ( + "flag" + "runtime" + "syscall" + "testing" +) + +var runningInContainer = flag.Bool("incontainer", false, "Indicates if the test is running in a container") + +// IsRunningInContainer returns whether the test is running inside a container. +func IsRunningInContainer() bool { + return (*runningInContainer) +} + +// SetupTestNetNS joins a new network namespace, and returns its associated +// teardown function. +// +// Example usage: +// +// defer SetupTestNetNS(t)() +// +func SetupTestNetNS(t *testing.T) func() { + runtime.LockOSThread() + if err := syscall.Unshare(syscall.CLONE_NEWNET); err != nil { + t.Fatalf("Failed to enter netns: %v", err) + } + + fd, err := syscall.Open("/proc/self/ns/net", syscall.O_RDONLY, 0) + if err != nil { + t.Fatal("Failed to open netns file") + } + + return func() { + if err := syscall.Close(fd); err != nil { + t.Logf("Warning: netns closing failed (%v)", err) + } + runtime.UnlockOSThread() + } +} diff --git a/vendor/src/github.com/docker/libnetwork/netutils/utils.go b/vendor/src/github.com/docker/libnetwork/netutils/utils.go new file mode 100644 index 0000000000..0c3706d3e4 --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/netutils/utils.go @@ -0,0 +1,324 @@ +// Network utility functions. + +package netutils + +import ( + "bytes" + "crypto/rand" + "encoding/hex" + "errors" + "fmt" + "io" + "net" + "strings" + + "github.com/vishvananda/netlink" +) + +var ( + // ErrNetworkOverlapsWithNameservers preformatted error + ErrNetworkOverlapsWithNameservers = errors.New("requested network overlaps with nameserver") + // ErrNetworkOverlaps preformatted error + ErrNetworkOverlaps = errors.New("requested network overlaps with existing network") + // ErrNoDefaultRoute preformatted error + ErrNoDefaultRoute = errors.New("no default route") + + networkGetRoutesFct = netlink.RouteList +) + +// ErrInvalidProtocolBinding is returned when the port binding protocol is not valid. +type ErrInvalidProtocolBinding string + +func (ipb ErrInvalidProtocolBinding) Error() string { + return fmt.Sprintf("invalid transport protocol: %s", string(ipb)) +} + +// TransportPort represent a local Layer 4 endpoint +type TransportPort struct { + Proto Protocol + Port uint16 +} + +// GetCopy returns a copy of this TransportPort structure instance +func (t *TransportPort) GetCopy() TransportPort { + return TransportPort{Proto: t.Proto, Port: t.Port} +} + +// PortBinding represent a port binding between the container an the host +type PortBinding struct { + Proto Protocol + IP net.IP + Port uint16 + HostIP net.IP + HostPort uint16 +} + +// HostAddr returns the host side transport address +func (p PortBinding) HostAddr() (net.Addr, error) { + switch p.Proto { + case UDP: + return &net.UDPAddr{IP: p.HostIP, Port: int(p.HostPort)}, nil + case TCP: + return &net.TCPAddr{IP: p.HostIP, Port: int(p.HostPort)}, nil + default: + return nil, ErrInvalidProtocolBinding(p.Proto.String()) + } +} + +// ContainerAddr returns the container side transport address +func (p PortBinding) ContainerAddr() (net.Addr, error) { + switch p.Proto { + case UDP: + return &net.UDPAddr{IP: p.IP, Port: int(p.Port)}, nil + case TCP: + return &net.TCPAddr{IP: p.IP, Port: int(p.Port)}, nil + default: + return nil, ErrInvalidProtocolBinding(p.Proto.String()) + } +} + +// GetCopy returns a copy of this PortBinding structure instance +func (p *PortBinding) GetCopy() PortBinding { + return PortBinding{ + Proto: p.Proto, + IP: GetIPCopy(p.IP), + Port: p.Port, + HostIP: GetIPCopy(p.HostIP), + HostPort: p.HostPort, + } +} + +// Equal checks if this instance of PortBinding is equal to the passed one +func (p *PortBinding) Equal(o *PortBinding) bool { + if p == o { + return true + } + + if o == nil { + return false + } + + if p.Proto != o.Proto || p.Port != o.Port || p.HostPort != o.HostPort { + return false + } + + if p.IP != nil { + if !p.IP.Equal(o.IP) { + return false + } + } else { + if o.IP != nil { + return false + } + } + + if p.HostIP != nil { + if !p.HostIP.Equal(o.HostIP) { + return false + } + } else { + if o.HostIP != nil { + return false + } + } + + return true +} + +const ( + // ICMP is for the ICMP ip protocol + ICMP = 1 + // TCP is for the TCP ip protocol + TCP = 6 + // UDP is for the UDP ip protocol + UDP = 17 +) + +// Protocol represents a IP protocol number +type Protocol uint8 + +func (p Protocol) String() string { + switch p { + case ICMP: + return "icmp" + case TCP: + return "tcp" + case UDP: + return "udp" + default: + return fmt.Sprintf("%d", p) + } +} + +// ParseProtocol returns the respective Protocol type for the passed string +func ParseProtocol(s string) Protocol { + switch strings.ToLower(s) { + case "icmp": + return ICMP + case "udp": + return UDP + case "tcp": + return TCP + default: + return 0 + } +} + +// CheckNameserverOverlaps checks whether the passed network overlaps with any of the nameservers +func CheckNameserverOverlaps(nameservers []string, toCheck *net.IPNet) error { + if len(nameservers) > 0 { + for _, ns := range nameservers { + _, nsNetwork, err := net.ParseCIDR(ns) + if err != nil { + return err + } + if NetworkOverlaps(toCheck, nsNetwork) { + return ErrNetworkOverlapsWithNameservers + } + } + } + return nil +} + +// CheckRouteOverlaps checks whether the passed network overlaps with any existing routes +func CheckRouteOverlaps(toCheck *net.IPNet) error { + 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 + } + } + return nil +} + +// NetworkOverlaps detects overlap between one IPNet and another +func NetworkOverlaps(netX *net.IPNet, netY *net.IPNet) bool { + // Check if both netX and netY are ipv4 or ipv6 + if (netX.IP.To4() != nil && netY.IP.To4() != nil) || + (netX.IP.To4() == nil && netY.IP.To4() == nil) { + if firstIP, _ := NetworkRange(netX); netY.Contains(firstIP) { + return true + } + if firstIP, _ := NetworkRange(netY); netX.Contains(firstIP) { + return true + } + } + return false +} + +// NetworkRange calculates the first and last IP addresses in an IPNet +func NetworkRange(network *net.IPNet) (net.IP, net.IP) { + var netIP net.IP + if network.IP.To4() != nil { + netIP = network.IP.To4() + } else if network.IP.To16() != nil { + netIP = network.IP.To16() + } else { + return nil, nil + } + + lastIP := make([]byte, len(netIP), len(netIP)) + for i := 0; i < len(netIP); i++ { + lastIP[i] = netIP[i] | ^network.Mask[i] + } + return netIP.Mask(network.Mask), net.IP(lastIP) +} + +// GetIfaceAddr returns the first IPv4 address and slice of IPv6 addresses for the specified network interface +func GetIfaceAddr(name string) (net.Addr, []net.Addr, error) { + iface, err := net.InterfaceByName(name) + if err != nil { + return nil, nil, err + } + addrs, err := iface.Addrs() + if err != nil { + return nil, nil, err + } + var addrs4 []net.Addr + var addrs6 []net.Addr + for _, addr := range addrs { + ip := (addr.(*net.IPNet)).IP + if ip4 := ip.To4(); ip4 != nil { + addrs4 = append(addrs4, addr) + } else if ip6 := ip.To16(); len(ip6) == net.IPv6len { + addrs6 = append(addrs6, addr) + } + } + switch { + case len(addrs4) == 0: + return nil, nil, fmt.Errorf("Interface %v has no IPv4 addresses", name) + case len(addrs4) > 1: + fmt.Printf("Interface %v has more than 1 IPv4 address. Defaulting to using %v\n", + name, (addrs4[0].(*net.IPNet)).IP) + } + return addrs4[0], addrs6, nil +} + +// GenerateRandomMAC returns a new 6-byte(48-bit) hardware address (MAC) +func GenerateRandomMAC() net.HardwareAddr { + hw := make(net.HardwareAddr, 6) + // The first byte of the MAC address has to comply with these rules: + // 1. Unicast: Set the least-significant bit to 0. + // 2. Address is locally administered: Set the second-least-significant bit (U/L) to 1. + // 3. As "small" as possible: The veth address has to be "smaller" than the bridge address. + hw[0] = 0x02 + // The first 24 bits of the MAC represent the Organizationally Unique Identifier (OUI). + // Since this address is locally administered, we can do whatever we want as long as + // it doesn't conflict with other addresses. + hw[1] = 0x42 + // Randomly generate the remaining 4 bytes (2^32) + _, err := rand.Read(hw[2:]) + if err != nil { + return nil + } + return hw +} + +// GenerateRandomName returns a new name joined with a prefix. This size +// specified is used to truncate the randomly generated value +func GenerateRandomName(prefix string, size int) (string, error) { + id := make([]byte, 32) + if _, err := io.ReadFull(rand.Reader, id); err != nil { + return "", err + } + return prefix + hex.EncodeToString(id)[:size], nil +} + +// GetMacCopy returns a copy of the passed MAC address +func GetMacCopy(from net.HardwareAddr) net.HardwareAddr { + to := make(net.HardwareAddr, len(from)) + copy(to, from) + return to +} + +// GetIPCopy returns a copy of the passed IP address +func GetIPCopy(from net.IP) net.IP { + to := make(net.IP, len(from)) + copy(to, from) + return to +} + +// GetIPNetCopy returns a copy of the passed IP Network +func GetIPNetCopy(from *net.IPNet) *net.IPNet { + if from == nil { + return nil + } + bm := make(net.IPMask, len(from.Mask)) + copy(bm, from.Mask) + return &net.IPNet{IP: GetIPCopy(from.IP), Mask: bm} +} + +// CompareIPNet returns equal if the two IP Networks are equal +func CompareIPNet(a, b *net.IPNet) bool { + if a == b { + return true + } + if a == nil || b == nil { + return false + } + return a.IP.Equal(b.IP) && bytes.Equal(a.Mask, b.Mask) +} diff --git a/daemon/networkdriver/network_test.go b/vendor/src/github.com/docker/libnetwork/netutils/utils_test.go similarity index 52% rename from daemon/networkdriver/network_test.go rename to vendor/src/github.com/docker/libnetwork/netutils/utils_test.go index 1a6336b5de..4ef8d48040 100644 --- a/daemon/networkdriver/network_test.go +++ b/vendor/src/github.com/docker/libnetwork/netutils/utils_test.go @@ -1,9 +1,11 @@ -package networkdriver +package netutils import ( - "github.com/docker/libcontainer/netlink" + "bytes" "net" "testing" + + "github.com/vishvananda/netlink" ) func TestNonOverlapingNameservers(t *testing.T) { @@ -39,13 +41,13 @@ func TestCheckRouteOverlaps(t *testing.T) { defer func() { networkGetRoutesFct = orig }() - networkGetRoutesFct = func() ([]netlink.Route, error) { + networkGetRoutesFct = func(netlink.Link, int) ([]netlink.Route, error) { routesData := []string{"10.0.2.0/32", "10.0.3.0/24", "10.0.42.0/24", "172.16.42.0/24", "192.168.142.0/24"} routes := []netlink.Route{} for _, addr := range routesData { _, netX, _ := net.ParseCIDR(addr) - routes = append(routes, netlink.Route{IPNet: netX}) + routes = append(routes, netlink.Route{Dst: netX}) } return routes, nil } @@ -173,3 +175,169 @@ func TestNetworkRange(t *testing.T) { t.Error(last.String()) } } + +// Test veth name generation "veth"+rand (e.g.veth0f60e2c) +func TestGenerateRandomName(t *testing.T) { + name1, err := GenerateRandomName("veth", 7) + if err != nil { + t.Fatal(err) + } + // veth plus generated append equals a len of 11 + if len(name1) != 11 { + t.Fatalf("Expected 11 characters, instead received %d characters", len(name1)) + } + name2, err := GenerateRandomName("veth", 7) + if err != nil { + t.Fatal(err) + } + // Fail if the random generated names equal one another + if name1 == name2 { + t.Fatalf("Expected differing values but received %s and %s", name1, name2) + } +} + +// Test mac generation. +func TestUtilGenerateRandomMAC(t *testing.T) { + mac1 := GenerateRandomMAC() + mac2 := GenerateRandomMAC() + // ensure bytes are unique + if bytes.Equal(mac1, mac2) { + t.Fatalf("mac1 %s should not equal mac2 %s", mac1, mac2) + } + // existing tests check string functionality so keeping the pattern + if mac1.String() == mac2.String() { + t.Fatalf("mac1 %s should not equal mac2 %s", mac1, mac2) + } +} + +func TestCompareIPNet(t *testing.T) { + if CompareIPNet(nil, nil) == false { + t.Fatalf("Failed to detect two nil net.IPNets are equal") + } + + _, net1, _ := net.ParseCIDR("192.168.30.22/24") + if CompareIPNet(net1, net1) == false { + t.Fatalf("Failed to detect same net.IPNet pointers equality") + } + + _, net2, _ := net.ParseCIDR("192.168.30.22/24") + if CompareIPNet(net1, net2) == false { + t.Fatalf("Failed to detect same net.IPNet object equality") + } + + _, net3, _ := net.ParseCIDR("192.168.30.33/24") + if CompareIPNet(net1, net3) == false { + t.Fatalf("Failed to detect semantically equivalent net.IPNets") + } + + _, net3, _ = net.ParseCIDR("192.168.31.33/24") + if CompareIPNet(net2, net3) == true { + t.Fatalf("Failed to detect different net.IPNets") + } +} + +func TestIPCopyFunctions(t *testing.T) { + ip := net.ParseIP("172.28.30.134") + cp := GetIPCopy(ip) + + if !ip.Equal(cp) { + t.Fatalf("Failed to return a copy of net.IP") + } + + if &ip == &cp { + t.Fatalf("Failed to return a true copy of net.IP") + } +} + +func TestNetIPCopyFunctions(t *testing.T) { + _, net, _ := net.ParseCIDR("192.168.30.23/24") + cp := GetIPNetCopy(net) + + if CompareIPNet(net, cp) == false { + t.Fatalf("Failed to return a copy of net.IPNet") + } + + if net == cp { + t.Fatalf("Failed to return a true copy of net.IPNet") + } +} + +func TestPortBindingEqual(t *testing.T) { + pb1 := &PortBinding{ + Proto: TCP, + IP: net.ParseIP("172.17.0.1"), + Port: 80, + HostIP: net.ParseIP("192.168.100.1"), + HostPort: 8080, + } + + pb2 := &PortBinding{ + Proto: UDP, + IP: net.ParseIP("172.17.0.1"), + Port: 22, + HostIP: net.ParseIP("192.168.100.1"), + HostPort: 2222, + } + if !pb1.Equal(pb1) { + t.Fatalf("PortBinding.Equal() returned false negative") + } + + if pb1.Equal(nil) { + t.Fatalf("PortBinding.Equal() returned false negative") + } + + if pb1.Equal(pb2) { + t.Fatalf("PortBinding.Equal() returned false positive") + } + + if pb1.Equal(pb2) != pb2.Equal(pb1) { + t.Fatalf("PortBinding.Equal() failed commutative check") + } +} + +func TestPortBindingGetCopy(t *testing.T) { + pb := &PortBinding{ + Proto: TCP, + IP: net.ParseIP("172.17.0.1"), + Port: 80, + HostIP: net.ParseIP("192.168.100.1"), + HostPort: 8080, + } + cp := pb.GetCopy() + + if !pb.Equal(&cp) { + t.Fatalf("Failed to return a copy of PortBinding") + } + + if pb == &cp { + t.Fatalf("Failed to return a true copy of PortBinding") + } +} + +func TestPortBindingContainerAddr(t *testing.T) { + pb := PortBinding{ + Proto: TCP, + IP: net.ParseIP("172.17.0.1"), + Port: 80, + HostIP: net.ParseIP("192.168.100.1"), + HostPort: 8080, + } + + container, err := pb.ContainerAddr() + + if err != nil { + t.Fatal(err) + } + + switch netAddr := container.(type) { + case *net.TCPAddr: + if !pb.IP.Equal(netAddr.IP) { + t.Fatalf("PortBinding.ContainerAddr() Failed to return a ContainerAddr") + } + if int(pb.Port) != netAddr.Port { + t.Fatalf("PortBinding.ContainerAddr() Failed to return a ContainerAddr") + } + case *net.UDPAddr: + t.Fatalf("PortBinding.ContainerAddr() Failed to check correct proto") + } +} diff --git a/vendor/src/github.com/docker/libnetwork/network.go b/vendor/src/github.com/docker/libnetwork/network.go new file mode 100644 index 0000000000..e2955c8d65 --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/network.go @@ -0,0 +1,207 @@ +package libnetwork + +import ( + "sync" + + "github.com/docker/docker/pkg/stringid" + "github.com/docker/libnetwork/driverapi" + "github.com/docker/libnetwork/netlabel" + "github.com/docker/libnetwork/options" + "github.com/docker/libnetwork/types" +) + +// A Network represents a logical connectivity zone that containers may +// join using the Link method. A Network is managed by a specific driver. +type Network interface { + // A user chosen name for this network. + Name() string + + // A system generated id for this network. + ID() string + + // The type of network, which corresponds to its managing driver. + Type() string + + // Create a new endpoint to this network symbolically identified by the + // specified unique name. The options parameter carry driver specific options. + // Labels support will be added in the near future. + CreateEndpoint(name string, options ...EndpointOption) (Endpoint, error) + + // Delete the network. + Delete() error + + // Endpoints returns the list of Endpoint(s) in this network. + Endpoints() []Endpoint + + // WalkEndpoints uses the provided function to walk the Endpoints + WalkEndpoints(walker EndpointWalker) + + // EndpointByName returns the Endpoint which has the passed name. If not found, the error ErrNoSuchEndpoint is returned. + EndpointByName(name string) (Endpoint, error) + + // EndpointByID returns the Endpoint which has the passed id. If not found, the error ErrNoSuchEndpoint is returned. + EndpointByID(id string) (Endpoint, error) +} + +// EndpointWalker is a client provided function which will be used to walk the Endpoints. +// When the function returns true, the walk will stop. +type EndpointWalker func(ep Endpoint) bool + +type network struct { + ctrlr *controller + name string + networkType string + id types.UUID + driver driverapi.Driver + enableIPv6 bool + endpoints endpointTable + generic options.Generic + sync.Mutex +} + +func (n *network) Name() string { + return n.name +} + +func (n *network) ID() string { + return string(n.id) +} + +func (n *network) Type() string { + if n.driver == nil { + return "" + } + + return n.driver.Type() +} + +// NetworkOption is a option setter function type used to pass varios options to +// NewNetwork method. The various setter functions of type NetworkOption are +// provided by libnetwork, they look like NetworkOptionXXXX(...) +type NetworkOption func(n *network) + +// NetworkOptionGeneric function returns an option setter for a Generic option defined +// in a Dictionary of Key-Value pair +func NetworkOptionGeneric(generic map[string]interface{}) NetworkOption { + return func(n *network) { + n.generic = generic + if _, ok := generic[netlabel.EnableIPv6]; ok { + n.enableIPv6 = generic[netlabel.EnableIPv6].(bool) + } + } +} + +func (n *network) processOptions(options ...NetworkOption) { + for _, opt := range options { + if opt != nil { + opt(n) + } + } +} + +func (n *network) Delete() error { + var err error + + n.ctrlr.Lock() + _, ok := n.ctrlr.networks[n.id] + if !ok { + n.ctrlr.Unlock() + return &UnknownNetworkError{name: n.name, id: string(n.id)} + } + + n.Lock() + numEps := len(n.endpoints) + n.Unlock() + if numEps != 0 { + n.ctrlr.Unlock() + return &ActiveEndpointsError{name: n.name, id: string(n.id)} + } + + delete(n.ctrlr.networks, n.id) + n.ctrlr.Unlock() + defer func() { + if err != nil { + n.ctrlr.Lock() + n.ctrlr.networks[n.id] = n + n.ctrlr.Unlock() + } + }() + + err = n.driver.DeleteNetwork(n.id) + return err +} + +func (n *network) CreateEndpoint(name string, options ...EndpointOption) (Endpoint, error) { + if name == "" { + return nil, ErrInvalidName + } + ep := &endpoint{name: name, iFaces: []*endpointInterface{}, generic: make(map[string]interface{})} + ep.id = types.UUID(stringid.GenerateRandomID()) + ep.network = n + ep.processOptions(options...) + + d := n.driver + err := d.CreateEndpoint(n.id, ep.id, ep, ep.generic) + if err != nil { + return nil, err + } + + n.Lock() + n.endpoints[ep.id] = ep + n.Unlock() + return ep, nil +} + +func (n *network) Endpoints() []Endpoint { + n.Lock() + defer n.Unlock() + list := make([]Endpoint, 0, len(n.endpoints)) + for _, e := range n.endpoints { + list = append(list, e) + } + + return list +} + +func (n *network) WalkEndpoints(walker EndpointWalker) { + for _, e := range n.Endpoints() { + if walker(e) { + return + } + } +} + +func (n *network) EndpointByName(name string) (Endpoint, error) { + if name == "" { + return nil, ErrInvalidName + } + var e Endpoint + + s := func(current Endpoint) bool { + if current.Name() == name { + e = current + return true + } + return false + } + + n.WalkEndpoints(s) + + if e == nil { + return nil, ErrNoSuchEndpoint + } + + return e, nil +} + +func (n *network) EndpointByID(id string) (Endpoint, error) { + if id == "" { + return nil, ErrInvalidID + } + n.Lock() + defer n.Unlock() + if e, ok := n.endpoints[types.UUID(id)]; ok { + return e, nil + } + return nil, ErrNoSuchEndpoint +} diff --git a/vendor/src/github.com/docker/libnetwork/options/options.go b/vendor/src/github.com/docker/libnetwork/options/options.go new file mode 100644 index 0000000000..e0e93ff9b7 --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/options/options.go @@ -0,0 +1,73 @@ +// Package options provides a way to pass unstructured sets of options to a +// component expecting a strongly-typed configuration structure. +package options + +import ( + "fmt" + "reflect" +) + +// NoSuchFieldError is the error returned when the generic parameters hold a +// value for a field absent from the destination structure. +type NoSuchFieldError struct { + Field string + Type string +} + +func (e NoSuchFieldError) Error() string { + return fmt.Sprintf("no field %q in type %q", e.Field, e.Type) +} + +// CannotSetFieldError is the error returned when the generic parameters hold a +// value for a field that cannot be set in the destination structure. +type CannotSetFieldError struct { + Field string + Type string +} + +func (e CannotSetFieldError) Error() string { + return fmt.Sprintf("cannot set field %q of type %q", e.Field, e.Type) +} + +// Generic is an basic type to store arbitrary settings. +type Generic map[string]interface{} + +// NewGeneric returns a new Generic instance. +func NewGeneric() Generic { + return make(Generic) +} + +// GenerateFromModel takes the generic options, and tries to build a new +// instance of the model's type by matching keys from the generic options to +// fields in the model. +// +// The return value is of the same type than the model (including a potential +// pointer qualifier). +func GenerateFromModel(options Generic, model interface{}) (interface{}, error) { + modType := reflect.TypeOf(model) + + // If the model is of pointer type, we need to dereference for New. + resType := reflect.TypeOf(model) + if modType.Kind() == reflect.Ptr { + resType = resType.Elem() + } + + // Populate the result structure with the generic layout content. + res := reflect.New(resType) + for name, value := range options { + field := res.Elem().FieldByName(name) + if !field.IsValid() { + return nil, NoSuchFieldError{name, resType.String()} + } + if !field.CanSet() { + return nil, CannotSetFieldError{name, resType.String()} + } + field.Set(reflect.ValueOf(value)) + } + + // If the model is not of pointer type, return content of the result. + if modType.Kind() == reflect.Ptr { + return res.Interface(), nil + } + return res.Elem().Interface(), nil +} diff --git a/vendor/src/github.com/docker/libnetwork/options/options_test.go b/vendor/src/github.com/docker/libnetwork/options/options_test.go new file mode 100644 index 0000000000..ecd3b3b311 --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/options/options_test.go @@ -0,0 +1,97 @@ +package options + +import ( + "reflect" + "strings" + "testing" + + _ "github.com/docker/libnetwork/netutils" +) + +func TestGenerate(t *testing.T) { + gen := NewGeneric() + gen["Int"] = 1 + gen["Rune"] = 'b' + gen["Float64"] = 2.0 + + type Model struct { + Int int + Rune rune + Float64 float64 + } + + result, err := GenerateFromModel(gen, Model{}) + + if err != nil { + t.Fatal(err) + } + + cast, ok := result.(Model) + if !ok { + t.Fatalf("result has unexpected type %s", reflect.TypeOf(result)) + } + if expected := 1; cast.Int != expected { + t.Fatalf("wrong value for field Int: expected %v, got %v", expected, cast.Int) + } + if expected := 'b'; cast.Rune != expected { + t.Fatalf("wrong value for field Rune: expected %v, got %v", expected, cast.Rune) + } + if expected := 2.0; cast.Float64 != expected { + t.Fatalf("wrong value for field Int: expected %v, got %v", expected, cast.Float64) + } +} + +func TestGeneratePtr(t *testing.T) { + gen := NewGeneric() + gen["Int"] = 1 + gen["Rune"] = 'b' + gen["Float64"] = 2.0 + + type Model struct { + Int int + Rune rune + Float64 float64 + } + + result, err := GenerateFromModel(gen, &Model{}) + + if err != nil { + t.Fatal(err) + } + + cast, ok := result.(*Model) + if !ok { + t.Fatalf("result has unexpected type %s", reflect.TypeOf(result)) + } + if expected := 1; cast.Int != expected { + t.Fatalf("wrong value for field Int: expected %v, got %v", expected, cast.Int) + } + if expected := 'b'; cast.Rune != expected { + t.Fatalf("wrong value for field Rune: expected %v, got %v", expected, cast.Rune) + } + if expected := 2.0; cast.Float64 != expected { + t.Fatalf("wrong value for field Int: expected %v, got %v", expected, cast.Float64) + } +} + +func TestGenerateMissingField(t *testing.T) { + type Model struct{} + _, err := GenerateFromModel(Generic{"foo": "bar"}, Model{}) + + if _, ok := err.(NoSuchFieldError); !ok { + t.Fatalf("expected NoSuchFieldError, got %#v", err) + } else if expected := "no field"; !strings.Contains(err.Error(), expected) { + t.Fatalf("expected %q in error message, got %s", expected, err.Error()) + } +} + +func TestFieldCannotBeSet(t *testing.T) { + type Model struct{ foo int } + _, err := GenerateFromModel(Generic{"foo": "bar"}, Model{}) + + if _, ok := err.(CannotSetFieldError); !ok { + t.Fatalf("expected CannotSetFieldError, got %#v", err) + } else if expected := "cannot set field"; !strings.Contains(err.Error(), expected) { + t.Fatalf("expected %q in error message, got %s", expected, err.Error()) + } +} diff --git a/daemon/networkdriver/portallocator/portallocator.go b/vendor/src/github.com/docker/libnetwork/portallocator/portallocator.go similarity index 69% rename from daemon/networkdriver/portallocator/portallocator.go rename to vendor/src/github.com/docker/libnetwork/portallocator/portallocator.go index c1f414b673..84b07b2523 100644 --- a/daemon/networkdriver/portallocator/portallocator.go +++ b/vendor/src/github.com/docker/libnetwork/portallocator/portallocator.go @@ -12,47 +12,60 @@ import ( ) const ( + // DefaultPortRangeStart indicates the first port in port range DefaultPortRangeStart = 49153 - DefaultPortRangeEnd = 65535 + // DefaultPortRangeEnd indicates the last port in port range + DefaultPortRangeEnd = 65535 ) type ipMapping map[string]protoMap var ( + // ErrAllPortsAllocated is returned when no more ports are available ErrAllPortsAllocated = errors.New("all ports are allocated") - ErrUnknownProtocol = errors.New("unknown protocol") - defaultIP = net.ParseIP("0.0.0.0") + // ErrUnknownProtocol is returned when an unknown protocol was specified + ErrUnknownProtocol = errors.New("unknown protocol") + defaultIP = net.ParseIP("0.0.0.0") + once sync.Once + instance *PortAllocator + createInstance = func() { instance = newInstance() } ) +// ErrPortAlreadyAllocated is the returned error information when a requested port is already being used type ErrPortAlreadyAllocated struct { ip string port int } -func NewErrPortAlreadyAllocated(ip string, port int) ErrPortAlreadyAllocated { +func newErrPortAlreadyAllocated(ip string, port int) ErrPortAlreadyAllocated { return ErrPortAlreadyAllocated{ ip: ip, port: port, } } +// IP returns the address to which the used port is associated func (e ErrPortAlreadyAllocated) IP() string { return e.ip } +// Port returns the value of the already used port func (e ErrPortAlreadyAllocated) Port() int { return e.port } +// IPPort returns the address and the port in the form ip:port func (e ErrPortAlreadyAllocated) IPPort() string { return fmt.Sprintf("%s:%d", e.ip, e.port) } +// Error is the implementation of error.Error interface func (e ErrPortAlreadyAllocated) Error() string { return fmt.Sprintf("Bind for %s:%d failed: port is already allocated", e.ip, e.port) } type ( + // PortAllocator manages the transport ports database PortAllocator struct { mutex sync.Mutex ipMap ipMapping @@ -67,7 +80,18 @@ type ( protoMap map[string]*portMap ) -func New() *PortAllocator { +// Get returns the default instance of PortAllocator +func Get() *PortAllocator { + // Port Allocator is a singleton + // Note: Long term solution will be each PortAllocator will have access to + // the OS so that it can have up to date view of the OS port allocation. + // When this happens singleton behavior will be removed. Clients do not + // need to worry about this, they will not see a change in behavior. + once.Do(createInstance) + return instance +} + +func newInstance() *PortAllocator { start, end, err := getDynamicPortRange() if err != nil { logrus.Warn(err) @@ -98,7 +122,7 @@ func getDynamicPortRange() (start int, end int, err error) { } // RequestPort requests new port from global ports pool for specified ip and proto. -// If port is 0 it returns first free port. Otherwise it cheks port availability +// If port is 0 it returns first free port. Otherwise it checks port availability // in pool and return that port or error if port is already busy. func (p *PortAllocator) RequestPort(ip net.IP, proto string, port int) (int, error) { p.mutex.Lock() @@ -127,7 +151,7 @@ func (p *PortAllocator) RequestPort(ip net.IP, proto string, port int) (int, err mapping.p[port] = struct{}{} return port, nil } - return 0, NewErrPortAlreadyAllocated(ipstr, port) + return 0, newErrPortAlreadyAllocated(ipstr, port) } port, err := mapping.findPort() diff --git a/daemon/networkdriver/portallocator/portallocator_test.go b/vendor/src/github.com/docker/libnetwork/portallocator/portallocator_test.go similarity index 91% rename from daemon/networkdriver/portallocator/portallocator_test.go rename to vendor/src/github.com/docker/libnetwork/portallocator/portallocator_test.go index 17201235e0..20756494af 100644 --- a/daemon/networkdriver/portallocator/portallocator_test.go +++ b/vendor/src/github.com/docker/libnetwork/portallocator/portallocator_test.go @@ -3,10 +3,17 @@ package portallocator import ( "net" "testing" + + _ "github.com/docker/libnetwork/netutils" ) +func resetPortAllocator() { + instance = newInstance() +} + func TestRequestNewPort(t *testing.T) { - p := New() + p := Get() + defer resetPortAllocator() port, err := p.RequestPort(defaultIP, "tcp", 0) if err != nil { @@ -19,19 +26,21 @@ func TestRequestNewPort(t *testing.T) { } func TestRequestSpecificPort(t *testing.T) { - p := New() + p := Get() + defer resetPortAllocator() port, err := p.RequestPort(defaultIP, "tcp", 5000) if err != nil { t.Fatal(err) } + if port != 5000 { t.Fatalf("Expected port 5000 got %d", port) } } func TestReleasePort(t *testing.T) { - p := New() + p := Get() port, err := p.RequestPort(defaultIP, "tcp", 5000) if err != nil { @@ -47,7 +56,8 @@ func TestReleasePort(t *testing.T) { } func TestReuseReleasedPort(t *testing.T) { - p := New() + p := Get() + defer resetPortAllocator() port, err := p.RequestPort(defaultIP, "tcp", 5000) if err != nil { @@ -68,7 +78,8 @@ func TestReuseReleasedPort(t *testing.T) { } func TestReleaseUnreadledPort(t *testing.T) { - p := New() + p := Get() + defer resetPortAllocator() port, err := p.RequestPort(defaultIP, "tcp", 5000) if err != nil { @@ -88,13 +99,14 @@ func TestReleaseUnreadledPort(t *testing.T) { } func TestUnknowProtocol(t *testing.T) { - if _, err := New().RequestPort(defaultIP, "tcpp", 0); err != ErrUnknownProtocol { + if _, err := Get().RequestPort(defaultIP, "tcpp", 0); err != ErrUnknownProtocol { t.Fatalf("Expected error %s got %s", ErrUnknownProtocol, err) } } func TestAllocateAllPorts(t *testing.T) { - p := New() + p := Get() + defer resetPortAllocator() for i := 0; i <= p.End-p.Begin; i++ { port, err := p.RequestPort(defaultIP, "tcp", 0) @@ -144,7 +156,8 @@ func TestAllocateAllPorts(t *testing.T) { } func BenchmarkAllocatePorts(b *testing.B) { - p := New() + p := Get() + defer resetPortAllocator() for i := 0; i < b.N; i++ { for i := 0; i <= p.End-p.Begin; i++ { @@ -162,7 +175,8 @@ func BenchmarkAllocatePorts(b *testing.B) { } func TestPortAllocation(t *testing.T) { - p := New() + p := Get() + defer resetPortAllocator() ip := net.ParseIP("192.168.0.1") ip2 := net.ParseIP("192.168.0.2") @@ -223,7 +237,8 @@ func TestPortAllocation(t *testing.T) { } func TestNoDuplicateBPR(t *testing.T) { - p := New() + p := Get() + defer resetPortAllocator() if port, err := p.RequestPort(defaultIP, "tcp", p.Begin); err != nil { t.Fatal(err) diff --git a/daemon/networkdriver/portmapper/mapper.go b/vendor/src/github.com/docker/libnetwork/portmapper/mapper.go similarity index 80% rename from daemon/networkdriver/portmapper/mapper.go rename to vendor/src/github.com/docker/libnetwork/portmapper/mapper.go index f0c7a507df..afaa036b8f 100644 --- a/daemon/networkdriver/portmapper/mapper.go +++ b/vendor/src/github.com/docker/libnetwork/portmapper/mapper.go @@ -7,25 +7,29 @@ import ( "sync" "github.com/Sirupsen/logrus" - "github.com/docker/docker/daemon/networkdriver/portallocator" - "github.com/docker/docker/pkg/iptables" + "github.com/docker/libnetwork/iptables" + "github.com/docker/libnetwork/portallocator" ) type mapping struct { proto string - userlandProxy UserlandProxy + userlandProxy userlandProxy host net.Addr container net.Addr } -var NewProxy = NewProxyCommand +var newProxy = newProxyCommand var ( + // ErrUnknownBackendAddressType refers to an unknown container or unsupported address type ErrUnknownBackendAddressType = errors.New("unknown container address type not supported") - ErrPortMappedForIP = errors.New("port is already mapped to ip") - ErrPortNotMapped = errors.New("port is not mapped") + // ErrPortMappedForIP refers to a port already mapped to an ip address + ErrPortMappedForIP = errors.New("port is already mapped to ip") + // ErrPortNotMapped refers to an unmapped port + ErrPortNotMapped = errors.New("port is not mapped") ) +// PortMapper manages the network address translation type PortMapper struct { chain *iptables.Chain @@ -36,10 +40,12 @@ type PortMapper struct { Allocator *portallocator.PortAllocator } +// New returns a new instance of PortMapper func New() *PortMapper { - return NewWithPortAllocator(portallocator.New()) + return NewWithPortAllocator(portallocator.Get()) } +// NewWithPortAllocator returns a new instance of PortMapper which will use the specified PortAllocator func NewWithPortAllocator(allocator *portallocator.PortAllocator) *PortMapper { return &PortMapper{ currentMappings: make(map[string]*mapping), @@ -47,10 +53,12 @@ func NewWithPortAllocator(allocator *portallocator.PortAllocator) *PortMapper { } } +// SetIptablesChain sets the specified chain into portmapper func (pm *PortMapper) SetIptablesChain(c *iptables.Chain) { pm.chain = c } +// Map maps the specified container transport address to the host's network address and transport port func (pm *PortMapper) Map(container net.Addr, hostIP net.IP, hostPort int, useProxy bool) (host net.Addr, err error) { pm.lock.Lock() defer pm.lock.Unlock() @@ -75,7 +83,7 @@ func (pm *PortMapper) Map(container net.Addr, hostIP net.IP, hostPort int, usePr } if useProxy { - m.userlandProxy = NewProxy(proto, hostIP, allocatedHostPort, container.(*net.TCPAddr).IP, container.(*net.TCPAddr).Port) + m.userlandProxy = newProxy(proto, hostIP, allocatedHostPort, container.(*net.TCPAddr).IP, container.(*net.TCPAddr).Port) } case *net.UDPAddr: proto = "udp" @@ -90,7 +98,7 @@ func (pm *PortMapper) Map(container net.Addr, hostIP net.IP, hostPort int, usePr } if useProxy { - m.userlandProxy = NewProxy(proto, hostIP, allocatedHostPort, container.(*net.UDPAddr).IP, container.(*net.UDPAddr).Port) + m.userlandProxy = newProxy(proto, hostIP, allocatedHostPort, container.(*net.UDPAddr).IP, container.(*net.UDPAddr).Port) } default: return nil, ErrUnknownBackendAddressType @@ -139,18 +147,7 @@ func (pm *PortMapper) Map(container net.Addr, hostIP net.IP, hostPort int, usePr return m.host, nil } -// re-apply all port mappings -func (pm *PortMapper) ReMapAll() { - logrus.Debugln("Re-applying all port mappings.") - for _, data := range pm.currentMappings { - containerIP, containerPort := getIPAndPort(data.container) - hostIP, hostPort := getIPAndPort(data.host) - if err := pm.forward(iptables.Append, data.proto, hostIP, hostPort, containerIP.String(), containerPort); err != nil { - logrus.Errorf("Error on iptables add: %s", err) - } - } -} - +// Unmap removes stored mapping for the specified host transport address func (pm *PortMapper) Unmap(host net.Addr) error { pm.lock.Lock() defer pm.lock.Unlock() diff --git a/vendor/src/github.com/docker/libnetwork/portmapper/mapper_test.go b/vendor/src/github.com/docker/libnetwork/portmapper/mapper_test.go new file mode 100644 index 0000000000..c704bad6e7 --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/portmapper/mapper_test.go @@ -0,0 +1,227 @@ +package portmapper + +import ( + "net" + "os" + "testing" + "time" + + "github.com/docker/docker/pkg/reexec" + "github.com/docker/libnetwork/iptables" + "github.com/docker/libnetwork/netutils" +) + +func TestMain(m *testing.M) { + if reexec.Init() { + return + } + os.Exit(m.Run()) +} + +func TestSetIptablesChain(t *testing.T) { + pm := New() + + c := &iptables.Chain{ + Name: "TEST", + Bridge: "192.168.1.1", + } + + if pm.chain != nil { + t.Fatal("chain should be nil at init") + } + + pm.SetIptablesChain(c) + if pm.chain == nil { + t.Fatal("chain should not be nil after set") + } +} + +func TestMapTCPPorts(t *testing.T) { + defer netutils.SetupTestNetNS(t)() + pm := New() + dstIP1 := net.ParseIP("192.168.0.1") + dstIP2 := net.ParseIP("192.168.0.2") + dstAddr1 := &net.TCPAddr{IP: dstIP1, Port: 80} + dstAddr2 := &net.TCPAddr{IP: dstIP2, Port: 80} + + srcAddr1 := &net.TCPAddr{Port: 1080, IP: net.ParseIP("172.16.0.1")} + srcAddr2 := &net.TCPAddr{Port: 1080, IP: net.ParseIP("172.16.0.2")} + + addrEqual := func(addr1, addr2 net.Addr) bool { + return (addr1.Network() == addr2.Network()) && (addr1.String() == addr2.String()) + } + + if host, err := pm.Map(srcAddr1, dstIP1, 80, true); err != nil { + t.Fatalf("Failed to allocate port: %s", err) + } else if !addrEqual(dstAddr1, host) { + t.Fatalf("Incorrect mapping result: expected %s:%s, got %s:%s", + dstAddr1.String(), dstAddr1.Network(), host.String(), host.Network()) + } + + if _, err := pm.Map(srcAddr1, dstIP1, 80, true); err == nil { + t.Fatalf("Port is in use - mapping should have failed") + } + + if _, err := pm.Map(srcAddr2, dstIP1, 80, true); err == nil { + t.Fatalf("Port is in use - mapping should have failed") + } + + if _, err := pm.Map(srcAddr2, dstIP2, 80, true); err != nil { + t.Fatalf("Failed to allocate port: %s", err) + } + + if pm.Unmap(dstAddr1) != nil { + t.Fatalf("Failed to release port") + } + + if pm.Unmap(dstAddr2) != nil { + t.Fatalf("Failed to release port") + } + + if pm.Unmap(dstAddr2) == nil { + t.Fatalf("Port already released, but no error reported") + } +} + +func TestGetUDPKey(t *testing.T) { + addr := &net.UDPAddr{IP: net.ParseIP("192.168.1.5"), Port: 53} + + key := getKey(addr) + + if expected := "192.168.1.5:53/udp"; key != expected { + t.Fatalf("expected key %s got %s", expected, key) + } +} + +func TestGetTCPKey(t *testing.T) { + addr := &net.TCPAddr{IP: net.ParseIP("192.168.1.5"), Port: 80} + + key := getKey(addr) + + if expected := "192.168.1.5:80/tcp"; key != expected { + t.Fatalf("expected key %s got %s", expected, key) + } +} + +func TestGetUDPIPAndPort(t *testing.T) { + addr := &net.UDPAddr{IP: net.ParseIP("192.168.1.5"), Port: 53} + + ip, port := getIPAndPort(addr) + if expected := "192.168.1.5"; ip.String() != expected { + t.Fatalf("expected ip %s got %s", expected, ip) + } + + if ep := 53; port != ep { + t.Fatalf("expected port %d got %d", ep, port) + } +} + +func TestMapUDPPorts(t *testing.T) { + defer netutils.SetupTestNetNS(t)() + pm := New() + dstIP1 := net.ParseIP("192.168.0.1") + dstIP2 := net.ParseIP("192.168.0.2") + dstAddr1 := &net.UDPAddr{IP: dstIP1, Port: 80} + dstAddr2 := &net.UDPAddr{IP: dstIP2, Port: 80} + + srcAddr1 := &net.UDPAddr{Port: 1080, IP: net.ParseIP("172.16.0.1")} + srcAddr2 := &net.UDPAddr{Port: 1080, IP: net.ParseIP("172.16.0.2")} + + addrEqual := func(addr1, addr2 net.Addr) bool { + return (addr1.Network() == addr2.Network()) && (addr1.String() == addr2.String()) + } + + if host, err := pm.Map(srcAddr1, dstIP1, 80, true); err != nil { + t.Fatalf("Failed to allocate port: %s", err) + } else if !addrEqual(dstAddr1, host) { + t.Fatalf("Incorrect mapping result: expected %s:%s, got %s:%s", + dstAddr1.String(), dstAddr1.Network(), host.String(), host.Network()) + } + + if _, err := pm.Map(srcAddr1, dstIP1, 80, true); err == nil { + t.Fatalf("Port is in use - mapping should have failed") + } + + if _, err := pm.Map(srcAddr2, dstIP1, 80, true); err == nil { + t.Fatalf("Port is in use - mapping should have failed") + } + + if _, err := pm.Map(srcAddr2, dstIP2, 80, true); err != nil { + t.Fatalf("Failed to allocate port: %s", err) + } + + if pm.Unmap(dstAddr1) != nil { + t.Fatalf("Failed to release port") + } + + if pm.Unmap(dstAddr2) != nil { + t.Fatalf("Failed to release port") + } + + if pm.Unmap(dstAddr2) == nil { + t.Fatalf("Port already released, but no error reported") + } +} + +func TestMapAllPortsSingleInterface(t *testing.T) { + newProxy = newMockProxyCommand + defer func() { + newProxy = newProxyCommand + }() + defer netutils.SetupTestNetNS(t)() + pm := New() + dstIP1 := net.ParseIP("0.0.0.0") + srcAddr1 := &net.TCPAddr{Port: 1080, IP: net.ParseIP("172.16.0.1")} + + hosts := []net.Addr{} + var host net.Addr + var err error + + for i := 0; i < 10; i++ { + start, end := pm.Allocator.Begin, pm.Allocator.End + for i := start; i < end; i++ { + if host, err = pm.Map(srcAddr1, dstIP1, 0, true); err != nil { + t.Fatal(err) + } + + hosts = append(hosts, host) + } + + if _, err := pm.Map(srcAddr1, dstIP1, start, true); err == nil { + t.Fatalf("Port %d should be bound but is not", start) + } + + for _, val := range hosts { + if err := pm.Unmap(val); err != nil { + t.Fatal(err) + } + } + + hosts = []net.Addr{} + } +} + +func TestExecProxy(t *testing.T) { + defer netutils.SetupTestNetNS(t)() + args := []string{ + userlandProxyCommandName, + "-proto", "tcp", + "-host-ip", "0.0.0.0", + "-host-port", "9999", + "-container-ip", "172.168.1.1", + "-container-port", "8888", + } + os.Args = args + doneChan := make(chan bool) + go func() { + execProxy() + doneChan <- true + }() + + select { + case <-doneChan: + t.Fatal("execProxy is not supposed to exit") + case <-time.After(3 * time.Second): + return + } +} diff --git a/daemon/networkdriver/portmapper/mock_proxy.go b/vendor/src/github.com/docker/libnetwork/portmapper/mock_proxy.go similarity index 63% rename from daemon/networkdriver/portmapper/mock_proxy.go rename to vendor/src/github.com/docker/libnetwork/portmapper/mock_proxy.go index 253ce83112..29b1605889 100644 --- a/daemon/networkdriver/portmapper/mock_proxy.go +++ b/vendor/src/github.com/docker/libnetwork/portmapper/mock_proxy.go @@ -2,7 +2,7 @@ package portmapper import "net" -func NewMockProxyCommand(proto string, hostIP net.IP, hostPort int, containerIP net.IP, containerPort int) UserlandProxy { +func newMockProxyCommand(proto string, hostIP net.IP, hostPort int, containerIP net.IP, containerPort int) userlandProxy { return &mockProxyCommand{} } diff --git a/daemon/networkdriver/portmapper/proxy.go b/vendor/src/github.com/docker/libnetwork/portmapper/proxy.go similarity index 95% rename from daemon/networkdriver/portmapper/proxy.go rename to vendor/src/github.com/docker/libnetwork/portmapper/proxy.go index 80b0027c70..5cbb4dc2a8 100644 --- a/daemon/networkdriver/portmapper/proxy.go +++ b/vendor/src/github.com/docker/libnetwork/portmapper/proxy.go @@ -23,7 +23,7 @@ func init() { reexec.Register(userlandProxyCommandName, execProxy) } -type UserlandProxy interface { +type userlandProxy interface { Start() error Stop() error } @@ -84,14 +84,14 @@ func handleStopSignals(p proxy.Proxy) { s := make(chan os.Signal, 10) signal.Notify(s, os.Interrupt, syscall.SIGTERM, syscall.SIGSTOP) - for range s { + for _ = range s { p.Close() os.Exit(0) } } -func NewProxyCommand(proto string, hostIP net.IP, hostPort int, containerIP net.IP, containerPort int) UserlandProxy { +func newProxyCommand(proto string, hostIP net.IP, hostPort int, containerIP net.IP, containerPort int) userlandProxy { args := []string{ userlandProxyCommandName, "-proto", proto, diff --git a/pkg/resolvconf/README.md b/vendor/src/github.com/docker/libnetwork/resolvconf/README.md similarity index 100% rename from pkg/resolvconf/README.md rename to vendor/src/github.com/docker/libnetwork/resolvconf/README.md diff --git a/pkg/resolvconf/dns/resolvconf.go b/vendor/src/github.com/docker/libnetwork/resolvconf/dns/resolvconf.go similarity index 61% rename from pkg/resolvconf/dns/resolvconf.go rename to vendor/src/github.com/docker/libnetwork/resolvconf/dns/resolvconf.go index c2f23ef34a..d581a1913d 100644 --- a/pkg/resolvconf/dns/resolvconf.go +++ b/vendor/src/github.com/docker/libnetwork/resolvconf/dns/resolvconf.go @@ -4,9 +4,10 @@ import ( "regexp" ) -const IpLocalhost = `((127\.([0-9]{1,3}.){2}[0-9]{1,3})|(::1))` +// IPLocalhost is a regex patter for localhost IP address range. +const IPLocalhost = `((127\.([0-9]{1,3}.){2}[0-9]{1,3})|(::1))` -var localhostIPRegexp = regexp.MustCompile(IpLocalhost) +var localhostIPRegexp = regexp.MustCompile(IPLocalhost) // IsLocalhost returns true if ip matches the localhost IP regular expression. // Used for determining if nameserver settings are being passed which are diff --git a/pkg/resolvconf/resolvconf.go b/vendor/src/github.com/docker/libnetwork/resolvconf/resolvconf.go similarity index 96% rename from pkg/resolvconf/resolvconf.go rename to vendor/src/github.com/docker/libnetwork/resolvconf/resolvconf.go index 907934c5c5..ebe3b71aa4 100644 --- a/pkg/resolvconf/resolvconf.go +++ b/vendor/src/github.com/docker/libnetwork/resolvconf/resolvconf.go @@ -10,7 +10,7 @@ import ( "github.com/Sirupsen/logrus" "github.com/docker/docker/pkg/ioutils" - "github.com/docker/docker/pkg/resolvconf/dns" + "github.com/docker/libnetwork/resolvconf/dns" ) var ( @@ -26,7 +26,7 @@ var ( // 1000+ character regexp with exact and complete IPv6 validation ipv6Address = `([0-9A-Fa-f]{0,4}:){2,7}([0-9A-Fa-f]{0,4})` - localhostNSRegexp = regexp.MustCompile(`(?m)^nameserver\s+` + dns.IpLocalhost + `\s*\n*`) + localhostNSRegexp = regexp.MustCompile(`(?m)^nameserver\s+` + dns.IPLocalhost + `\s*\n*`) nsIPv6Regexp = regexp.MustCompile(`(?m)^nameserver\s+` + ipv6Address + `\s*\n*`) nsRegexp = regexp.MustCompile(`^\s*nameserver\s*((` + ipv4Address + `)|(` + ipv6Address + `))\s*$`) searchRegexp = regexp.MustCompile(`^\s*search\s*(([^\s]+\s*)*)$`) @@ -80,7 +80,7 @@ func GetLastModified() ([]byte, string) { return lastModified.contents, lastModified.sha256 } -// FilterResolvDns cleans up the config in resolvConf. It has two main jobs: +// FilterResolvDNS cleans up the config in resolvConf. It has two main jobs: // 1. It looks for localhost (127.*|::1) entries in the provided // resolv.conf, removing local nameserver entries, and, if the resulting // cleaned config has no defined nameservers left, adds default DNS entries @@ -88,7 +88,7 @@ func GetLastModified() ([]byte, string) { // code will remove all IPv6 nameservers if it is not enabled for containers // // It returns a boolean to notify the caller if changes were made at all -func FilterResolvDns(resolvConf []byte, ipv6Enabled bool) ([]byte, bool) { +func FilterResolvDNS(resolvConf []byte, ipv6Enabled bool) ([]byte, bool) { changed := false cleanedResolvConf := localhostNSRegexp.ReplaceAll(resolvConf, []byte{}) // if IPv6 is not enabled, also clean out any IPv6 address nameserver diff --git a/pkg/resolvconf/resolvconf_test.go b/vendor/src/github.com/docker/libnetwork/resolvconf/resolvconf_test.go similarity index 91% rename from pkg/resolvconf/resolvconf_test.go rename to vendor/src/github.com/docker/libnetwork/resolvconf/resolvconf_test.go index b0647e7833..a21c7afb3e 100644 --- a/pkg/resolvconf/resolvconf_test.go +++ b/vendor/src/github.com/docker/libnetwork/resolvconf/resolvconf_test.go @@ -5,6 +5,8 @@ import ( "io/ioutil" "os" "testing" + + _ "github.com/docker/libnetwork/netutils" ) func TestGet(t *testing.T) { @@ -160,42 +162,42 @@ func TestBuildWithZeroLengthDomainSearch(t *testing.T) { func TestFilterResolvDns(t *testing.T) { ns0 := "nameserver 10.16.60.14\nnameserver 10.16.60.21\n" - if result, _ := FilterResolvDns([]byte(ns0), false); result != nil { + if result, _ := FilterResolvDNS([]byte(ns0), false); result != nil { if ns0 != string(result) { t.Fatalf("Failed No Localhost: expected \n<%s> got \n<%s>", ns0, string(result)) } } ns1 := "nameserver 10.16.60.14\nnameserver 10.16.60.21\nnameserver 127.0.0.1\n" - if result, _ := FilterResolvDns([]byte(ns1), false); result != nil { + if result, _ := FilterResolvDNS([]byte(ns1), false); result != nil { if ns0 != string(result) { t.Fatalf("Failed Localhost: expected \n<%s> got \n<%s>", ns0, string(result)) } } ns1 = "nameserver 10.16.60.14\nnameserver 127.0.0.1\nnameserver 10.16.60.21\n" - if result, _ := FilterResolvDns([]byte(ns1), false); result != nil { + if result, _ := FilterResolvDNS([]byte(ns1), false); result != nil { if ns0 != string(result) { t.Fatalf("Failed Localhost: expected \n<%s> got \n<%s>", ns0, string(result)) } } ns1 = "nameserver 127.0.1.1\nnameserver 10.16.60.14\nnameserver 10.16.60.21\n" - if result, _ := FilterResolvDns([]byte(ns1), false); result != nil { + if result, _ := FilterResolvDNS([]byte(ns1), false); result != nil { if ns0 != string(result) { t.Fatalf("Failed Localhost: expected \n<%s> got \n<%s>", ns0, string(result)) } } ns1 = "nameserver ::1\nnameserver 10.16.60.14\nnameserver 127.0.2.1\nnameserver 10.16.60.21\n" - if result, _ := FilterResolvDns([]byte(ns1), false); result != nil { + if result, _ := FilterResolvDNS([]byte(ns1), false); result != nil { if ns0 != string(result) { t.Fatalf("Failed Localhost: expected \n<%s> got \n<%s>", ns0, string(result)) } } ns1 = "nameserver 10.16.60.14\nnameserver ::1\nnameserver 10.16.60.21\nnameserver ::1" - if result, _ := FilterResolvDns([]byte(ns1), false); result != nil { + if result, _ := FilterResolvDNS([]byte(ns1), false); result != nil { if ns0 != string(result) { t.Fatalf("Failed Localhost: expected \n<%s> got \n<%s>", ns0, string(result)) } @@ -203,7 +205,7 @@ func TestFilterResolvDns(t *testing.T) { // with IPv6 disabled (false param), the IPv6 nameserver should be removed ns1 = "nameserver 10.16.60.14\nnameserver 2002:dead:beef::1\nnameserver 10.16.60.21\nnameserver ::1" - if result, _ := FilterResolvDns([]byte(ns1), false); result != nil { + if result, _ := FilterResolvDNS([]byte(ns1), false); result != nil { if ns0 != string(result) { t.Fatalf("Failed Localhost+IPv6 off: expected \n<%s> got \n<%s>", ns0, string(result)) } @@ -212,7 +214,7 @@ func TestFilterResolvDns(t *testing.T) { // with IPv6 enabled, the IPv6 nameserver should be preserved ns0 = "nameserver 10.16.60.14\nnameserver 2002:dead:beef::1\nnameserver 10.16.60.21\n" ns1 = "nameserver 10.16.60.14\nnameserver 2002:dead:beef::1\nnameserver 10.16.60.21\nnameserver ::1" - if result, _ := FilterResolvDns([]byte(ns1), true); result != nil { + if result, _ := FilterResolvDNS([]byte(ns1), true); result != nil { if ns0 != string(result) { t.Fatalf("Failed Localhost+IPv6 on: expected \n<%s> got \n<%s>", ns0, string(result)) } @@ -221,7 +223,7 @@ func TestFilterResolvDns(t *testing.T) { // with IPv6 enabled, and no non-localhost servers, Google defaults (both IPv4+IPv6) should be added ns0 = "\nnameserver 8.8.8.8\nnameserver 8.8.4.4\nnameserver 2001:4860:4860::8888\nnameserver 2001:4860:4860::8844" ns1 = "nameserver 127.0.0.1\nnameserver ::1\nnameserver 127.0.2.1" - if result, _ := FilterResolvDns([]byte(ns1), true); result != nil { + if result, _ := FilterResolvDNS([]byte(ns1), true); result != nil { if ns0 != string(result) { t.Fatalf("Failed no Localhost+IPv6 enabled: expected \n<%s> got \n<%s>", ns0, string(result)) } @@ -230,7 +232,7 @@ func TestFilterResolvDns(t *testing.T) { // with IPv6 disabled, and no non-localhost servers, Google defaults (only IPv4) should be added ns0 = "\nnameserver 8.8.8.8\nnameserver 8.8.4.4" ns1 = "nameserver 127.0.0.1\nnameserver ::1\nnameserver 127.0.2.1" - if result, _ := FilterResolvDns([]byte(ns1), false); result != nil { + if result, _ := FilterResolvDNS([]byte(ns1), false); result != nil { if ns0 != string(result) { t.Fatalf("Failed no Localhost+IPv6 enabled: expected \n<%s> got \n<%s>", ns0, string(result)) } diff --git a/vendor/src/github.com/docker/libnetwork/sandbox/configure_linux.go b/vendor/src/github.com/docker/libnetwork/sandbox/configure_linux.go new file mode 100644 index 0000000000..cae77890fd --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/sandbox/configure_linux.go @@ -0,0 +1,81 @@ +package sandbox + +import ( + "fmt" + "net" + "os" + "runtime" + + "github.com/vishvananda/netlink" + "github.com/vishvananda/netns" +) + +func configureInterface(iface netlink.Link, settings *Interface) error { + ifaceName := iface.Attrs().Name + ifaceConfigurators := []struct { + Fn func(netlink.Link, *Interface) error + ErrMessage string + }{ + {setInterfaceName, fmt.Sprintf("error renaming interface %q to %q", ifaceName, settings.DstName)}, + {setInterfaceIP, fmt.Sprintf("error setting interface %q IP to %q", ifaceName, settings.Address)}, + {setInterfaceIPv6, fmt.Sprintf("error setting interface %q IPv6 to %q", ifaceName, settings.AddressIPv6)}, + } + + for _, config := range ifaceConfigurators { + if err := config.Fn(iface, settings); err != nil { + return fmt.Errorf("%s: %v", config.ErrMessage, err) + } + } + return nil +} + +func programGateway(path string, gw net.IP) error { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + origns, err := netns.Get() + if err != nil { + return err + } + defer origns.Close() + + f, err := os.OpenFile(path, os.O_RDONLY, 0) + if err != nil { + return fmt.Errorf("failed get network namespace %q: %v", path, err) + } + defer f.Close() + + nsFD := f.Fd() + if err = netns.Set(netns.NsHandle(nsFD)); err != nil { + return err + } + defer netns.Set(origns) + + gwRoutes, err := netlink.RouteGet(gw) + if err != nil { + return fmt.Errorf("route for the gateway could not be found: %v", err) + } + + return netlink.RouteAdd(&netlink.Route{ + Scope: netlink.SCOPE_UNIVERSE, + LinkIndex: gwRoutes[0].LinkIndex, + Gw: gw, + }) +} + +func setInterfaceIP(iface netlink.Link, settings *Interface) error { + ipAddr := &netlink.Addr{IPNet: settings.Address, Label: ""} + return netlink.AddrAdd(iface, ipAddr) +} + +func setInterfaceIPv6(iface netlink.Link, settings *Interface) error { + if settings.AddressIPv6 == nil { + return nil + } + ipAddr := &netlink.Addr{IPNet: settings.AddressIPv6, Label: ""} + return netlink.AddrAdd(iface, ipAddr) +} + +func setInterfaceName(iface netlink.Link, settings *Interface) error { + return netlink.LinkSetName(iface, settings.DstName) +} diff --git a/vendor/src/github.com/docker/libnetwork/sandbox/namespace_linux.go b/vendor/src/github.com/docker/libnetwork/sandbox/namespace_linux.go new file mode 100644 index 0000000000..b4221f4f07 --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/sandbox/namespace_linux.go @@ -0,0 +1,263 @@ +package sandbox + +import ( + "fmt" + "net" + "os" + "runtime" + "sync" + "syscall" + + "github.com/vishvananda/netlink" + "github.com/vishvananda/netns" +) + +const prefix = "/var/run/docker/netns" + +var once sync.Once + +// 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 + sinfo *Info +} + +func createBasePath() { + err := os.MkdirAll(prefix, 0644) + if err != nil && !os.IsExist(err) { + panic("Could not create net namespace path directory") + } +} + +// 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 prefix + "/" + 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 bool) (Sandbox, error) { + info, err := createNetworkNamespace(key, osCreate) + if err != nil { + return nil, err + } + + return &networkNamespace{path: key, sinfo: info}, nil +} + +func createNetworkNamespace(path string, osCreate bool) (*Info, error) { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + origns, err := netns.Get() + if err != nil { + return nil, err + } + defer origns.Close() + + if err := createNamespaceFile(path); err != nil { + return nil, err + } + + if osCreate { + defer netns.Set(origns) + newns, err := netns.New() + if err != nil { + return nil, err + } + defer newns.Close() + + if err := loopbackUp(); err != nil { + return nil, err + } + } + + procNet := fmt.Sprintf("/proc/%d/task/%d/ns/net", os.Getpid(), syscall.Gettid()) + + if err := syscall.Mount(procNet, path, "bind", syscall.MS_BIND, ""); err != nil { + return nil, err + } + + interfaces := []*Interface{} + info := &Info{Interfaces: interfaces} + return info, nil +} + +func cleanupNamespaceFile(path string) { + if _, err := os.Stat(path); err == nil { + n := &networkNamespace{path: path} + n.Destroy() + } +} + +func createNamespaceFile(path string) (err error) { + var f *os.File + + once.Do(createBasePath) + // cleanup namespace file if it already exists because of a previous ungraceful exit. + cleanupNamespaceFile(path) + if f, err = os.Create(path); err == nil { + f.Close() + } + return err +} + +func loopbackUp() error { + iface, err := netlink.LinkByName("lo") + if err != nil { + return err + } + return netlink.LinkSetUp(iface) +} + +func (n *networkNamespace) RemoveInterface(i *Interface) error { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + origns, err := netns.Get() + if err != nil { + return err + } + defer origns.Close() + + f, err := os.OpenFile(n.path, os.O_RDONLY, 0) + if err != nil { + return fmt.Errorf("failed get network namespace %q: %v", n.path, err) + } + defer f.Close() + + nsFD := f.Fd() + if err = netns.Set(netns.NsHandle(nsFD)); err != nil { + return err + } + defer netns.Set(origns) + + // Find the network inteerface identified by the DstName attribute. + iface, err := netlink.LinkByName(i.DstName) + if err != nil { + return err + } + + // Down the interface before configuring + if err := netlink.LinkSetDown(iface); err != nil { + return err + } + + err = netlink.LinkSetName(iface, i.SrcName) + if err != nil { + fmt.Println("LinkSetName failed: ", err) + return err + } + + // Move the network interface to caller namespace. + if err := netlink.LinkSetNsFd(iface, int(origns)); err != nil { + fmt.Println("LinkSetNsPid failed: ", err) + return err + } + + return nil +} + +func (n *networkNamespace) AddInterface(i *Interface) error { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + origns, err := netns.Get() + if err != nil { + return err + } + defer origns.Close() + + f, err := os.OpenFile(n.path, os.O_RDONLY, 0) + if err != nil { + return fmt.Errorf("failed get network namespace %q: %v", n.path, err) + } + defer f.Close() + + // Find the network interface identified by the SrcName attribute. + iface, err := netlink.LinkByName(i.SrcName) + if err != nil { + return err + } + + // Move the network interface to the destination namespace. + nsFD := f.Fd() + if err := netlink.LinkSetNsFd(iface, int(nsFD)); err != nil { + return err + } + + if err = netns.Set(netns.NsHandle(nsFD)); err != nil { + return err + } + defer netns.Set(origns) + + // Down the interface before configuring + if err := netlink.LinkSetDown(iface); err != nil { + return err + } + + // Configure the interface now this is moved in the proper namespace. + if err := configureInterface(iface, i); err != nil { + return err + } + + // Up the interface. + if err := netlink.LinkSetUp(iface); err != nil { + return err + } + + n.sinfo.Interfaces = append(n.sinfo.Interfaces, i) + return nil +} + +func (n *networkNamespace) SetGateway(gw net.IP) error { + if len(gw) == 0 { + return nil + } + + err := programGateway(n.path, gw) + if err == nil { + n.sinfo.Gateway = gw + } + + return err +} + +func (n *networkNamespace) SetGatewayIPv6(gw net.IP) error { + if len(gw) == 0 { + return nil + } + + err := programGateway(n.path, gw) + if err == nil { + n.sinfo.GatewayIPv6 = gw + } + + return err +} + +func (n *networkNamespace) Interfaces() []*Interface { + return n.sinfo.Interfaces +} + +func (n *networkNamespace) Key() string { + return n.path +} + +func (n *networkNamespace) Destroy() error { + // 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 + } + + return os.Remove(n.path) +} diff --git a/vendor/src/github.com/docker/libnetwork/sandbox/sandbox.go b/vendor/src/github.com/docker/libnetwork/sandbox/sandbox.go new file mode 100644 index 0000000000..b535f6e430 --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/sandbox/sandbox.go @@ -0,0 +1,155 @@ +package sandbox + +import ( + "net" + + "github.com/docker/libnetwork/netutils" +) + +// 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 + + // The collection of Interface previously added with the AddInterface + // method. Note that this doesn't incude network interfaces added in any + // other way (such as the default loopback interface which are automatically + // created on creation of a sandbox). + Interfaces() []*Interface + + // 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. + AddInterface(*Interface) error + + // Remove an interface from the sandbox by renamin to original name + // and moving it out of the sandbox. + RemoveInterface(*Interface) 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 + + // Destroy the sandbox + Destroy() error +} + +// Info represents all possible information that +// the driver wants to place in the sandbox which includes +// interfaces, routes and gateway +type Info struct { + Interfaces []*Interface + + // IPv4 gateway for the sandbox. + Gateway net.IP + + // IPv6 gateway for the sandbox. + GatewayIPv6 net.IP + + // TODO: Add routes and 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 struct { + // 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. + DstName string + + // IPv4 address for the interface. + Address *net.IPNet + + // IPv6 address for the interface. + AddressIPv6 *net.IPNet +} + +// GetCopy returns a copy of this Interface structure +func (i *Interface) GetCopy() *Interface { + return &Interface{ + SrcName: i.SrcName, + DstName: i.DstName, + Address: netutils.GetIPNetCopy(i.Address), + AddressIPv6: netutils.GetIPNetCopy(i.AddressIPv6), + } +} + +// Equal checks if this instance of Interface is equal to the passed one +func (i *Interface) Equal(o *Interface) bool { + if i == o { + return true + } + + if o == nil { + return false + } + + if i.SrcName != o.SrcName || i.DstName != o.DstName { + return false + } + + if !netutils.CompareIPNet(i.Address, o.Address) { + return false + } + + if !netutils.CompareIPNet(i.AddressIPv6, o.AddressIPv6) { + return false + } + + return true +} + +// GetCopy returns a copy of this SandboxInfo structure +func (s *Info) GetCopy() *Info { + list := make([]*Interface, len(s.Interfaces)) + for i, iface := range s.Interfaces { + list[i] = iface.GetCopy() + } + gw := netutils.GetIPCopy(s.Gateway) + gw6 := netutils.GetIPCopy(s.GatewayIPv6) + + return &Info{Interfaces: list, Gateway: gw, GatewayIPv6: gw6} +} + +// Equal checks if this instance of SandboxInfo is equal to the passed one +func (s *Info) Equal(o *Info) bool { + if s == o { + return true + } + + if o == nil { + return false + } + + if !s.Gateway.Equal(o.Gateway) { + return false + } + + if !s.GatewayIPv6.Equal(o.GatewayIPv6) { + return false + } + + if (s.Interfaces == nil && o.Interfaces != nil) || + (s.Interfaces != nil && o.Interfaces == nil) || + (len(s.Interfaces) != len(o.Interfaces)) { + return false + } + + // Note: At the moment, the two lists must be in the same order + for i := 0; i < len(s.Interfaces); i++ { + if !s.Interfaces[i].Equal(o.Interfaces[i]) { + return false + } + } + + return true + +} diff --git a/vendor/src/github.com/docker/libnetwork/sandbox/sandbox_linux_test.go b/vendor/src/github.com/docker/libnetwork/sandbox/sandbox_linux_test.go new file mode 100644 index 0000000000..06e98c7846 --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/sandbox/sandbox_linux_test.go @@ -0,0 +1,105 @@ +package sandbox + +import ( + "net" + "os" + "path/filepath" + "runtime" + "testing" + + "github.com/docker/libnetwork/netutils" + "github.com/vishvananda/netlink" + "github.com/vishvananda/netns" +) + +const ( + vethName1 = "wierdlongname1" + vethName2 = "wierdlongname2" + sboxIfaceName = "containername" +) + +func newKey(t *testing.T) (string, error) { + name, err := netutils.GenerateRandomName("netns", 12) + if err != nil { + return "", err + } + + name = filepath.Join("/tmp", name) + if _, err := os.Create(name); err != nil { + return "", err + } + + return name, nil +} + +func newInfo(t *testing.T) (*Info, error) { + veth := &netlink.Veth{ + LinkAttrs: netlink.LinkAttrs{Name: vethName1, TxQLen: 0}, + PeerName: vethName2} + err := netlink.LinkAdd(veth) + if err != nil { + return nil, err + } + + // Store the sandbox side pipe interface + // This is needed for cleanup on DeleteEndpoint() + intf := &Interface{} + intf.SrcName = vethName2 + intf.DstName = sboxIfaceName + + ip4, addr, err := net.ParseCIDR("192.168.1.100/24") + if err != nil { + return nil, err + } + intf.Address = addr + intf.Address.IP = ip4 + + // ip6, addrv6, err := net.ParseCIDR("2001:DB8::ABCD/48") + ip6, addrv6, err := net.ParseCIDR("fe80::2/64") + if err != nil { + return nil, err + } + intf.AddressIPv6 = addrv6 + intf.AddressIPv6.IP = ip6 + + sinfo := &Info{Interfaces: []*Interface{intf}} + sinfo.Gateway = net.ParseIP("192.168.1.1") + // sinfo.GatewayIPv6 = net.ParseIP("2001:DB8::1") + sinfo.GatewayIPv6 = net.ParseIP("fe80::1") + + return sinfo, nil +} + +func verifySandbox(t *testing.T, s Sandbox) { + _, ok := s.(*networkNamespace) + if !ok { + t.Fatalf("The sandox interface returned is not of type networkNamespace") + } + + origns, err := netns.Get() + if err != nil { + t.Fatalf("Could not get the current netns: %v", err) + } + defer origns.Close() + + f, err := os.OpenFile(s.Key(), os.O_RDONLY, 0) + if err != nil { + t.Fatalf("Failed top open network namespace path %q: %v", s.Key(), err) + } + defer f.Close() + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + nsFD := f.Fd() + if err = netns.Set(netns.NsHandle(nsFD)); err != nil { + t.Fatalf("Setting to the namespace pointed to by the sandbox %s failed: %v", s.Key(), err) + } + defer netns.Set(origns) + + _, err = netlink.LinkByName(sboxIfaceName) + if err != nil { + t.Fatalf("Could not find the interface %s inside the sandbox: %v", sboxIfaceName, + err) + } +} diff --git a/vendor/src/github.com/docker/libnetwork/sandbox/sandbox_test.go b/vendor/src/github.com/docker/libnetwork/sandbox/sandbox_test.go new file mode 100644 index 0000000000..811af6d916 --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/sandbox/sandbox_test.go @@ -0,0 +1,149 @@ +package sandbox + +import ( + "net" + "testing" +) + +func TestSandboxCreate(t *testing.T) { + key, err := newKey(t) + if err != nil { + t.Fatalf("Failed to obtain a key: %v", err) + } + + s, err := NewSandbox(key, true) + if err != nil { + t.Fatalf("Failed to create a new sandbox: %v", err) + } + + if s.Key() != key { + t.Fatalf("s.Key() returned %s. Expected %s", s.Key(), key) + } + + info, err := newInfo(t) + if err != nil { + t.Fatalf("Failed to generate new sandbox info: %v", err) + } + + for _, i := range info.Interfaces { + err = s.AddInterface(i) + if err != nil { + t.Fatalf("Failed to add interfaces to sandbox: %v", err) + } + } + + err = s.SetGateway(info.Gateway) + if err != nil { + t.Fatalf("Failed to set gateway to sandbox: %v", err) + } + + err = s.SetGatewayIPv6(info.GatewayIPv6) + if err != nil { + t.Fatalf("Failed to set ipv6 gateway to sandbox: %v", err) + } + + verifySandbox(t, s) + s.Destroy() +} + +func TestSandboxCreateTwice(t *testing.T) { + key, err := newKey(t) + if err != nil { + t.Fatalf("Failed to obtain a key: %v", err) + } + + _, err = NewSandbox(key, true) + if err != nil { + t.Fatalf("Failed to create a new sandbox: %v", err) + } + + // Create another sandbox with the same key to see if we handle it + // gracefully. + s, err := NewSandbox(key, true) + if err != nil { + t.Fatalf("Failed to create a new sandbox: %v", err) + } + s.Destroy() +} + +func TestInterfaceEqual(t *testing.T) { + list := getInterfaceList() + + if !list[0].Equal(list[0]) { + t.Fatalf("Interface.Equal() returned false negative") + } + + if list[0].Equal(list[1]) { + t.Fatalf("Interface.Equal() returned false positive") + } + + if list[0].Equal(list[1]) != list[1].Equal(list[0]) { + t.Fatalf("Interface.Equal() failed commutative check") + } +} + +func TestSandboxInfoEqual(t *testing.T) { + si1 := &Info{Interfaces: getInterfaceList(), Gateway: net.ParseIP("192.168.1.254"), GatewayIPv6: net.ParseIP("2001:2345::abcd:8889")} + si2 := &Info{Interfaces: getInterfaceList(), Gateway: net.ParseIP("172.18.255.254"), GatewayIPv6: net.ParseIP("2001:2345::abcd:8888")} + + if !si1.Equal(si1) { + t.Fatalf("Info.Equal() returned false negative") + } + + if si1.Equal(si2) { + t.Fatalf("Info.Equal() returned false positive") + } + + if si1.Equal(si2) != si2.Equal(si1) { + t.Fatalf("Info.Equal() failed commutative check") + } +} + +func TestInterfaceCopy(t *testing.T) { + for _, iface := range getInterfaceList() { + cp := iface.GetCopy() + + if !iface.Equal(cp) { + t.Fatalf("Failed to return a copy of Interface") + } + + if iface == cp { + t.Fatalf("Failed to return a true copy of Interface") + } + } +} + +func TestSandboxInfoCopy(t *testing.T) { + si := Info{Interfaces: getInterfaceList(), Gateway: net.ParseIP("192.168.1.254"), GatewayIPv6: net.ParseIP("2001:2345::abcd:8889")} + cp := si.GetCopy() + + if !si.Equal(cp) { + t.Fatalf("Failed to return a copy of Info") + } + + if &si == cp { + t.Fatalf("Failed to return a true copy of Info") + } +} + +func getInterfaceList() []*Interface { + _, netv4a, _ := net.ParseCIDR("192.168.30.1/24") + _, netv4b, _ := net.ParseCIDR("172.18.255.2/23") + _, netv6a, _ := net.ParseCIDR("2001:2345::abcd:8888/80") + _, netv6b, _ := net.ParseCIDR("2001:2345::abcd:8889/80") + + return []*Interface{ + &Interface{ + SrcName: "veth1234567", + DstName: "eth0", + Address: netv4a, + AddressIPv6: netv6a, + }, + &Interface{ + SrcName: "veth7654321", + DstName: "eth1", + Address: netv4b, + AddressIPv6: netv6b, + }, + } +} diff --git a/vendor/src/github.com/docker/libnetwork/sandbox/sandbox_unsupported.go b/vendor/src/github.com/docker/libnetwork/sandbox/sandbox_unsupported.go new file mode 100644 index 0000000000..aa116fda07 --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/sandbox/sandbox_unsupported.go @@ -0,0 +1,15 @@ +// +build !linux + +package sandbox + +import "errors" + +var ( + 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) (Sandbox, error) { + return nil, ErrNotImplemented +} diff --git a/vendor/src/github.com/docker/libnetwork/sandbox/sandbox_unsupported_test.go b/vendor/src/github.com/docker/libnetwork/sandbox/sandbox_unsupported_test.go new file mode 100644 index 0000000000..48dc2aa726 --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/sandbox/sandbox_unsupported_test.go @@ -0,0 +1,20 @@ +// +build !linux + +package sandbox + +import ( + "errors" + "testing" +) + +var ( + ErrNotImplemented = errors.New("not implemented") +) + +func newKey(t *testing.T) (string, error) { + return nil, ErrNotImplemented +} + +func verifySandbox(t *testing.T, s Sandbox) { + return +} diff --git a/vendor/src/github.com/docker/libnetwork/system.go b/vendor/src/github.com/docker/libnetwork/system.go new file mode 100644 index 0000000000..7beec2876a --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/system.go @@ -0,0 +1,34 @@ +package libnetwork + +import ( + "fmt" + "runtime" + "syscall" +) + +// Via http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=7b21fddd087678a70ad64afc0f632e0f1071b092 +// +// We need different setns values for the different platforms and arch +// We are declaring the macro here because the SETNS syscall does not exist in th stdlib +var setNsMap = map[string]uintptr{ + "linux/386": 346, + "linux/amd64": 308, + "linux/arm": 374, + "linux/ppc64": 350, + "linux/ppc64le": 350, + "linux/s390x": 339, +} + +func setns(fd uintptr, flags uintptr) error { + ns, exists := setNsMap[fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH)] + if !exists { + return fmt.Errorf("unsupported platform %s/%s", runtime.GOOS, runtime.GOARCH) + } + + _, _, err := syscall.RawSyscall(ns, fd, flags, 0) + if err != 0 { + return err + } + + return nil +} diff --git a/vendor/src/github.com/docker/libnetwork/test/integration/README.md b/vendor/src/github.com/docker/libnetwork/test/integration/README.md new file mode 100644 index 0000000000..777b1cfa46 --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/test/integration/README.md @@ -0,0 +1,34 @@ +# LibNetwork Integration Tests + +Integration tests provide end-to-end testing of LibNetwork and Drivers. + +While unit tests verify the code is working as expected by relying on mocks and +artificially created fixtures, integration tests actually use real docker +engines and communicate to it through the CLI. + +Note that integration tests do **not** replace unit tests and Docker is used as a good use-case. + +As a rule of thumb, code should be tested thoroughly with unit tests. +Integration tests on the other hand are meant to test a specific feature end to end. + +Integration tests are written in *bash* using the +[bats](https://github.com/sstephenson/bats) framework. + +## Pre-Requisites + +1. Bats (https://github.com/sstephenson/bats#installing-bats-from-source) +2. Docker Machine (https://github.com/docker/machine) +3. Virtualbox (as a Docker machine driver) + +## Running integration tests + +* Start by [installing] (https://github.com/sstephenson/bats#installing-bats-from-source) *bats* on your system. +* If not done already, [install](https://docs.docker.com/machine/) *docker-machine* into /usr/bin +* Make sure Virtualbox is installed as well, which will be used by docker-machine as a driver to launch VMs + +In order to run all integration tests, pass *bats* the test path: +``` +$ bats test/integration/daemon-configs.bats +``` + + diff --git a/vendor/src/github.com/docker/libnetwork/test/integration/daemon-configs.bats b/vendor/src/github.com/docker/libnetwork/test/integration/daemon-configs.bats new file mode 100644 index 0000000000..fd48fbe199 --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/test/integration/daemon-configs.bats @@ -0,0 +1,104 @@ +#!/usr/bin/env bats + +load helpers + +export DRIVER=virtualbox +export NAME="bats-$DRIVER-daemon-configs" +export MACHINE_STORAGE_PATH=/tmp/machine-bats-daemon-test-$DRIVER +# Default memsize is 1024MB and disksize is 20000MB +# These values are defined in drivers/virtualbox/virtualbox.go +export DEFAULT_MEMSIZE=1024 +export DEFAULT_DISKSIZE=20000 +export CUSTOM_MEMSIZE=1536 +export CUSTOM_DISKSIZE=10000 +export CUSTOM_CPUCOUNT=1 +export BAD_URL="http://dev.null:9111/bad.iso" + +function setup() { + # add sleep because vbox; ugh + sleep 1 +} + +findDiskSize() { + # SATA-0-0 is usually the boot2disk.iso image + # We assume that SATA 1-0 is root disk VMDK and grab this UUID + # e.g. "SATA-ImageUUID-1-0"="fb5f33a7-e4e3-4cb9-877c-f9415ae2adea" + # TODO(slashk): does this work on Windows ? + run bash -c "VBoxManage showvminfo --machinereadable $NAME | grep SATA-ImageUUID-1-0 | cut -d'=' -f2" + run bash -c "VBoxManage showhdinfo $output | grep "Capacity:" | awk -F' ' '{ print $2 }'" +} + +findMemorySize() { + run bash -c "VBoxManage showvminfo --machinereadable $NAME | grep memory= | cut -d'=' -f2" +} + +findCPUCount() { + run bash -c "VBoxManage showvminfo --machinereadable $NAME | grep cpus= | cut -d'=' -f2" +} + +buildMachineWithOldIsoCheckUpgrade() { + run wget https://github.com/boot2docker/boot2docker/releases/download/v1.4.1/boot2docker.iso -O $MACHINE_STORAGE_PATH/cache/boot2docker.iso + run machine create -d virtualbox $NAME + run machine upgrade $NAME +} + +@test "$DRIVER: machine should not exist" { + run machine active $NAME + [ "$status" -eq 1 ] +} + +@test "$DRIVER: VM should not exist" { + run VBoxManage showvminfo $NAME + [ "$status" -eq 1 ] +} + +@test "$DRIVER: create" { + run machine create -d $DRIVER $NAME + [ "$status" -eq 0 ] +} + +@test "$DRIVER: active" { + run machine active $NAME + [ "$status" -eq 0 ] +} + +@test "$DRIVER: check default machine memory size" { + findMemorySize + [[ ${output} == "${DEFAULT_MEMSIZE}" ]] +} + +@test "$DRIVER: check default machine disksize" { + findDiskSize + [[ ${output} == *"$DEFAULT_DISKSIZE"* ]] +} + +@test "$DRIVER: test bridge-ip" { + run machine ssh $NAME sudo /etc/init.d/docker stop + run machine ssh $NAME sudo ifconfig docker0 down + run machine ssh $NAME sudo ip link delete docker0 + BIP='--bip=172.168.45.1/24' + set_extra_config $BIP + cat ${TMP_EXTRA_ARGS_FILE} | machine ssh $NAME sudo tee /var/lib/boot2docker/profile + cat ${DAEMON_CFG_FILE} | machine ssh $NAME "sudo tee -a /var/lib/boot2docker/profile" + run machine ssh $NAME sudo /etc/init.d/docker start + run machine ssh $NAME ifconfig docker0 + [ "$status" -eq 0 ] + [[ ${lines[1]} =~ "172.168.45.1" ]] +} + +@test "$DRIVER: run busybox container" { + run machine ssh $NAME sudo cat /var/lib/boot2docker/profile + run docker $(machine config $NAME) run busybox echo hello world + [ "$status" -eq 0 ] +} + +@test "$DRIVER: remove machine" { + run machine rm -f $NAME +} + +# Cleanup of machine store should always be the last 'test' +@test "$DRIVER: cleanup" { + run rm -rf $MACHINE_STORAGE_PATH + [ "$status" -eq 0 ] +} + diff --git a/vendor/src/github.com/docker/libnetwork/test/integration/daemon.cfg b/vendor/src/github.com/docker/libnetwork/test/integration/daemon.cfg new file mode 100644 index 0000000000..fc93dbd604 --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/test/integration/daemon.cfg @@ -0,0 +1,4 @@ +CACERT=/var/lib/boot2docker/ca.pem +SERVERCERT=/var/lib/boot2docker/server-key.pem +SERVERKEY=/var/lib/boot2docker/server.pem +DOCKER_TLS=no diff --git a/vendor/src/github.com/docker/libnetwork/test/integration/helpers.bash b/vendor/src/github.com/docker/libnetwork/test/integration/helpers.bash new file mode 100644 index 0000000000..ec18e5d4eb --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/test/integration/helpers.bash @@ -0,0 +1,50 @@ +#!/bin/bash + +# Root directory of the repository. +MACHINE_ROOT=/usr/bin + +PLATFORM=`uname -s | tr '[:upper:]' '[:lower:]'` +ARCH=`uname -m` + +if [ "$ARCH" = "x86_64" ]; then + ARCH="amd64" +else + ARCH="386" +fi +MACHINE_BIN_NAME=docker-machine_$PLATFORM-$ARCH +BATS_LOG=/tmp/bats.log + +touch ${BATS_LOG} +rm ${BATS_LOG} + +teardown() { + echo "$BATS_TEST_NAME +---------- +$output +---------- + +" >> ${BATS_LOG} +} + +EXTRA_ARGS_CFG='EXTRA_ARGS' +EXTRA_ARGS='--tlsverify --tlscacert=/var/lib/boot2docker/ca.pem --tlskey=/var/lib/boot2docker/server-key.pem --tlscert=/var/lib/boot2docker/server.pem --label=provider=virtualbox -H tcp://0.0.0.0:2376' +TMP_EXTRA_ARGS_FILE=/tmp/tmp_extra_args +DAEMON_CFG_FILE=${BATS_TEST_DIRNAME}/daemon.cfg +set_extra_config() { + if [ -f ${TMP_EXTRA_ARGS_FILE} ]; + then + rm ${TMP_EXTRA_ARGS_FILE} + fi + echo -n "${EXTRA_ARGS_CFG}='" > ${TMP_EXTRA_ARGS_FILE} + echo -n "$1 " >> ${TMP_EXTRA_ARGS_FILE} + echo "${EXTRA_ARGS}'" >> ${TMP_EXTRA_ARGS_FILE} +} + +if [ ! -e $MACHINE_ROOT/$MACHINE_BIN_NAME ]; then + echo "${MACHINE_ROOT}/${MACHINE_BIN_NAME} not found" + exit 1 +fi + +function machine() { + ${MACHINE_ROOT}/$MACHINE_BIN_NAME "$@" +} diff --git a/vendor/src/github.com/docker/libnetwork/types/types.go b/vendor/src/github.com/docker/libnetwork/types/types.go new file mode 100644 index 0000000000..363d4ab73c --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/types/types.go @@ -0,0 +1,5 @@ +// Package types contains types that are common across libnetwork project +package types + +// UUID represents a globally unique ID of various resources like network and endpoint +type UUID string diff --git a/vendor/src/github.com/go-fsnotify/fsnotify/.gitignore b/vendor/src/github.com/go-fsnotify/fsnotify/.gitignore deleted file mode 100644 index 4cd0cbaf43..0000000000 --- a/vendor/src/github.com/go-fsnotify/fsnotify/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -# Setup a Global .gitignore for OS and editor generated files: -# https://help.github.com/articles/ignoring-files -# git config --global core.excludesfile ~/.gitignore_global - -.vagrant -*.sublime-project diff --git a/vendor/src/github.com/go-fsnotify/fsnotify/.travis.yml b/vendor/src/github.com/go-fsnotify/fsnotify/.travis.yml deleted file mode 100644 index 67467e1407..0000000000 --- a/vendor/src/github.com/go-fsnotify/fsnotify/.travis.yml +++ /dev/null @@ -1,15 +0,0 @@ -sudo: false -language: go - -go: - - 1.4.1 - -before_script: - - FIXED=$(go fmt ./... | wc -l); if [ $FIXED -gt 0 ]; then echo "gofmt - $FIXED file(s) not formatted correctly, please run gofmt to fix this." && exit 1; fi - -os: - - linux - - osx - -notifications: - email: false diff --git a/vendor/src/github.com/go-fsnotify/fsnotify/AUTHORS b/vendor/src/github.com/go-fsnotify/fsnotify/AUTHORS deleted file mode 100644 index 4e0e8284e9..0000000000 --- a/vendor/src/github.com/go-fsnotify/fsnotify/AUTHORS +++ /dev/null @@ -1,34 +0,0 @@ -# Names should be added to this file as -# Name or Organization -# The email address is not required for organizations. - -# You can update this list using the following command: -# -# $ git shortlog -se | awk '{print $2 " " $3 " " $4}' - -# Please keep the list sorted. - -Adrien Bustany -Caleb Spare -Case Nelson -Chris Howey -Christoffer Buchholz -Dave Cheney -Francisco Souza -Hari haran -John C Barstow -Kelvin Fo -Matt Layher -Nathan Youngman -Paul Hammond -Pieter Droogendijk -Pursuit92 -Rob Figueiredo -Soge Zhang -Tilak Sharma -Travis Cline -Tudor Golubenco -Yukang -bronze1man -debrando -henrikedwards diff --git a/vendor/src/github.com/go-fsnotify/fsnotify/CHANGELOG.md b/vendor/src/github.com/go-fsnotify/fsnotify/CHANGELOG.md deleted file mode 100644 index ea9428a2a4..0000000000 --- a/vendor/src/github.com/go-fsnotify/fsnotify/CHANGELOG.md +++ /dev/null @@ -1,263 +0,0 @@ -# Changelog - -## v1.2.0 / 2015-02-08 - -* inotify: use epoll to wake up readEvents [#66](https://github.com/go-fsnotify/fsnotify/pull/66) (thanks @PieterD) -* inotify: closing watcher should now always shut down goroutine [#63](https://github.com/go-fsnotify/fsnotify/pull/63) (thanks @PieterD) -* kqueue: close kqueue after removing watches, fixes [#59](https://github.com/go-fsnotify/fsnotify/issues/59) - -## v1.1.1 / 2015-02-05 - -* inotify: Retry read on EINTR [#61](https://github.com/go-fsnotify/fsnotify/issues/61) (thanks @PieterD) - -## v1.1.0 / 2014-12-12 - -* kqueue: rework internals [#43](https://github.com/go-fsnotify/fsnotify/pull/43) - * add low-level functions - * only need to store flags on directories - * less mutexes [#13](https://github.com/go-fsnotify/fsnotify/issues/13) - * done can be an unbuffered channel - * remove calls to os.NewSyscallError -* More efficient string concatenation for Event.String() [#52](https://github.com/go-fsnotify/fsnotify/pull/52) (thanks @mdlayher) -* kqueue: fix regression in rework causing subdirectories to be watched [#48](https://github.com/go-fsnotify/fsnotify/issues/48) -* kqueue: cleanup internal watch before sending remove event [#51](https://github.com/go-fsnotify/fsnotify/issues/51) - -## v1.0.4 / 2014-09-07 - -* kqueue: add dragonfly to the build tags. -* Rename source code files, rearrange code so exported APIs are at the top. -* Add done channel to example code. [#37](https://github.com/go-fsnotify/fsnotify/pull/37) (thanks @chenyukang) - -## v1.0.3 / 2014-08-19 - -* [Fix] Windows MOVED_TO now translates to Create like on BSD and Linux. [#36](https://github.com/go-fsnotify/fsnotify/issues/36) - -## v1.0.2 / 2014-08-17 - -* [Fix] Missing create events on OS X. [#14](https://github.com/go-fsnotify/fsnotify/issues/14) (thanks @zhsso) -* [Fix] Make ./path and path equivalent. (thanks @zhsso) - -## v1.0.0 / 2014-08-15 - -* [API] Remove AddWatch on Windows, use Add. -* Improve documentation for exported identifiers. [#30](https://github.com/go-fsnotify/fsnotify/issues/30) -* Minor updates based on feedback from golint. - -## dev / 2014-07-09 - -* Moved to [github.com/go-fsnotify/fsnotify](https://github.com/go-fsnotify/fsnotify). -* Use os.NewSyscallError instead of returning errno (thanks @hariharan-uno) - -## dev / 2014-07-04 - -* kqueue: fix incorrect mutex used in Close() -* Update example to demonstrate usage of Op. - -## dev / 2014-06-28 - -* [API] Don't set the Write Op for attribute notifications [#4](https://github.com/go-fsnotify/fsnotify/issues/4) -* Fix for String() method on Event (thanks Alex Brainman) -* Don't build on Plan 9 or Solaris (thanks @4ad) - -## dev / 2014-06-21 - -* Events channel of type Event rather than *Event. -* [internal] use syscall constants directly for inotify and kqueue. -* [internal] kqueue: rename events to kevents and fileEvent to event. - -## dev / 2014-06-19 - -* Go 1.3+ required on Windows (uses syscall.ERROR_MORE_DATA internally). -* [internal] remove cookie from Event struct (unused). -* [internal] Event struct has the same definition across every OS. -* [internal] remove internal watch and removeWatch methods. - -## dev / 2014-06-12 - -* [API] Renamed Watch() to Add() and RemoveWatch() to Remove(). -* [API] Pluralized channel names: Events and Errors. -* [API] Renamed FileEvent struct to Event. -* [API] Op constants replace methods like IsCreate(). - -## dev / 2014-06-12 - -* Fix data race on kevent buffer (thanks @tilaks) [#98](https://github.com/howeyc/fsnotify/pull/98) - -## dev / 2014-05-23 - -* [API] Remove current implementation of WatchFlags. - * current implementation doesn't take advantage of OS for efficiency - * provides little benefit over filtering events as they are received, but has extra bookkeeping and mutexes - * no tests for the current implementation - * not fully implemented on Windows [#93](https://github.com/howeyc/fsnotify/issues/93#issuecomment-39285195) - -## v0.9.3 / 2014-12-31 - -* kqueue: cleanup internal watch before sending remove event [#51](https://github.com/go-fsnotify/fsnotify/issues/51) - -## v0.9.2 / 2014-08-17 - -* [Backport] Fix missing create events on OS X. [#14](https://github.com/go-fsnotify/fsnotify/issues/14) (thanks @zhsso) - -## v0.9.1 / 2014-06-12 - -* Fix data race on kevent buffer (thanks @tilaks) [#98](https://github.com/howeyc/fsnotify/pull/98) - -## v0.9.0 / 2014-01-17 - -* IsAttrib() for events that only concern a file's metadata [#79][] (thanks @abustany) -* [Fix] kqueue: fix deadlock [#77][] (thanks @cespare) -* [NOTICE] Development has moved to `code.google.com/p/go.exp/fsnotify` in preparation for inclusion in the Go standard library. - -## v0.8.12 / 2013-11-13 - -* [API] Remove FD_SET and friends from Linux adapter - -## v0.8.11 / 2013-11-02 - -* [Doc] Add Changelog [#72][] (thanks @nathany) -* [Doc] Spotlight and double modify events on OS X [#62][] (reported by @paulhammond) - -## v0.8.10 / 2013-10-19 - -* [Fix] kqueue: remove file watches when parent directory is removed [#71][] (reported by @mdwhatcott) -* [Fix] kqueue: race between Close and readEvents [#70][] (reported by @bernerdschaefer) -* [Doc] specify OS-specific limits in README (thanks @debrando) - -## v0.8.9 / 2013-09-08 - -* [Doc] Contributing (thanks @nathany) -* [Doc] update package path in example code [#63][] (thanks @paulhammond) -* [Doc] GoCI badge in README (Linux only) [#60][] -* [Doc] Cross-platform testing with Vagrant [#59][] (thanks @nathany) - -## v0.8.8 / 2013-06-17 - -* [Fix] Windows: handle `ERROR_MORE_DATA` on Windows [#49][] (thanks @jbowtie) - -## v0.8.7 / 2013-06-03 - -* [API] Make syscall flags internal -* [Fix] inotify: ignore event changes -* [Fix] race in symlink test [#45][] (reported by @srid) -* [Fix] tests on Windows -* lower case error messages - -## v0.8.6 / 2013-05-23 - -* kqueue: Use EVT_ONLY flag on Darwin -* [Doc] Update README with full example - -## v0.8.5 / 2013-05-09 - -* [Fix] inotify: allow monitoring of "broken" symlinks (thanks @tsg) - -## v0.8.4 / 2013-04-07 - -* [Fix] kqueue: watch all file events [#40][] (thanks @ChrisBuchholz) - -## v0.8.3 / 2013-03-13 - -* [Fix] inoitfy/kqueue memory leak [#36][] (reported by @nbkolchin) -* [Fix] kqueue: use fsnFlags for watching a directory [#33][] (reported by @nbkolchin) - -## v0.8.2 / 2013-02-07 - -* [Doc] add Authors -* [Fix] fix data races for map access [#29][] (thanks @fsouza) - -## v0.8.1 / 2013-01-09 - -* [Fix] Windows path separators -* [Doc] BSD License - -## v0.8.0 / 2012-11-09 - -* kqueue: directory watching improvements (thanks @vmirage) -* inotify: add `IN_MOVED_TO` [#25][] (requested by @cpisto) -* [Fix] kqueue: deleting watched directory [#24][] (reported by @jakerr) - -## v0.7.4 / 2012-10-09 - -* [Fix] inotify: fixes from https://codereview.appspot.com/5418045/ (ugorji) -* [Fix] kqueue: preserve watch flags when watching for delete [#21][] (reported by @robfig) -* [Fix] kqueue: watch the directory even if it isn't a new watch (thanks @robfig) -* [Fix] kqueue: modify after recreation of file - -## v0.7.3 / 2012-09-27 - -* [Fix] kqueue: watch with an existing folder inside the watched folder (thanks @vmirage) -* [Fix] kqueue: no longer get duplicate CREATE events - -## v0.7.2 / 2012-09-01 - -* kqueue: events for created directories - -## v0.7.1 / 2012-07-14 - -* [Fix] for renaming files - -## v0.7.0 / 2012-07-02 - -* [Feature] FSNotify flags -* [Fix] inotify: Added file name back to event path - -## v0.6.0 / 2012-06-06 - -* kqueue: watch files after directory created (thanks @tmc) - -## v0.5.1 / 2012-05-22 - -* [Fix] inotify: remove all watches before Close() - -## v0.5.0 / 2012-05-03 - -* [API] kqueue: return errors during watch instead of sending over channel -* kqueue: match symlink behavior on Linux -* inotify: add `DELETE_SELF` (requested by @taralx) -* [Fix] kqueue: handle EINTR (reported by @robfig) -* [Doc] Godoc example [#1][] (thanks @davecheney) - -## v0.4.0 / 2012-03-30 - -* Go 1 released: build with go tool -* [Feature] Windows support using winfsnotify -* Windows does not have attribute change notifications -* Roll attribute notifications into IsModify - -## v0.3.0 / 2012-02-19 - -* kqueue: add files when watch directory - -## v0.2.0 / 2011-12-30 - -* update to latest Go weekly code - -## v0.1.0 / 2011-10-19 - -* kqueue: add watch on file creation to match inotify -* kqueue: create file event -* inotify: ignore `IN_IGNORED` events -* event String() -* linux: common FileEvent functions -* initial commit - -[#79]: https://github.com/howeyc/fsnotify/pull/79 -[#77]: https://github.com/howeyc/fsnotify/pull/77 -[#72]: https://github.com/howeyc/fsnotify/issues/72 -[#71]: https://github.com/howeyc/fsnotify/issues/71 -[#70]: https://github.com/howeyc/fsnotify/issues/70 -[#63]: https://github.com/howeyc/fsnotify/issues/63 -[#62]: https://github.com/howeyc/fsnotify/issues/62 -[#60]: https://github.com/howeyc/fsnotify/issues/60 -[#59]: https://github.com/howeyc/fsnotify/issues/59 -[#49]: https://github.com/howeyc/fsnotify/issues/49 -[#45]: https://github.com/howeyc/fsnotify/issues/45 -[#40]: https://github.com/howeyc/fsnotify/issues/40 -[#36]: https://github.com/howeyc/fsnotify/issues/36 -[#33]: https://github.com/howeyc/fsnotify/issues/33 -[#29]: https://github.com/howeyc/fsnotify/issues/29 -[#25]: https://github.com/howeyc/fsnotify/issues/25 -[#24]: https://github.com/howeyc/fsnotify/issues/24 -[#21]: https://github.com/howeyc/fsnotify/issues/21 - diff --git a/vendor/src/github.com/go-fsnotify/fsnotify/CONTRIBUTING.md b/vendor/src/github.com/go-fsnotify/fsnotify/CONTRIBUTING.md deleted file mode 100644 index 0f377f341b..0000000000 --- a/vendor/src/github.com/go-fsnotify/fsnotify/CONTRIBUTING.md +++ /dev/null @@ -1,77 +0,0 @@ -# Contributing - -## Issues - -* Request features and report bugs using the [GitHub Issue Tracker](https://github.com/go-fsnotify/fsnotify/issues). -* Please indicate the platform you are using fsnotify on. -* A code example to reproduce the problem is appreciated. - -## Pull Requests - -### Contributor License Agreement - -fsnotify is derived from code in the [golang.org/x/exp](https://godoc.org/golang.org/x/exp) package and it may be included [in the standard library](https://github.com/go-fsnotify/fsnotify/issues/1) in the future. Therefore fsnotify carries the same [LICENSE](https://github.com/go-fsnotify/fsnotify/blob/master/LICENSE) as Go. Contributors retain their copyright, so you need to fill out a short form before we can accept your contribution: [Google Individual Contributor License Agreement](https://developers.google.com/open-source/cla/individual). - -Please indicate that you have signed the CLA in your pull request. - -### How fsnotify is Developed - -* Development is done on feature branches. -* Tests are run on BSD, Linux, OS X and Windows. -* Pull requests are reviewed and [applied to master][am] using [hub][]. - * Maintainers may modify or squash commits rather than asking contributors to. -* To issue a new release, the maintainers will: - * Update the CHANGELOG - * Tag a version, which will become available through gopkg.in. - -### How to Fork - -For smooth sailing, always use the original import path. Installing with `go get` makes this easy. - -1. Install from GitHub (`go get -u github.com/go-fsnotify/fsnotify`) -2. Create your feature branch (`git checkout -b my-new-feature`) -3. Ensure everything works and the tests pass (see below) -4. Commit your changes (`git commit -am 'Add some feature'`) - -Contribute upstream: - -1. Fork fsnotify on GitHub -2. Add your remote (`git remote add fork git@github.com:mycompany/repo.git`) -3. Push to the branch (`git push fork my-new-feature`) -4. Create a new Pull Request on GitHub - -This workflow is [thoroughly explained by Katrina Owen](https://blog.splice.com/contributing-open-source-git-repositories-go/). - -### Testing - -fsnotify uses build tags to compile different code on Linux, BSD, OS X, and Windows. - -Before doing a pull request, please do your best to test your changes on multiple platforms, and list which platforms you were able/unable to test on. - -To aid in cross-platform testing there is a Vagrantfile for Linux and BSD. - -* Install [Vagrant](http://www.vagrantup.com/) and [VirtualBox](https://www.virtualbox.org/) -* Setup [Vagrant Gopher](https://github.com/nathany/vagrant-gopher) in your `src` folder. -* Run `vagrant up` from the project folder. You can also setup just one box with `vagrant up linux` or `vagrant up bsd` (note: the BSD box doesn't support Windows hosts at this time, and NFS may prompt for your host OS password) -* Once setup, you can run the test suite on a given OS with a single command `vagrant ssh linux -c 'cd go-fsnotify/fsnotify; go test'`. -* When you're done, you will want to halt or destroy the Vagrant boxes. - -Notice: fsnotify file system events won't trigger in shared folders. The tests get around this limitation by using the /tmp directory. - -Right now there is no equivalent solution for Windows and OS X, but there are Windows VMs [freely available from Microsoft](http://www.modern.ie/en-us/virtualization-tools#downloads). - -### Maintainers - -Help maintaining fsnotify is welcome. To be a maintainer: - -* Submit a pull request and sign the CLA as above. -* You must be able to run the test suite on Mac, Windows, Linux and BSD. - -To keep master clean, the fsnotify project uses the "apply mail" workflow outlined in Nathaniel Talbott's post ["Merge pull request" Considered Harmful][am]. This requires installing [hub][]. - -All code changes should be internal pull requests. - -Releases are tagged using [Semantic Versioning](http://semver.org/). - -[hub]: https://github.com/github/hub -[am]: http://blog.spreedly.com/2014/06/24/merge-pull-request-considered-harmful/#.VGa5yZPF_Zs diff --git a/vendor/src/github.com/go-fsnotify/fsnotify/LICENSE b/vendor/src/github.com/go-fsnotify/fsnotify/LICENSE deleted file mode 100644 index f21e540800..0000000000 --- a/vendor/src/github.com/go-fsnotify/fsnotify/LICENSE +++ /dev/null @@ -1,28 +0,0 @@ -Copyright (c) 2012 The Go Authors. All rights reserved. -Copyright (c) 2012 fsnotify Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/src/github.com/go-fsnotify/fsnotify/NotUsed.xcworkspace b/vendor/src/github.com/go-fsnotify/fsnotify/NotUsed.xcworkspace deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/vendor/src/github.com/go-fsnotify/fsnotify/README.md b/vendor/src/github.com/go-fsnotify/fsnotify/README.md deleted file mode 100644 index 7a0b247364..0000000000 --- a/vendor/src/github.com/go-fsnotify/fsnotify/README.md +++ /dev/null @@ -1,59 +0,0 @@ -# File system notifications for Go - -[![Coverage](http://gocover.io/_badge/github.com/go-fsnotify/fsnotify)](http://gocover.io/github.com/go-fsnotify/fsnotify) [![GoDoc](https://godoc.org/gopkg.in/fsnotify.v1?status.svg)](https://godoc.org/gopkg.in/fsnotify.v1) - -Go 1.3+ required. - -Cross platform: Windows, Linux, BSD and OS X. - -|Adapter |OS |Status | -|----------|----------|----------| -|inotify |Linux, Android\*|Supported [![Build Status](https://travis-ci.org/go-fsnotify/fsnotify.svg?branch=master)](https://travis-ci.org/go-fsnotify/fsnotify)| -|kqueue |BSD, OS X, iOS\*|Supported [![Circle CI](https://circleci.com/gh/go-fsnotify/fsnotify.svg?style=svg)](https://circleci.com/gh/go-fsnotify/fsnotify)| -|ReadDirectoryChangesW|Windows|Supported [![Build status](https://ci.appveyor.com/api/projects/status/ivwjubaih4r0udeh/branch/master?svg=true)](https://ci.appveyor.com/project/NathanYoungman/fsnotify/branch/master)| -|FSEvents |OS X |[Planned](https://github.com/go-fsnotify/fsnotify/issues/11)| -|FEN |Solaris 11 |[Planned](https://github.com/go-fsnotify/fsnotify/issues/12)| -|fanotify |Linux 2.6.37+ | | -|USN Journals |Windows |[Maybe](https://github.com/go-fsnotify/fsnotify/issues/53)| -|Polling |*All* |[Maybe](https://github.com/go-fsnotify/fsnotify/issues/9)| - -\* Android and iOS are untested. - -Please see [the documentation](https://godoc.org/gopkg.in/fsnotify.v1) for usage. Consult the [Wiki](https://github.com/go-fsnotify/fsnotify/wiki) for the FAQ and further information. - -## API stability - -Two major versions of fsnotify exist. - -**[fsnotify.v0](https://gopkg.in/fsnotify.v0)** is API-compatible with [howeyc/fsnotify](https://godoc.org/github.com/howeyc/fsnotify). Bugfixes *may* be backported, but I recommend upgrading to v1. - -```go -import "gopkg.in/fsnotify.v0" -``` - -\* Refer to the package as fsnotify (without the .v0 suffix). - -**[fsnotify.v1](https://gopkg.in/fsnotify.v1)** provides [a new API](https://godoc.org/gopkg.in/fsnotify.v1) based on [this design document](http://goo.gl/MrYxyA). You can import v1 with: - -```go -import "gopkg.in/fsnotify.v1" -``` - -Further API changes are [planned](https://github.com/go-fsnotify/fsnotify/milestones), but a new major revision will be tagged, so you can depend on the v1 API. - -**Master** may have unreleased changes. Use it to test the very latest code or when [contributing][], but don't expect it to remain API-compatible: - -```go -import "github.com/go-fsnotify/fsnotify" -``` - -## Contributing - -Please refer to [CONTRIBUTING][] before opening an issue or pull request. - -## Example - -See [example_test.go](https://github.com/go-fsnotify/fsnotify/blob/master/example_test.go). - - -[contributing]: https://github.com/go-fsnotify/fsnotify/blob/master/CONTRIBUTING.md diff --git a/vendor/src/github.com/go-fsnotify/fsnotify/circle.yml b/vendor/src/github.com/go-fsnotify/fsnotify/circle.yml deleted file mode 100644 index 204217fb0b..0000000000 --- a/vendor/src/github.com/go-fsnotify/fsnotify/circle.yml +++ /dev/null @@ -1,26 +0,0 @@ -## OS X build (CircleCI iOS beta) - -# Pretend like it's an Xcode project, at least to get it running. -machine: - environment: - XCODE_WORKSPACE: NotUsed.xcworkspace - XCODE_SCHEME: NotUsed - # This is where the go project is actually checked out to: - CIRCLE_BUILD_DIR: $HOME/.go_project/src/github.com/go-fsnotify/fsnotify - -dependencies: - pre: - - brew upgrade go - -test: - override: - - go test ./... - -# Idealized future config, eventually with cross-platform build matrix :-) - -# machine: -# go: -# version: 1.4 -# os: -# - osx -# - linux diff --git a/vendor/src/github.com/go-fsnotify/fsnotify/example_test.go b/vendor/src/github.com/go-fsnotify/fsnotify/example_test.go deleted file mode 100644 index 3063796602..0000000000 --- a/vendor/src/github.com/go-fsnotify/fsnotify/example_test.go +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build !plan9,!solaris - -package fsnotify_test - -import ( - "log" - - "github.com/go-fsnotify/fsnotify" -) - -func ExampleNewWatcher() { - watcher, err := fsnotify.NewWatcher() - if err != nil { - log.Fatal(err) - } - defer watcher.Close() - - done := make(chan bool) - go func() { - for { - select { - case event := <-watcher.Events: - log.Println("event:", event) - if event.Op&fsnotify.Write == fsnotify.Write { - log.Println("modified file:", event.Name) - } - case err := <-watcher.Errors: - log.Println("error:", err) - } - } - }() - - err = watcher.Add("/tmp/foo") - if err != nil { - log.Fatal(err) - } - <-done -} diff --git a/vendor/src/github.com/go-fsnotify/fsnotify/fsnotify.go b/vendor/src/github.com/go-fsnotify/fsnotify/fsnotify.go deleted file mode 100644 index c899ee0083..0000000000 --- a/vendor/src/github.com/go-fsnotify/fsnotify/fsnotify.go +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build !plan9,!solaris - -// Package fsnotify provides a platform-independent interface for file system notifications. -package fsnotify - -import ( - "bytes" - "fmt" -) - -// Event represents a single file system notification. -type Event struct { - Name string // Relative path to the file or directory. - Op Op // File operation that triggered the event. -} - -// Op describes a set of file operations. -type Op uint32 - -// These are the generalized file operations that can trigger a notification. -const ( - Create Op = 1 << iota - Write - Remove - Rename - Chmod -) - -// String returns a string representation of the event in the form -// "file: REMOVE|WRITE|..." -func (e Event) String() string { - // Use a buffer for efficient string concatenation - var buffer bytes.Buffer - - if e.Op&Create == Create { - buffer.WriteString("|CREATE") - } - if e.Op&Remove == Remove { - buffer.WriteString("|REMOVE") - } - if e.Op&Write == Write { - buffer.WriteString("|WRITE") - } - if e.Op&Rename == Rename { - buffer.WriteString("|RENAME") - } - if e.Op&Chmod == Chmod { - buffer.WriteString("|CHMOD") - } - - // If buffer remains empty, return no event names - if buffer.Len() == 0 { - return fmt.Sprintf("%q: ", e.Name) - } - - // Return a list of event names, with leading pipe character stripped - return fmt.Sprintf("%q: %s", e.Name, buffer.String()[1:]) -} diff --git a/vendor/src/github.com/go-fsnotify/fsnotify/inotify.go b/vendor/src/github.com/go-fsnotify/fsnotify/inotify.go deleted file mode 100644 index d7759ec8c8..0000000000 --- a/vendor/src/github.com/go-fsnotify/fsnotify/inotify.go +++ /dev/null @@ -1,306 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build linux - -package fsnotify - -import ( - "errors" - "fmt" - "io" - "os" - "path/filepath" - "strings" - "sync" - "syscall" - "unsafe" -) - -// Watcher watches a set of files, delivering events to a channel. -type Watcher struct { - Events chan Event - Errors chan error - mu sync.Mutex // Map access - fd int - poller *fdPoller - watches map[string]*watch // Map of inotify watches (key: path) - paths map[int]string // Map of watched paths (key: watch descriptor) - done chan struct{} // Channel for sending a "quit message" to the reader goroutine - doneResp chan struct{} // Channel to respond to Close -} - -// NewWatcher establishes a new watcher with the underlying OS and begins waiting for events. -func NewWatcher() (*Watcher, error) { - // Create inotify fd - fd, errno := syscall.InotifyInit() - if fd == -1 { - return nil, errno - } - // Create epoll - poller, err := newFdPoller(fd) - if err != nil { - syscall.Close(fd) - return nil, err - } - w := &Watcher{ - fd: fd, - poller: poller, - watches: make(map[string]*watch), - paths: make(map[int]string), - Events: make(chan Event), - Errors: make(chan error), - done: make(chan struct{}), - doneResp: make(chan struct{}), - } - - go w.readEvents() - return w, nil -} - -func (w *Watcher) isClosed() bool { - select { - case <-w.done: - return true - default: - return false - } -} - -// Close removes all watches and closes the events channel. -func (w *Watcher) Close() error { - if w.isClosed() { - return nil - } - - // Send 'close' signal to goroutine, and set the Watcher to closed. - close(w.done) - - // Wake up goroutine - w.poller.wake() - - // Wait for goroutine to close - <-w.doneResp - - return nil -} - -// Add starts watching the named file or directory (non-recursively). -func (w *Watcher) Add(name string) error { - name = filepath.Clean(name) - if w.isClosed() { - return errors.New("inotify instance already closed") - } - - const agnosticEvents = syscall.IN_MOVED_TO | syscall.IN_MOVED_FROM | - syscall.IN_CREATE | syscall.IN_ATTRIB | syscall.IN_MODIFY | - syscall.IN_MOVE_SELF | syscall.IN_DELETE | syscall.IN_DELETE_SELF - - var flags uint32 = agnosticEvents - - w.mu.Lock() - watchEntry, found := w.watches[name] - w.mu.Unlock() - if found { - watchEntry.flags |= flags - flags |= syscall.IN_MASK_ADD - } - wd, errno := syscall.InotifyAddWatch(w.fd, name, flags) - if wd == -1 { - return errno - } - - w.mu.Lock() - w.watches[name] = &watch{wd: uint32(wd), flags: flags} - w.paths[wd] = name - w.mu.Unlock() - - return nil -} - -// Remove stops watching the named file or directory (non-recursively). -func (w *Watcher) Remove(name string) error { - name = filepath.Clean(name) - - // Fetch the watch. - w.mu.Lock() - defer w.mu.Unlock() - watch, ok := w.watches[name] - - // Remove it from inotify. - if !ok { - return fmt.Errorf("can't remove non-existent inotify watch for: %s", name) - } - // inotify_rm_watch will return EINVAL if the file has been deleted; - // the inotify will already have been removed. - // That means we can safely delete it from our watches, whatever inotify_rm_watch does. - delete(w.watches, name) - success, errno := syscall.InotifyRmWatch(w.fd, watch.wd) - if success == -1 { - // TODO: Perhaps it's not helpful to return an error here in every case. - // the only two possible errors are: - // EBADF, which happens when w.fd is not a valid file descriptor of any kind. - // EINVAL, which is when fd is not an inotify descriptor or wd is not a valid watch descriptor. - // Watch descriptors are invalidated when they are removed explicitly or implicitly; - // explicitly by inotify_rm_watch, implicitly when the file they are watching is deleted. - return errno - } - return nil -} - -type watch struct { - wd uint32 // Watch descriptor (as returned by the inotify_add_watch() syscall) - flags uint32 // inotify flags of this watch (see inotify(7) for the list of valid flags) -} - -// readEvents reads from the inotify file descriptor, converts the -// received events into Event objects and sends them via the Events channel -func (w *Watcher) readEvents() { - var ( - buf [syscall.SizeofInotifyEvent * 4096]byte // Buffer for a maximum of 4096 raw events - n int // Number of bytes read with read() - errno error // Syscall errno - ok bool // For poller.wait - ) - - defer close(w.doneResp) - defer close(w.Errors) - defer close(w.Events) - defer syscall.Close(w.fd) - defer w.poller.close() - - for { - // See if we have been closed. - if w.isClosed() { - return - } - - ok, errno = w.poller.wait() - if errno != nil { - select { - case w.Errors <- errno: - case <-w.done: - return - } - continue - } - - if !ok { - continue - } - - n, errno = syscall.Read(w.fd, buf[:]) - // If a signal interrupted execution, see if we've been asked to close, and try again. - // http://man7.org/linux/man-pages/man7/signal.7.html : - // "Before Linux 3.8, reads from an inotify(7) file descriptor were not restartable" - if errno == syscall.EINTR { - continue - } - - // syscall.Read might have been woken up by Close. If so, we're done. - if w.isClosed() { - return - } - - if n < syscall.SizeofInotifyEvent { - var err error - if n == 0 { - // If EOF is received. This should really never happen. - err = io.EOF - } else if n < 0 { - // If an error occured while reading. - err = errno - } else { - // Read was too short. - err = errors.New("notify: short read in readEvents()") - } - select { - case w.Errors <- err: - case <-w.done: - return - } - continue - } - - var offset uint32 - // We don't know how many events we just read into the buffer - // While the offset points to at least one whole event... - for offset <= uint32(n-syscall.SizeofInotifyEvent) { - // Point "raw" to the event in the buffer - raw := (*syscall.InotifyEvent)(unsafe.Pointer(&buf[offset])) - - mask := uint32(raw.Mask) - nameLen := uint32(raw.Len) - // If the event happened to the watched directory or the watched file, the kernel - // doesn't append the filename to the event, but we would like to always fill the - // the "Name" field with a valid filename. We retrieve the path of the watch from - // the "paths" map. - w.mu.Lock() - name := w.paths[int(raw.Wd)] - w.mu.Unlock() - if nameLen > 0 { - // Point "bytes" at the first byte of the filename - bytes := (*[syscall.PathMax]byte)(unsafe.Pointer(&buf[offset+syscall.SizeofInotifyEvent])) - // The filename is padded with NULL bytes. TrimRight() gets rid of those. - name += "/" + strings.TrimRight(string(bytes[0:nameLen]), "\000") - } - - event := newEvent(name, mask) - - // Send the events that are not ignored on the events channel - if !event.ignoreLinux(mask) { - select { - case w.Events <- event: - case <-w.done: - return - } - } - - // Move to the next event in the buffer - offset += syscall.SizeofInotifyEvent + nameLen - } - } -} - -// Certain types of events can be "ignored" and not sent over the Events -// channel. Such as events marked ignore by the kernel, or MODIFY events -// against files that do not exist. -func (e *Event) ignoreLinux(mask uint32) bool { - // Ignore anything the inotify API says to ignore - if mask&syscall.IN_IGNORED == syscall.IN_IGNORED { - return true - } - - // If the event is not a DELETE or RENAME, the file must exist. - // Otherwise the event is ignored. - // *Note*: this was put in place because it was seen that a MODIFY - // event was sent after the DELETE. This ignores that MODIFY and - // assumes a DELETE will come or has come if the file doesn't exist. - if !(e.Op&Remove == Remove || e.Op&Rename == Rename) { - _, statErr := os.Lstat(e.Name) - return os.IsNotExist(statErr) - } - return false -} - -// newEvent returns an platform-independent Event based on an inotify mask. -func newEvent(name string, mask uint32) Event { - e := Event{Name: name} - if mask&syscall.IN_CREATE == syscall.IN_CREATE || mask&syscall.IN_MOVED_TO == syscall.IN_MOVED_TO { - e.Op |= Create - } - if mask&syscall.IN_DELETE_SELF == syscall.IN_DELETE_SELF || mask&syscall.IN_DELETE == syscall.IN_DELETE { - e.Op |= Remove - } - if mask&syscall.IN_MODIFY == syscall.IN_MODIFY { - e.Op |= Write - } - if mask&syscall.IN_MOVE_SELF == syscall.IN_MOVE_SELF || mask&syscall.IN_MOVED_FROM == syscall.IN_MOVED_FROM { - e.Op |= Rename - } - if mask&syscall.IN_ATTRIB == syscall.IN_ATTRIB { - e.Op |= Chmod - } - return e -} diff --git a/vendor/src/github.com/go-fsnotify/fsnotify/inotify_poller.go b/vendor/src/github.com/go-fsnotify/fsnotify/inotify_poller.go deleted file mode 100644 index 3b41784041..0000000000 --- a/vendor/src/github.com/go-fsnotify/fsnotify/inotify_poller.go +++ /dev/null @@ -1,186 +0,0 @@ -// Copyright 2015 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build linux - -package fsnotify - -import ( - "errors" - "syscall" -) - -type fdPoller struct { - fd int // File descriptor (as returned by the inotify_init() syscall) - epfd int // Epoll file descriptor - pipe [2]int // Pipe for waking up -} - -func emptyPoller(fd int) *fdPoller { - poller := new(fdPoller) - poller.fd = fd - poller.epfd = -1 - poller.pipe[0] = -1 - poller.pipe[1] = -1 - return poller -} - -// Create a new inotify poller. -// This creates an inotify handler, and an epoll handler. -func newFdPoller(fd int) (*fdPoller, error) { - var errno error - poller := emptyPoller(fd) - defer func() { - if errno != nil { - poller.close() - } - }() - poller.fd = fd - - // Create epoll fd - poller.epfd, errno = syscall.EpollCreate(1) - if poller.epfd == -1 { - return nil, errno - } - // Create pipe; pipe[0] is the read end, pipe[1] the write end. - errno = syscall.Pipe2(poller.pipe[:], syscall.O_NONBLOCK) - if errno != nil { - return nil, errno - } - - // Register inotify fd with epoll - event := syscall.EpollEvent{ - Fd: int32(poller.fd), - Events: syscall.EPOLLIN, - } - errno = syscall.EpollCtl(poller.epfd, syscall.EPOLL_CTL_ADD, poller.fd, &event) - if errno != nil { - return nil, errno - } - - // Register pipe fd with epoll - event = syscall.EpollEvent{ - Fd: int32(poller.pipe[0]), - Events: syscall.EPOLLIN, - } - errno = syscall.EpollCtl(poller.epfd, syscall.EPOLL_CTL_ADD, poller.pipe[0], &event) - if errno != nil { - return nil, errno - } - - return poller, nil -} - -// Wait using epoll. -// Returns true if something is ready to be read, -// false if there is not. -func (poller *fdPoller) wait() (bool, error) { - // 3 possible events per fd, and 2 fds, makes a maximum of 6 events. - // I don't know whether epoll_wait returns the number of events returned, - // or the total number of events ready. - // I decided to catch both by making the buffer one larger than the maximum. - events := make([]syscall.EpollEvent, 7) - for { - n, errno := syscall.EpollWait(poller.epfd, events, -1) - if n == -1 { - if errno == syscall.EINTR { - continue - } - return false, errno - } - if n == 0 { - // If there are no events, try again. - continue - } - if n > 6 { - // This should never happen. More events were returned than should be possible. - return false, errors.New("epoll_wait returned more events than I know what to do with") - } - ready := events[:n] - epollhup := false - epollerr := false - epollin := false - for _, event := range ready { - if event.Fd == int32(poller.fd) { - if event.Events&syscall.EPOLLHUP != 0 { - // This should not happen, but if it does, treat it as a wakeup. - epollhup = true - } - if event.Events&syscall.EPOLLERR != 0 { - // If an error is waiting on the file descriptor, we should pretend - // something is ready to read, and let syscall.Read pick up the error. - epollerr = true - } - if event.Events&syscall.EPOLLIN != 0 { - // There is data to read. - epollin = true - } - } - if event.Fd == int32(poller.pipe[0]) { - if event.Events&syscall.EPOLLHUP != 0 { - // Write pipe descriptor was closed, by us. This means we're closing down the - // watcher, and we should wake up. - } - if event.Events&syscall.EPOLLERR != 0 { - // If an error is waiting on the pipe file descriptor. - // This is an absolute mystery, and should never ever happen. - return false, errors.New("Error on the pipe descriptor.") - } - if event.Events&syscall.EPOLLIN != 0 { - // This is a regular wakeup, so we have to clear the buffer. - err := poller.clearWake() - if err != nil { - return false, err - } - } - } - } - - if epollhup || epollerr || epollin { - return true, nil - } - return false, nil - } -} - -// Close the write end of the poller. -func (poller *fdPoller) wake() error { - buf := make([]byte, 1) - n, errno := syscall.Write(poller.pipe[1], buf) - if n == -1 { - if errno == syscall.EAGAIN { - // Buffer is full, poller will wake. - return nil - } - return errno - } - return nil -} - -func (poller *fdPoller) clearWake() error { - // You have to be woken up a LOT in order to get to 100! - buf := make([]byte, 100) - n, errno := syscall.Read(poller.pipe[0], buf) - if n == -1 { - if errno == syscall.EAGAIN { - // Buffer is empty, someone else cleared our wake. - return nil - } - return errno - } - return nil -} - -// Close all poller file descriptors, but not the one passed to it. -func (poller *fdPoller) close() { - if poller.pipe[1] != -1 { - syscall.Close(poller.pipe[1]) - } - if poller.pipe[0] != -1 { - syscall.Close(poller.pipe[0]) - } - if poller.epfd != -1 { - syscall.Close(poller.epfd) - } -} diff --git a/vendor/src/github.com/go-fsnotify/fsnotify/inotify_poller_test.go b/vendor/src/github.com/go-fsnotify/fsnotify/inotify_poller_test.go deleted file mode 100644 index af9f407f8d..0000000000 --- a/vendor/src/github.com/go-fsnotify/fsnotify/inotify_poller_test.go +++ /dev/null @@ -1,228 +0,0 @@ -// Copyright 2015 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build linux - -package fsnotify - -import ( - "syscall" - "testing" - "time" -) - -type testFd [2]int - -func makeTestFd(t *testing.T) testFd { - var tfd testFd - errno := syscall.Pipe(tfd[:]) - if errno != nil { - t.Fatalf("Failed to create pipe: %v", errno) - } - return tfd -} - -func (tfd testFd) fd() int { - return tfd[0] -} - -func (tfd testFd) closeWrite(t *testing.T) { - errno := syscall.Close(tfd[1]) - if errno != nil { - t.Fatalf("Failed to close write end of pipe: %v", errno) - } -} - -func (tfd testFd) put(t *testing.T) { - buf := make([]byte, 10) - _, errno := syscall.Write(tfd[1], buf) - if errno != nil { - t.Fatalf("Failed to write to pipe: %v", errno) - } -} - -func (tfd testFd) get(t *testing.T) { - buf := make([]byte, 10) - _, errno := syscall.Read(tfd[0], buf) - if errno != nil { - t.Fatalf("Failed to read from pipe: %v", errno) - } -} - -func (tfd testFd) close() { - syscall.Close(tfd[1]) - syscall.Close(tfd[0]) -} - -func makePoller(t *testing.T) (testFd, *fdPoller) { - tfd := makeTestFd(t) - poller, err := newFdPoller(tfd.fd()) - if err != nil { - t.Fatalf("Failed to create poller: %v", err) - } - return tfd, poller -} - -func TestPollerWithBadFd(t *testing.T) { - _, err := newFdPoller(-1) - if err != syscall.EBADF { - t.Fatalf("Expected EBADF, got: %v", err) - } -} - -func TestPollerWithData(t *testing.T) { - tfd, poller := makePoller(t) - defer tfd.close() - defer poller.close() - - tfd.put(t) - ok, err := poller.wait() - if err != nil { - t.Fatalf("poller failed: %v", err) - } - if !ok { - t.Fatalf("expected poller to return true") - } - tfd.get(t) -} - -func TestPollerWithWakeup(t *testing.T) { - tfd, poller := makePoller(t) - defer tfd.close() - defer poller.close() - - err := poller.wake() - if err != nil { - t.Fatalf("wake failed: %v", err) - } - ok, err := poller.wait() - if err != nil { - t.Fatalf("poller failed: %v", err) - } - if ok { - t.Fatalf("expected poller to return false") - } -} - -func TestPollerWithClose(t *testing.T) { - tfd, poller := makePoller(t) - defer tfd.close() - defer poller.close() - - tfd.closeWrite(t) - ok, err := poller.wait() - if err != nil { - t.Fatalf("poller failed: %v", err) - } - if !ok { - t.Fatalf("expected poller to return true") - } -} - -func TestPollerWithWakeupAndData(t *testing.T) { - tfd, poller := makePoller(t) - defer tfd.close() - defer poller.close() - - tfd.put(t) - err := poller.wake() - if err != nil { - t.Fatalf("wake failed: %v", err) - } - - // both data and wakeup - ok, err := poller.wait() - if err != nil { - t.Fatalf("poller failed: %v", err) - } - if !ok { - t.Fatalf("expected poller to return true") - } - - // data is still in the buffer, wakeup is cleared - ok, err = poller.wait() - if err != nil { - t.Fatalf("poller failed: %v", err) - } - if !ok { - t.Fatalf("expected poller to return true") - } - - tfd.get(t) - // data is gone, only wakeup now - err = poller.wake() - if err != nil { - t.Fatalf("wake failed: %v", err) - } - ok, err = poller.wait() - if err != nil { - t.Fatalf("poller failed: %v", err) - } - if ok { - t.Fatalf("expected poller to return false") - } -} - -func TestPollerConcurrent(t *testing.T) { - tfd, poller := makePoller(t) - defer tfd.close() - defer poller.close() - - oks := make(chan bool) - live := make(chan bool) - defer close(live) - go func() { - defer close(oks) - for { - ok, err := poller.wait() - if err != nil { - t.Fatalf("poller failed: %v", err) - } - oks <- ok - if !<-live { - return - } - } - }() - - // Try a write - select { - case <-time.After(50 * time.Millisecond): - case <-oks: - t.Fatalf("poller did not wait") - } - tfd.put(t) - if !<-oks { - t.Fatalf("expected true") - } - tfd.get(t) - live <- true - - // Try a wakeup - select { - case <-time.After(50 * time.Millisecond): - case <-oks: - t.Fatalf("poller did not wait") - } - err := poller.wake() - if err != nil { - t.Fatalf("wake failed: %v", err) - } - if <-oks { - t.Fatalf("expected false") - } - live <- true - - // Try a close - select { - case <-time.After(50 * time.Millisecond): - case <-oks: - t.Fatalf("poller did not wait") - } - tfd.closeWrite(t) - if !<-oks { - t.Fatalf("expected true") - } - tfd.get(t) -} diff --git a/vendor/src/github.com/go-fsnotify/fsnotify/inotify_test.go b/vendor/src/github.com/go-fsnotify/fsnotify/inotify_test.go deleted file mode 100644 index 035ee8f95d..0000000000 --- a/vendor/src/github.com/go-fsnotify/fsnotify/inotify_test.go +++ /dev/null @@ -1,292 +0,0 @@ -// Copyright 2015 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build linux - -package fsnotify - -import ( - "os" - "path/filepath" - "syscall" - "testing" - "time" -) - -func TestInotifyCloseRightAway(t *testing.T) { - w, err := NewWatcher() - if err != nil { - t.Fatalf("Failed to create watcher") - } - - // Close immediately; it won't even reach the first syscall.Read. - w.Close() - - // Wait for the close to complete. - <-time.After(50 * time.Millisecond) - isWatcherReallyClosed(t, w) -} - -func TestInotifyCloseSlightlyLater(t *testing.T) { - w, err := NewWatcher() - if err != nil { - t.Fatalf("Failed to create watcher") - } - - // Wait until readEvents has reached syscall.Read, and Close. - <-time.After(50 * time.Millisecond) - w.Close() - - // Wait for the close to complete. - <-time.After(50 * time.Millisecond) - isWatcherReallyClosed(t, w) -} - -func TestInotifyCloseSlightlyLaterWithWatch(t *testing.T) { - testDir := tempMkdir(t) - defer os.RemoveAll(testDir) - - w, err := NewWatcher() - if err != nil { - t.Fatalf("Failed to create watcher") - } - w.Add(testDir) - - // Wait until readEvents has reached syscall.Read, and Close. - <-time.After(50 * time.Millisecond) - w.Close() - - // Wait for the close to complete. - <-time.After(50 * time.Millisecond) - isWatcherReallyClosed(t, w) -} - -func TestInotifyCloseAfterRead(t *testing.T) { - testDir := tempMkdir(t) - defer os.RemoveAll(testDir) - - w, err := NewWatcher() - if err != nil { - t.Fatalf("Failed to create watcher") - } - - err = w.Add(testDir) - if err != nil { - t.Fatalf("Failed to add .") - } - - // Generate an event. - os.Create(filepath.Join(testDir, "somethingSOMETHINGsomethingSOMETHING")) - - // Wait for readEvents to read the event, then close the watcher. - <-time.After(50 * time.Millisecond) - w.Close() - - // Wait for the close to complete. - <-time.After(50 * time.Millisecond) - isWatcherReallyClosed(t, w) -} - -func isWatcherReallyClosed(t *testing.T, w *Watcher) { - select { - case err, ok := <-w.Errors: - if ok { - t.Fatalf("w.Errors is not closed; readEvents is still alive after closing (error: %v)", err) - } - default: - t.Fatalf("w.Errors would have blocked; readEvents is still alive!") - } - - select { - case _, ok := <-w.Events: - if ok { - t.Fatalf("w.Events is not closed; readEvents is still alive after closing") - } - default: - t.Fatalf("w.Events would have blocked; readEvents is still alive!") - } -} - -func TestInotifyCloseCreate(t *testing.T) { - testDir := tempMkdir(t) - defer os.RemoveAll(testDir) - - w, err := NewWatcher() - if err != nil { - t.Fatalf("Failed to create watcher: %v", err) - } - defer w.Close() - - err = w.Add(testDir) - if err != nil { - t.Fatalf("Failed to add testDir: %v", err) - } - h, err := os.Create(filepath.Join(testDir, "testfile")) - if err != nil { - t.Fatalf("Failed to create file in testdir: %v", err) - } - h.Close() - select { - case _ = <-w.Events: - case err := <-w.Errors: - t.Fatalf("Error from watcher: %v", err) - case <-time.After(50 * time.Millisecond): - t.Fatalf("Took too long to wait for event") - } - - // At this point, we've received one event, so the goroutine is ready. - // It's also blocking on syscall.Read. - // Now we try to swap the file descriptor under its nose. - w.Close() - w, err = NewWatcher() - defer w.Close() - if err != nil { - t.Fatalf("Failed to create second watcher: %v", err) - } - - <-time.After(50 * time.Millisecond) - err = w.Add(testDir) - if err != nil { - t.Fatalf("Error adding testDir again: %v", err) - } -} - -func TestInotifyStress(t *testing.T) { - testDir := tempMkdir(t) - defer os.RemoveAll(testDir) - testFile := filepath.Join(testDir, "testfile") - - w, err := NewWatcher() - if err != nil { - t.Fatalf("Failed to create watcher: %v", err) - } - defer w.Close() - - killchan := make(chan struct{}) - defer close(killchan) - - err = w.Add(testDir) - if err != nil { - t.Fatalf("Failed to add testDir: %v", err) - } - - proc, err := os.FindProcess(os.Getpid()) - if err != nil { - t.Fatalf("Error finding process: %v", err) - } - - go func() { - for { - select { - case <-time.After(5 * time.Millisecond): - err := proc.Signal(syscall.SIGUSR1) - if err != nil { - t.Fatalf("Signal failed: %v", err) - } - case <-killchan: - return - } - } - }() - - go func() { - for { - select { - case <-time.After(11 * time.Millisecond): - err := w.poller.wake() - if err != nil { - t.Fatalf("Wake failed: %v", err) - } - case <-killchan: - return - } - } - }() - - go func() { - for { - select { - case <-killchan: - return - default: - handle, err := os.Create(testFile) - if err != nil { - t.Fatalf("Create failed: %v", err) - } - handle.Close() - time.Sleep(time.Millisecond) - err = os.Remove(testFile) - if err != nil { - t.Fatalf("Remove failed: %v", err) - } - } - } - }() - - creates := 0 - removes := 0 - after := time.After(5 * time.Second) - for { - select { - case <-after: - if creates-removes > 1 || creates-removes < -1 { - t.Fatalf("Creates and removes should not be off by more than one: %d creates, %d removes", creates, removes) - } - if creates < 50 { - t.Fatalf("Expected at least 50 creates, got %d", creates) - } - return - case err := <-w.Errors: - t.Fatalf("Got an error from watcher: %v", err) - case evt := <-w.Events: - if evt.Name != testFile { - t.Fatalf("Got an event for an unknown file: %s", evt.Name) - } - if evt.Op == Create { - creates++ - } - if evt.Op == Remove { - removes++ - } - } - } -} - -func TestInotifyRemoveTwice(t *testing.T) { - testDir := tempMkdir(t) - defer os.RemoveAll(testDir) - testFile := filepath.Join(testDir, "testfile") - - handle, err := os.Create(testFile) - if err != nil { - t.Fatalf("Create failed: %v", err) - } - handle.Close() - - w, err := NewWatcher() - if err != nil { - t.Fatalf("Failed to create watcher: %v", err) - } - defer w.Close() - - err = w.Add(testFile) - if err != nil { - t.Fatalf("Failed to add testFile: %v", err) - } - - err = os.Remove(testFile) - if err != nil { - t.Fatalf("Failed to remove testFile: %v", err) - } - - err = w.Remove(testFile) - if err != syscall.EINVAL { - t.Fatalf("Expected EINVAL from Remove, got: %v", err) - } - - err = w.Remove(testFile) - if err == syscall.EINVAL { - t.Fatalf("Got EINVAL again, watch was not removed") - } -} diff --git a/vendor/src/github.com/go-fsnotify/fsnotify/integration_test.go b/vendor/src/github.com/go-fsnotify/fsnotify/integration_test.go deleted file mode 100644 index 59169c6afa..0000000000 --- a/vendor/src/github.com/go-fsnotify/fsnotify/integration_test.go +++ /dev/null @@ -1,1135 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build !plan9,!solaris - -package fsnotify - -import ( - "io/ioutil" - "os" - "os/exec" - "path/filepath" - "runtime" - "sync/atomic" - "testing" - "time" -) - -// An atomic counter -type counter struct { - val int32 -} - -func (c *counter) increment() { - atomic.AddInt32(&c.val, 1) -} - -func (c *counter) value() int32 { - return atomic.LoadInt32(&c.val) -} - -func (c *counter) reset() { - atomic.StoreInt32(&c.val, 0) -} - -// tempMkdir makes a temporary directory -func tempMkdir(t *testing.T) string { - dir, err := ioutil.TempDir("", "fsnotify") - if err != nil { - t.Fatalf("failed to create test directory: %s", err) - } - return dir -} - -// newWatcher initializes an fsnotify Watcher instance. -func newWatcher(t *testing.T) *Watcher { - watcher, err := NewWatcher() - if err != nil { - t.Fatalf("NewWatcher() failed: %s", err) - } - return watcher -} - -// addWatch adds a watch for a directory -func addWatch(t *testing.T, watcher *Watcher, dir string) { - if err := watcher.Add(dir); err != nil { - t.Fatalf("watcher.Add(%q) failed: %s", dir, err) - } -} - -func TestFsnotifyMultipleOperations(t *testing.T) { - watcher := newWatcher(t) - - // Receive errors on the error channel on a separate goroutine - go func() { - for err := range watcher.Errors { - t.Fatalf("error received: %s", err) - } - }() - - // Create directory to watch - testDir := tempMkdir(t) - defer os.RemoveAll(testDir) - - // Create directory that's not watched - testDirToMoveFiles := tempMkdir(t) - defer os.RemoveAll(testDirToMoveFiles) - - testFile := filepath.Join(testDir, "TestFsnotifySeq.testfile") - testFileRenamed := filepath.Join(testDirToMoveFiles, "TestFsnotifySeqRename.testfile") - - addWatch(t, watcher, testDir) - - // Receive events on the event channel on a separate goroutine - eventstream := watcher.Events - var createReceived, modifyReceived, deleteReceived, renameReceived counter - done := make(chan bool) - go func() { - for event := range eventstream { - // Only count relevant events - if event.Name == filepath.Clean(testDir) || event.Name == filepath.Clean(testFile) { - t.Logf("event received: %s", event) - if event.Op&Remove == Remove { - deleteReceived.increment() - } - if event.Op&Write == Write { - modifyReceived.increment() - } - if event.Op&Create == Create { - createReceived.increment() - } - if event.Op&Rename == Rename { - renameReceived.increment() - } - } else { - t.Logf("unexpected event received: %s", event) - } - } - done <- true - }() - - // Create a file - // This should add at least one event to the fsnotify event queue - var f *os.File - f, err := os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666) - if err != nil { - t.Fatalf("creating test file failed: %s", err) - } - f.Sync() - - time.Sleep(time.Millisecond) - f.WriteString("data") - f.Sync() - f.Close() - - time.Sleep(50 * time.Millisecond) // give system time to sync write change before delete - - if err := testRename(testFile, testFileRenamed); err != nil { - t.Fatalf("rename failed: %s", err) - } - - // Modify the file outside of the watched dir - f, err = os.Open(testFileRenamed) - if err != nil { - t.Fatalf("open test renamed file failed: %s", err) - } - f.WriteString("data") - f.Sync() - f.Close() - - time.Sleep(50 * time.Millisecond) // give system time to sync write change before delete - - // Recreate the file that was moved - f, err = os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666) - if err != nil { - t.Fatalf("creating test file failed: %s", err) - } - f.Close() - time.Sleep(50 * time.Millisecond) // give system time to sync write change before delete - - // We expect this event to be received almost immediately, but let's wait 500 ms to be sure - time.Sleep(500 * time.Millisecond) - cReceived := createReceived.value() - if cReceived != 2 { - t.Fatalf("incorrect number of create events received after 500 ms (%d vs %d)", cReceived, 2) - } - mReceived := modifyReceived.value() - if mReceived != 1 { - t.Fatalf("incorrect number of modify events received after 500 ms (%d vs %d)", mReceived, 1) - } - dReceived := deleteReceived.value() - rReceived := renameReceived.value() - if dReceived+rReceived != 1 { - t.Fatalf("incorrect number of rename+delete events received after 500 ms (%d vs %d)", rReceived+dReceived, 1) - } - - // Try closing the fsnotify instance - t.Log("calling Close()") - watcher.Close() - t.Log("waiting for the event channel to become closed...") - select { - case <-done: - t.Log("event channel closed") - case <-time.After(2 * time.Second): - t.Fatal("event stream was not closed after 2 seconds") - } -} - -func TestFsnotifyMultipleCreates(t *testing.T) { - watcher := newWatcher(t) - - // Receive errors on the error channel on a separate goroutine - go func() { - for err := range watcher.Errors { - t.Fatalf("error received: %s", err) - } - }() - - // Create directory to watch - testDir := tempMkdir(t) - defer os.RemoveAll(testDir) - - testFile := filepath.Join(testDir, "TestFsnotifySeq.testfile") - - addWatch(t, watcher, testDir) - - // Receive events on the event channel on a separate goroutine - eventstream := watcher.Events - var createReceived, modifyReceived, deleteReceived counter - done := make(chan bool) - go func() { - for event := range eventstream { - // Only count relevant events - if event.Name == filepath.Clean(testDir) || event.Name == filepath.Clean(testFile) { - t.Logf("event received: %s", event) - if event.Op&Remove == Remove { - deleteReceived.increment() - } - if event.Op&Create == Create { - createReceived.increment() - } - if event.Op&Write == Write { - modifyReceived.increment() - } - } else { - t.Logf("unexpected event received: %s", event) - } - } - done <- true - }() - - // Create a file - // This should add at least one event to the fsnotify event queue - var f *os.File - f, err := os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666) - if err != nil { - t.Fatalf("creating test file failed: %s", err) - } - f.Sync() - - time.Sleep(time.Millisecond) - f.WriteString("data") - f.Sync() - f.Close() - - time.Sleep(50 * time.Millisecond) // give system time to sync write change before delete - - os.Remove(testFile) - - time.Sleep(50 * time.Millisecond) // give system time to sync write change before delete - - // Recreate the file - f, err = os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666) - if err != nil { - t.Fatalf("creating test file failed: %s", err) - } - f.Close() - time.Sleep(50 * time.Millisecond) // give system time to sync write change before delete - - // Modify - f, err = os.OpenFile(testFile, os.O_WRONLY, 0666) - if err != nil { - t.Fatalf("creating test file failed: %s", err) - } - f.Sync() - - time.Sleep(time.Millisecond) - f.WriteString("data") - f.Sync() - f.Close() - - time.Sleep(50 * time.Millisecond) // give system time to sync write change before delete - - // Modify - f, err = os.OpenFile(testFile, os.O_WRONLY, 0666) - if err != nil { - t.Fatalf("creating test file failed: %s", err) - } - f.Sync() - - time.Sleep(time.Millisecond) - f.WriteString("data") - f.Sync() - f.Close() - - time.Sleep(50 * time.Millisecond) // give system time to sync write change before delete - - // We expect this event to be received almost immediately, but let's wait 500 ms to be sure - time.Sleep(500 * time.Millisecond) - cReceived := createReceived.value() - if cReceived != 2 { - t.Fatalf("incorrect number of create events received after 500 ms (%d vs %d)", cReceived, 2) - } - mReceived := modifyReceived.value() - if mReceived < 3 { - t.Fatalf("incorrect number of modify events received after 500 ms (%d vs atleast %d)", mReceived, 3) - } - dReceived := deleteReceived.value() - if dReceived != 1 { - t.Fatalf("incorrect number of rename+delete events received after 500 ms (%d vs %d)", dReceived, 1) - } - - // Try closing the fsnotify instance - t.Log("calling Close()") - watcher.Close() - t.Log("waiting for the event channel to become closed...") - select { - case <-done: - t.Log("event channel closed") - case <-time.After(2 * time.Second): - t.Fatal("event stream was not closed after 2 seconds") - } -} - -func TestFsnotifyDirOnly(t *testing.T) { - watcher := newWatcher(t) - - // Create directory to watch - testDir := tempMkdir(t) - defer os.RemoveAll(testDir) - - // Create a file before watching directory - // This should NOT add any events to the fsnotify event queue - testFileAlreadyExists := filepath.Join(testDir, "TestFsnotifyEventsExisting.testfile") - { - var f *os.File - f, err := os.OpenFile(testFileAlreadyExists, os.O_WRONLY|os.O_CREATE, 0666) - if err != nil { - t.Fatalf("creating test file failed: %s", err) - } - f.Sync() - f.Close() - } - - addWatch(t, watcher, testDir) - - // Receive errors on the error channel on a separate goroutine - go func() { - for err := range watcher.Errors { - t.Fatalf("error received: %s", err) - } - }() - - testFile := filepath.Join(testDir, "TestFsnotifyDirOnly.testfile") - - // Receive events on the event channel on a separate goroutine - eventstream := watcher.Events - var createReceived, modifyReceived, deleteReceived counter - done := make(chan bool) - go func() { - for event := range eventstream { - // Only count relevant events - if event.Name == filepath.Clean(testDir) || event.Name == filepath.Clean(testFile) || event.Name == filepath.Clean(testFileAlreadyExists) { - t.Logf("event received: %s", event) - if event.Op&Remove == Remove { - deleteReceived.increment() - } - if event.Op&Write == Write { - modifyReceived.increment() - } - if event.Op&Create == Create { - createReceived.increment() - } - } else { - t.Logf("unexpected event received: %s", event) - } - } - done <- true - }() - - // Create a file - // This should add at least one event to the fsnotify event queue - var f *os.File - f, err := os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666) - if err != nil { - t.Fatalf("creating test file failed: %s", err) - } - f.Sync() - - time.Sleep(time.Millisecond) - f.WriteString("data") - f.Sync() - f.Close() - - time.Sleep(50 * time.Millisecond) // give system time to sync write change before delete - - os.Remove(testFile) - os.Remove(testFileAlreadyExists) - - // We expect this event to be received almost immediately, but let's wait 500 ms to be sure - time.Sleep(500 * time.Millisecond) - cReceived := createReceived.value() - if cReceived != 1 { - t.Fatalf("incorrect number of create events received after 500 ms (%d vs %d)", cReceived, 1) - } - mReceived := modifyReceived.value() - if mReceived != 1 { - t.Fatalf("incorrect number of modify events received after 500 ms (%d vs %d)", mReceived, 1) - } - dReceived := deleteReceived.value() - if dReceived != 2 { - t.Fatalf("incorrect number of delete events received after 500 ms (%d vs %d)", dReceived, 2) - } - - // Try closing the fsnotify instance - t.Log("calling Close()") - watcher.Close() - t.Log("waiting for the event channel to become closed...") - select { - case <-done: - t.Log("event channel closed") - case <-time.After(2 * time.Second): - t.Fatal("event stream was not closed after 2 seconds") - } -} - -func TestFsnotifyDeleteWatchedDir(t *testing.T) { - watcher := newWatcher(t) - defer watcher.Close() - - // Create directory to watch - testDir := tempMkdir(t) - defer os.RemoveAll(testDir) - - // Create a file before watching directory - testFileAlreadyExists := filepath.Join(testDir, "TestFsnotifyEventsExisting.testfile") - { - var f *os.File - f, err := os.OpenFile(testFileAlreadyExists, os.O_WRONLY|os.O_CREATE, 0666) - if err != nil { - t.Fatalf("creating test file failed: %s", err) - } - f.Sync() - f.Close() - } - - addWatch(t, watcher, testDir) - - // Add a watch for testFile - addWatch(t, watcher, testFileAlreadyExists) - - // Receive errors on the error channel on a separate goroutine - go func() { - for err := range watcher.Errors { - t.Fatalf("error received: %s", err) - } - }() - - // Receive events on the event channel on a separate goroutine - eventstream := watcher.Events - var deleteReceived counter - go func() { - for event := range eventstream { - // Only count relevant events - if event.Name == filepath.Clean(testDir) || event.Name == filepath.Clean(testFileAlreadyExists) { - t.Logf("event received: %s", event) - if event.Op&Remove == Remove { - deleteReceived.increment() - } - } else { - t.Logf("unexpected event received: %s", event) - } - } - }() - - os.RemoveAll(testDir) - - // We expect this event to be received almost immediately, but let's wait 500 ms to be sure - time.Sleep(500 * time.Millisecond) - dReceived := deleteReceived.value() - if dReceived < 2 { - t.Fatalf("did not receive at least %d delete events, received %d after 500 ms", 2, dReceived) - } -} - -func TestFsnotifySubDir(t *testing.T) { - watcher := newWatcher(t) - - // Create directory to watch - testDir := tempMkdir(t) - defer os.RemoveAll(testDir) - - testFile1 := filepath.Join(testDir, "TestFsnotifyFile1.testfile") - testSubDir := filepath.Join(testDir, "sub") - testSubDirFile := filepath.Join(testDir, "sub/TestFsnotifyFile1.testfile") - - // Receive errors on the error channel on a separate goroutine - go func() { - for err := range watcher.Errors { - t.Fatalf("error received: %s", err) - } - }() - - // Receive events on the event channel on a separate goroutine - eventstream := watcher.Events - var createReceived, deleteReceived counter - done := make(chan bool) - go func() { - for event := range eventstream { - // Only count relevant events - if event.Name == filepath.Clean(testDir) || event.Name == filepath.Clean(testSubDir) || event.Name == filepath.Clean(testFile1) { - t.Logf("event received: %s", event) - if event.Op&Create == Create { - createReceived.increment() - } - if event.Op&Remove == Remove { - deleteReceived.increment() - } - } else { - t.Logf("unexpected event received: %s", event) - } - } - done <- true - }() - - addWatch(t, watcher, testDir) - - // Create sub-directory - if err := os.Mkdir(testSubDir, 0777); err != nil { - t.Fatalf("failed to create test sub-directory: %s", err) - } - - // Create a file - var f *os.File - f, err := os.OpenFile(testFile1, os.O_WRONLY|os.O_CREATE, 0666) - if err != nil { - t.Fatalf("creating test file failed: %s", err) - } - f.Sync() - f.Close() - - // Create a file (Should not see this! we are not watching subdir) - var fs *os.File - fs, err = os.OpenFile(testSubDirFile, os.O_WRONLY|os.O_CREATE, 0666) - if err != nil { - t.Fatalf("creating test file failed: %s", err) - } - fs.Sync() - fs.Close() - - time.Sleep(200 * time.Millisecond) - - // Make sure receive deletes for both file and sub-directory - os.RemoveAll(testSubDir) - os.Remove(testFile1) - - // We expect this event to be received almost immediately, but let's wait 500 ms to be sure - time.Sleep(500 * time.Millisecond) - cReceived := createReceived.value() - if cReceived != 2 { - t.Fatalf("incorrect number of create events received after 500 ms (%d vs %d)", cReceived, 2) - } - dReceived := deleteReceived.value() - if dReceived != 2 { - t.Fatalf("incorrect number of delete events received after 500 ms (%d vs %d)", dReceived, 2) - } - - // Try closing the fsnotify instance - t.Log("calling Close()") - watcher.Close() - t.Log("waiting for the event channel to become closed...") - select { - case <-done: - t.Log("event channel closed") - case <-time.After(2 * time.Second): - t.Fatal("event stream was not closed after 2 seconds") - } -} - -func TestFsnotifyRename(t *testing.T) { - watcher := newWatcher(t) - - // Create directory to watch - testDir := tempMkdir(t) - defer os.RemoveAll(testDir) - - addWatch(t, watcher, testDir) - - // Receive errors on the error channel on a separate goroutine - go func() { - for err := range watcher.Errors { - t.Fatalf("error received: %s", err) - } - }() - - testFile := filepath.Join(testDir, "TestFsnotifyEvents.testfile") - testFileRenamed := filepath.Join(testDir, "TestFsnotifyEvents.testfileRenamed") - - // Receive events on the event channel on a separate goroutine - eventstream := watcher.Events - var renameReceived counter - done := make(chan bool) - go func() { - for event := range eventstream { - // Only count relevant events - if event.Name == filepath.Clean(testDir) || event.Name == filepath.Clean(testFile) || event.Name == filepath.Clean(testFileRenamed) { - if event.Op&Rename == Rename { - renameReceived.increment() - } - t.Logf("event received: %s", event) - } else { - t.Logf("unexpected event received: %s", event) - } - } - done <- true - }() - - // Create a file - // This should add at least one event to the fsnotify event queue - var f *os.File - f, err := os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666) - if err != nil { - t.Fatalf("creating test file failed: %s", err) - } - f.Sync() - - f.WriteString("data") - f.Sync() - f.Close() - - // Add a watch for testFile - addWatch(t, watcher, testFile) - - if err := testRename(testFile, testFileRenamed); err != nil { - t.Fatalf("rename failed: %s", err) - } - - // We expect this event to be received almost immediately, but let's wait 500 ms to be sure - time.Sleep(500 * time.Millisecond) - if renameReceived.value() == 0 { - t.Fatal("fsnotify rename events have not been received after 500 ms") - } - - // Try closing the fsnotify instance - t.Log("calling Close()") - watcher.Close() - t.Log("waiting for the event channel to become closed...") - select { - case <-done: - t.Log("event channel closed") - case <-time.After(2 * time.Second): - t.Fatal("event stream was not closed after 2 seconds") - } - - os.Remove(testFileRenamed) -} - -func TestFsnotifyRenameToCreate(t *testing.T) { - watcher := newWatcher(t) - - // Create directory to watch - testDir := tempMkdir(t) - defer os.RemoveAll(testDir) - - // Create directory to get file - testDirFrom := tempMkdir(t) - defer os.RemoveAll(testDirFrom) - - addWatch(t, watcher, testDir) - - // Receive errors on the error channel on a separate goroutine - go func() { - for err := range watcher.Errors { - t.Fatalf("error received: %s", err) - } - }() - - testFile := filepath.Join(testDirFrom, "TestFsnotifyEvents.testfile") - testFileRenamed := filepath.Join(testDir, "TestFsnotifyEvents.testfileRenamed") - - // Receive events on the event channel on a separate goroutine - eventstream := watcher.Events - var createReceived counter - done := make(chan bool) - go func() { - for event := range eventstream { - // Only count relevant events - if event.Name == filepath.Clean(testDir) || event.Name == filepath.Clean(testFile) || event.Name == filepath.Clean(testFileRenamed) { - if event.Op&Create == Create { - createReceived.increment() - } - t.Logf("event received: %s", event) - } else { - t.Logf("unexpected event received: %s", event) - } - } - done <- true - }() - - // Create a file - // This should add at least one event to the fsnotify event queue - var f *os.File - f, err := os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666) - if err != nil { - t.Fatalf("creating test file failed: %s", err) - } - f.Sync() - f.Close() - - if err := testRename(testFile, testFileRenamed); err != nil { - t.Fatalf("rename failed: %s", err) - } - - // We expect this event to be received almost immediately, but let's wait 500 ms to be sure - time.Sleep(500 * time.Millisecond) - if createReceived.value() == 0 { - t.Fatal("fsnotify create events have not been received after 500 ms") - } - - // Try closing the fsnotify instance - t.Log("calling Close()") - watcher.Close() - t.Log("waiting for the event channel to become closed...") - select { - case <-done: - t.Log("event channel closed") - case <-time.After(2 * time.Second): - t.Fatal("event stream was not closed after 2 seconds") - } - - os.Remove(testFileRenamed) -} - -func TestFsnotifyRenameToOverwrite(t *testing.T) { - switch runtime.GOOS { - case "plan9", "windows": - t.Skipf("skipping test on %q (os.Rename over existing file does not create event).", runtime.GOOS) - } - - watcher := newWatcher(t) - - // Create directory to watch - testDir := tempMkdir(t) - defer os.RemoveAll(testDir) - - // Create directory to get file - testDirFrom := tempMkdir(t) - defer os.RemoveAll(testDirFrom) - - testFile := filepath.Join(testDirFrom, "TestFsnotifyEvents.testfile") - testFileRenamed := filepath.Join(testDir, "TestFsnotifyEvents.testfileRenamed") - - // Create a file - var fr *os.File - fr, err := os.OpenFile(testFileRenamed, os.O_WRONLY|os.O_CREATE, 0666) - if err != nil { - t.Fatalf("creating test file failed: %s", err) - } - fr.Sync() - fr.Close() - - addWatch(t, watcher, testDir) - - // Receive errors on the error channel on a separate goroutine - go func() { - for err := range watcher.Errors { - t.Fatalf("error received: %s", err) - } - }() - - // Receive events on the event channel on a separate goroutine - eventstream := watcher.Events - var eventReceived counter - done := make(chan bool) - go func() { - for event := range eventstream { - // Only count relevant events - if event.Name == filepath.Clean(testFileRenamed) { - eventReceived.increment() - t.Logf("event received: %s", event) - } else { - t.Logf("unexpected event received: %s", event) - } - } - done <- true - }() - - // Create a file - // This should add at least one event to the fsnotify event queue - var f *os.File - f, err = os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666) - if err != nil { - t.Fatalf("creating test file failed: %s", err) - } - f.Sync() - f.Close() - - if err := testRename(testFile, testFileRenamed); err != nil { - t.Fatalf("rename failed: %s", err) - } - - // We expect this event to be received almost immediately, but let's wait 500 ms to be sure - time.Sleep(500 * time.Millisecond) - if eventReceived.value() == 0 { - t.Fatal("fsnotify events have not been received after 500 ms") - } - - // Try closing the fsnotify instance - t.Log("calling Close()") - watcher.Close() - t.Log("waiting for the event channel to become closed...") - select { - case <-done: - t.Log("event channel closed") - case <-time.After(2 * time.Second): - t.Fatal("event stream was not closed after 2 seconds") - } - - os.Remove(testFileRenamed) -} - -func TestRemovalOfWatch(t *testing.T) { - // Create directory to watch - testDir := tempMkdir(t) - defer os.RemoveAll(testDir) - - // Create a file before watching directory - testFileAlreadyExists := filepath.Join(testDir, "TestFsnotifyEventsExisting.testfile") - { - var f *os.File - f, err := os.OpenFile(testFileAlreadyExists, os.O_WRONLY|os.O_CREATE, 0666) - if err != nil { - t.Fatalf("creating test file failed: %s", err) - } - f.Sync() - f.Close() - } - - watcher := newWatcher(t) - defer watcher.Close() - - addWatch(t, watcher, testDir) - if err := watcher.Remove(testDir); err != nil { - t.Fatalf("Could not remove the watch: %v\n", err) - } - - go func() { - select { - case ev := <-watcher.Events: - t.Fatalf("We received event: %v\n", ev) - case <-time.After(500 * time.Millisecond): - t.Log("No event received, as expected.") - } - }() - - time.Sleep(200 * time.Millisecond) - // Modify the file outside of the watched dir - f, err := os.Open(testFileAlreadyExists) - if err != nil { - t.Fatalf("Open test file failed: %s", err) - } - f.WriteString("data") - f.Sync() - f.Close() - if err := os.Chmod(testFileAlreadyExists, 0700); err != nil { - t.Fatalf("chmod failed: %s", err) - } - time.Sleep(400 * time.Millisecond) -} - -func TestFsnotifyAttrib(t *testing.T) { - if runtime.GOOS == "windows" { - t.Skip("attributes don't work on Windows.") - } - - watcher := newWatcher(t) - - // Create directory to watch - testDir := tempMkdir(t) - defer os.RemoveAll(testDir) - - // Receive errors on the error channel on a separate goroutine - go func() { - for err := range watcher.Errors { - t.Fatalf("error received: %s", err) - } - }() - - testFile := filepath.Join(testDir, "TestFsnotifyAttrib.testfile") - - // Receive events on the event channel on a separate goroutine - eventstream := watcher.Events - // The modifyReceived counter counts IsModify events that are not IsAttrib, - // and the attribReceived counts IsAttrib events (which are also IsModify as - // a consequence). - var modifyReceived counter - var attribReceived counter - done := make(chan bool) - go func() { - for event := range eventstream { - // Only count relevant events - if event.Name == filepath.Clean(testDir) || event.Name == filepath.Clean(testFile) { - if event.Op&Write == Write { - modifyReceived.increment() - } - if event.Op&Chmod == Chmod { - attribReceived.increment() - } - t.Logf("event received: %s", event) - } else { - t.Logf("unexpected event received: %s", event) - } - } - done <- true - }() - - // Create a file - // This should add at least one event to the fsnotify event queue - var f *os.File - f, err := os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666) - if err != nil { - t.Fatalf("creating test file failed: %s", err) - } - f.Sync() - - f.WriteString("data") - f.Sync() - f.Close() - - // Add a watch for testFile - addWatch(t, watcher, testFile) - - if err := os.Chmod(testFile, 0700); err != nil { - t.Fatalf("chmod failed: %s", err) - } - - // We expect this event to be received almost immediately, but let's wait 500 ms to be sure - // Creating/writing a file changes also the mtime, so IsAttrib should be set to true here - time.Sleep(500 * time.Millisecond) - if modifyReceived.value() != 0 { - t.Fatal("received an unexpected modify event when creating a test file") - } - if attribReceived.value() == 0 { - t.Fatal("fsnotify attribute events have not received after 500 ms") - } - - // Modifying the contents of the file does not set the attrib flag (although eg. the mtime - // might have been modified). - modifyReceived.reset() - attribReceived.reset() - - f, err = os.OpenFile(testFile, os.O_WRONLY, 0) - if err != nil { - t.Fatalf("reopening test file failed: %s", err) - } - - f.WriteString("more data") - f.Sync() - f.Close() - - time.Sleep(500 * time.Millisecond) - - if modifyReceived.value() != 1 { - t.Fatal("didn't receive a modify event after changing test file contents") - } - - if attribReceived.value() != 0 { - t.Fatal("did receive an unexpected attrib event after changing test file contents") - } - - modifyReceived.reset() - attribReceived.reset() - - // Doing a chmod on the file should trigger an event with the "attrib" flag set (the contents - // of the file are not changed though) - if err := os.Chmod(testFile, 0600); err != nil { - t.Fatalf("chmod failed: %s", err) - } - - time.Sleep(500 * time.Millisecond) - - if attribReceived.value() != 1 { - t.Fatal("didn't receive an attribute change after 500ms") - } - - // Try closing the fsnotify instance - t.Log("calling Close()") - watcher.Close() - t.Log("waiting for the event channel to become closed...") - select { - case <-done: - t.Log("event channel closed") - case <-time.After(1e9): - t.Fatal("event stream was not closed after 1 second") - } - - os.Remove(testFile) -} - -func TestFsnotifyClose(t *testing.T) { - watcher := newWatcher(t) - watcher.Close() - - var done int32 - go func() { - watcher.Close() - atomic.StoreInt32(&done, 1) - }() - - time.Sleep(50e6) // 50 ms - if atomic.LoadInt32(&done) == 0 { - t.Fatal("double Close() test failed: second Close() call didn't return") - } - - testDir := tempMkdir(t) - defer os.RemoveAll(testDir) - - if err := watcher.Add(testDir); err == nil { - t.Fatal("expected error on Watch() after Close(), got nil") - } -} - -func TestFsnotifyFakeSymlink(t *testing.T) { - if runtime.GOOS == "windows" { - t.Skip("symlinks don't work on Windows.") - } - - watcher := newWatcher(t) - - // Create directory to watch - testDir := tempMkdir(t) - defer os.RemoveAll(testDir) - - var errorsReceived counter - // Receive errors on the error channel on a separate goroutine - go func() { - for errors := range watcher.Errors { - t.Logf("Received error: %s", errors) - errorsReceived.increment() - } - }() - - // Count the CREATE events received - var createEventsReceived, otherEventsReceived counter - go func() { - for ev := range watcher.Events { - t.Logf("event received: %s", ev) - if ev.Op&Create == Create { - createEventsReceived.increment() - } else { - otherEventsReceived.increment() - } - } - }() - - addWatch(t, watcher, testDir) - - if err := os.Symlink(filepath.Join(testDir, "zzz"), filepath.Join(testDir, "zzznew")); err != nil { - t.Fatalf("Failed to create bogus symlink: %s", err) - } - t.Logf("Created bogus symlink") - - // We expect this event to be received almost immediately, but let's wait 500 ms to be sure - time.Sleep(500 * time.Millisecond) - - // Should not be error, just no events for broken links (watching nothing) - if errorsReceived.value() > 0 { - t.Fatal("fsnotify errors have been received.") - } - if otherEventsReceived.value() > 0 { - t.Fatal("fsnotify other events received on the broken link") - } - - // Except for 1 create event (for the link itself) - if createEventsReceived.value() == 0 { - t.Fatal("fsnotify create events were not received after 500 ms") - } - if createEventsReceived.value() > 1 { - t.Fatal("fsnotify more create events received than expected") - } - - // Try closing the fsnotify instance - t.Log("calling Close()") - watcher.Close() -} - -// TestConcurrentRemovalOfWatch tests that concurrent calls to RemoveWatch do not race. -// See https://codereview.appspot.com/103300045/ -// go test -test.run=TestConcurrentRemovalOfWatch -test.cpu=1,1,1,1,1 -race -func TestConcurrentRemovalOfWatch(t *testing.T) { - if runtime.GOOS != "darwin" { - t.Skip("regression test for race only present on darwin") - } - - // Create directory to watch - testDir := tempMkdir(t) - defer os.RemoveAll(testDir) - - // Create a file before watching directory - testFileAlreadyExists := filepath.Join(testDir, "TestFsnotifyEventsExisting.testfile") - { - var f *os.File - f, err := os.OpenFile(testFileAlreadyExists, os.O_WRONLY|os.O_CREATE, 0666) - if err != nil { - t.Fatalf("creating test file failed: %s", err) - } - f.Sync() - f.Close() - } - - watcher := newWatcher(t) - defer watcher.Close() - - addWatch(t, watcher, testDir) - - // Test that RemoveWatch can be invoked concurrently, with no data races. - removed1 := make(chan struct{}) - go func() { - defer close(removed1) - watcher.Remove(testDir) - }() - removed2 := make(chan struct{}) - go func() { - close(removed2) - watcher.Remove(testDir) - }() - <-removed1 - <-removed2 -} - -func TestClose(t *testing.T) { - // Regression test for #59 bad file descriptor from Close - testDir := tempMkdir(t) - defer os.RemoveAll(testDir) - - watcher := newWatcher(t) - if err := watcher.Add(testDir); err != nil { - t.Fatalf("Expected no error on Add, got %v", err) - } - err := watcher.Close() - if err != nil { - t.Fatalf("Expected no error on Close, got %v.", err) - } -} - -func testRename(file1, file2 string) error { - switch runtime.GOOS { - case "windows", "plan9": - return os.Rename(file1, file2) - default: - cmd := exec.Command("mv", file1, file2) - return cmd.Run() - } -} diff --git a/vendor/src/github.com/go-fsnotify/fsnotify/kqueue.go b/vendor/src/github.com/go-fsnotify/fsnotify/kqueue.go deleted file mode 100644 index 265622d201..0000000000 --- a/vendor/src/github.com/go-fsnotify/fsnotify/kqueue.go +++ /dev/null @@ -1,463 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build freebsd openbsd netbsd dragonfly darwin - -package fsnotify - -import ( - "errors" - "fmt" - "io/ioutil" - "os" - "path/filepath" - "sync" - "syscall" - "time" -) - -// Watcher watches a set of files, delivering events to a channel. -type Watcher struct { - Events chan Event - Errors chan error - done chan bool // Channel for sending a "quit message" to the reader goroutine - - kq int // File descriptor (as returned by the kqueue() syscall). - - mu sync.Mutex // Protects access to watcher data - watches map[string]int // Map of watched file descriptors (key: path). - externalWatches map[string]bool // Map of watches added by user of the library. - dirFlags map[string]uint32 // Map of watched directories to fflags used in kqueue. - paths map[int]pathInfo // Map file descriptors to path names for processing kqueue events. - fileExists map[string]bool // Keep track of if we know this file exists (to stop duplicate create events). - isClosed bool // Set to true when Close() is first called -} - -type pathInfo struct { - name string - isDir bool -} - -// NewWatcher establishes a new watcher with the underlying OS and begins waiting for events. -func NewWatcher() (*Watcher, error) { - kq, err := kqueue() - if err != nil { - return nil, err - } - - w := &Watcher{ - kq: kq, - watches: make(map[string]int), - dirFlags: make(map[string]uint32), - paths: make(map[int]pathInfo), - fileExists: make(map[string]bool), - externalWatches: make(map[string]bool), - Events: make(chan Event), - Errors: make(chan error), - done: make(chan bool), - } - - go w.readEvents() - return w, nil -} - -// Close removes all watches and closes the events channel. -func (w *Watcher) Close() error { - w.mu.Lock() - if w.isClosed { - w.mu.Unlock() - return nil - } - w.isClosed = true - w.mu.Unlock() - - w.mu.Lock() - ws := w.watches - w.mu.Unlock() - - var err error - for name := range ws { - if e := w.Remove(name); e != nil && err == nil { - err = e - } - } - - // Send "quit" message to the reader goroutine: - w.done <- true - - return nil -} - -// Add starts watching the named file or directory (non-recursively). -func (w *Watcher) Add(name string) error { - w.mu.Lock() - w.externalWatches[name] = true - w.mu.Unlock() - return w.addWatch(name, noteAllEvents) -} - -// Remove stops watching the the named file or directory (non-recursively). -func (w *Watcher) Remove(name string) error { - name = filepath.Clean(name) - w.mu.Lock() - watchfd, ok := w.watches[name] - w.mu.Unlock() - if !ok { - return fmt.Errorf("can't remove non-existent kevent watch for: %s", name) - } - - const registerRemove = syscall.EV_DELETE - if err := register(w.kq, []int{watchfd}, registerRemove, 0); err != nil { - return err - } - - syscall.Close(watchfd) - - w.mu.Lock() - isDir := w.paths[watchfd].isDir - delete(w.watches, name) - delete(w.paths, watchfd) - delete(w.dirFlags, name) - w.mu.Unlock() - - // Find all watched paths that are in this directory that are not external. - if isDir { - var pathsToRemove []string - w.mu.Lock() - for _, path := range w.paths { - wdir, _ := filepath.Split(path.name) - if filepath.Clean(wdir) == name { - if !w.externalWatches[path.name] { - pathsToRemove = append(pathsToRemove, path.name) - } - } - } - w.mu.Unlock() - for _, name := range pathsToRemove { - // Since these are internal, not much sense in propagating error - // to the user, as that will just confuse them with an error about - // a path they did not explicitly watch themselves. - w.Remove(name) - } - } - - return nil -} - -// Watch all events (except NOTE_EXTEND, NOTE_LINK, NOTE_REVOKE) -const noteAllEvents = syscall.NOTE_DELETE | syscall.NOTE_WRITE | syscall.NOTE_ATTRIB | syscall.NOTE_RENAME - -// keventWaitTime to block on each read from kevent -var keventWaitTime = durationToTimespec(100 * time.Millisecond) - -// addWatch adds name to the watched file set. -// The flags are interpreted as described in kevent(2). -func (w *Watcher) addWatch(name string, flags uint32) error { - var isDir bool - // Make ./name and name equivalent - name = filepath.Clean(name) - - w.mu.Lock() - if w.isClosed { - w.mu.Unlock() - return errors.New("kevent instance already closed") - } - watchfd, alreadyWatching := w.watches[name] - // We already have a watch, but we can still override flags. - if alreadyWatching { - isDir = w.paths[watchfd].isDir - } - w.mu.Unlock() - - if !alreadyWatching { - fi, err := os.Lstat(name) - if err != nil { - return err - } - - // Don't watch sockets. - if fi.Mode()&os.ModeSocket == os.ModeSocket { - return nil - } - - // Follow Symlinks - // Unfortunately, Linux can add bogus symlinks to watch list without - // issue, and Windows can't do symlinks period (AFAIK). To maintain - // consistency, we will act like everything is fine. There will simply - // be no file events for broken symlinks. - // Hence the returns of nil on errors. - if fi.Mode()&os.ModeSymlink == os.ModeSymlink { - name, err = filepath.EvalSymlinks(name) - if err != nil { - return nil - } - - fi, err = os.Lstat(name) - if err != nil { - return nil - } - } - - watchfd, err = syscall.Open(name, openMode, 0700) - if watchfd == -1 { - return err - } - - isDir = fi.IsDir() - } - - const registerAdd = syscall.EV_ADD | syscall.EV_CLEAR | syscall.EV_ENABLE - if err := register(w.kq, []int{watchfd}, registerAdd, flags); err != nil { - syscall.Close(watchfd) - return err - } - - if !alreadyWatching { - w.mu.Lock() - w.watches[name] = watchfd - w.paths[watchfd] = pathInfo{name: name, isDir: isDir} - w.mu.Unlock() - } - - if isDir { - // Watch the directory if it has not been watched before, - // or if it was watched before, but perhaps only a NOTE_DELETE (watchDirectoryFiles) - w.mu.Lock() - watchDir := (flags&syscall.NOTE_WRITE) == syscall.NOTE_WRITE && - (!alreadyWatching || (w.dirFlags[name]&syscall.NOTE_WRITE) != syscall.NOTE_WRITE) - // Store flags so this watch can be updated later - w.dirFlags[name] = flags - w.mu.Unlock() - - if watchDir { - if err := w.watchDirectoryFiles(name); err != nil { - return err - } - } - } - return nil -} - -// readEvents reads from kqueue and converts the received kevents into -// Event values that it sends down the Events channel. -func (w *Watcher) readEvents() { - eventBuffer := make([]syscall.Kevent_t, 10) - - for { - // See if there is a message on the "done" channel - select { - case <-w.done: - err := syscall.Close(w.kq) - if err != nil { - w.Errors <- err - } - close(w.Events) - close(w.Errors) - return - default: - } - - // Get new events - kevents, err := read(w.kq, eventBuffer, &keventWaitTime) - // EINTR is okay, the syscall was interrupted before timeout expired. - if err != nil && err != syscall.EINTR { - w.Errors <- err - continue - } - - // Flush the events we received to the Events channel - for len(kevents) > 0 { - kevent := &kevents[0] - watchfd := int(kevent.Ident) - mask := uint32(kevent.Fflags) - w.mu.Lock() - path := w.paths[watchfd] - w.mu.Unlock() - event := newEvent(path.name, mask) - - if path.isDir && !(event.Op&Remove == Remove) { - // Double check to make sure the directory exists. This can happen when - // we do a rm -fr on a recursively watched folders and we receive a - // modification event first but the folder has been deleted and later - // receive the delete event - if _, err := os.Lstat(event.Name); os.IsNotExist(err) { - // mark is as delete event - event.Op |= Remove - } - } - - if event.Op&Rename == Rename || event.Op&Remove == Remove { - w.Remove(event.Name) - w.mu.Lock() - delete(w.fileExists, event.Name) - w.mu.Unlock() - } - - if path.isDir && event.Op&Write == Write && !(event.Op&Remove == Remove) { - w.sendDirectoryChangeEvents(event.Name) - } else { - // Send the event on the Events channel - w.Events <- event - } - - if event.Op&Remove == Remove { - // Look for a file that may have overwritten this. - // For example, mv f1 f2 will delete f2, then create f2. - fileDir, _ := filepath.Split(event.Name) - fileDir = filepath.Clean(fileDir) - w.mu.Lock() - _, found := w.watches[fileDir] - w.mu.Unlock() - if found { - // make sure the directory exists before we watch for changes. When we - // do a recursive watch and perform rm -fr, the parent directory might - // have gone missing, ignore the missing directory and let the - // upcoming delete event remove the watch from the parent directory. - if _, err := os.Lstat(fileDir); os.IsExist(err) { - w.sendDirectoryChangeEvents(fileDir) - // FIXME: should this be for events on files or just isDir? - } - } - } - - // Move to next event - kevents = kevents[1:] - } - } -} - -// newEvent returns an platform-independent Event based on kqueue Fflags. -func newEvent(name string, mask uint32) Event { - e := Event{Name: name} - if mask&syscall.NOTE_DELETE == syscall.NOTE_DELETE { - e.Op |= Remove - } - if mask&syscall.NOTE_WRITE == syscall.NOTE_WRITE { - e.Op |= Write - } - if mask&syscall.NOTE_RENAME == syscall.NOTE_RENAME { - e.Op |= Rename - } - if mask&syscall.NOTE_ATTRIB == syscall.NOTE_ATTRIB { - e.Op |= Chmod - } - return e -} - -func newCreateEvent(name string) Event { - return Event{Name: name, Op: Create} -} - -// watchDirectoryFiles to mimic inotify when adding a watch on a directory -func (w *Watcher) watchDirectoryFiles(dirPath string) error { - // Get all files - files, err := ioutil.ReadDir(dirPath) - if err != nil { - return err - } - - for _, fileInfo := range files { - filePath := filepath.Join(dirPath, fileInfo.Name()) - if err := w.internalWatch(filePath, fileInfo); err != nil { - return err - } - - w.mu.Lock() - w.fileExists[filePath] = true - w.mu.Unlock() - } - - return nil -} - -// sendDirectoryEvents searches the directory for newly created files -// and sends them over the event channel. This functionality is to have -// the BSD version of fsnotify match Linux inotify which provides a -// create event for files created in a watched directory. -func (w *Watcher) sendDirectoryChangeEvents(dirPath string) { - // Get all files - files, err := ioutil.ReadDir(dirPath) - if err != nil { - w.Errors <- err - } - - // Search for new files - for _, fileInfo := range files { - filePath := filepath.Join(dirPath, fileInfo.Name()) - w.mu.Lock() - _, doesExist := w.fileExists[filePath] - w.mu.Unlock() - if !doesExist { - // Send create event - w.Events <- newCreateEvent(filePath) - } - - // like watchDirectoryFiles (but without doing another ReadDir) - if err := w.internalWatch(filePath, fileInfo); err != nil { - return - } - - w.mu.Lock() - w.fileExists[filePath] = true - w.mu.Unlock() - } -} - -func (w *Watcher) internalWatch(name string, fileInfo os.FileInfo) error { - if fileInfo.IsDir() { - // mimic Linux providing delete events for subdirectories - // but preserve the flags used if currently watching subdirectory - w.mu.Lock() - flags := w.dirFlags[name] - w.mu.Unlock() - - flags |= syscall.NOTE_DELETE - return w.addWatch(name, flags) - } - - // watch file to mimic Linux inotify - return w.addWatch(name, noteAllEvents) -} - -// kqueue creates a new kernel event queue and returns a descriptor. -func kqueue() (kq int, err error) { - kq, err = syscall.Kqueue() - if kq == -1 { - return kq, err - } - return kq, nil -} - -// register events with the queue -func register(kq int, fds []int, flags int, fflags uint32) error { - changes := make([]syscall.Kevent_t, len(fds)) - - for i, fd := range fds { - // SetKevent converts int to the platform-specific types: - syscall.SetKevent(&changes[i], fd, syscall.EVFILT_VNODE, flags) - changes[i].Fflags = fflags - } - - // register the events - success, err := syscall.Kevent(kq, changes, nil, nil) - if success == -1 { - return err - } - return nil -} - -// read retrieves pending events, or waits until an event occurs. -// A timeout of nil blocks indefinitely, while 0 polls the queue. -func read(kq int, events []syscall.Kevent_t, timeout *syscall.Timespec) ([]syscall.Kevent_t, error) { - n, err := syscall.Kevent(kq, nil, events, timeout) - if err != nil { - return nil, err - } - return events[0:n], nil -} - -// durationToTimespec prepares a timeout value -func durationToTimespec(d time.Duration) syscall.Timespec { - return syscall.NsecToTimespec(d.Nanoseconds()) -} diff --git a/vendor/src/github.com/go-fsnotify/fsnotify/open_mode_bsd.go b/vendor/src/github.com/go-fsnotify/fsnotify/open_mode_bsd.go deleted file mode 100644 index c57ccb427b..0000000000 --- a/vendor/src/github.com/go-fsnotify/fsnotify/open_mode_bsd.go +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build freebsd openbsd netbsd dragonfly - -package fsnotify - -import "syscall" - -const openMode = syscall.O_NONBLOCK | syscall.O_RDONLY diff --git a/vendor/src/github.com/go-fsnotify/fsnotify/open_mode_darwin.go b/vendor/src/github.com/go-fsnotify/fsnotify/open_mode_darwin.go deleted file mode 100644 index 174b2c331f..0000000000 --- a/vendor/src/github.com/go-fsnotify/fsnotify/open_mode_darwin.go +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build darwin - -package fsnotify - -import "syscall" - -// note: this constant is not defined on BSD -const openMode = syscall.O_EVTONLY diff --git a/vendor/src/github.com/go-fsnotify/fsnotify/windows.go b/vendor/src/github.com/go-fsnotify/fsnotify/windows.go deleted file mode 100644 index 811585227d..0000000000 --- a/vendor/src/github.com/go-fsnotify/fsnotify/windows.go +++ /dev/null @@ -1,561 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build windows - -package fsnotify - -import ( - "errors" - "fmt" - "os" - "path/filepath" - "runtime" - "sync" - "syscall" - "unsafe" -) - -// Watcher watches a set of files, delivering events to a channel. -type Watcher struct { - Events chan Event - Errors chan error - isClosed bool // Set to true when Close() is first called - mu sync.Mutex // Map access - port syscall.Handle // Handle to completion port - watches watchMap // Map of watches (key: i-number) - input chan *input // Inputs to the reader are sent on this channel - quit chan chan<- error -} - -// NewWatcher establishes a new watcher with the underlying OS and begins waiting for events. -func NewWatcher() (*Watcher, error) { - port, e := syscall.CreateIoCompletionPort(syscall.InvalidHandle, 0, 0, 0) - if e != nil { - return nil, os.NewSyscallError("CreateIoCompletionPort", e) - } - w := &Watcher{ - port: port, - watches: make(watchMap), - input: make(chan *input, 1), - Events: make(chan Event, 50), - Errors: make(chan error), - quit: make(chan chan<- error, 1), - } - go w.readEvents() - return w, nil -} - -// Close removes all watches and closes the events channel. -func (w *Watcher) Close() error { - if w.isClosed { - return nil - } - w.isClosed = true - - // Send "quit" message to the reader goroutine - ch := make(chan error) - w.quit <- ch - if err := w.wakeupReader(); err != nil { - return err - } - return <-ch -} - -// Add starts watching the named file or directory (non-recursively). -func (w *Watcher) Add(name string) error { - if w.isClosed { - return errors.New("watcher already closed") - } - in := &input{ - op: opAddWatch, - path: filepath.Clean(name), - flags: sys_FS_ALL_EVENTS, - reply: make(chan error), - } - w.input <- in - if err := w.wakeupReader(); err != nil { - return err - } - return <-in.reply -} - -// Remove stops watching the the named file or directory (non-recursively). -func (w *Watcher) Remove(name string) error { - in := &input{ - op: opRemoveWatch, - path: filepath.Clean(name), - reply: make(chan error), - } - w.input <- in - if err := w.wakeupReader(); err != nil { - return err - } - return <-in.reply -} - -const ( - // Options for AddWatch - sys_FS_ONESHOT = 0x80000000 - sys_FS_ONLYDIR = 0x1000000 - - // Events - sys_FS_ACCESS = 0x1 - sys_FS_ALL_EVENTS = 0xfff - sys_FS_ATTRIB = 0x4 - sys_FS_CLOSE = 0x18 - sys_FS_CREATE = 0x100 - sys_FS_DELETE = 0x200 - sys_FS_DELETE_SELF = 0x400 - sys_FS_MODIFY = 0x2 - sys_FS_MOVE = 0xc0 - sys_FS_MOVED_FROM = 0x40 - sys_FS_MOVED_TO = 0x80 - sys_FS_MOVE_SELF = 0x800 - - // Special events - sys_FS_IGNORED = 0x8000 - sys_FS_Q_OVERFLOW = 0x4000 -) - -func newEvent(name string, mask uint32) Event { - e := Event{Name: name} - if mask&sys_FS_CREATE == sys_FS_CREATE || mask&sys_FS_MOVED_TO == sys_FS_MOVED_TO { - e.Op |= Create - } - if mask&sys_FS_DELETE == sys_FS_DELETE || mask&sys_FS_DELETE_SELF == sys_FS_DELETE_SELF { - e.Op |= Remove - } - if mask&sys_FS_MODIFY == sys_FS_MODIFY { - e.Op |= Write - } - if mask&sys_FS_MOVE == sys_FS_MOVE || mask&sys_FS_MOVE_SELF == sys_FS_MOVE_SELF || mask&sys_FS_MOVED_FROM == sys_FS_MOVED_FROM { - e.Op |= Rename - } - if mask&sys_FS_ATTRIB == sys_FS_ATTRIB { - e.Op |= Chmod - } - return e -} - -const ( - opAddWatch = iota - opRemoveWatch -) - -const ( - provisional uint64 = 1 << (32 + iota) -) - -type input struct { - op int - path string - flags uint32 - reply chan error -} - -type inode struct { - handle syscall.Handle - volume uint32 - index uint64 -} - -type watch struct { - ov syscall.Overlapped - ino *inode // i-number - path string // Directory path - mask uint64 // Directory itself is being watched with these notify flags - names map[string]uint64 // Map of names being watched and their notify flags - rename string // Remembers the old name while renaming a file - buf [4096]byte -} - -type indexMap map[uint64]*watch -type watchMap map[uint32]indexMap - -func (w *Watcher) wakeupReader() error { - e := syscall.PostQueuedCompletionStatus(w.port, 0, 0, nil) - if e != nil { - return os.NewSyscallError("PostQueuedCompletionStatus", e) - } - return nil -} - -func getDir(pathname string) (dir string, err error) { - attr, e := syscall.GetFileAttributes(syscall.StringToUTF16Ptr(pathname)) - if e != nil { - return "", os.NewSyscallError("GetFileAttributes", e) - } - if attr&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 { - dir = pathname - } else { - dir, _ = filepath.Split(pathname) - dir = filepath.Clean(dir) - } - return -} - -func getIno(path string) (ino *inode, err error) { - h, e := syscall.CreateFile(syscall.StringToUTF16Ptr(path), - syscall.FILE_LIST_DIRECTORY, - syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE, - nil, syscall.OPEN_EXISTING, - syscall.FILE_FLAG_BACKUP_SEMANTICS|syscall.FILE_FLAG_OVERLAPPED, 0) - if e != nil { - return nil, os.NewSyscallError("CreateFile", e) - } - var fi syscall.ByHandleFileInformation - if e = syscall.GetFileInformationByHandle(h, &fi); e != nil { - syscall.CloseHandle(h) - return nil, os.NewSyscallError("GetFileInformationByHandle", e) - } - ino = &inode{ - handle: h, - volume: fi.VolumeSerialNumber, - index: uint64(fi.FileIndexHigh)<<32 | uint64(fi.FileIndexLow), - } - return ino, nil -} - -// Must run within the I/O thread. -func (m watchMap) get(ino *inode) *watch { - if i := m[ino.volume]; i != nil { - return i[ino.index] - } - return nil -} - -// Must run within the I/O thread. -func (m watchMap) set(ino *inode, watch *watch) { - i := m[ino.volume] - if i == nil { - i = make(indexMap) - m[ino.volume] = i - } - i[ino.index] = watch -} - -// Must run within the I/O thread. -func (w *Watcher) addWatch(pathname string, flags uint64) error { - dir, err := getDir(pathname) - if err != nil { - return err - } - if flags&sys_FS_ONLYDIR != 0 && pathname != dir { - return nil - } - ino, err := getIno(dir) - if err != nil { - return err - } - w.mu.Lock() - watchEntry := w.watches.get(ino) - w.mu.Unlock() - if watchEntry == nil { - if _, e := syscall.CreateIoCompletionPort(ino.handle, w.port, 0, 0); e != nil { - syscall.CloseHandle(ino.handle) - return os.NewSyscallError("CreateIoCompletionPort", e) - } - watchEntry = &watch{ - ino: ino, - path: dir, - names: make(map[string]uint64), - } - w.mu.Lock() - w.watches.set(ino, watchEntry) - w.mu.Unlock() - flags |= provisional - } else { - syscall.CloseHandle(ino.handle) - } - if pathname == dir { - watchEntry.mask |= flags - } else { - watchEntry.names[filepath.Base(pathname)] |= flags - } - if err = w.startRead(watchEntry); err != nil { - return err - } - if pathname == dir { - watchEntry.mask &= ^provisional - } else { - watchEntry.names[filepath.Base(pathname)] &= ^provisional - } - return nil -} - -// Must run within the I/O thread. -func (w *Watcher) remWatch(pathname string) error { - dir, err := getDir(pathname) - if err != nil { - return err - } - ino, err := getIno(dir) - if err != nil { - return err - } - w.mu.Lock() - watch := w.watches.get(ino) - w.mu.Unlock() - if watch == nil { - return fmt.Errorf("can't remove non-existent watch for: %s", pathname) - } - if pathname == dir { - w.sendEvent(watch.path, watch.mask&sys_FS_IGNORED) - watch.mask = 0 - } else { - name := filepath.Base(pathname) - w.sendEvent(watch.path+"\\"+name, watch.names[name]&sys_FS_IGNORED) - delete(watch.names, name) - } - return w.startRead(watch) -} - -// Must run within the I/O thread. -func (w *Watcher) deleteWatch(watch *watch) { - for name, mask := range watch.names { - if mask&provisional == 0 { - w.sendEvent(watch.path+"\\"+name, mask&sys_FS_IGNORED) - } - delete(watch.names, name) - } - if watch.mask != 0 { - if watch.mask&provisional == 0 { - w.sendEvent(watch.path, watch.mask&sys_FS_IGNORED) - } - watch.mask = 0 - } -} - -// Must run within the I/O thread. -func (w *Watcher) startRead(watch *watch) error { - if e := syscall.CancelIo(watch.ino.handle); e != nil { - w.Errors <- os.NewSyscallError("CancelIo", e) - w.deleteWatch(watch) - } - mask := toWindowsFlags(watch.mask) - for _, m := range watch.names { - mask |= toWindowsFlags(m) - } - if mask == 0 { - if e := syscall.CloseHandle(watch.ino.handle); e != nil { - w.Errors <- os.NewSyscallError("CloseHandle", e) - } - w.mu.Lock() - delete(w.watches[watch.ino.volume], watch.ino.index) - w.mu.Unlock() - return nil - } - e := syscall.ReadDirectoryChanges(watch.ino.handle, &watch.buf[0], - uint32(unsafe.Sizeof(watch.buf)), false, mask, nil, &watch.ov, 0) - if e != nil { - err := os.NewSyscallError("ReadDirectoryChanges", e) - if e == syscall.ERROR_ACCESS_DENIED && watch.mask&provisional == 0 { - // Watched directory was probably removed - if w.sendEvent(watch.path, watch.mask&sys_FS_DELETE_SELF) { - if watch.mask&sys_FS_ONESHOT != 0 { - watch.mask = 0 - } - } - err = nil - } - w.deleteWatch(watch) - w.startRead(watch) - return err - } - return nil -} - -// readEvents reads from the I/O completion port, converts the -// received events into Event objects and sends them via the Events channel. -// Entry point to the I/O thread. -func (w *Watcher) readEvents() { - var ( - n, key uint32 - ov *syscall.Overlapped - ) - runtime.LockOSThread() - - for { - e := syscall.GetQueuedCompletionStatus(w.port, &n, &key, &ov, syscall.INFINITE) - watch := (*watch)(unsafe.Pointer(ov)) - - if watch == nil { - select { - case ch := <-w.quit: - w.mu.Lock() - var indexes []indexMap - for _, index := range w.watches { - indexes = append(indexes, index) - } - w.mu.Unlock() - for _, index := range indexes { - for _, watch := range index { - w.deleteWatch(watch) - w.startRead(watch) - } - } - var err error - if e := syscall.CloseHandle(w.port); e != nil { - err = os.NewSyscallError("CloseHandle", e) - } - close(w.Events) - close(w.Errors) - ch <- err - return - case in := <-w.input: - switch in.op { - case opAddWatch: - in.reply <- w.addWatch(in.path, uint64(in.flags)) - case opRemoveWatch: - in.reply <- w.remWatch(in.path) - } - default: - } - continue - } - - switch e { - case syscall.ERROR_MORE_DATA: - if watch == nil { - w.Errors <- errors.New("ERROR_MORE_DATA has unexpectedly null lpOverlapped buffer") - } else { - // The i/o succeeded but the buffer is full. - // In theory we should be building up a full packet. - // In practice we can get away with just carrying on. - n = uint32(unsafe.Sizeof(watch.buf)) - } - case syscall.ERROR_ACCESS_DENIED: - // Watched directory was probably removed - w.sendEvent(watch.path, watch.mask&sys_FS_DELETE_SELF) - w.deleteWatch(watch) - w.startRead(watch) - continue - case syscall.ERROR_OPERATION_ABORTED: - // CancelIo was called on this handle - continue - default: - w.Errors <- os.NewSyscallError("GetQueuedCompletionPort", e) - continue - case nil: - } - - var offset uint32 - for { - if n == 0 { - w.Events <- newEvent("", sys_FS_Q_OVERFLOW) - w.Errors <- errors.New("short read in readEvents()") - break - } - - // Point "raw" to the event in the buffer - raw := (*syscall.FileNotifyInformation)(unsafe.Pointer(&watch.buf[offset])) - buf := (*[syscall.MAX_PATH]uint16)(unsafe.Pointer(&raw.FileName)) - name := syscall.UTF16ToString(buf[:raw.FileNameLength/2]) - fullname := watch.path + "\\" + name - - var mask uint64 - switch raw.Action { - case syscall.FILE_ACTION_REMOVED: - mask = sys_FS_DELETE_SELF - case syscall.FILE_ACTION_MODIFIED: - mask = sys_FS_MODIFY - case syscall.FILE_ACTION_RENAMED_OLD_NAME: - watch.rename = name - case syscall.FILE_ACTION_RENAMED_NEW_NAME: - if watch.names[watch.rename] != 0 { - watch.names[name] |= watch.names[watch.rename] - delete(watch.names, watch.rename) - mask = sys_FS_MOVE_SELF - } - } - - sendNameEvent := func() { - if w.sendEvent(fullname, watch.names[name]&mask) { - if watch.names[name]&sys_FS_ONESHOT != 0 { - delete(watch.names, name) - } - } - } - if raw.Action != syscall.FILE_ACTION_RENAMED_NEW_NAME { - sendNameEvent() - } - if raw.Action == syscall.FILE_ACTION_REMOVED { - w.sendEvent(fullname, watch.names[name]&sys_FS_IGNORED) - delete(watch.names, name) - } - if w.sendEvent(fullname, watch.mask&toFSnotifyFlags(raw.Action)) { - if watch.mask&sys_FS_ONESHOT != 0 { - watch.mask = 0 - } - } - if raw.Action == syscall.FILE_ACTION_RENAMED_NEW_NAME { - fullname = watch.path + "\\" + watch.rename - sendNameEvent() - } - - // Move to the next event in the buffer - if raw.NextEntryOffset == 0 { - break - } - offset += raw.NextEntryOffset - - // Error! - if offset >= n { - w.Errors <- errors.New("Windows system assumed buffer larger than it is, events have likely been missed.") - break - } - } - - if err := w.startRead(watch); err != nil { - w.Errors <- err - } - } -} - -func (w *Watcher) sendEvent(name string, mask uint64) bool { - if mask == 0 { - return false - } - event := newEvent(name, uint32(mask)) - select { - case ch := <-w.quit: - w.quit <- ch - case w.Events <- event: - } - return true -} - -func toWindowsFlags(mask uint64) uint32 { - var m uint32 - if mask&sys_FS_ACCESS != 0 { - m |= syscall.FILE_NOTIFY_CHANGE_LAST_ACCESS - } - if mask&sys_FS_MODIFY != 0 { - m |= syscall.FILE_NOTIFY_CHANGE_LAST_WRITE - } - if mask&sys_FS_ATTRIB != 0 { - m |= syscall.FILE_NOTIFY_CHANGE_ATTRIBUTES - } - if mask&(sys_FS_MOVE|sys_FS_CREATE|sys_FS_DELETE) != 0 { - m |= syscall.FILE_NOTIFY_CHANGE_FILE_NAME | syscall.FILE_NOTIFY_CHANGE_DIR_NAME - } - return m -} - -func toFSnotifyFlags(action uint32) uint64 { - switch action { - case syscall.FILE_ACTION_ADDED: - return sys_FS_CREATE - case syscall.FILE_ACTION_REMOVED: - return sys_FS_DELETE - case syscall.FILE_ACTION_MODIFIED: - return sys_FS_MODIFY - case syscall.FILE_ACTION_RENAMED_OLD_NAME: - return sys_FS_MOVED_FROM - case syscall.FILE_ACTION_RENAMED_NEW_NAME: - return sys_FS_MOVED_TO - } - return 0 -} diff --git a/vendor/src/github.com/vishvananda/netlink/.travis.yml b/vendor/src/github.com/vishvananda/netlink/.travis.yml new file mode 100644 index 0000000000..1970069d51 --- /dev/null +++ b/vendor/src/github.com/vishvananda/netlink/.travis.yml @@ -0,0 +1,3 @@ +language: go +install: + - go get github.com/vishvananda/netns diff --git a/vendor/src/github.com/vishvananda/netlink/LICENSE b/vendor/src/github.com/vishvananda/netlink/LICENSE new file mode 100644 index 0000000000..9f64db8582 --- /dev/null +++ b/vendor/src/github.com/vishvananda/netlink/LICENSE @@ -0,0 +1,192 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + Copyright 2014 Vishvananda Ishaya. + Copyright 2014 Docker, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/src/github.com/vishvananda/netlink/Makefile b/vendor/src/github.com/vishvananda/netlink/Makefile new file mode 100644 index 0000000000..b3250185f4 --- /dev/null +++ b/vendor/src/github.com/vishvananda/netlink/Makefile @@ -0,0 +1,29 @@ +DIRS := \ + . \ + nl + +DEPS = \ + github.com/vishvananda/netns + +uniq = $(if $1,$(firstword $1) $(call uniq,$(filter-out $(firstword $1),$1))) +testdirs = $(call uniq,$(foreach d,$(1),$(dir $(wildcard $(d)/*_test.go)))) +goroot = $(addprefix ../../../,$(1)) +unroot = $(subst ../../../,,$(1)) +fmt = $(addprefix fmt-,$(1)) + +all: fmt + +$(call goroot,$(DEPS)): + go get $(call unroot,$@) + +.PHONY: $(call testdirs,$(DIRS)) +$(call testdirs,$(DIRS)): + sudo -E go test -v github.com/vishvananda/netlink/$@ + +$(call fmt,$(call testdirs,$(DIRS))): + ! gofmt -l $(subst fmt-,,$@)/*.go | grep '' + +.PHONY: fmt +fmt: $(call fmt,$(call testdirs,$(DIRS))) + +test: fmt $(call goroot,$(DEPS)) $(call testdirs,$(DIRS)) diff --git a/vendor/src/github.com/vishvananda/netlink/README.md b/vendor/src/github.com/vishvananda/netlink/README.md new file mode 100644 index 0000000000..555f886523 --- /dev/null +++ b/vendor/src/github.com/vishvananda/netlink/README.md @@ -0,0 +1,83 @@ +# netlink - netlink library for go # + +[![Build Status](https://travis-ci.org/vishvananda/netlink.png?branch=master)](https://travis-ci.org/vishvananda/netlink) [![GoDoc](https://godoc.org/github.com/vishvananda/netlink?status.svg)](https://godoc.org/github.com/vishvananda/netlink) + +The netlink package provides a simple netlink library for go. Netlink +is the interface a user-space program in linux uses to communicate with +the kernel. It can be used to add and remove interfaces, set ip addresses +and routes, and configure ipsec. Netlink communication requires elevated +privileges, so in most cases this code needs to be run as root. Since +low-level netlink messages are inscrutable at best, the library attempts +to provide an api that is loosely modeled on the CLI provied by iproute2. +Actions like `ip link add` will be accomplished via a similarly named +function like AddLink(). This library began its life as a fork of the +netlink functionality in +[docker/libcontainer](https://github.com/docker/libcontainer) but was +heavily rewritten to improve testability, performance, and to add new +functionality like ipsec xfrm handling. + +## Local Build and Test ## + +You can use go get command: + + go get github.com/vishvananda/netlink + +Testing dependencies: + + go get github.com/vishvananda/netns + +Testing (requires root): + + sudo -E go test github.com/vishvananda/netlink + +## Examples ## + +Add a new bridge and add eth1 into it: + +```go +package main + +import ( + "net" + "github.com/vishvananda/netlink" +) + +func main() { + mybridge := &netlink.Bridge{netlink.LinkAttrs{Name: "foo"}} + _ := netlink.LinkAdd(mybridge) + eth1, _ := netlink.LinkByName("eth1") + netlink.LinkSetMaster(eth1, mybridge) +} + +``` + +Add a new ip address to loopback: + +```go +package main + +import ( + "net" + "github.com/vishvananda/netlink" +) + +func main() { + lo, _ := netlink.LinkByName("lo") + addr, _ := netlink.ParseAddr("169.254.169.254/32") + netlink.AddrAdd(lo, addr) +} + +``` + +## Future Work ## + +Many pieces of netlink are not yet fully supported in the high-level +interface. Aspects of virtually all of the high-level objects don't exist. +Many of the underlying primitives are there, so its a matter of putting +the right fields into the high-level objects and making sure that they +are serialized and deserialized correctly in the Add and List methods. + +There are also a few pieces of low level netlink functionality that still +need to be implemented. Routing rules are not in place and some of the +more advanced link types. Hopefully there is decent structure and testing +in place to make these fairly straightforward to add. diff --git a/vendor/src/github.com/vishvananda/netlink/addr.go b/vendor/src/github.com/vishvananda/netlink/addr.go new file mode 100644 index 0000000000..5c12f4e998 --- /dev/null +++ b/vendor/src/github.com/vishvananda/netlink/addr.go @@ -0,0 +1,43 @@ +package netlink + +import ( + "fmt" + "net" + "strings" +) + +// Addr represents an IP address from netlink. Netlink ip addresses +// include a mask, so it stores the address as a net.IPNet. +type Addr struct { + *net.IPNet + Label string +} + +// String returns $ip/$netmask $label +func (addr Addr) String() string { + return fmt.Sprintf("%s %s", addr.IPNet, addr.Label) +} + +// ParseAddr parses the string representation of an address in the +// form $ip/$netmask $label. The label portion is optional +func ParseAddr(s string) (*Addr, error) { + label := "" + parts := strings.Split(s, " ") + if len(parts) > 1 { + s = parts[0] + label = parts[1] + } + m, err := ParseIPNet(s) + if err != nil { + return nil, err + } + return &Addr{IPNet: m, Label: label}, nil +} + +// Equal returns true if both Addrs have the same net.IPNet value. +func (a Addr) Equal(x Addr) bool { + sizea, _ := a.Mask.Size() + sizeb, _ := x.Mask.Size() + // ignore label for comparison + return a.IP.Equal(x.IP) && sizea == sizeb +} diff --git a/vendor/src/github.com/vishvananda/netlink/addr_linux.go b/vendor/src/github.com/vishvananda/netlink/addr_linux.go new file mode 100644 index 0000000000..dd26f4aec7 --- /dev/null +++ b/vendor/src/github.com/vishvananda/netlink/addr_linux.go @@ -0,0 +1,114 @@ +package netlink + +import ( + "fmt" + "net" + "strings" + "syscall" + + "github.com/vishvananda/netlink/nl" +) + +// AddrAdd will add an IP address to a link device. +// Equivalent to: `ip addr add $addr dev $link` +func AddrAdd(link Link, addr *Addr) error { + + req := nl.NewNetlinkRequest(syscall.RTM_NEWADDR, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK) + return addrHandle(link, addr, req) +} + +// AddrDel will delete an IP address from a link device. +// Equivalent to: `ip addr del $addr dev $link` +func AddrDel(link Link, addr *Addr) error { + req := nl.NewNetlinkRequest(syscall.RTM_DELADDR, syscall.NLM_F_ACK) + return addrHandle(link, addr, req) +} + +func addrHandle(link Link, addr *Addr, req *nl.NetlinkRequest) error { + base := link.Attrs() + if addr.Label != "" && !strings.HasPrefix(addr.Label, base.Name) { + return fmt.Errorf("label must begin with interface name") + } + ensureIndex(base) + + family := nl.GetIPFamily(addr.IP) + + msg := nl.NewIfAddrmsg(family) + msg.Index = uint32(base.Index) + prefixlen, _ := addr.Mask.Size() + msg.Prefixlen = uint8(prefixlen) + req.AddData(msg) + + var addrData []byte + if family == FAMILY_V4 { + addrData = addr.IP.To4() + } else { + addrData = addr.IP.To16() + } + + localData := nl.NewRtAttr(syscall.IFA_LOCAL, addrData) + req.AddData(localData) + + addressData := nl.NewRtAttr(syscall.IFA_ADDRESS, addrData) + req.AddData(addressData) + + if addr.Label != "" { + labelData := nl.NewRtAttr(syscall.IFA_LABEL, nl.ZeroTerminated(addr.Label)) + req.AddData(labelData) + } + + _, err := req.Execute(syscall.NETLINK_ROUTE, 0) + return err +} + +// AddrList gets a list of IP addresses in the system. +// Equivalent to: `ip addr show`. +// The list can be filtered by link and ip family. +func AddrList(link Link, family int) ([]Addr, error) { + req := nl.NewNetlinkRequest(syscall.RTM_GETADDR, syscall.NLM_F_DUMP) + msg := nl.NewIfInfomsg(family) + req.AddData(msg) + + msgs, err := req.Execute(syscall.NETLINK_ROUTE, syscall.RTM_NEWADDR) + if err != nil { + return nil, err + } + + index := 0 + if link != nil { + base := link.Attrs() + ensureIndex(base) + index = base.Index + } + + res := make([]Addr, 0) + for _, m := range msgs { + msg := nl.DeserializeIfAddrmsg(m) + + if link != nil && msg.Index != uint32(index) { + // Ignore messages from other interfaces + continue + } + + attrs, err := nl.ParseRouteAttr(m[msg.Len():]) + if err != nil { + return nil, err + } + + var addr Addr + for _, attr := range attrs { + switch attr.Attr.Type { + case syscall.IFA_ADDRESS: + addr.IPNet = &net.IPNet{ + IP: attr.Value, + Mask: net.CIDRMask(int(msg.Prefixlen), 8*len(attr.Value)), + } + case syscall.IFA_LABEL: + addr.Label = string(attr.Value[:len(attr.Value)-1]) + } + } + res = append(res, addr) + } + + return res, nil +} diff --git a/vendor/src/github.com/vishvananda/netlink/addr_test.go b/vendor/src/github.com/vishvananda/netlink/addr_test.go new file mode 100644 index 0000000000..45e22c0526 --- /dev/null +++ b/vendor/src/github.com/vishvananda/netlink/addr_test.go @@ -0,0 +1,45 @@ +package netlink + +import ( + "testing" +) + +func TestAddrAddDel(t *testing.T) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + + link, err := LinkByName("lo") + if err != nil { + t.Fatal(err) + } + + addr, err := ParseAddr("127.1.1.1/24 local") + if err != nil { + t.Fatal(err) + } + + if err = AddrAdd(link, addr); err != nil { + t.Fatal(err) + } + + addrs, err := AddrList(link, FAMILY_ALL) + if err != nil { + t.Fatal(err) + } + + if len(addrs) != 1 || !addr.Equal(addrs[0]) || addrs[0].Label != addr.Label { + t.Fatal("Address not added properly") + } + + if err = AddrDel(link, addr); err != nil { + t.Fatal(err) + } + addrs, err = AddrList(link, FAMILY_ALL) + if err != nil { + t.Fatal(err) + } + + if len(addrs) != 0 { + t.Fatal("Address not removed properly") + } +} diff --git a/vendor/src/github.com/vishvananda/netlink/link.go b/vendor/src/github.com/vishvananda/netlink/link.go new file mode 100644 index 0000000000..276c2f80b0 --- /dev/null +++ b/vendor/src/github.com/vishvananda/netlink/link.go @@ -0,0 +1,175 @@ +package netlink + +import "net" + +// Link represents a link device from netlink. Shared link attributes +// like name may be retrieved using the Attrs() method. Unique data +// can be retrieved by casting the object to the proper type. +type Link interface { + Attrs() *LinkAttrs + Type() string +} + +// LinkAttrs represents data shared by most link types +type LinkAttrs struct { + Index int + MTU int + TxQLen uint32 // Transmit Queue Length + Name string + HardwareAddr net.HardwareAddr + Flags net.Flags + ParentIndex int // index of the parent link device + MasterIndex int // must be the index of a bridge +} + +// Device links cannot be created via netlink. These links +// are links created by udev like 'lo' and 'etho0' +type Device struct { + LinkAttrs +} + +func (device *Device) Attrs() *LinkAttrs { + return &device.LinkAttrs +} + +func (device *Device) Type() string { + return "device" +} + +// Dummy links are dummy ethernet devices +type Dummy struct { + LinkAttrs +} + +func (dummy *Dummy) Attrs() *LinkAttrs { + return &dummy.LinkAttrs +} + +func (dummy *Dummy) Type() string { + return "dummy" +} + +// Bridge links are simple linux bridges +type Bridge struct { + LinkAttrs +} + +func (bridge *Bridge) Attrs() *LinkAttrs { + return &bridge.LinkAttrs +} + +func (bridge *Bridge) Type() string { + return "bridge" +} + +// Vlan links have ParentIndex set in their Attrs() +type Vlan struct { + LinkAttrs + VlanId int +} + +func (vlan *Vlan) Attrs() *LinkAttrs { + return &vlan.LinkAttrs +} + +func (vlan *Vlan) Type() string { + return "vlan" +} + +// Macvlan links have ParentIndex set in their Attrs() +type Macvlan struct { + LinkAttrs +} + +func (macvlan *Macvlan) Attrs() *LinkAttrs { + return &macvlan.LinkAttrs +} + +func (macvlan *Macvlan) Type() string { + return "macvlan" +} + +// Veth devices must specify PeerName on create +type Veth struct { + LinkAttrs + PeerName string // veth on create only +} + +func (veth *Veth) Attrs() *LinkAttrs { + return &veth.LinkAttrs +} + +func (veth *Veth) Type() string { + return "veth" +} + +// Generic links represent types that are not currently understood +// by this netlink library. +type Generic struct { + LinkAttrs + LinkType string +} + +func (generic *Generic) Attrs() *LinkAttrs { + return &generic.LinkAttrs +} + +func (generic *Generic) Type() string { + return generic.LinkType +} + +type Vxlan struct { + LinkAttrs + VxlanId int + VtepDevIndex int + SrcAddr net.IP + Group net.IP + TTL int + TOS int + Learning bool + Proxy bool + RSC bool + L2miss bool + L3miss bool + NoAge bool + Age int + Limit int + Port int + PortLow int + PortHigh int +} + +func (vxlan *Vxlan) Attrs() *LinkAttrs { + return &vxlan.LinkAttrs +} + +func (vxlan *Vxlan) Type() string { + return "vxlan" +} + +type IPVlanMode uint16 + +const ( + IPVLAN_MODE_L2 IPVlanMode = iota + IPVLAN_MODE_L3 + IPVLAN_MODE_MAX +) + +type IPVlan struct { + LinkAttrs + Mode IPVlanMode +} + +func (ipvlan *IPVlan) Attrs() *LinkAttrs { + return &ipvlan.LinkAttrs +} + +func (ipvlan *IPVlan) Type() string { + return "ipvlan" +} + +// iproute2 supported devices; +// vlan | veth | vcan | dummy | ifb | macvlan | macvtap | +// bridge | bond | ipoib | ip6tnl | ipip | sit | vxlan | +// gre | gretap | ip6gre | ip6gretap | vti | nlmon | +// bond_slave | ipvlan diff --git a/vendor/src/github.com/vishvananda/netlink/link_linux.go b/vendor/src/github.com/vishvananda/netlink/link_linux.go new file mode 100644 index 0000000000..aedea165d9 --- /dev/null +++ b/vendor/src/github.com/vishvananda/netlink/link_linux.go @@ -0,0 +1,696 @@ +package netlink + +import ( + "bytes" + "encoding/binary" + "fmt" + "net" + "syscall" + + "github.com/vishvananda/netlink/nl" +) + +var native = nl.NativeEndian() +var lookupByDump = false + +func ensureIndex(link *LinkAttrs) { + if link != nil && link.Index == 0 { + newlink, _ := LinkByName(link.Name) + if newlink != nil { + link.Index = newlink.Attrs().Index + } + } +} + +// LinkSetUp enables the link device. +// Equivalent to: `ip link set $link up` +func LinkSetUp(link Link) error { + base := link.Attrs() + ensureIndex(base) + req := nl.NewNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_ACK) + + msg := nl.NewIfInfomsg(syscall.AF_UNSPEC) + msg.Change = syscall.IFF_UP + msg.Flags = syscall.IFF_UP + msg.Index = int32(base.Index) + req.AddData(msg) + + _, err := req.Execute(syscall.NETLINK_ROUTE, 0) + return err +} + +// LinkSetUp disables link device. +// Equivalent to: `ip link set $link down` +func LinkSetDown(link Link) error { + base := link.Attrs() + ensureIndex(base) + req := nl.NewNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_ACK) + + msg := nl.NewIfInfomsg(syscall.AF_UNSPEC) + msg.Change = syscall.IFF_UP + msg.Flags = 0 & ^syscall.IFF_UP + msg.Index = int32(base.Index) + req.AddData(msg) + + _, err := req.Execute(syscall.NETLINK_ROUTE, 0) + return err +} + +// LinkSetMTU sets the mtu of the link device. +// Equivalent to: `ip link set $link mtu $mtu` +func LinkSetMTU(link Link, mtu int) error { + base := link.Attrs() + ensureIndex(base) + req := nl.NewNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK) + + msg := nl.NewIfInfomsg(syscall.AF_UNSPEC) + msg.Type = syscall.RTM_SETLINK + msg.Flags = syscall.NLM_F_REQUEST + msg.Index = int32(base.Index) + msg.Change = nl.DEFAULT_CHANGE + req.AddData(msg) + + b := make([]byte, 4) + native.PutUint32(b, uint32(mtu)) + + data := nl.NewRtAttr(syscall.IFLA_MTU, b) + req.AddData(data) + + _, err := req.Execute(syscall.NETLINK_ROUTE, 0) + return err +} + +// LinkSetName sets the name of the link device. +// Equivalent to: `ip link set $link name $name` +func LinkSetName(link Link, name string) error { + base := link.Attrs() + ensureIndex(base) + req := nl.NewNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK) + + msg := nl.NewIfInfomsg(syscall.AF_UNSPEC) + msg.Type = syscall.RTM_SETLINK + msg.Flags = syscall.NLM_F_REQUEST + msg.Index = int32(base.Index) + msg.Change = nl.DEFAULT_CHANGE + req.AddData(msg) + + data := nl.NewRtAttr(syscall.IFLA_IFNAME, []byte(name)) + req.AddData(data) + + _, err := req.Execute(syscall.NETLINK_ROUTE, 0) + return err +} + +// LinkSetHardwareAddr sets the hardware address of the link device. +// Equivalent to: `ip link set $link address $hwaddr` +func LinkSetHardwareAddr(link Link, hwaddr net.HardwareAddr) error { + base := link.Attrs() + ensureIndex(base) + req := nl.NewNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK) + + msg := nl.NewIfInfomsg(syscall.AF_UNSPEC) + msg.Type = syscall.RTM_SETLINK + msg.Flags = syscall.NLM_F_REQUEST + msg.Index = int32(base.Index) + msg.Change = nl.DEFAULT_CHANGE + req.AddData(msg) + + data := nl.NewRtAttr(syscall.IFLA_ADDRESS, []byte(hwaddr)) + req.AddData(data) + + _, err := req.Execute(syscall.NETLINK_ROUTE, 0) + return err +} + +// LinkSetMaster sets the master of the link device. +// Equivalent to: `ip link set $link master $master` +func LinkSetMaster(link Link, master *Bridge) error { + index := 0 + if master != nil { + masterBase := master.Attrs() + ensureIndex(masterBase) + index = masterBase.Index + } + return LinkSetMasterByIndex(link, index) +} + +// LinkSetMasterByIndex sets the master of the link device. +// Equivalent to: `ip link set $link master $master` +func LinkSetMasterByIndex(link Link, masterIndex int) error { + base := link.Attrs() + ensureIndex(base) + req := nl.NewNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK) + + msg := nl.NewIfInfomsg(syscall.AF_UNSPEC) + msg.Type = syscall.RTM_SETLINK + msg.Flags = syscall.NLM_F_REQUEST + msg.Index = int32(base.Index) + msg.Change = nl.DEFAULT_CHANGE + req.AddData(msg) + + b := make([]byte, 4) + native.PutUint32(b, uint32(masterIndex)) + + data := nl.NewRtAttr(syscall.IFLA_MASTER, b) + req.AddData(data) + + _, err := req.Execute(syscall.NETLINK_ROUTE, 0) + return err +} + +// LinkSetNsPid puts the device into a new network namespace. The +// pid must be a pid of a running process. +// Equivalent to: `ip link set $link netns $pid` +func LinkSetNsPid(link Link, nspid int) error { + base := link.Attrs() + ensureIndex(base) + req := nl.NewNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK) + + msg := nl.NewIfInfomsg(syscall.AF_UNSPEC) + msg.Type = syscall.RTM_SETLINK + msg.Flags = syscall.NLM_F_REQUEST + msg.Index = int32(base.Index) + msg.Change = nl.DEFAULT_CHANGE + req.AddData(msg) + + b := make([]byte, 4) + native.PutUint32(b, uint32(nspid)) + + data := nl.NewRtAttr(syscall.IFLA_NET_NS_PID, b) + req.AddData(data) + + _, err := req.Execute(syscall.NETLINK_ROUTE, 0) + return err +} + +// LinkSetNsPid puts the device into a new network namespace. The +// fd must be an open file descriptor to a network namespace. +// Similar to: `ip link set $link netns $ns` +func LinkSetNsFd(link Link, fd int) error { + base := link.Attrs() + ensureIndex(base) + req := nl.NewNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK) + + msg := nl.NewIfInfomsg(syscall.AF_UNSPEC) + msg.Type = syscall.RTM_SETLINK + msg.Flags = syscall.NLM_F_REQUEST + msg.Index = int32(base.Index) + msg.Change = nl.DEFAULT_CHANGE + req.AddData(msg) + + b := make([]byte, 4) + native.PutUint32(b, uint32(fd)) + + data := nl.NewRtAttr(nl.IFLA_NET_NS_FD, b) + req.AddData(data) + + _, err := req.Execute(syscall.NETLINK_ROUTE, 0) + return err +} + +func boolAttr(val bool) []byte { + var v uint8 + if val { + v = 1 + } + return nl.Uint8Attr(v) +} + +type vxlanPortRange struct { + Lo, Hi uint16 +} + +func addVxlanAttrs(vxlan *Vxlan, linkInfo *nl.RtAttr) { + data := nl.NewRtAttrChild(linkInfo, nl.IFLA_INFO_DATA, nil) + nl.NewRtAttrChild(data, nl.IFLA_VXLAN_ID, nl.Uint32Attr(uint32(vxlan.VxlanId))) + if vxlan.VtepDevIndex != 0 { + nl.NewRtAttrChild(data, nl.IFLA_VXLAN_LINK, nl.Uint32Attr(uint32(vxlan.VtepDevIndex))) + } + if vxlan.SrcAddr != nil { + ip := vxlan.SrcAddr.To4() + if ip != nil { + nl.NewRtAttrChild(data, nl.IFLA_VXLAN_LOCAL, []byte(ip)) + } else { + ip = vxlan.SrcAddr.To16() + if ip != nil { + nl.NewRtAttrChild(data, nl.IFLA_VXLAN_LOCAL6, []byte(ip)) + } + } + } + if vxlan.Group != nil { + group := vxlan.Group.To4() + if group != nil { + nl.NewRtAttrChild(data, nl.IFLA_VXLAN_GROUP, []byte(group)) + } else { + group = vxlan.Group.To16() + if group != nil { + nl.NewRtAttrChild(data, nl.IFLA_VXLAN_GROUP6, []byte(group)) + } + } + } + + nl.NewRtAttrChild(data, nl.IFLA_VXLAN_TTL, nl.Uint8Attr(uint8(vxlan.TTL))) + nl.NewRtAttrChild(data, nl.IFLA_VXLAN_TOS, nl.Uint8Attr(uint8(vxlan.TOS))) + nl.NewRtAttrChild(data, nl.IFLA_VXLAN_LEARNING, boolAttr(vxlan.Learning)) + nl.NewRtAttrChild(data, nl.IFLA_VXLAN_PROXY, boolAttr(vxlan.Proxy)) + nl.NewRtAttrChild(data, nl.IFLA_VXLAN_RSC, boolAttr(vxlan.RSC)) + nl.NewRtAttrChild(data, nl.IFLA_VXLAN_L2MISS, boolAttr(vxlan.L2miss)) + nl.NewRtAttrChild(data, nl.IFLA_VXLAN_L3MISS, boolAttr(vxlan.L3miss)) + + if vxlan.NoAge { + nl.NewRtAttrChild(data, nl.IFLA_VXLAN_AGEING, nl.Uint32Attr(0)) + } else if vxlan.Age > 0 { + nl.NewRtAttrChild(data, nl.IFLA_VXLAN_AGEING, nl.Uint32Attr(uint32(vxlan.Age))) + } + if vxlan.Limit > 0 { + nl.NewRtAttrChild(data, nl.IFLA_VXLAN_LIMIT, nl.Uint32Attr(uint32(vxlan.Limit))) + } + if vxlan.Port > 0 { + nl.NewRtAttrChild(data, nl.IFLA_VXLAN_PORT, nl.Uint16Attr(uint16(vxlan.Port))) + } + if vxlan.PortLow > 0 || vxlan.PortHigh > 0 { + pr := vxlanPortRange{uint16(vxlan.PortLow), uint16(vxlan.PortHigh)} + + buf := new(bytes.Buffer) + binary.Write(buf, binary.BigEndian, &pr) + + nl.NewRtAttrChild(data, nl.IFLA_VXLAN_PORT_RANGE, buf.Bytes()) + } +} + +// LinkAdd adds a new link device. The type and features of the device +// are taken fromt the parameters in the link object. +// Equivalent to: `ip link add $link` +func LinkAdd(link Link) error { + // TODO: set mtu and hardware address + // TODO: support extra data for macvlan + base := link.Attrs() + + if base.Name == "" { + return fmt.Errorf("LinkAttrs.Name cannot be empty!") + } + + req := nl.NewNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK) + + msg := nl.NewIfInfomsg(syscall.AF_UNSPEC) + req.AddData(msg) + + if base.ParentIndex != 0 { + b := make([]byte, 4) + native.PutUint32(b, uint32(base.ParentIndex)) + data := nl.NewRtAttr(syscall.IFLA_LINK, b) + req.AddData(data) + } else if link.Type() == "ipvlan" { + return fmt.Errorf("Can't create ipvlan link without ParentIndex") + } + + nameData := nl.NewRtAttr(syscall.IFLA_IFNAME, nl.ZeroTerminated(base.Name)) + req.AddData(nameData) + + if base.MTU > 0 { + mtu := nl.NewRtAttr(syscall.IFLA_MTU, nl.Uint32Attr(uint32(base.MTU))) + req.AddData(mtu) + } + + linkInfo := nl.NewRtAttr(syscall.IFLA_LINKINFO, nil) + nl.NewRtAttrChild(linkInfo, nl.IFLA_INFO_KIND, nl.NonZeroTerminated(link.Type())) + + nl.NewRtAttrChild(linkInfo, syscall.IFLA_TXQLEN, nl.Uint32Attr(base.TxQLen)) + + if vlan, ok := link.(*Vlan); ok { + b := make([]byte, 2) + native.PutUint16(b, uint16(vlan.VlanId)) + data := nl.NewRtAttrChild(linkInfo, nl.IFLA_INFO_DATA, nil) + nl.NewRtAttrChild(data, nl.IFLA_VLAN_ID, b) + } else if veth, ok := link.(*Veth); ok { + data := nl.NewRtAttrChild(linkInfo, nl.IFLA_INFO_DATA, nil) + peer := nl.NewRtAttrChild(data, nl.VETH_INFO_PEER, nil) + nl.NewIfInfomsgChild(peer, syscall.AF_UNSPEC) + nl.NewRtAttrChild(peer, syscall.IFLA_IFNAME, nl.ZeroTerminated(veth.PeerName)) + nl.NewRtAttrChild(peer, syscall.IFLA_TXQLEN, nl.Uint32Attr(base.TxQLen)) + if base.MTU > 0 { + nl.NewRtAttrChild(peer, syscall.IFLA_MTU, nl.Uint32Attr(uint32(base.MTU))) + } + } else if vxlan, ok := link.(*Vxlan); ok { + addVxlanAttrs(vxlan, linkInfo) + } else if ipv, ok := link.(*IPVlan); ok { + data := nl.NewRtAttrChild(linkInfo, nl.IFLA_INFO_DATA, nil) + nl.NewRtAttrChild(data, nl.IFLA_IPVLAN_MODE, nl.Uint16Attr(uint16(ipv.Mode))) + } + + req.AddData(linkInfo) + + _, err := req.Execute(syscall.NETLINK_ROUTE, 0) + if err != nil { + return err + } + + ensureIndex(base) + + // can't set master during create, so set it afterwards + if base.MasterIndex != 0 { + // TODO: verify MasterIndex is actually a bridge? + return LinkSetMasterByIndex(link, base.MasterIndex) + } + return nil +} + +// LinkDel deletes link device. Either Index or Name must be set in +// the link object for it to be deleted. The other values are ignored. +// Equivalent to: `ip link del $link` +func LinkDel(link Link) error { + base := link.Attrs() + + ensureIndex(base) + + req := nl.NewNetlinkRequest(syscall.RTM_DELLINK, syscall.NLM_F_ACK) + + msg := nl.NewIfInfomsg(syscall.AF_UNSPEC) + msg.Index = int32(base.Index) + req.AddData(msg) + + _, err := req.Execute(syscall.NETLINK_ROUTE, 0) + return err +} + +func linkByNameDump(name string) (Link, error) { + links, err := LinkList() + if err != nil { + return nil, err + } + + for _, link := range links { + if link.Attrs().Name == name { + return link, nil + } + } + return nil, fmt.Errorf("Link %s not found", name) +} + +// LinkByName finds a link by name and returns a pointer to the object. +func LinkByName(name string) (Link, error) { + if lookupByDump { + return linkByNameDump(name) + } + + req := nl.NewNetlinkRequest(syscall.RTM_GETLINK, syscall.NLM_F_ACK) + + msg := nl.NewIfInfomsg(syscall.AF_UNSPEC) + req.AddData(msg) + + nameData := nl.NewRtAttr(syscall.IFLA_IFNAME, nl.ZeroTerminated(name)) + req.AddData(nameData) + + link, err := execGetLink(req) + if err == syscall.EINVAL { + // older kernels don't support looking up via IFLA_IFNAME + // so fall back to dumping all links + lookupByDump = true + return linkByNameDump(name) + } + + return link, err +} + +// LinkByIndex finds a link by index and returns a pointer to the object. +func LinkByIndex(index int) (Link, error) { + req := nl.NewNetlinkRequest(syscall.RTM_GETLINK, syscall.NLM_F_ACK) + + msg := nl.NewIfInfomsg(syscall.AF_UNSPEC) + msg.Index = int32(index) + req.AddData(msg) + + return execGetLink(req) +} + +func execGetLink(req *nl.NetlinkRequest) (Link, error) { + msgs, err := req.Execute(syscall.NETLINK_ROUTE, 0) + if err != nil { + if errno, ok := err.(syscall.Errno); ok { + if errno == syscall.ENODEV { + return nil, fmt.Errorf("Link not found") + } + } + return nil, err + } + + switch { + case len(msgs) == 0: + return nil, fmt.Errorf("Link not found") + + case len(msgs) == 1: + return linkDeserialize(msgs[0]) + + default: + return nil, fmt.Errorf("More than one link found") + } +} + +// linkDeserialize deserializes a raw message received from netlink into +// a link object. +func linkDeserialize(m []byte) (Link, error) { + msg := nl.DeserializeIfInfomsg(m) + + attrs, err := nl.ParseRouteAttr(m[msg.Len():]) + if err != nil { + return nil, err + } + + base := LinkAttrs{Index: int(msg.Index), Flags: linkFlags(msg.Flags)} + var link Link + linkType := "" + for _, attr := range attrs { + switch attr.Attr.Type { + case syscall.IFLA_LINKINFO: + infos, err := nl.ParseRouteAttr(attr.Value) + if err != nil { + return nil, err + } + for _, info := range infos { + switch info.Attr.Type { + case nl.IFLA_INFO_KIND: + linkType = string(info.Value[:len(info.Value)-1]) + switch linkType { + case "dummy": + link = &Dummy{} + case "bridge": + link = &Bridge{} + case "vlan": + link = &Vlan{} + case "veth": + link = &Veth{} + case "vxlan": + link = &Vxlan{} + case "ipvlan": + link = &IPVlan{} + default: + link = &Generic{LinkType: linkType} + } + case nl.IFLA_INFO_DATA: + data, err := nl.ParseRouteAttr(info.Value) + if err != nil { + return nil, err + } + switch linkType { + case "vlan": + parseVlanData(link, data) + case "vxlan": + parseVxlanData(link, data) + case "ipvlan": + parseIPVlanData(link, data) + } + } + } + case syscall.IFLA_ADDRESS: + var nonzero bool + for _, b := range attr.Value { + if b != 0 { + nonzero = true + } + } + if nonzero { + base.HardwareAddr = attr.Value[:] + } + case syscall.IFLA_IFNAME: + base.Name = string(attr.Value[:len(attr.Value)-1]) + case syscall.IFLA_MTU: + base.MTU = int(native.Uint32(attr.Value[0:4])) + case syscall.IFLA_LINK: + base.ParentIndex = int(native.Uint32(attr.Value[0:4])) + case syscall.IFLA_MASTER: + base.MasterIndex = int(native.Uint32(attr.Value[0:4])) + case syscall.IFLA_TXQLEN: + base.TxQLen = native.Uint32(attr.Value[0:4]) + } + } + // Links that don't have IFLA_INFO_KIND are hardware devices + if link == nil { + link = &Device{} + } + *link.Attrs() = base + + return link, nil +} + +// LinkList gets a list of link devices. +// Equivalent to: `ip link show` +func LinkList() ([]Link, error) { + // NOTE(vish): This duplicates functionality in net/iface_linux.go, but we need + // to get the message ourselves to parse link type. + req := nl.NewNetlinkRequest(syscall.RTM_GETLINK, syscall.NLM_F_DUMP) + + msg := nl.NewIfInfomsg(syscall.AF_UNSPEC) + req.AddData(msg) + + msgs, err := req.Execute(syscall.NETLINK_ROUTE, syscall.RTM_NEWLINK) + if err != nil { + return nil, err + } + + res := make([]Link, 0) + + for _, m := range msgs { + link, err := linkDeserialize(m) + if err != nil { + return nil, err + } + res = append(res, link) + } + + return res, nil +} + +func LinkSetHairpin(link Link, mode bool) error { + return setProtinfoAttr(link, mode, nl.IFLA_BRPORT_MODE) +} + +func LinkSetGuard(link Link, mode bool) error { + return setProtinfoAttr(link, mode, nl.IFLA_BRPORT_GUARD) +} + +func LinkSetFastLeave(link Link, mode bool) error { + return setProtinfoAttr(link, mode, nl.IFLA_BRPORT_FAST_LEAVE) +} + +func LinkSetLearning(link Link, mode bool) error { + return setProtinfoAttr(link, mode, nl.IFLA_BRPORT_LEARNING) +} + +func LinkSetRootBlock(link Link, mode bool) error { + return setProtinfoAttr(link, mode, nl.IFLA_BRPORT_PROTECT) +} + +func LinkSetFlood(link Link, mode bool) error { + return setProtinfoAttr(link, mode, nl.IFLA_BRPORT_UNICAST_FLOOD) +} + +func setProtinfoAttr(link Link, mode bool, attr int) error { + base := link.Attrs() + ensureIndex(base) + req := nl.NewNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK) + + msg := nl.NewIfInfomsg(syscall.AF_BRIDGE) + msg.Type = syscall.RTM_SETLINK + msg.Flags = syscall.NLM_F_REQUEST + msg.Index = int32(base.Index) + msg.Change = nl.DEFAULT_CHANGE + req.AddData(msg) + + br := nl.NewRtAttr(syscall.IFLA_PROTINFO|syscall.NLA_F_NESTED, nil) + nl.NewRtAttrChild(br, attr, boolToByte(mode)) + req.AddData(br) + _, err := req.Execute(syscall.NETLINK_ROUTE, 0) + if err != nil { + return err + } + return nil +} + +func parseVlanData(link Link, data []syscall.NetlinkRouteAttr) { + vlan := link.(*Vlan) + for _, datum := range data { + switch datum.Attr.Type { + case nl.IFLA_VLAN_ID: + vlan.VlanId = int(native.Uint16(datum.Value[0:2])) + } + } +} + +func parseVxlanData(link Link, data []syscall.NetlinkRouteAttr) { + vxlan := link.(*Vxlan) + for _, datum := range data { + switch datum.Attr.Type { + case nl.IFLA_VXLAN_ID: + vxlan.VxlanId = int(native.Uint32(datum.Value[0:4])) + case nl.IFLA_VXLAN_LINK: + vxlan.VtepDevIndex = int(native.Uint32(datum.Value[0:4])) + case nl.IFLA_VXLAN_LOCAL: + vxlan.SrcAddr = net.IP(datum.Value[0:4]) + case nl.IFLA_VXLAN_LOCAL6: + vxlan.SrcAddr = net.IP(datum.Value[0:16]) + case nl.IFLA_VXLAN_GROUP: + vxlan.Group = net.IP(datum.Value[0:4]) + case nl.IFLA_VXLAN_GROUP6: + vxlan.Group = net.IP(datum.Value[0:16]) + case nl.IFLA_VXLAN_TTL: + vxlan.TTL = int(datum.Value[0]) + case nl.IFLA_VXLAN_TOS: + vxlan.TOS = int(datum.Value[0]) + case nl.IFLA_VXLAN_LEARNING: + vxlan.Learning = int8(datum.Value[0]) != 0 + case nl.IFLA_VXLAN_PROXY: + vxlan.Proxy = int8(datum.Value[0]) != 0 + case nl.IFLA_VXLAN_RSC: + vxlan.RSC = int8(datum.Value[0]) != 0 + case nl.IFLA_VXLAN_L2MISS: + vxlan.L2miss = int8(datum.Value[0]) != 0 + case nl.IFLA_VXLAN_L3MISS: + vxlan.L3miss = int8(datum.Value[0]) != 0 + case nl.IFLA_VXLAN_AGEING: + vxlan.Age = int(native.Uint32(datum.Value[0:4])) + vxlan.NoAge = vxlan.Age == 0 + case nl.IFLA_VXLAN_LIMIT: + vxlan.Limit = int(native.Uint32(datum.Value[0:4])) + case nl.IFLA_VXLAN_PORT: + vxlan.Port = int(native.Uint16(datum.Value[0:2])) + case nl.IFLA_VXLAN_PORT_RANGE: + buf := bytes.NewBuffer(datum.Value[0:4]) + var pr vxlanPortRange + if binary.Read(buf, binary.BigEndian, &pr) != nil { + vxlan.PortLow = int(pr.Lo) + vxlan.PortHigh = int(pr.Hi) + } + } + } +} + +func parseIPVlanData(link Link, data []syscall.NetlinkRouteAttr) { + ipv := link.(*IPVlan) + for _, datum := range data { + if datum.Attr.Type == nl.IFLA_IPVLAN_MODE { + ipv.Mode = IPVlanMode(native.Uint32(datum.Value[0:4])) + return + } + } +} + +// copied from pkg/net_linux.go +func linkFlags(rawFlags uint32) net.Flags { + var f net.Flags + if rawFlags&syscall.IFF_UP != 0 { + f |= net.FlagUp + } + if rawFlags&syscall.IFF_BROADCAST != 0 { + f |= net.FlagBroadcast + } + if rawFlags&syscall.IFF_LOOPBACK != 0 { + f |= net.FlagLoopback + } + if rawFlags&syscall.IFF_POINTOPOINT != 0 { + f |= net.FlagPointToPoint + } + if rawFlags&syscall.IFF_MULTICAST != 0 { + f |= net.FlagMulticast + } + return f +} diff --git a/vendor/src/github.com/vishvananda/netlink/link_test.go b/vendor/src/github.com/vishvananda/netlink/link_test.go new file mode 100644 index 0000000000..05b8e95586 --- /dev/null +++ b/vendor/src/github.com/vishvananda/netlink/link_test.go @@ -0,0 +1,531 @@ +package netlink + +import ( + "bytes" + "net" + "testing" + + "github.com/vishvananda/netns" +) + +const testTxQLen uint32 = 100 + +func testLinkAddDel(t *testing.T, link Link) { + links, err := LinkList() + if err != nil { + t.Fatal(err) + } + num := len(links) + + if err := LinkAdd(link); err != nil { + t.Fatal(err) + } + + base := link.Attrs() + + result, err := LinkByName(base.Name) + if err != nil { + t.Fatal(err) + } + + rBase := result.Attrs() + + if vlan, ok := link.(*Vlan); ok { + other, ok := result.(*Vlan) + if !ok { + t.Fatal("Result of create is not a vlan") + } + if vlan.VlanId != other.VlanId { + t.Fatal("Link.VlanId id doesn't match") + } + } + + if rBase.ParentIndex == 0 && base.ParentIndex != 0 { + t.Fatal("Created link doesn't have a Parent but it should") + } else if rBase.ParentIndex != 0 && base.ParentIndex == 0 { + t.Fatal("Created link has a Parent but it shouldn't") + } else if rBase.ParentIndex != 0 && base.ParentIndex != 0 { + if rBase.ParentIndex != base.ParentIndex { + t.Fatal("Link.ParentIndex doesn't match") + } + } + + if veth, ok := link.(*Veth); ok { + if veth.TxQLen != testTxQLen { + t.Fatalf("TxQLen is %d, should be %d", veth.TxQLen, testTxQLen) + } + if rBase.MTU != base.MTU { + t.Fatalf("MTU is %d, should be %d", rBase.MTU, base.MTU) + } + + if veth.PeerName != "" { + var peer *Veth + other, err := LinkByName(veth.PeerName) + if err != nil { + t.Fatalf("Peer %s not created", veth.PeerName) + } + if peer, ok = other.(*Veth); !ok { + t.Fatalf("Peer %s is incorrect type", veth.PeerName) + } + if peer.TxQLen != testTxQLen { + t.Fatalf("TxQLen of peer is %d, should be %d", peer.TxQLen, testTxQLen) + } + } + } + + if vxlan, ok := link.(*Vxlan); ok { + other, ok := result.(*Vxlan) + if !ok { + t.Fatal("Result of create is not a vxlan") + } + compareVxlan(t, vxlan, other) + } + + if ipv, ok := link.(*IPVlan); ok { + other, ok := result.(*IPVlan) + if !ok { + t.Fatal("Result of create is not a ipvlan") + } + if ipv.Mode != other.Mode { + t.Fatalf("Got unexpected mode: %d, expected: %d", other.Mode, ipv.Mode) + } + } + + if err = LinkDel(link); err != nil { + t.Fatal(err) + } + + links, err = LinkList() + if err != nil { + t.Fatal(err) + } + + if len(links) != num { + t.Fatal("Link not removed properly") + } +} + +func compareVxlan(t *testing.T, expected, actual *Vxlan) { + + if actual.VxlanId != expected.VxlanId { + t.Fatal("Vxlan.VxlanId doesn't match") + } + if expected.SrcAddr != nil && !actual.SrcAddr.Equal(expected.SrcAddr) { + t.Fatal("Vxlan.SrcAddr doesn't match") + } + if expected.Group != nil && !actual.Group.Equal(expected.Group) { + t.Fatal("Vxlan.Group doesn't match") + } + if expected.TTL != -1 && actual.TTL != expected.TTL { + t.Fatal("Vxlan.TTL doesn't match") + } + if expected.TOS != -1 && actual.TOS != expected.TOS { + t.Fatal("Vxlan.TOS doesn't match") + } + if actual.Learning != expected.Learning { + t.Fatal("Vxlan.Learning doesn't match") + } + if actual.Proxy != expected.Proxy { + t.Fatal("Vxlan.Proxy doesn't match") + } + if actual.RSC != expected.RSC { + t.Fatal("Vxlan.RSC doesn't match") + } + if actual.L2miss != expected.L2miss { + t.Fatal("Vxlan.L2miss doesn't match") + } + if actual.L3miss != expected.L3miss { + t.Fatal("Vxlan.L3miss doesn't match") + } + if expected.NoAge { + if !actual.NoAge { + t.Fatal("Vxlan.NoAge doesn't match") + } + } else if expected.Age > 0 && actual.Age != expected.Age { + t.Fatal("Vxlan.Age doesn't match") + } + if expected.Limit > 0 && actual.Limit != expected.Limit { + t.Fatal("Vxlan.Limit doesn't match") + } + if expected.Port > 0 && actual.Port != expected.Port { + t.Fatal("Vxlan.Port doesn't match") + } + if expected.PortLow > 0 || expected.PortHigh > 0 { + if actual.PortLow != expected.PortLow { + t.Fatal("Vxlan.PortLow doesn't match") + } + if actual.PortHigh != expected.PortHigh { + t.Fatal("Vxlan.PortHigh doesn't match") + } + } +} + +func TestLinkAddDelDummy(t *testing.T) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + + testLinkAddDel(t, &Dummy{LinkAttrs{Name: "foo"}}) +} + +func TestLinkAddDelBridge(t *testing.T) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + + testLinkAddDel(t, &Bridge{LinkAttrs{Name: "foo", MTU: 1400}}) +} + +func TestLinkAddDelVlan(t *testing.T) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + + parent := &Dummy{LinkAttrs{Name: "foo"}} + if err := LinkAdd(parent); err != nil { + t.Fatal(err) + } + + testLinkAddDel(t, &Vlan{LinkAttrs{Name: "bar", ParentIndex: parent.Attrs().Index}, 900}) + + if err := LinkDel(parent); err != nil { + t.Fatal(err) + } +} + +func TestLinkAddDelMacvlan(t *testing.T) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + + parent := &Dummy{LinkAttrs{Name: "foo"}} + if err := LinkAdd(parent); err != nil { + t.Fatal(err) + } + + testLinkAddDel(t, &Macvlan{LinkAttrs{Name: "bar", ParentIndex: parent.Attrs().Index}}) + + if err := LinkDel(parent); err != nil { + t.Fatal(err) + } +} + +func TestLinkAddDelVeth(t *testing.T) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + + testLinkAddDel(t, &Veth{LinkAttrs{Name: "foo", TxQLen: testTxQLen, MTU: 1400}, "bar"}) +} + +func TestLinkAddDelBridgeMaster(t *testing.T) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + + master := &Bridge{LinkAttrs{Name: "foo"}} + if err := LinkAdd(master); err != nil { + t.Fatal(err) + } + testLinkAddDel(t, &Dummy{LinkAttrs{Name: "bar", MasterIndex: master.Attrs().Index}}) + + if err := LinkDel(master); err != nil { + t.Fatal(err) + } +} + +func TestLinkSetUnsetResetMaster(t *testing.T) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + + master := &Bridge{LinkAttrs{Name: "foo"}} + if err := LinkAdd(master); err != nil { + t.Fatal(err) + } + + newmaster := &Bridge{LinkAttrs{Name: "bar"}} + if err := LinkAdd(newmaster); err != nil { + t.Fatal(err) + } + + slave := &Dummy{LinkAttrs{Name: "baz"}} + if err := LinkAdd(slave); err != nil { + t.Fatal(err) + } + + if err := LinkSetMaster(slave, master); err != nil { + t.Fatal(err) + } + + link, err := LinkByName("baz") + if err != nil { + t.Fatal(err) + } + + if link.Attrs().MasterIndex != master.Attrs().Index { + t.Fatal("Master not set properly") + } + + if err := LinkSetMaster(slave, newmaster); err != nil { + t.Fatal(err) + } + + link, err = LinkByName("baz") + if err != nil { + t.Fatal(err) + } + + if link.Attrs().MasterIndex != newmaster.Attrs().Index { + t.Fatal("Master not reset properly") + } + + if err := LinkSetMaster(slave, nil); err != nil { + t.Fatal(err) + } + + link, err = LinkByName("baz") + if err != nil { + t.Fatal(err) + } + + if link.Attrs().MasterIndex != 0 { + t.Fatal("Master not unset properly") + } + if err := LinkDel(slave); err != nil { + t.Fatal(err) + } + + if err := LinkDel(newmaster); err != nil { + t.Fatal(err) + } + + if err := LinkDel(master); err != nil { + t.Fatal(err) + } +} + +func TestLinkSetNs(t *testing.T) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + + basens, err := netns.Get() + if err != nil { + t.Fatal("Failed to get basens") + } + defer basens.Close() + + newns, err := netns.New() + if err != nil { + t.Fatal("Failed to create newns") + } + defer newns.Close() + + link := &Veth{LinkAttrs{Name: "foo"}, "bar"} + if err := LinkAdd(link); err != nil { + t.Fatal(err) + } + + peer, err := LinkByName("bar") + if err != nil { + t.Fatal(err) + } + + LinkSetNsFd(peer, int(basens)) + if err != nil { + t.Fatal("Failed to set newns for link") + } + + _, err = LinkByName("bar") + if err == nil { + t.Fatal("Link bar is still in newns") + } + + err = netns.Set(basens) + if err != nil { + t.Fatal("Failed to set basens") + } + + peer, err = LinkByName("bar") + if err != nil { + t.Fatal("Link is not in basens") + } + + if err := LinkDel(peer); err != nil { + t.Fatal(err) + } + + err = netns.Set(newns) + if err != nil { + t.Fatal("Failed to set newns") + } + + _, err = LinkByName("foo") + if err == nil { + t.Fatal("Other half of veth pair not deleted") + } + +} + +func TestLinkAddDelVxlan(t *testing.T) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + + parent := &Dummy{ + LinkAttrs{Name: "foo"}, + } + if err := LinkAdd(parent); err != nil { + t.Fatal(err) + } + + vxlan := Vxlan{ + LinkAttrs: LinkAttrs{ + Name: "bar", + }, + VxlanId: 10, + VtepDevIndex: parent.Index, + Learning: true, + L2miss: true, + L3miss: true, + } + + testLinkAddDel(t, &vxlan) + if err := LinkDel(parent); err != nil { + t.Fatal(err) + } +} + +func TestLinkAddDelIPVlanL2(t *testing.T) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + parent := &Dummy{LinkAttrs{Name: "foo"}} + if err := LinkAdd(parent); err != nil { + t.Fatal(err) + } + + ipv := IPVlan{ + LinkAttrs: LinkAttrs{ + Name: "bar", + ParentIndex: parent.Index, + }, + Mode: IPVLAN_MODE_L2, + } + + testLinkAddDel(t, &ipv) +} + +func TestLinkAddDelIPVlanL3(t *testing.T) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + parent := &Dummy{LinkAttrs{Name: "foo"}} + if err := LinkAdd(parent); err != nil { + t.Fatal(err) + } + + ipv := IPVlan{ + LinkAttrs: LinkAttrs{ + Name: "bar", + ParentIndex: parent.Index, + }, + Mode: IPVLAN_MODE_L3, + } + + testLinkAddDel(t, &ipv) +} + +func TestLinkAddDelIPVlanNoParent(t *testing.T) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + + ipv := IPVlan{ + LinkAttrs: LinkAttrs{ + Name: "bar", + }, + Mode: IPVLAN_MODE_L3, + } + err := LinkAdd(&ipv) + if err == nil { + t.Fatal("Add should fail if ipvlan creating without ParentIndex") + } + if err.Error() != "Can't create ipvlan link without ParentIndex" { + t.Fatalf("Error should be about missing ParentIndex, got %q", err) + } +} + +func TestLinkByIndex(t *testing.T) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + + dummy := &Dummy{LinkAttrs{Name: "dummy"}} + if err := LinkAdd(dummy); err != nil { + t.Fatal(err) + } + + found, err := LinkByIndex(dummy.Index) + if err != nil { + t.Fatal(err) + } + + if found.Attrs().Index != dummy.Attrs().Index { + t.Fatalf("Indices don't match: %v != %v", found.Attrs().Index, dummy.Attrs().Index) + } + + LinkDel(dummy) + + // test not found + _, err = LinkByIndex(dummy.Attrs().Index) + if err == nil { + t.Fatalf("LinkByIndex(%v) found deleted link", err) + } +} + +func TestLinkSet(t *testing.T) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + + iface := &Dummy{LinkAttrs{Name: "foo"}} + if err := LinkAdd(iface); err != nil { + t.Fatal(err) + } + + link, err := LinkByName("foo") + if err != nil { + t.Fatal(err) + } + + err = LinkSetName(link, "bar") + if err != nil { + t.Fatalf("Could not change interface name: %v", err) + } + + link, err = LinkByName("bar") + if err != nil { + t.Fatalf("Interface name not changed: %v", err) + } + + err = LinkSetMTU(link, 1400) + if err != nil { + t.Fatalf("Could not set MTU: %v", err) + } + + link, err = LinkByName("bar") + if err != nil { + t.Fatal(err) + } + + if link.Attrs().MTU != 1400 { + t.Fatal("MTU not changed!") + } + + addr, err := net.ParseMAC("00:12:34:56:78:AB") + if err != nil { + t.Fatal(err) + } + + err = LinkSetHardwareAddr(link, addr) + if err != nil { + t.Fatal(err) + } + + link, err = LinkByName("bar") + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(link.Attrs().HardwareAddr, addr) { + t.Fatalf("hardware address not changed!") + } +} diff --git a/vendor/src/github.com/vishvananda/netlink/neigh.go b/vendor/src/github.com/vishvananda/netlink/neigh.go new file mode 100644 index 0000000000..0e5eb90c9e --- /dev/null +++ b/vendor/src/github.com/vishvananda/netlink/neigh.go @@ -0,0 +1,22 @@ +package netlink + +import ( + "fmt" + "net" +) + +// Neigh represents a link layer neighbor from netlink. +type Neigh struct { + LinkIndex int + Family int + State int + Type int + Flags int + IP net.IP + HardwareAddr net.HardwareAddr +} + +// String returns $ip/$hwaddr $label +func (neigh *Neigh) String() string { + return fmt.Sprintf("%s %s", neigh.IP, neigh.HardwareAddr) +} diff --git a/vendor/src/github.com/vishvananda/netlink/neigh_linux.go b/vendor/src/github.com/vishvananda/netlink/neigh_linux.go new file mode 100644 index 0000000000..1fdaa3a37e --- /dev/null +++ b/vendor/src/github.com/vishvananda/netlink/neigh_linux.go @@ -0,0 +1,189 @@ +package netlink + +import ( + "net" + "syscall" + "unsafe" + + "github.com/vishvananda/netlink/nl" +) + +const ( + NDA_UNSPEC = iota + NDA_DST + NDA_LLADDR + NDA_CACHEINFO + NDA_PROBES + NDA_VLAN + NDA_PORT + NDA_VNI + NDA_IFINDEX + NDA_MAX = NDA_IFINDEX +) + +// Neighbor Cache Entry States. +const ( + NUD_NONE = 0x00 + NUD_INCOMPLETE = 0x01 + NUD_REACHABLE = 0x02 + NUD_STALE = 0x04 + NUD_DELAY = 0x08 + NUD_PROBE = 0x10 + NUD_FAILED = 0x20 + NUD_NOARP = 0x40 + NUD_PERMANENT = 0x80 +) + +// Neighbor Flags +const ( + NTF_USE = 0x01 + NTF_SELF = 0x02 + NTF_MASTER = 0x04 + NTF_PROXY = 0x08 + NTF_ROUTER = 0x80 +) + +type Ndmsg struct { + Family uint8 + Index uint32 + State uint16 + Flags uint8 + Type uint8 +} + +func deserializeNdmsg(b []byte) *Ndmsg { + var dummy Ndmsg + return (*Ndmsg)(unsafe.Pointer(&b[0:unsafe.Sizeof(dummy)][0])) +} + +func (msg *Ndmsg) Serialize() []byte { + return (*(*[unsafe.Sizeof(*msg)]byte)(unsafe.Pointer(msg)))[:] +} + +func (msg *Ndmsg) Len() int { + return int(unsafe.Sizeof(*msg)) +} + +// NeighAdd will add an IP to MAC mapping to the ARP table +// Equivalent to: `ip neigh add ....` +func NeighAdd(neigh *Neigh) error { + return neighAdd(neigh, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL) +} + +// NeighAdd will add or replace an IP to MAC mapping to the ARP table +// Equivalent to: `ip neigh replace....` +func NeighSet(neigh *Neigh) error { + return neighAdd(neigh, syscall.NLM_F_CREATE) +} + +// NeighAppend will append an entry to FDB +// Equivalent to: `bridge fdb append...` +func NeighAppend(neigh *Neigh) error { + return neighAdd(neigh, syscall.NLM_F_CREATE|syscall.NLM_F_APPEND) +} + +func neighAdd(neigh *Neigh, mode int) error { + req := nl.NewNetlinkRequest(syscall.RTM_NEWNEIGH, mode|syscall.NLM_F_ACK) + return neighHandle(neigh, req) +} + +// NeighDel will delete an IP address from a link device. +// Equivalent to: `ip addr del $addr dev $link` +func NeighDel(neigh *Neigh) error { + req := nl.NewNetlinkRequest(syscall.RTM_DELNEIGH, syscall.NLM_F_ACK) + return neighHandle(neigh, req) +} + +func neighHandle(neigh *Neigh, req *nl.NetlinkRequest) error { + var family int + if neigh.Family > 0 { + family = neigh.Family + } else { + family = nl.GetIPFamily(neigh.IP) + } + + msg := Ndmsg{ + Family: uint8(family), + Index: uint32(neigh.LinkIndex), + State: uint16(neigh.State), + Type: uint8(neigh.Type), + Flags: uint8(neigh.Flags), + } + req.AddData(&msg) + + ipData := neigh.IP.To4() + if ipData == nil { + ipData = neigh.IP.To16() + } + + dstData := nl.NewRtAttr(NDA_DST, ipData) + req.AddData(dstData) + + hwData := nl.NewRtAttr(NDA_LLADDR, []byte(neigh.HardwareAddr)) + req.AddData(hwData) + + _, err := req.Execute(syscall.NETLINK_ROUTE, 0) + return err +} + +// NeighList gets a list of IP-MAC mappings in the system (ARP table). +// Equivalent to: `ip neighbor show`. +// The list can be filtered by link and ip family. +func NeighList(linkIndex, family int) ([]Neigh, error) { + req := nl.NewNetlinkRequest(syscall.RTM_GETNEIGH, syscall.NLM_F_DUMP) + msg := Ndmsg{ + Family: uint8(family), + } + req.AddData(&msg) + + msgs, err := req.Execute(syscall.NETLINK_ROUTE, syscall.RTM_NEWNEIGH) + if err != nil { + return nil, err + } + + res := make([]Neigh, 0) + for _, m := range msgs { + ndm := deserializeNdmsg(m) + if linkIndex != 0 && int(ndm.Index) != linkIndex { + // Ignore messages from other interfaces + continue + } + + neigh, err := NeighDeserialize(m) + if err != nil { + continue + } + + res = append(res, *neigh) + } + + return res, nil +} + +func NeighDeserialize(m []byte) (*Neigh, error) { + msg := deserializeNdmsg(m) + + neigh := Neigh{ + LinkIndex: int(msg.Index), + Family: int(msg.Family), + State: int(msg.State), + Type: int(msg.Type), + Flags: int(msg.Flags), + } + + attrs, err := nl.ParseRouteAttr(m[msg.Len():]) + if err != nil { + return nil, err + } + + for _, attr := range attrs { + switch attr.Attr.Type { + case NDA_DST: + neigh.IP = net.IP(attr.Value) + case NDA_LLADDR: + neigh.HardwareAddr = net.HardwareAddr(attr.Value) + } + } + + return &neigh, nil +} diff --git a/vendor/src/github.com/vishvananda/netlink/neigh_test.go b/vendor/src/github.com/vishvananda/netlink/neigh_test.go new file mode 100644 index 0000000000..50da59c5c5 --- /dev/null +++ b/vendor/src/github.com/vishvananda/netlink/neigh_test.go @@ -0,0 +1,104 @@ +package netlink + +import ( + "net" + "testing" +) + +type arpEntry struct { + ip net.IP + mac net.HardwareAddr +} + +func parseMAC(s string) net.HardwareAddr { + m, err := net.ParseMAC(s) + if err != nil { + panic(err) + } + return m +} + +func dumpContains(dump []Neigh, e arpEntry) bool { + for _, n := range dump { + if n.IP.Equal(e.ip) && (n.State&NUD_INCOMPLETE) == 0 { + return true + } + } + return false +} + +func TestNeighAddDel(t *testing.T) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + + dummy := Dummy{LinkAttrs{Name: "neigh0"}} + if err := LinkAdd(&dummy); err != nil { + t.Fatal(err) + } + + ensureIndex(dummy.Attrs()) + + arpTable := []arpEntry{ + {net.ParseIP("10.99.0.1"), parseMAC("aa:bb:cc:dd:00:01")}, + {net.ParseIP("10.99.0.2"), parseMAC("aa:bb:cc:dd:00:02")}, + {net.ParseIP("10.99.0.3"), parseMAC("aa:bb:cc:dd:00:03")}, + {net.ParseIP("10.99.0.4"), parseMAC("aa:bb:cc:dd:00:04")}, + {net.ParseIP("10.99.0.5"), parseMAC("aa:bb:cc:dd:00:05")}, + } + + // Add the arpTable + for _, entry := range arpTable { + err := NeighAdd(&Neigh{ + LinkIndex: dummy.Index, + State: NUD_REACHABLE, + IP: entry.ip, + HardwareAddr: entry.mac, + }) + + if err != nil { + t.Errorf("Failed to NeighAdd: %v", err) + } + } + + // Dump and see that all added entries are there + dump, err := NeighList(dummy.Index, 0) + if err != nil { + t.Errorf("Failed to NeighList: %v", err) + } + + for _, entry := range arpTable { + if !dumpContains(dump, entry) { + t.Errorf("Dump does not contain: %v", entry) + } + } + + // Delete the arpTable + for _, entry := range arpTable { + err := NeighDel(&Neigh{ + LinkIndex: dummy.Index, + IP: entry.ip, + HardwareAddr: entry.mac, + }) + + if err != nil { + t.Errorf("Failed to NeighDel: %v", err) + } + } + + // TODO: seems not working because of cache + //// Dump and see that none of deleted entries are there + //dump, err = NeighList(dummy.Index, 0) + //if err != nil { + //t.Errorf("Failed to NeighList: %v", err) + //} + + //for _, entry := range arpTable { + //if dumpContains(dump, entry) { + //t.Errorf("Dump contains: %v", entry) + //} + //} + + if err := LinkDel(&dummy); err != nil { + t.Fatal(err) + } +} diff --git a/vendor/src/github.com/vishvananda/netlink/netlink.go b/vendor/src/github.com/vishvananda/netlink/netlink.go new file mode 100644 index 0000000000..41ebdb11f1 --- /dev/null +++ b/vendor/src/github.com/vishvananda/netlink/netlink.go @@ -0,0 +1,39 @@ +// Package netlink provides a simple library for netlink. Netlink is +// the interface a user-space program in linux uses to communicate with +// the kernel. It can be used to add and remove interfaces, set up ip +// addresses and routes, and confiugre ipsec. Netlink communication +// requires elevated privileges, so in most cases this code needs to +// be run as root. The low level primitives for netlink are contained +// in the nl subpackage. This package attempts to provide a high-level +// interface that is loosly modeled on the iproute2 cli. +package netlink + +import ( + "net" + + "github.com/vishvananda/netlink/nl" +) + +const ( + // Family type definitions + FAMILY_ALL = nl.FAMILY_ALL + FAMILY_V4 = nl.FAMILY_V4 + FAMILY_V6 = nl.FAMILY_V6 +) + +// ParseIPNet parses a string in ip/net format and returns a net.IPNet. +// This is valuable because addresses in netlink are often IPNets and +// ParseCIDR returns an IPNet with the IP part set to the base IP of the +// range. +func ParseIPNet(s string) (*net.IPNet, error) { + ip, ipNet, err := net.ParseCIDR(s) + if err != nil { + return nil, err + } + return &net.IPNet{IP: ip, Mask: ipNet.Mask}, nil +} + +// NewIPNet generates an IPNet from an ip address using a netmask of 32. +func NewIPNet(ip net.IP) *net.IPNet { + return &net.IPNet{IP: ip, Mask: net.CIDRMask(32, 32)} +} diff --git a/vendor/src/github.com/vishvananda/netlink/netlink_test.go b/vendor/src/github.com/vishvananda/netlink/netlink_test.go new file mode 100644 index 0000000000..3292b750a9 --- /dev/null +++ b/vendor/src/github.com/vishvananda/netlink/netlink_test.go @@ -0,0 +1,34 @@ +package netlink + +import ( + "log" + "os" + "runtime" + "testing" + + "github.com/vishvananda/netns" +) + +type tearDownNetlinkTest func() + +func setUpNetlinkTest(t *testing.T) tearDownNetlinkTest { + if os.Getuid() != 0 { + msg := "Skipped test because it requires root privileges." + log.Printf(msg) + t.Skip(msg) + } + + // new temporary namespace so we don't pollute the host + // lock thread since the namespace is thread local + runtime.LockOSThread() + var err error + ns, err := netns.New() + if err != nil { + t.Fatal("Failed to create newns", ns) + } + + return func() { + ns.Close() + runtime.UnlockOSThread() + } +} diff --git a/vendor/src/github.com/vishvananda/netlink/netlink_unspecified.go b/vendor/src/github.com/vishvananda/netlink/netlink_unspecified.go new file mode 100644 index 0000000000..10c49c1bfc --- /dev/null +++ b/vendor/src/github.com/vishvananda/netlink/netlink_unspecified.go @@ -0,0 +1,143 @@ +// +build !linux + +package netlink + +import ( + "errors" +) + +var ( + ErrNotImplemented = errors.New("not implemented") +) + +func LinkSetUp(link *Link) error { + return ErrNotImplemented +} + +func LinkSetDown(link *Link) error { + return ErrNotImplemented +} + +func LinkSetMTU(link *Link, mtu int) error { + return ErrNotImplemented +} + +func LinkSetMaster(link *Link, master *Link) error { + return ErrNotImplemented +} + +func LinkSetNsPid(link *Link, nspid int) error { + return ErrNotImplemented +} + +func LinkSetNsFd(link *Link, fd int) error { + return ErrNotImplemented +} + +func LinkAdd(link *Link) error { + return ErrNotImplemented +} + +func LinkDel(link *Link) error { + return ErrNotImplemented +} + +func SetHairpin(link Link, mode bool) error { + return ErrNotImplemented +} + +func SetGuard(link Link, mode bool) error { + return ErrNotImplemented +} + +func SetFastLeave(link Link, mode bool) error { + return ErrNotImplemented +} + +func SetLearning(link Link, mode bool) error { + return ErrNotImplemented +} + +func SetRootBlock(link Link, mode bool) error { + return ErrNotImplemented +} + +func SetFlood(link Link, mode bool) error { + return ErrNotImplemented +} + +func LinkList() ([]Link, error) { + return nil, ErrNotImplemented +} + +func AddrAdd(link *Link, addr *Addr) error { + return ErrNotImplemented +} + +func AddrDel(link *Link, addr *Addr) error { + return ErrNotImplemented +} + +func AddrList(link *Link, family int) ([]Addr, error) { + return nil, ErrNotImplemented +} + +func RouteAdd(route *Route) error { + return ErrNotImplemented +} + +func RouteDel(route *Route) error { + return ErrNotImplemented +} + +func RouteList(link *Link, family int) ([]Route, error) { + return nil, ErrNotImplemented +} + +func XfrmPolicyAdd(policy *XfrmPolicy) error { + return ErrNotImplemented +} + +func XfrmPolicyDel(policy *XfrmPolicy) error { + return ErrNotImplemented +} + +func XfrmPolicyList(family int) ([]XfrmPolicy, error) { + return nil, ErrNotImplemented +} + +func XfrmStateAdd(policy *XfrmState) error { + return ErrNotImplemented +} + +func XfrmStateDel(policy *XfrmState) error { + return ErrNotImplemented +} + +func XfrmStateList(family int) ([]XfrmState, error) { + return nil, ErrNotImplemented +} + +func NeighAdd(neigh *Neigh) error { + return ErrNotImplemented +} + +func NeighSet(neigh *Neigh) error { + return ErrNotImplemented +} + +func NeighAppend(neigh *Neigh) error { + return ErrNotImplemented +} + +func NeighDel(neigh *Neigh) error { + return ErrNotImplemented +} + +func NeighList(linkIndex, family int) ([]Neigh, error) { + return nil, ErrNotImplemented +} + +func NeighDeserialize(m []byte) (*Ndmsg, *Neigh, error) { + return nil, nil, ErrNotImplemented +} diff --git a/vendor/src/github.com/vishvananda/netlink/nl/addr_linux.go b/vendor/src/github.com/vishvananda/netlink/nl/addr_linux.go new file mode 100644 index 0000000000..17088fa0c0 --- /dev/null +++ b/vendor/src/github.com/vishvananda/netlink/nl/addr_linux.go @@ -0,0 +1,47 @@ +package nl + +import ( + "syscall" + "unsafe" +) + +type IfAddrmsg struct { + syscall.IfAddrmsg +} + +func NewIfAddrmsg(family int) *IfAddrmsg { + return &IfAddrmsg{ + IfAddrmsg: syscall.IfAddrmsg{ + Family: uint8(family), + }, + } +} + +// struct ifaddrmsg { +// __u8 ifa_family; +// __u8 ifa_prefixlen; /* The prefix length */ +// __u8 ifa_flags; /* Flags */ +// __u8 ifa_scope; /* Address scope */ +// __u32 ifa_index; /* Link index */ +// }; + +// type IfAddrmsg struct { +// Family uint8 +// Prefixlen uint8 +// Flags uint8 +// Scope uint8 +// Index uint32 +// } +// SizeofIfAddrmsg = 0x8 + +func DeserializeIfAddrmsg(b []byte) *IfAddrmsg { + return (*IfAddrmsg)(unsafe.Pointer(&b[0:syscall.SizeofIfAddrmsg][0])) +} + +func (msg *IfAddrmsg) Serialize() []byte { + return (*(*[syscall.SizeofIfAddrmsg]byte)(unsafe.Pointer(msg)))[:] +} + +func (msg *IfAddrmsg) Len() int { + return syscall.SizeofIfAddrmsg +} diff --git a/vendor/src/github.com/vishvananda/netlink/nl/addr_linux_test.go b/vendor/src/github.com/vishvananda/netlink/nl/addr_linux_test.go new file mode 100644 index 0000000000..98c3b211f3 --- /dev/null +++ b/vendor/src/github.com/vishvananda/netlink/nl/addr_linux_test.go @@ -0,0 +1,39 @@ +package nl + +import ( + "bytes" + "crypto/rand" + "encoding/binary" + "syscall" + "testing" +) + +func (msg *IfAddrmsg) write(b []byte) { + native := NativeEndian() + b[0] = msg.Family + b[1] = msg.Prefixlen + b[2] = msg.Flags + b[3] = msg.Scope + native.PutUint32(b[4:8], msg.Index) +} + +func (msg *IfAddrmsg) serializeSafe() []byte { + len := syscall.SizeofIfAddrmsg + b := make([]byte, len) + msg.write(b) + return b +} + +func deserializeIfAddrmsgSafe(b []byte) *IfAddrmsg { + var msg = IfAddrmsg{} + binary.Read(bytes.NewReader(b[0:syscall.SizeofIfAddrmsg]), NativeEndian(), &msg) + return &msg +} + +func TestIfAddrmsgDeserializeSerialize(t *testing.T) { + var orig = make([]byte, syscall.SizeofIfAddrmsg) + rand.Read(orig) + safemsg := deserializeIfAddrmsgSafe(orig) + msg := DeserializeIfAddrmsg(orig) + testDeserializeSerialize(t, orig, safemsg, msg) +} diff --git a/vendor/src/github.com/vishvananda/netlink/nl/link_linux.go b/vendor/src/github.com/vishvananda/netlink/nl/link_linux.go new file mode 100644 index 0000000000..ab0dede65d --- /dev/null +++ b/vendor/src/github.com/vishvananda/netlink/nl/link_linux.go @@ -0,0 +1,81 @@ +package nl + +const ( + DEFAULT_CHANGE = 0xFFFFFFFF +) + +const ( + IFLA_INFO_UNSPEC = iota + IFLA_INFO_KIND + IFLA_INFO_DATA + IFLA_INFO_XSTATS + IFLA_INFO_MAX = IFLA_INFO_XSTATS +) + +const ( + IFLA_VLAN_UNSPEC = iota + IFLA_VLAN_ID + IFLA_VLAN_FLAGS + IFLA_VLAN_EGRESS_QOS + IFLA_VLAN_INGRESS_QOS + IFLA_VLAN_PROTOCOL + IFLA_VLAN_MAX = IFLA_VLAN_PROTOCOL +) + +const ( + VETH_INFO_UNSPEC = iota + VETH_INFO_PEER + VETH_INFO_MAX = VETH_INFO_PEER +) + +const ( + IFLA_VXLAN_UNSPEC = iota + IFLA_VXLAN_ID + IFLA_VXLAN_GROUP + IFLA_VXLAN_LINK + IFLA_VXLAN_LOCAL + IFLA_VXLAN_TTL + IFLA_VXLAN_TOS + IFLA_VXLAN_LEARNING + IFLA_VXLAN_AGEING + IFLA_VXLAN_LIMIT + IFLA_VXLAN_PORT_RANGE + IFLA_VXLAN_PROXY + IFLA_VXLAN_RSC + IFLA_VXLAN_L2MISS + IFLA_VXLAN_L3MISS + IFLA_VXLAN_PORT + IFLA_VXLAN_GROUP6 + IFLA_VXLAN_LOCAL6 + IFLA_VXLAN_MAX = IFLA_VXLAN_LOCAL6 +) + +const ( + BRIDGE_MODE_UNSPEC = iota + BRIDGE_MODE_HAIRPIN +) + +const ( + IFLA_BRPORT_UNSPEC = iota + IFLA_BRPORT_STATE + IFLA_BRPORT_PRIORITY + IFLA_BRPORT_COST + IFLA_BRPORT_MODE + IFLA_BRPORT_GUARD + IFLA_BRPORT_PROTECT + IFLA_BRPORT_FAST_LEAVE + IFLA_BRPORT_LEARNING + IFLA_BRPORT_UNICAST_FLOOD + IFLA_BRPORT_MAX = IFLA_BRPORT_UNICAST_FLOOD +) + +const ( + IFLA_IPVLAN_UNSPEC = iota + IFLA_IPVLAN_MODE + IFLA_IPVLAN_MAX = IFLA_IPVLAN_MODE +) + +const ( + // not defined in syscall + IFLA_NET_NS_FD = 28 +) diff --git a/vendor/src/github.com/vishvananda/netlink/nl/nl_linux.go b/vendor/src/github.com/vishvananda/netlink/nl/nl_linux.go new file mode 100644 index 0000000000..72f2813773 --- /dev/null +++ b/vendor/src/github.com/vishvananda/netlink/nl/nl_linux.go @@ -0,0 +1,417 @@ +// Package nl has low level primitives for making Netlink calls. +package nl + +import ( + "bytes" + "encoding/binary" + "fmt" + "net" + "sync/atomic" + "syscall" + "unsafe" +) + +const ( + // Family type definitions + FAMILY_ALL = syscall.AF_UNSPEC + FAMILY_V4 = syscall.AF_INET + FAMILY_V6 = syscall.AF_INET6 +) + +var nextSeqNr uint32 + +// GetIPFamily returns the family type of a net.IP. +func GetIPFamily(ip net.IP) int { + if len(ip) <= net.IPv4len { + return FAMILY_V4 + } + if ip.To4() != nil { + return FAMILY_V4 + } + return FAMILY_V6 +} + +var nativeEndian binary.ByteOrder + +// Get native endianness for the system +func NativeEndian() binary.ByteOrder { + if nativeEndian == nil { + var x uint32 = 0x01020304 + if *(*byte)(unsafe.Pointer(&x)) == 0x01 { + nativeEndian = binary.BigEndian + } + nativeEndian = binary.LittleEndian + } + return nativeEndian +} + +// Byte swap a 16 bit value if we aren't big endian +func Swap16(i uint16) uint16 { + if NativeEndian() == binary.BigEndian { + return i + } + return (i&0xff00)>>8 | (i&0xff)<<8 +} + +// Byte swap a 32 bit value if aren't big endian +func Swap32(i uint32) uint32 { + if NativeEndian() == binary.BigEndian { + return i + } + return (i&0xff000000)>>24 | (i&0xff0000)>>8 | (i&0xff00)<<8 | (i&0xff)<<24 +} + +type NetlinkRequestData interface { + Len() int + Serialize() []byte +} + +// IfInfomsg is related to links, but it is used for list requests as well +type IfInfomsg struct { + syscall.IfInfomsg +} + +// Create an IfInfomsg with family specified +func NewIfInfomsg(family int) *IfInfomsg { + return &IfInfomsg{ + IfInfomsg: syscall.IfInfomsg{ + Family: uint8(family), + }, + } +} + +func DeserializeIfInfomsg(b []byte) *IfInfomsg { + return (*IfInfomsg)(unsafe.Pointer(&b[0:syscall.SizeofIfInfomsg][0])) +} + +func (msg *IfInfomsg) Serialize() []byte { + return (*(*[syscall.SizeofIfInfomsg]byte)(unsafe.Pointer(msg)))[:] +} + +func (msg *IfInfomsg) Len() int { + return syscall.SizeofIfInfomsg +} + +func rtaAlignOf(attrlen int) int { + return (attrlen + syscall.RTA_ALIGNTO - 1) & ^(syscall.RTA_ALIGNTO - 1) +} + +func NewIfInfomsgChild(parent *RtAttr, family int) *IfInfomsg { + msg := NewIfInfomsg(family) + parent.children = append(parent.children, msg) + return msg +} + +// Extend RtAttr to handle data and children +type RtAttr struct { + syscall.RtAttr + Data []byte + children []NetlinkRequestData +} + +// Create a new Extended RtAttr object +func NewRtAttr(attrType int, data []byte) *RtAttr { + return &RtAttr{ + RtAttr: syscall.RtAttr{ + Type: uint16(attrType), + }, + children: []NetlinkRequestData{}, + Data: data, + } +} + +// Create a new RtAttr obj anc add it as a child of an existing object +func NewRtAttrChild(parent *RtAttr, attrType int, data []byte) *RtAttr { + attr := NewRtAttr(attrType, data) + parent.children = append(parent.children, attr) + return attr +} + +func (a *RtAttr) Len() int { + if len(a.children) == 0 { + return (syscall.SizeofRtAttr + len(a.Data)) + } + + l := 0 + for _, child := range a.children { + l += rtaAlignOf(child.Len()) + } + l += syscall.SizeofRtAttr + return rtaAlignOf(l + len(a.Data)) +} + +// Serialize the RtAttr into a byte array +// This can't ust unsafe.cast because it must iterate through children. +func (a *RtAttr) Serialize() []byte { + native := NativeEndian() + + length := a.Len() + buf := make([]byte, rtaAlignOf(length)) + + if a.Data != nil { + copy(buf[4:], a.Data) + } else { + next := 4 + for _, child := range a.children { + childBuf := child.Serialize() + copy(buf[next:], childBuf) + next += rtaAlignOf(len(childBuf)) + } + } + + if l := uint16(length); l != 0 { + native.PutUint16(buf[0:2], l) + } + native.PutUint16(buf[2:4], a.Type) + return buf +} + +type NetlinkRequest struct { + syscall.NlMsghdr + Data []NetlinkRequestData +} + +// Serialize the Netlink Request into a byte array +func (msg *NetlinkRequest) Serialize() []byte { + length := syscall.SizeofNlMsghdr + dataBytes := make([][]byte, len(msg.Data)) + for i, data := range msg.Data { + dataBytes[i] = data.Serialize() + length = length + len(dataBytes[i]) + } + msg.Len = uint32(length) + b := make([]byte, length) + hdr := (*(*[syscall.SizeofNlMsghdr]byte)(unsafe.Pointer(msg)))[:] + next := syscall.SizeofNlMsghdr + copy(b[0:next], hdr) + for _, data := range dataBytes { + for _, dataByte := range data { + b[next] = dataByte + next = next + 1 + } + } + return b +} + +func (msg *NetlinkRequest) AddData(data NetlinkRequestData) { + if data != nil { + msg.Data = append(msg.Data, data) + } +} + +// Execute the request against a the given sockType. +// Returns a list of netlink messages in seriaized format, optionally filtered +// by resType. +func (req *NetlinkRequest) Execute(sockType int, resType uint16) ([][]byte, error) { + s, err := getNetlinkSocket(sockType) + if err != nil { + return nil, err + } + defer s.Close() + + if err := s.Send(req); err != nil { + return nil, err + } + + pid, err := s.GetPid() + if err != nil { + return nil, err + } + + res := make([][]byte, 0) + +done: + for { + msgs, err := s.Recieve() + if err != nil { + return nil, err + } + for _, m := range msgs { + if m.Header.Seq != req.Seq { + return nil, fmt.Errorf("Wrong Seq nr %d, expected 1", m.Header.Seq) + } + if m.Header.Pid != pid { + return nil, fmt.Errorf("Wrong pid %d, expected %d", m.Header.Pid, pid) + } + if m.Header.Type == syscall.NLMSG_DONE { + break done + } + if m.Header.Type == syscall.NLMSG_ERROR { + native := NativeEndian() + error := int32(native.Uint32(m.Data[0:4])) + if error == 0 { + break done + } + return nil, syscall.Errno(-error) + } + if resType != 0 && m.Header.Type != resType { + continue + } + res = append(res, m.Data) + if m.Header.Flags&syscall.NLM_F_MULTI == 0 { + break done + } + } + } + return res, nil +} + +// Create a new netlink request from proto and flags +// Note the Len value will be inaccurate once data is added until +// the message is serialized +func NewNetlinkRequest(proto, flags int) *NetlinkRequest { + return &NetlinkRequest{ + NlMsghdr: syscall.NlMsghdr{ + Len: uint32(syscall.SizeofNlMsghdr), + Type: uint16(proto), + Flags: syscall.NLM_F_REQUEST | uint16(flags), + Seq: atomic.AddUint32(&nextSeqNr, 1), + }, + } +} + +type NetlinkSocket struct { + fd int + lsa syscall.SockaddrNetlink +} + +func getNetlinkSocket(protocol int) (*NetlinkSocket, error) { + fd, err := syscall.Socket(syscall.AF_NETLINK, syscall.SOCK_RAW, protocol) + if err != nil { + return nil, err + } + s := &NetlinkSocket{ + fd: fd, + } + s.lsa.Family = syscall.AF_NETLINK + if err := syscall.Bind(fd, &s.lsa); err != nil { + syscall.Close(fd) + return nil, err + } + + return s, nil +} + +// Create a netlink socket with a given protocol (e.g. NETLINK_ROUTE) +// and subscribe it to multicast groups passed in variable argument list. +// Returns the netlink socket on whic hReceive() method can be called +// to retrieve the messages from the kernel. +func Subscribe(protocol int, groups ...uint) (*NetlinkSocket, error) { + fd, err := syscall.Socket(syscall.AF_NETLINK, syscall.SOCK_RAW, protocol) + if err != nil { + return nil, err + } + s := &NetlinkSocket{ + fd: fd, + } + s.lsa.Family = syscall.AF_NETLINK + + for _, g := range groups { + s.lsa.Groups |= (1 << (g - 1)) + } + + if err := syscall.Bind(fd, &s.lsa); err != nil { + syscall.Close(fd) + return nil, err + } + + return s, nil +} + +func (s *NetlinkSocket) Close() { + syscall.Close(s.fd) +} + +func (s *NetlinkSocket) Send(request *NetlinkRequest) error { + if err := syscall.Sendto(s.fd, request.Serialize(), 0, &s.lsa); err != nil { + return err + } + return nil +} + +func (s *NetlinkSocket) Recieve() ([]syscall.NetlinkMessage, error) { + rb := make([]byte, syscall.Getpagesize()) + nr, _, err := syscall.Recvfrom(s.fd, rb, 0) + if err != nil { + return nil, err + } + if nr < syscall.NLMSG_HDRLEN { + return nil, fmt.Errorf("Got short response from netlink") + } + rb = rb[:nr] + return syscall.ParseNetlinkMessage(rb) +} + +func (s *NetlinkSocket) GetPid() (uint32, error) { + lsa, err := syscall.Getsockname(s.fd) + if err != nil { + return 0, err + } + switch v := lsa.(type) { + case *syscall.SockaddrNetlink: + return v.Pid, nil + } + return 0, fmt.Errorf("Wrong socket type") +} + +func ZeroTerminated(s string) []byte { + bytes := make([]byte, len(s)+1) + for i := 0; i < len(s); i++ { + bytes[i] = s[i] + } + bytes[len(s)] = 0 + return bytes +} + +func NonZeroTerminated(s string) []byte { + bytes := make([]byte, len(s)) + for i := 0; i < len(s); i++ { + bytes[i] = s[i] + } + return bytes +} + +func BytesToString(b []byte) string { + n := bytes.Index(b, []byte{0}) + return string(b[:n]) +} + +func Uint8Attr(v uint8) []byte { + return []byte{byte(v)} +} + +func Uint16Attr(v uint16) []byte { + native := NativeEndian() + bytes := make([]byte, 2) + native.PutUint16(bytes, v) + return bytes +} + +func Uint32Attr(v uint32) []byte { + native := NativeEndian() + bytes := make([]byte, 4) + native.PutUint32(bytes, v) + return bytes +} + +func ParseRouteAttr(b []byte) ([]syscall.NetlinkRouteAttr, error) { + var attrs []syscall.NetlinkRouteAttr + for len(b) >= syscall.SizeofRtAttr { + a, vbuf, alen, err := netlinkRouteAttrAndValue(b) + if err != nil { + return nil, err + } + ra := syscall.NetlinkRouteAttr{Attr: *a, Value: vbuf[:int(a.Len)-syscall.SizeofRtAttr]} + attrs = append(attrs, ra) + b = b[alen:] + } + return attrs, nil +} + +func netlinkRouteAttrAndValue(b []byte) (*syscall.RtAttr, []byte, int, error) { + a := (*syscall.RtAttr)(unsafe.Pointer(&b[0])) + if int(a.Len) < syscall.SizeofRtAttr || int(a.Len) > len(b) { + return nil, nil, 0, syscall.EINVAL + } + return a, b[syscall.SizeofRtAttr:], rtaAlignOf(int(a.Len)), nil +} diff --git a/vendor/src/github.com/vishvananda/netlink/nl/nl_linux_test.go b/vendor/src/github.com/vishvananda/netlink/nl/nl_linux_test.go new file mode 100644 index 0000000000..4672684c7a --- /dev/null +++ b/vendor/src/github.com/vishvananda/netlink/nl/nl_linux_test.go @@ -0,0 +1,60 @@ +package nl + +import ( + "bytes" + "crypto/rand" + "encoding/binary" + "reflect" + "syscall" + "testing" +) + +type testSerializer interface { + serializeSafe() []byte + Serialize() []byte +} + +func testDeserializeSerialize(t *testing.T, orig []byte, safemsg testSerializer, msg testSerializer) { + if !reflect.DeepEqual(safemsg, msg) { + t.Fatal("Deserialization failed.\n", safemsg, "\n", msg) + } + safe := msg.serializeSafe() + if !bytes.Equal(safe, orig) { + t.Fatal("Safe serialization failed.\n", safe, "\n", orig) + } + b := msg.Serialize() + if !bytes.Equal(b, safe) { + t.Fatal("Serialization failed.\n", b, "\n", safe) + } +} + +func (msg *IfInfomsg) write(b []byte) { + native := NativeEndian() + b[0] = msg.Family + b[1] = msg.X__ifi_pad + native.PutUint16(b[2:4], msg.Type) + native.PutUint32(b[4:8], uint32(msg.Index)) + native.PutUint32(b[8:12], msg.Flags) + native.PutUint32(b[12:16], msg.Change) +} + +func (msg *IfInfomsg) serializeSafe() []byte { + length := syscall.SizeofIfInfomsg + b := make([]byte, length) + msg.write(b) + return b +} + +func deserializeIfInfomsgSafe(b []byte) *IfInfomsg { + var msg = IfInfomsg{} + binary.Read(bytes.NewReader(b[0:syscall.SizeofIfInfomsg]), NativeEndian(), &msg) + return &msg +} + +func TestIfInfomsgDeserializeSerialize(t *testing.T) { + var orig = make([]byte, syscall.SizeofIfInfomsg) + rand.Read(orig) + safemsg := deserializeIfInfomsgSafe(orig) + msg := DeserializeIfInfomsg(orig) + testDeserializeSerialize(t, orig, safemsg, msg) +} diff --git a/vendor/src/github.com/vishvananda/netlink/nl/route_linux.go b/vendor/src/github.com/vishvananda/netlink/nl/route_linux.go new file mode 100644 index 0000000000..5dde998e96 --- /dev/null +++ b/vendor/src/github.com/vishvananda/netlink/nl/route_linux.go @@ -0,0 +1,33 @@ +package nl + +import ( + "syscall" + "unsafe" +) + +type RtMsg struct { + syscall.RtMsg +} + +func NewRtMsg() *RtMsg { + return &RtMsg{ + RtMsg: syscall.RtMsg{ + Table: syscall.RT_TABLE_MAIN, + Scope: syscall.RT_SCOPE_UNIVERSE, + Protocol: syscall.RTPROT_BOOT, + Type: syscall.RTN_UNICAST, + }, + } +} + +func (msg *RtMsg) Len() int { + return syscall.SizeofRtMsg +} + +func DeserializeRtMsg(b []byte) *RtMsg { + return (*RtMsg)(unsafe.Pointer(&b[0:syscall.SizeofRtMsg][0])) +} + +func (msg *RtMsg) Serialize() []byte { + return (*(*[syscall.SizeofRtMsg]byte)(unsafe.Pointer(msg)))[:] +} diff --git a/vendor/src/github.com/vishvananda/netlink/nl/route_linux_test.go b/vendor/src/github.com/vishvananda/netlink/nl/route_linux_test.go new file mode 100644 index 0000000000..ba9c410ee1 --- /dev/null +++ b/vendor/src/github.com/vishvananda/netlink/nl/route_linux_test.go @@ -0,0 +1,43 @@ +package nl + +import ( + "bytes" + "crypto/rand" + "encoding/binary" + "syscall" + "testing" +) + +func (msg *RtMsg) write(b []byte) { + native := NativeEndian() + b[0] = msg.Family + b[1] = msg.Dst_len + b[2] = msg.Src_len + b[3] = msg.Tos + b[4] = msg.Table + b[5] = msg.Protocol + b[6] = msg.Scope + b[7] = msg.Type + native.PutUint32(b[8:12], msg.Flags) +} + +func (msg *RtMsg) serializeSafe() []byte { + len := syscall.SizeofRtMsg + b := make([]byte, len) + msg.write(b) + return b +} + +func deserializeRtMsgSafe(b []byte) *RtMsg { + var msg = RtMsg{} + binary.Read(bytes.NewReader(b[0:syscall.SizeofRtMsg]), NativeEndian(), &msg) + return &msg +} + +func TestRtMsgDeserializeSerialize(t *testing.T) { + var orig = make([]byte, syscall.SizeofRtMsg) + rand.Read(orig) + safemsg := deserializeRtMsgSafe(orig) + msg := DeserializeRtMsg(orig) + testDeserializeSerialize(t, orig, safemsg, msg) +} diff --git a/vendor/src/github.com/vishvananda/netlink/nl/xfrm_linux.go b/vendor/src/github.com/vishvananda/netlink/nl/xfrm_linux.go new file mode 100644 index 0000000000..d953130870 --- /dev/null +++ b/vendor/src/github.com/vishvananda/netlink/nl/xfrm_linux.go @@ -0,0 +1,259 @@ +package nl + +import ( + "bytes" + "net" + "unsafe" +) + +// Infinity for packet and byte counts +const ( + XFRM_INF = ^uint64(0) +) + +// Message Types +const ( + XFRM_MSG_BASE = 0x10 + XFRM_MSG_NEWSA = 0x10 + XFRM_MSG_DELSA = 0x11 + XFRM_MSG_GETSA = 0x12 + XFRM_MSG_NEWPOLICY = 0x13 + XFRM_MSG_DELPOLICY = 0x14 + XFRM_MSG_GETPOLICY = 0x15 + XFRM_MSG_ALLOCSPI = 0x16 + XFRM_MSG_ACQUIRE = 0x17 + XFRM_MSG_EXPIRE = 0x18 + XFRM_MSG_UPDPOLICY = 0x19 + XFRM_MSG_UPDSA = 0x1a + XFRM_MSG_POLEXPIRE = 0x1b + XFRM_MSG_FLUSHSA = 0x1c + XFRM_MSG_FLUSHPOLICY = 0x1d + XFRM_MSG_NEWAE = 0x1e + XFRM_MSG_GETAE = 0x1f + XFRM_MSG_REPORT = 0x20 + XFRM_MSG_MIGRATE = 0x21 + XFRM_MSG_NEWSADINFO = 0x22 + XFRM_MSG_GETSADINFO = 0x23 + XFRM_MSG_NEWSPDINFO = 0x24 + XFRM_MSG_GETSPDINFO = 0x25 + XFRM_MSG_MAPPING = 0x26 + XFRM_MSG_MAX = 0x26 + XFRM_NR_MSGTYPES = 0x17 +) + +// Attribute types +const ( + /* Netlink message attributes. */ + XFRMA_UNSPEC = 0x00 + XFRMA_ALG_AUTH = 0x01 /* struct xfrm_algo */ + XFRMA_ALG_CRYPT = 0x02 /* struct xfrm_algo */ + XFRMA_ALG_COMP = 0x03 /* struct xfrm_algo */ + XFRMA_ENCAP = 0x04 /* struct xfrm_algo + struct xfrm_encap_tmpl */ + XFRMA_TMPL = 0x05 /* 1 or more struct xfrm_user_tmpl */ + XFRMA_SA = 0x06 /* struct xfrm_usersa_info */ + XFRMA_POLICY = 0x07 /* struct xfrm_userpolicy_info */ + XFRMA_SEC_CTX = 0x08 /* struct xfrm_sec_ctx */ + XFRMA_LTIME_VAL = 0x09 + XFRMA_REPLAY_VAL = 0x0a + XFRMA_REPLAY_THRESH = 0x0b + XFRMA_ETIMER_THRESH = 0x0c + XFRMA_SRCADDR = 0x0d /* xfrm_address_t */ + XFRMA_COADDR = 0x0e /* xfrm_address_t */ + XFRMA_LASTUSED = 0x0f /* unsigned long */ + XFRMA_POLICY_TYPE = 0x10 /* struct xfrm_userpolicy_type */ + XFRMA_MIGRATE = 0x11 + XFRMA_ALG_AEAD = 0x12 /* struct xfrm_algo_aead */ + XFRMA_KMADDRESS = 0x13 /* struct xfrm_user_kmaddress */ + XFRMA_ALG_AUTH_TRUNC = 0x14 /* struct xfrm_algo_auth */ + XFRMA_MARK = 0x15 /* struct xfrm_mark */ + XFRMA_TFCPAD = 0x16 /* __u32 */ + XFRMA_REPLAY_ESN_VAL = 0x17 /* struct xfrm_replay_esn */ + XFRMA_SA_EXTRA_FLAGS = 0x18 /* __u32 */ + XFRMA_MAX = 0x18 +) + +const ( + SizeofXfrmAddress = 0x10 + SizeofXfrmSelector = 0x38 + SizeofXfrmLifetimeCfg = 0x40 + SizeofXfrmLifetimeCur = 0x20 + SizeofXfrmId = 0x18 +) + +// typedef union { +// __be32 a4; +// __be32 a6[4]; +// } xfrm_address_t; + +type XfrmAddress [SizeofXfrmAddress]byte + +func (x *XfrmAddress) ToIP() net.IP { + var empty = [12]byte{} + ip := make(net.IP, net.IPv6len) + if bytes.Equal(x[4:16], empty[:]) { + ip[10] = 0xff + ip[11] = 0xff + copy(ip[12:16], x[0:4]) + } else { + copy(ip[:], x[:]) + } + return ip +} + +func (x *XfrmAddress) ToIPNet(prefixlen uint8) *net.IPNet { + ip := x.ToIP() + if GetIPFamily(ip) == FAMILY_V4 { + return &net.IPNet{IP: ip, Mask: net.CIDRMask(int(prefixlen), 32)} + } else { + return &net.IPNet{IP: ip, Mask: net.CIDRMask(int(prefixlen), 128)} + } +} + +func (x *XfrmAddress) FromIP(ip net.IP) { + var empty = [16]byte{} + if len(ip) < net.IPv4len { + copy(x[4:16], empty[:]) + } else if GetIPFamily(ip) == FAMILY_V4 { + copy(x[0:4], ip.To4()[0:4]) + copy(x[4:16], empty[:12]) + } else { + copy(x[0:16], ip.To16()[0:16]) + } +} + +func DeserializeXfrmAddress(b []byte) *XfrmAddress { + return (*XfrmAddress)(unsafe.Pointer(&b[0:SizeofXfrmAddress][0])) +} + +func (msg *XfrmAddress) Serialize() []byte { + return (*(*[SizeofXfrmAddress]byte)(unsafe.Pointer(msg)))[:] +} + +// struct xfrm_selector { +// xfrm_address_t daddr; +// xfrm_address_t saddr; +// __be16 dport; +// __be16 dport_mask; +// __be16 sport; +// __be16 sport_mask; +// __u16 family; +// __u8 prefixlen_d; +// __u8 prefixlen_s; +// __u8 proto; +// int ifindex; +// __kernel_uid32_t user; +// }; + +type XfrmSelector struct { + Daddr XfrmAddress + Saddr XfrmAddress + Dport uint16 // big endian + DportMask uint16 // big endian + Sport uint16 // big endian + SportMask uint16 // big endian + Family uint16 + PrefixlenD uint8 + PrefixlenS uint8 + Proto uint8 + Pad [3]byte + Ifindex int32 + User uint32 +} + +func (msg *XfrmSelector) Len() int { + return SizeofXfrmSelector +} + +func DeserializeXfrmSelector(b []byte) *XfrmSelector { + return (*XfrmSelector)(unsafe.Pointer(&b[0:SizeofXfrmSelector][0])) +} + +func (msg *XfrmSelector) Serialize() []byte { + return (*(*[SizeofXfrmSelector]byte)(unsafe.Pointer(msg)))[:] +} + +// struct xfrm_lifetime_cfg { +// __u64 soft_byte_limit; +// __u64 hard_byte_limit; +// __u64 soft_packet_limit; +// __u64 hard_packet_limit; +// __u64 soft_add_expires_seconds; +// __u64 hard_add_expires_seconds; +// __u64 soft_use_expires_seconds; +// __u64 hard_use_expires_seconds; +// }; +// + +type XfrmLifetimeCfg struct { + SoftByteLimit uint64 + HardByteLimit uint64 + SoftPacketLimit uint64 + HardPacketLimit uint64 + SoftAddExpiresSeconds uint64 + HardAddExpiresSeconds uint64 + SoftUseExpiresSeconds uint64 + HardUseExpiresSeconds uint64 +} + +func (msg *XfrmLifetimeCfg) Len() int { + return SizeofXfrmLifetimeCfg +} + +func DeserializeXfrmLifetimeCfg(b []byte) *XfrmLifetimeCfg { + return (*XfrmLifetimeCfg)(unsafe.Pointer(&b[0:SizeofXfrmLifetimeCfg][0])) +} + +func (msg *XfrmLifetimeCfg) Serialize() []byte { + return (*(*[SizeofXfrmLifetimeCfg]byte)(unsafe.Pointer(msg)))[:] +} + +// struct xfrm_lifetime_cur { +// __u64 bytes; +// __u64 packets; +// __u64 add_time; +// __u64 use_time; +// }; + +type XfrmLifetimeCur struct { + Bytes uint64 + Packets uint64 + AddTime uint64 + UseTime uint64 +} + +func (msg *XfrmLifetimeCur) Len() int { + return SizeofXfrmLifetimeCur +} + +func DeserializeXfrmLifetimeCur(b []byte) *XfrmLifetimeCur { + return (*XfrmLifetimeCur)(unsafe.Pointer(&b[0:SizeofXfrmLifetimeCur][0])) +} + +func (msg *XfrmLifetimeCur) Serialize() []byte { + return (*(*[SizeofXfrmLifetimeCur]byte)(unsafe.Pointer(msg)))[:] +} + +// struct xfrm_id { +// xfrm_address_t daddr; +// __be32 spi; +// __u8 proto; +// }; + +type XfrmId struct { + Daddr XfrmAddress + Spi uint32 // big endian + Proto uint8 + Pad [3]byte +} + +func (msg *XfrmId) Len() int { + return SizeofXfrmId +} + +func DeserializeXfrmId(b []byte) *XfrmId { + return (*XfrmId)(unsafe.Pointer(&b[0:SizeofXfrmId][0])) +} + +func (msg *XfrmId) Serialize() []byte { + return (*(*[SizeofXfrmId]byte)(unsafe.Pointer(msg)))[:] +} diff --git a/vendor/src/github.com/vishvananda/netlink/nl/xfrm_linux_test.go b/vendor/src/github.com/vishvananda/netlink/nl/xfrm_linux_test.go new file mode 100644 index 0000000000..04404d7511 --- /dev/null +++ b/vendor/src/github.com/vishvananda/netlink/nl/xfrm_linux_test.go @@ -0,0 +1,161 @@ +package nl + +import ( + "bytes" + "crypto/rand" + "encoding/binary" + "testing" +) + +func (msg *XfrmAddress) write(b []byte) { + copy(b[0:SizeofXfrmAddress], msg[:]) +} + +func (msg *XfrmAddress) serializeSafe() []byte { + b := make([]byte, SizeofXfrmAddress) + msg.write(b) + return b +} + +func deserializeXfrmAddressSafe(b []byte) *XfrmAddress { + var msg = XfrmAddress{} + binary.Read(bytes.NewReader(b[0:SizeofXfrmAddress]), NativeEndian(), &msg) + return &msg +} + +func TestXfrmAddressDeserializeSerialize(t *testing.T) { + var orig = make([]byte, SizeofXfrmAddress) + rand.Read(orig) + safemsg := deserializeXfrmAddressSafe(orig) + msg := DeserializeXfrmAddress(orig) + testDeserializeSerialize(t, orig, safemsg, msg) +} + +func (msg *XfrmSelector) write(b []byte) { + const AddrEnd = SizeofXfrmAddress * 2 + native := NativeEndian() + msg.Daddr.write(b[0:SizeofXfrmAddress]) + msg.Saddr.write(b[SizeofXfrmAddress:AddrEnd]) + native.PutUint16(b[AddrEnd:AddrEnd+2], msg.Dport) + native.PutUint16(b[AddrEnd+2:AddrEnd+4], msg.DportMask) + native.PutUint16(b[AddrEnd+4:AddrEnd+6], msg.Sport) + native.PutUint16(b[AddrEnd+6:AddrEnd+8], msg.SportMask) + native.PutUint16(b[AddrEnd+8:AddrEnd+10], msg.Family) + b[AddrEnd+10] = msg.PrefixlenD + b[AddrEnd+11] = msg.PrefixlenS + b[AddrEnd+12] = msg.Proto + copy(b[AddrEnd+13:AddrEnd+16], msg.Pad[:]) + native.PutUint32(b[AddrEnd+16:AddrEnd+20], uint32(msg.Ifindex)) + native.PutUint32(b[AddrEnd+20:AddrEnd+24], msg.User) +} + +func (msg *XfrmSelector) serializeSafe() []byte { + length := SizeofXfrmSelector + b := make([]byte, length) + msg.write(b) + return b +} + +func deserializeXfrmSelectorSafe(b []byte) *XfrmSelector { + var msg = XfrmSelector{} + binary.Read(bytes.NewReader(b[0:SizeofXfrmSelector]), NativeEndian(), &msg) + return &msg +} + +func TestXfrmSelectorDeserializeSerialize(t *testing.T) { + var orig = make([]byte, SizeofXfrmSelector) + rand.Read(orig) + safemsg := deserializeXfrmSelectorSafe(orig) + msg := DeserializeXfrmSelector(orig) + testDeserializeSerialize(t, orig, safemsg, msg) +} + +func (msg *XfrmLifetimeCfg) write(b []byte) { + native := NativeEndian() + native.PutUint64(b[0:8], msg.SoftByteLimit) + native.PutUint64(b[8:16], msg.HardByteLimit) + native.PutUint64(b[16:24], msg.SoftPacketLimit) + native.PutUint64(b[24:32], msg.HardPacketLimit) + native.PutUint64(b[32:40], msg.SoftAddExpiresSeconds) + native.PutUint64(b[40:48], msg.HardAddExpiresSeconds) + native.PutUint64(b[48:56], msg.SoftUseExpiresSeconds) + native.PutUint64(b[56:64], msg.HardUseExpiresSeconds) +} + +func (msg *XfrmLifetimeCfg) serializeSafe() []byte { + length := SizeofXfrmLifetimeCfg + b := make([]byte, length) + msg.write(b) + return b +} + +func deserializeXfrmLifetimeCfgSafe(b []byte) *XfrmLifetimeCfg { + var msg = XfrmLifetimeCfg{} + binary.Read(bytes.NewReader(b[0:SizeofXfrmLifetimeCfg]), NativeEndian(), &msg) + return &msg +} + +func TestXfrmLifetimeCfgDeserializeSerialize(t *testing.T) { + var orig = make([]byte, SizeofXfrmLifetimeCfg) + rand.Read(orig) + safemsg := deserializeXfrmLifetimeCfgSafe(orig) + msg := DeserializeXfrmLifetimeCfg(orig) + testDeserializeSerialize(t, orig, safemsg, msg) +} + +func (msg *XfrmLifetimeCur) write(b []byte) { + native := NativeEndian() + native.PutUint64(b[0:8], msg.Bytes) + native.PutUint64(b[8:16], msg.Packets) + native.PutUint64(b[16:24], msg.AddTime) + native.PutUint64(b[24:32], msg.UseTime) +} + +func (msg *XfrmLifetimeCur) serializeSafe() []byte { + length := SizeofXfrmLifetimeCur + b := make([]byte, length) + msg.write(b) + return b +} + +func deserializeXfrmLifetimeCurSafe(b []byte) *XfrmLifetimeCur { + var msg = XfrmLifetimeCur{} + binary.Read(bytes.NewReader(b[0:SizeofXfrmLifetimeCur]), NativeEndian(), &msg) + return &msg +} + +func TestXfrmLifetimeCurDeserializeSerialize(t *testing.T) { + var orig = make([]byte, SizeofXfrmLifetimeCur) + rand.Read(orig) + safemsg := deserializeXfrmLifetimeCurSafe(orig) + msg := DeserializeXfrmLifetimeCur(orig) + testDeserializeSerialize(t, orig, safemsg, msg) +} + +func (msg *XfrmId) write(b []byte) { + native := NativeEndian() + msg.Daddr.write(b[0:SizeofXfrmAddress]) + native.PutUint32(b[SizeofXfrmAddress:SizeofXfrmAddress+4], msg.Spi) + b[SizeofXfrmAddress+4] = msg.Proto + copy(b[SizeofXfrmAddress+5:SizeofXfrmAddress+8], msg.Pad[:]) +} + +func (msg *XfrmId) serializeSafe() []byte { + b := make([]byte, SizeofXfrmId) + msg.write(b) + return b +} + +func deserializeXfrmIdSafe(b []byte) *XfrmId { + var msg = XfrmId{} + binary.Read(bytes.NewReader(b[0:SizeofXfrmId]), NativeEndian(), &msg) + return &msg +} + +func TestXfrmIdDeserializeSerialize(t *testing.T) { + var orig = make([]byte, SizeofXfrmId) + rand.Read(orig) + safemsg := deserializeXfrmIdSafe(orig) + msg := DeserializeXfrmId(orig) + testDeserializeSerialize(t, orig, safemsg, msg) +} diff --git a/vendor/src/github.com/vishvananda/netlink/nl/xfrm_policy_linux.go b/vendor/src/github.com/vishvananda/netlink/nl/xfrm_policy_linux.go new file mode 100644 index 0000000000..66f7e03d2d --- /dev/null +++ b/vendor/src/github.com/vishvananda/netlink/nl/xfrm_policy_linux.go @@ -0,0 +1,119 @@ +package nl + +import ( + "unsafe" +) + +const ( + SizeofXfrmUserpolicyId = 0x40 + SizeofXfrmUserpolicyInfo = 0xa8 + SizeofXfrmUserTmpl = 0x40 +) + +// struct xfrm_userpolicy_id { +// struct xfrm_selector sel; +// __u32 index; +// __u8 dir; +// }; +// + +type XfrmUserpolicyId struct { + Sel XfrmSelector + Index uint32 + Dir uint8 + Pad [3]byte +} + +func (msg *XfrmUserpolicyId) Len() int { + return SizeofXfrmUserpolicyId +} + +func DeserializeXfrmUserpolicyId(b []byte) *XfrmUserpolicyId { + return (*XfrmUserpolicyId)(unsafe.Pointer(&b[0:SizeofXfrmUserpolicyId][0])) +} + +func (msg *XfrmUserpolicyId) Serialize() []byte { + return (*(*[SizeofXfrmUserpolicyId]byte)(unsafe.Pointer(msg)))[:] +} + +// struct xfrm_userpolicy_info { +// struct xfrm_selector sel; +// struct xfrm_lifetime_cfg lft; +// struct xfrm_lifetime_cur curlft; +// __u32 priority; +// __u32 index; +// __u8 dir; +// __u8 action; +// #define XFRM_POLICY_ALLOW 0 +// #define XFRM_POLICY_BLOCK 1 +// __u8 flags; +// #define XFRM_POLICY_LOCALOK 1 /* Allow user to override global policy */ +// /* Automatically expand selector to include matching ICMP payloads. */ +// #define XFRM_POLICY_ICMP 2 +// __u8 share; +// }; + +type XfrmUserpolicyInfo struct { + Sel XfrmSelector + Lft XfrmLifetimeCfg + Curlft XfrmLifetimeCur + Priority uint32 + Index uint32 + Dir uint8 + Action uint8 + Flags uint8 + Share uint8 + Pad [4]byte +} + +func (msg *XfrmUserpolicyInfo) Len() int { + return SizeofXfrmUserpolicyInfo +} + +func DeserializeXfrmUserpolicyInfo(b []byte) *XfrmUserpolicyInfo { + return (*XfrmUserpolicyInfo)(unsafe.Pointer(&b[0:SizeofXfrmUserpolicyInfo][0])) +} + +func (msg *XfrmUserpolicyInfo) Serialize() []byte { + return (*(*[SizeofXfrmUserpolicyInfo]byte)(unsafe.Pointer(msg)))[:] +} + +// struct xfrm_user_tmpl { +// struct xfrm_id id; +// __u16 family; +// xfrm_address_t saddr; +// __u32 reqid; +// __u8 mode; +// __u8 share; +// __u8 optional; +// __u32 aalgos; +// __u32 ealgos; +// __u32 calgos; +// } + +type XfrmUserTmpl struct { + XfrmId XfrmId + Family uint16 + Pad1 [2]byte + Saddr XfrmAddress + Reqid uint32 + Mode uint8 + Share uint8 + Optional uint8 + Pad2 byte + Aalgos uint32 + Ealgos uint32 + Calgos uint32 +} + +func (msg *XfrmUserTmpl) Len() int { + return SizeofXfrmUserTmpl +} + +func DeserializeXfrmUserTmpl(b []byte) *XfrmUserTmpl { + return (*XfrmUserTmpl)(unsafe.Pointer(&b[0:SizeofXfrmUserTmpl][0])) +} + +func (msg *XfrmUserTmpl) Serialize() []byte { + return (*(*[SizeofXfrmUserTmpl]byte)(unsafe.Pointer(msg)))[:] +} diff --git a/vendor/src/github.com/vishvananda/netlink/nl/xfrm_policy_linux_test.go b/vendor/src/github.com/vishvananda/netlink/nl/xfrm_policy_linux_test.go new file mode 100644 index 0000000000..08a604b9cc --- /dev/null +++ b/vendor/src/github.com/vishvananda/netlink/nl/xfrm_policy_linux_test.go @@ -0,0 +1,109 @@ +package nl + +import ( + "bytes" + "crypto/rand" + "encoding/binary" + "testing" +) + +func (msg *XfrmUserpolicyId) write(b []byte) { + native := NativeEndian() + msg.Sel.write(b[0:SizeofXfrmSelector]) + native.PutUint32(b[SizeofXfrmSelector:SizeofXfrmSelector+4], msg.Index) + b[SizeofXfrmSelector+4] = msg.Dir + copy(b[SizeofXfrmSelector+5:SizeofXfrmSelector+8], msg.Pad[:]) +} + +func (msg *XfrmUserpolicyId) serializeSafe() []byte { + b := make([]byte, SizeofXfrmUserpolicyId) + msg.write(b) + return b +} + +func deserializeXfrmUserpolicyIdSafe(b []byte) *XfrmUserpolicyId { + var msg = XfrmUserpolicyId{} + binary.Read(bytes.NewReader(b[0:SizeofXfrmUserpolicyId]), NativeEndian(), &msg) + return &msg +} + +func TestXfrmUserpolicyIdDeserializeSerialize(t *testing.T) { + var orig = make([]byte, SizeofXfrmUserpolicyId) + rand.Read(orig) + safemsg := deserializeXfrmUserpolicyIdSafe(orig) + msg := DeserializeXfrmUserpolicyId(orig) + testDeserializeSerialize(t, orig, safemsg, msg) +} + +func (msg *XfrmUserpolicyInfo) write(b []byte) { + const CfgEnd = SizeofXfrmSelector + SizeofXfrmLifetimeCfg + const CurEnd = CfgEnd + SizeofXfrmLifetimeCur + native := NativeEndian() + msg.Sel.write(b[0:SizeofXfrmSelector]) + msg.Lft.write(b[SizeofXfrmSelector:CfgEnd]) + msg.Curlft.write(b[CfgEnd:CurEnd]) + native.PutUint32(b[CurEnd:CurEnd+4], msg.Priority) + native.PutUint32(b[CurEnd+4:CurEnd+8], msg.Index) + b[CurEnd+8] = msg.Dir + b[CurEnd+9] = msg.Action + b[CurEnd+10] = msg.Flags + b[CurEnd+11] = msg.Share + copy(b[CurEnd+12:CurEnd+16], msg.Pad[:]) +} + +func (msg *XfrmUserpolicyInfo) serializeSafe() []byte { + b := make([]byte, SizeofXfrmUserpolicyInfo) + msg.write(b) + return b +} + +func deserializeXfrmUserpolicyInfoSafe(b []byte) *XfrmUserpolicyInfo { + var msg = XfrmUserpolicyInfo{} + binary.Read(bytes.NewReader(b[0:SizeofXfrmUserpolicyInfo]), NativeEndian(), &msg) + return &msg +} + +func TestXfrmUserpolicyInfoDeserializeSerialize(t *testing.T) { + var orig = make([]byte, SizeofXfrmUserpolicyInfo) + rand.Read(orig) + safemsg := deserializeXfrmUserpolicyInfoSafe(orig) + msg := DeserializeXfrmUserpolicyInfo(orig) + testDeserializeSerialize(t, orig, safemsg, msg) +} + +func (msg *XfrmUserTmpl) write(b []byte) { + const AddrEnd = SizeofXfrmId + 4 + SizeofXfrmAddress + native := NativeEndian() + msg.XfrmId.write(b[0:SizeofXfrmId]) + native.PutUint16(b[SizeofXfrmId:SizeofXfrmId+2], msg.Family) + copy(b[SizeofXfrmId+2:SizeofXfrmId+4], msg.Pad1[:]) + msg.Saddr.write(b[SizeofXfrmId+4 : AddrEnd]) + native.PutUint32(b[AddrEnd:AddrEnd+4], msg.Reqid) + b[AddrEnd+4] = msg.Mode + b[AddrEnd+5] = msg.Share + b[AddrEnd+6] = msg.Optional + b[AddrEnd+7] = msg.Pad2 + native.PutUint32(b[AddrEnd+8:AddrEnd+12], msg.Aalgos) + native.PutUint32(b[AddrEnd+12:AddrEnd+16], msg.Ealgos) + native.PutUint32(b[AddrEnd+16:AddrEnd+20], msg.Calgos) +} + +func (msg *XfrmUserTmpl) serializeSafe() []byte { + b := make([]byte, SizeofXfrmUserTmpl) + msg.write(b) + return b +} + +func deserializeXfrmUserTmplSafe(b []byte) *XfrmUserTmpl { + var msg = XfrmUserTmpl{} + binary.Read(bytes.NewReader(b[0:SizeofXfrmUserTmpl]), NativeEndian(), &msg) + return &msg +} + +func TestXfrmUserTmplDeserializeSerialize(t *testing.T) { + var orig = make([]byte, SizeofXfrmUserTmpl) + rand.Read(orig) + safemsg := deserializeXfrmUserTmplSafe(orig) + msg := DeserializeXfrmUserTmpl(orig) + testDeserializeSerialize(t, orig, safemsg, msg) +} diff --git a/vendor/src/github.com/vishvananda/netlink/nl/xfrm_state_linux.go b/vendor/src/github.com/vishvananda/netlink/nl/xfrm_state_linux.go new file mode 100644 index 0000000000..4876ce4583 --- /dev/null +++ b/vendor/src/github.com/vishvananda/netlink/nl/xfrm_state_linux.go @@ -0,0 +1,221 @@ +package nl + +import ( + "unsafe" +) + +const ( + SizeofXfrmUsersaId = 0x18 + SizeofXfrmStats = 0x0c + SizeofXfrmUsersaInfo = 0xe0 + SizeofXfrmAlgo = 0x44 + SizeofXfrmAlgoAuth = 0x48 + SizeofXfrmEncapTmpl = 0x18 +) + +// struct xfrm_usersa_id { +// xfrm_address_t daddr; +// __be32 spi; +// __u16 family; +// __u8 proto; +// }; + +type XfrmUsersaId struct { + Daddr XfrmAddress + Spi uint32 // big endian + Family uint16 + Proto uint8 + Pad byte +} + +func (msg *XfrmUsersaId) Len() int { + return SizeofXfrmUsersaId +} + +func DeserializeXfrmUsersaId(b []byte) *XfrmUsersaId { + return (*XfrmUsersaId)(unsafe.Pointer(&b[0:SizeofXfrmUsersaId][0])) +} + +func (msg *XfrmUsersaId) Serialize() []byte { + return (*(*[SizeofXfrmUsersaId]byte)(unsafe.Pointer(msg)))[:] +} + +// struct xfrm_stats { +// __u32 replay_window; +// __u32 replay; +// __u32 integrity_failed; +// }; + +type XfrmStats struct { + ReplayWindow uint32 + Replay uint32 + IntegrityFailed uint32 +} + +func (msg *XfrmStats) Len() int { + return SizeofXfrmStats +} + +func DeserializeXfrmStats(b []byte) *XfrmStats { + return (*XfrmStats)(unsafe.Pointer(&b[0:SizeofXfrmStats][0])) +} + +func (msg *XfrmStats) Serialize() []byte { + return (*(*[SizeofXfrmStats]byte)(unsafe.Pointer(msg)))[:] +} + +// struct xfrm_usersa_info { +// struct xfrm_selector sel; +// struct xfrm_id id; +// xfrm_address_t saddr; +// struct xfrm_lifetime_cfg lft; +// struct xfrm_lifetime_cur curlft; +// struct xfrm_stats stats; +// __u32 seq; +// __u32 reqid; +// __u16 family; +// __u8 mode; /* XFRM_MODE_xxx */ +// __u8 replay_window; +// __u8 flags; +// #define XFRM_STATE_NOECN 1 +// #define XFRM_STATE_DECAP_DSCP 2 +// #define XFRM_STATE_NOPMTUDISC 4 +// #define XFRM_STATE_WILDRECV 8 +// #define XFRM_STATE_ICMP 16 +// #define XFRM_STATE_AF_UNSPEC 32 +// #define XFRM_STATE_ALIGN4 64 +// #define XFRM_STATE_ESN 128 +// }; +// +// #define XFRM_SA_XFLAG_DONT_ENCAP_DSCP 1 +// + +type XfrmUsersaInfo struct { + Sel XfrmSelector + Id XfrmId + Saddr XfrmAddress + Lft XfrmLifetimeCfg + Curlft XfrmLifetimeCur + Stats XfrmStats + Seq uint32 + Reqid uint32 + Family uint16 + Mode uint8 + ReplayWindow uint8 + Flags uint8 + Pad [7]byte +} + +func (msg *XfrmUsersaInfo) Len() int { + return SizeofXfrmUsersaInfo +} + +func DeserializeXfrmUsersaInfo(b []byte) *XfrmUsersaInfo { + return (*XfrmUsersaInfo)(unsafe.Pointer(&b[0:SizeofXfrmUsersaInfo][0])) +} + +func (msg *XfrmUsersaInfo) Serialize() []byte { + return (*(*[SizeofXfrmUsersaInfo]byte)(unsafe.Pointer(msg)))[:] +} + +// struct xfrm_algo { +// char alg_name[64]; +// unsigned int alg_key_len; /* in bits */ +// char alg_key[0]; +// }; + +type XfrmAlgo struct { + AlgName [64]byte + AlgKeyLen uint32 + AlgKey []byte +} + +func (msg *XfrmAlgo) Len() int { + return SizeofXfrmAlgo + int(msg.AlgKeyLen/8) +} + +func DeserializeXfrmAlgo(b []byte) *XfrmAlgo { + ret := XfrmAlgo{} + copy(ret.AlgName[:], b[0:64]) + ret.AlgKeyLen = *(*uint32)(unsafe.Pointer(&b[64])) + ret.AlgKey = b[68:ret.Len()] + return &ret +} + +func (msg *XfrmAlgo) Serialize() []byte { + b := make([]byte, msg.Len()) + copy(b[0:64], msg.AlgName[:]) + copy(b[64:68], (*(*[4]byte)(unsafe.Pointer(&msg.AlgKeyLen)))[:]) + copy(b[68:msg.Len()], msg.AlgKey[:]) + return b +} + +// struct xfrm_algo_auth { +// char alg_name[64]; +// unsigned int alg_key_len; /* in bits */ +// unsigned int alg_trunc_len; /* in bits */ +// char alg_key[0]; +// }; + +type XfrmAlgoAuth struct { + AlgName [64]byte + AlgKeyLen uint32 + AlgTruncLen uint32 + AlgKey []byte +} + +func (msg *XfrmAlgoAuth) Len() int { + return SizeofXfrmAlgoAuth + int(msg.AlgKeyLen/8) +} + +func DeserializeXfrmAlgoAuth(b []byte) *XfrmAlgoAuth { + ret := XfrmAlgoAuth{} + copy(ret.AlgName[:], b[0:64]) + ret.AlgKeyLen = *(*uint32)(unsafe.Pointer(&b[64])) + ret.AlgTruncLen = *(*uint32)(unsafe.Pointer(&b[68])) + ret.AlgKey = b[72:ret.Len()] + return &ret +} + +func (msg *XfrmAlgoAuth) Serialize() []byte { + b := make([]byte, msg.Len()) + copy(b[0:64], msg.AlgName[:]) + copy(b[64:68], (*(*[4]byte)(unsafe.Pointer(&msg.AlgKeyLen)))[:]) + copy(b[68:72], (*(*[4]byte)(unsafe.Pointer(&msg.AlgTruncLen)))[:]) + copy(b[72:msg.Len()], msg.AlgKey[:]) + return b +} + +// struct xfrm_algo_aead { +// char alg_name[64]; +// unsigned int alg_key_len; /* in bits */ +// unsigned int alg_icv_len; /* in bits */ +// char alg_key[0]; +// } + +// struct xfrm_encap_tmpl { +// __u16 encap_type; +// __be16 encap_sport; +// __be16 encap_dport; +// xfrm_address_t encap_oa; +// }; + +type XfrmEncapTmpl struct { + EncapType uint16 + EncapSport uint16 // big endian + EncapDport uint16 // big endian + Pad [2]byte + EncapOa XfrmAddress +} + +func (msg *XfrmEncapTmpl) Len() int { + return SizeofXfrmEncapTmpl +} + +func DeserializeXfrmEncapTmpl(b []byte) *XfrmEncapTmpl { + return (*XfrmEncapTmpl)(unsafe.Pointer(&b[0:SizeofXfrmEncapTmpl][0])) +} + +func (msg *XfrmEncapTmpl) Serialize() []byte { + return (*(*[SizeofXfrmEncapTmpl]byte)(unsafe.Pointer(msg)))[:] +} diff --git a/vendor/src/github.com/vishvananda/netlink/nl/xfrm_state_linux_test.go b/vendor/src/github.com/vishvananda/netlink/nl/xfrm_state_linux_test.go new file mode 100644 index 0000000000..d5281e9a64 --- /dev/null +++ b/vendor/src/github.com/vishvananda/netlink/nl/xfrm_state_linux_test.go @@ -0,0 +1,207 @@ +package nl + +import ( + "bytes" + "crypto/rand" + "encoding/binary" + "testing" +) + +func (msg *XfrmUsersaId) write(b []byte) { + native := NativeEndian() + msg.Daddr.write(b[0:SizeofXfrmAddress]) + native.PutUint32(b[SizeofXfrmAddress:SizeofXfrmAddress+4], msg.Spi) + native.PutUint16(b[SizeofXfrmAddress+4:SizeofXfrmAddress+6], msg.Family) + b[SizeofXfrmAddress+6] = msg.Proto + b[SizeofXfrmAddress+7] = msg.Pad +} + +func (msg *XfrmUsersaId) serializeSafe() []byte { + b := make([]byte, SizeofXfrmUsersaId) + msg.write(b) + return b +} + +func deserializeXfrmUsersaIdSafe(b []byte) *XfrmUsersaId { + var msg = XfrmUsersaId{} + binary.Read(bytes.NewReader(b[0:SizeofXfrmUsersaId]), NativeEndian(), &msg) + return &msg +} + +func TestXfrmUsersaIdDeserializeSerialize(t *testing.T) { + var orig = make([]byte, SizeofXfrmUsersaId) + rand.Read(orig) + safemsg := deserializeXfrmUsersaIdSafe(orig) + msg := DeserializeXfrmUsersaId(orig) + testDeserializeSerialize(t, orig, safemsg, msg) +} + +func (msg *XfrmStats) write(b []byte) { + native := NativeEndian() + native.PutUint32(b[0:4], msg.ReplayWindow) + native.PutUint32(b[4:8], msg.Replay) + native.PutUint32(b[8:12], msg.IntegrityFailed) +} + +func (msg *XfrmStats) serializeSafe() []byte { + b := make([]byte, SizeofXfrmStats) + msg.write(b) + return b +} + +func deserializeXfrmStatsSafe(b []byte) *XfrmStats { + var msg = XfrmStats{} + binary.Read(bytes.NewReader(b[0:SizeofXfrmStats]), NativeEndian(), &msg) + return &msg +} + +func TestXfrmStatsDeserializeSerialize(t *testing.T) { + var orig = make([]byte, SizeofXfrmStats) + rand.Read(orig) + safemsg := deserializeXfrmStatsSafe(orig) + msg := DeserializeXfrmStats(orig) + testDeserializeSerialize(t, orig, safemsg, msg) +} + +func (msg *XfrmUsersaInfo) write(b []byte) { + const IdEnd = SizeofXfrmSelector + SizeofXfrmId + const AddressEnd = IdEnd + SizeofXfrmAddress + const CfgEnd = AddressEnd + SizeofXfrmLifetimeCfg + const CurEnd = CfgEnd + SizeofXfrmLifetimeCur + const StatsEnd = CurEnd + SizeofXfrmStats + native := NativeEndian() + msg.Sel.write(b[0:SizeofXfrmSelector]) + msg.Id.write(b[SizeofXfrmSelector:IdEnd]) + msg.Saddr.write(b[IdEnd:AddressEnd]) + msg.Lft.write(b[AddressEnd:CfgEnd]) + msg.Curlft.write(b[CfgEnd:CurEnd]) + msg.Stats.write(b[CurEnd:StatsEnd]) + native.PutUint32(b[StatsEnd:StatsEnd+4], msg.Seq) + native.PutUint32(b[StatsEnd+4:StatsEnd+8], msg.Reqid) + native.PutUint16(b[StatsEnd+8:StatsEnd+10], msg.Family) + b[StatsEnd+10] = msg.Mode + b[StatsEnd+11] = msg.ReplayWindow + b[StatsEnd+12] = msg.Flags + copy(b[StatsEnd+13:StatsEnd+20], msg.Pad[:]) +} + +func (msg *XfrmUsersaInfo) serializeSafe() []byte { + b := make([]byte, SizeofXfrmUsersaInfo) + msg.write(b) + return b +} + +func deserializeXfrmUsersaInfoSafe(b []byte) *XfrmUsersaInfo { + var msg = XfrmUsersaInfo{} + binary.Read(bytes.NewReader(b[0:SizeofXfrmUsersaInfo]), NativeEndian(), &msg) + return &msg +} + +func TestXfrmUsersaInfoDeserializeSerialize(t *testing.T) { + var orig = make([]byte, SizeofXfrmUsersaInfo) + rand.Read(orig) + safemsg := deserializeXfrmUsersaInfoSafe(orig) + msg := DeserializeXfrmUsersaInfo(orig) + testDeserializeSerialize(t, orig, safemsg, msg) +} + +func (msg *XfrmAlgo) write(b []byte) { + native := NativeEndian() + copy(b[0:64], msg.AlgName[:]) + native.PutUint32(b[64:68], msg.AlgKeyLen) + copy(b[68:msg.Len()], msg.AlgKey[:]) +} + +func (msg *XfrmAlgo) serializeSafe() []byte { + b := make([]byte, msg.Len()) + msg.write(b) + return b +} + +func deserializeXfrmAlgoSafe(b []byte) *XfrmAlgo { + var msg = XfrmAlgo{} + copy(msg.AlgName[:], b[0:64]) + binary.Read(bytes.NewReader(b[64:68]), NativeEndian(), &msg.AlgKeyLen) + msg.AlgKey = b[68:msg.Len()] + return &msg +} + +func TestXfrmAlgoDeserializeSerialize(t *testing.T) { + // use a 32 byte key len + var orig = make([]byte, SizeofXfrmAlgo+32) + rand.Read(orig) + // set the key len to 256 bits + orig[64] = 0 + orig[65] = 1 + orig[66] = 0 + orig[67] = 0 + safemsg := deserializeXfrmAlgoSafe(orig) + msg := DeserializeXfrmAlgo(orig) + testDeserializeSerialize(t, orig, safemsg, msg) +} + +func (msg *XfrmAlgoAuth) write(b []byte) { + native := NativeEndian() + copy(b[0:64], msg.AlgName[:]) + native.PutUint32(b[64:68], msg.AlgKeyLen) + native.PutUint32(b[68:72], msg.AlgTruncLen) + copy(b[72:msg.Len()], msg.AlgKey[:]) +} + +func (msg *XfrmAlgoAuth) serializeSafe() []byte { + b := make([]byte, msg.Len()) + msg.write(b) + return b +} + +func deserializeXfrmAlgoAuthSafe(b []byte) *XfrmAlgoAuth { + var msg = XfrmAlgoAuth{} + copy(msg.AlgName[:], b[0:64]) + binary.Read(bytes.NewReader(b[64:68]), NativeEndian(), &msg.AlgKeyLen) + binary.Read(bytes.NewReader(b[68:72]), NativeEndian(), &msg.AlgTruncLen) + msg.AlgKey = b[72:msg.Len()] + return &msg +} + +func TestXfrmAlgoAuthDeserializeSerialize(t *testing.T) { + // use a 32 byte key len + var orig = make([]byte, SizeofXfrmAlgoAuth+32) + rand.Read(orig) + // set the key len to 256 bits + orig[64] = 0 + orig[65] = 1 + orig[66] = 0 + orig[67] = 0 + safemsg := deserializeXfrmAlgoAuthSafe(orig) + msg := DeserializeXfrmAlgoAuth(orig) + testDeserializeSerialize(t, orig, safemsg, msg) +} + +func (msg *XfrmEncapTmpl) write(b []byte) { + native := NativeEndian() + native.PutUint16(b[0:2], msg.EncapType) + native.PutUint16(b[2:4], msg.EncapSport) + native.PutUint16(b[4:6], msg.EncapDport) + copy(b[6:8], msg.Pad[:]) + msg.EncapOa.write(b[8:SizeofXfrmAddress]) +} + +func (msg *XfrmEncapTmpl) serializeSafe() []byte { + b := make([]byte, SizeofXfrmEncapTmpl) + msg.write(b) + return b +} + +func deserializeXfrmEncapTmplSafe(b []byte) *XfrmEncapTmpl { + var msg = XfrmEncapTmpl{} + binary.Read(bytes.NewReader(b[0:SizeofXfrmEncapTmpl]), NativeEndian(), &msg) + return &msg +} + +func TestXfrmEncapTmplDeserializeSerialize(t *testing.T) { + var orig = make([]byte, SizeofXfrmEncapTmpl) + rand.Read(orig) + safemsg := deserializeXfrmEncapTmplSafe(orig) + msg := DeserializeXfrmEncapTmpl(orig) + testDeserializeSerialize(t, orig, safemsg, msg) +} diff --git a/vendor/src/github.com/vishvananda/netlink/protinfo.go b/vendor/src/github.com/vishvananda/netlink/protinfo.go new file mode 100644 index 0000000000..79396da7ca --- /dev/null +++ b/vendor/src/github.com/vishvananda/netlink/protinfo.go @@ -0,0 +1,53 @@ +package netlink + +import ( + "strings" +) + +// Protinfo represents bridge flags from netlink. +type Protinfo struct { + Hairpin bool + Guard bool + FastLeave bool + RootBlock bool + Learning bool + Flood bool +} + +// String returns a list of enabled flags +func (prot *Protinfo) String() string { + boolStrings := make([]string, 0) + if prot.Hairpin { + boolStrings = append(boolStrings, "Hairpin") + } + if prot.Guard { + boolStrings = append(boolStrings, "Guard") + } + if prot.FastLeave { + boolStrings = append(boolStrings, "FastLeave") + } + if prot.RootBlock { + boolStrings = append(boolStrings, "RootBlock") + } + if prot.Learning { + boolStrings = append(boolStrings, "Learning") + } + if prot.Flood { + boolStrings = append(boolStrings, "Flood") + } + return strings.Join(boolStrings, " ") +} + +func boolToByte(x bool) []byte { + if x { + return []byte{1} + } + return []byte{0} +} + +func byteToBool(x byte) bool { + if uint8(x) != 0 { + return true + } + return false +} diff --git a/vendor/src/github.com/vishvananda/netlink/protinfo_linux.go b/vendor/src/github.com/vishvananda/netlink/protinfo_linux.go new file mode 100644 index 0000000000..7181eba100 --- /dev/null +++ b/vendor/src/github.com/vishvananda/netlink/protinfo_linux.go @@ -0,0 +1,60 @@ +package netlink + +import ( + "fmt" + "syscall" + + "github.com/vishvananda/netlink/nl" +) + +func LinkGetProtinfo(link Link) (Protinfo, error) { + base := link.Attrs() + ensureIndex(base) + var pi Protinfo + req := nl.NewNetlinkRequest(syscall.RTM_GETLINK, syscall.NLM_F_DUMP) + msg := nl.NewIfInfomsg(syscall.AF_BRIDGE) + req.AddData(msg) + msgs, err := req.Execute(syscall.NETLINK_ROUTE, 0) + if err != nil { + return pi, err + } + + for _, m := range msgs { + ans := nl.DeserializeIfInfomsg(m) + if int(ans.Index) != base.Index { + continue + } + attrs, err := nl.ParseRouteAttr(m[ans.Len():]) + if err != nil { + return pi, err + } + for _, attr := range attrs { + if attr.Attr.Type != syscall.IFLA_PROTINFO|syscall.NLA_F_NESTED { + continue + } + infos, err := nl.ParseRouteAttr(attr.Value) + if err != nil { + return pi, err + } + var pi Protinfo + for _, info := range infos { + switch info.Attr.Type { + case nl.IFLA_BRPORT_MODE: + pi.Hairpin = byteToBool(info.Value[0]) + case nl.IFLA_BRPORT_GUARD: + pi.Guard = byteToBool(info.Value[0]) + case nl.IFLA_BRPORT_FAST_LEAVE: + pi.FastLeave = byteToBool(info.Value[0]) + case nl.IFLA_BRPORT_PROTECT: + pi.RootBlock = byteToBool(info.Value[0]) + case nl.IFLA_BRPORT_LEARNING: + pi.Learning = byteToBool(info.Value[0]) + case nl.IFLA_BRPORT_UNICAST_FLOOD: + pi.Flood = byteToBool(info.Value[0]) + } + } + return pi, nil + } + } + return pi, fmt.Errorf("Device with index %d not found", base.Index) +} diff --git a/vendor/src/github.com/vishvananda/netlink/protinfo_test.go b/vendor/src/github.com/vishvananda/netlink/protinfo_test.go new file mode 100644 index 0000000000..f94c42b1c7 --- /dev/null +++ b/vendor/src/github.com/vishvananda/netlink/protinfo_test.go @@ -0,0 +1,98 @@ +package netlink + +import "testing" + +func TestProtinfo(t *testing.T) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + master := &Bridge{LinkAttrs{Name: "foo"}} + if err := LinkAdd(master); err != nil { + t.Fatal(err) + } + iface1 := &Dummy{LinkAttrs{Name: "bar1", MasterIndex: master.Index}} + iface2 := &Dummy{LinkAttrs{Name: "bar2", MasterIndex: master.Index}} + iface3 := &Dummy{LinkAttrs{Name: "bar3"}} + + if err := LinkAdd(iface1); err != nil { + t.Fatal(err) + } + if err := LinkAdd(iface2); err != nil { + t.Fatal(err) + } + if err := LinkAdd(iface3); err != nil { + t.Fatal(err) + } + + oldpi1, err := LinkGetProtinfo(iface1) + if err != nil { + t.Fatal(err) + } + oldpi2, err := LinkGetProtinfo(iface2) + if err != nil { + t.Fatal(err) + } + + if err := LinkSetHairpin(iface1, true); err != nil { + t.Fatal(err) + } + + if err := LinkSetRootBlock(iface1, true); err != nil { + t.Fatal(err) + } + + pi1, err := LinkGetProtinfo(iface1) + if err != nil { + t.Fatal(err) + } + if !pi1.Hairpin { + t.Fatalf("Hairpin mode is not enabled for %s, but should", iface1.Name) + } + if !pi1.RootBlock { + t.Fatalf("RootBlock is not enabled for %s, but should", iface1.Name) + } + if pi1.Guard != oldpi1.Guard { + t.Fatalf("Guard field was changed for %s but shouldn't", iface1.Name) + } + if pi1.FastLeave != oldpi1.FastLeave { + t.Fatalf("FastLeave field was changed for %s but shouldn't", iface1.Name) + } + if pi1.Learning != oldpi1.Learning { + t.Fatalf("Learning field was changed for %s but shouldn't", iface1.Name) + } + if pi1.Flood != oldpi1.Flood { + t.Fatalf("Flood field was changed for %s but shouldn't", iface1.Name) + } + + if err := LinkSetGuard(iface2, true); err != nil { + t.Fatal(err) + } + if err := LinkSetLearning(iface2, false); err != nil { + t.Fatal(err) + } + pi2, err := LinkGetProtinfo(iface2) + if err != nil { + t.Fatal(err) + } + if pi2.Hairpin { + t.Fatalf("Hairpin mode is enabled for %s, but shouldn't", iface2.Name) + } + if !pi2.Guard { + t.Fatalf("Guard is not enabled for %s, but should", iface2.Name) + } + if pi2.Learning { + t.Fatalf("Learning is enabled for %s, but shouldn't", iface2.Name) + } + if pi2.RootBlock != oldpi2.RootBlock { + t.Fatalf("RootBlock field was changed for %s but shouldn't", iface2.Name) + } + if pi2.FastLeave != oldpi2.FastLeave { + t.Fatalf("FastLeave field was changed for %s but shouldn't", iface2.Name) + } + if pi2.Flood != oldpi2.Flood { + t.Fatalf("Flood field was changed for %s but shouldn't", iface2.Name) + } + + if err := LinkSetHairpin(iface3, true); err == nil || err.Error() != "operation not supported" { + t.Fatalf("Set protinfo attrs for link without master is not supported, but err: %s", err) + } +} diff --git a/vendor/src/github.com/vishvananda/netlink/route.go b/vendor/src/github.com/vishvananda/netlink/route.go new file mode 100644 index 0000000000..6218546f80 --- /dev/null +++ b/vendor/src/github.com/vishvananda/netlink/route.go @@ -0,0 +1,35 @@ +package netlink + +import ( + "fmt" + "net" + "syscall" +) + +// Scope is an enum representing a route scope. +type Scope uint8 + +const ( + SCOPE_UNIVERSE Scope = syscall.RT_SCOPE_UNIVERSE + SCOPE_SITE Scope = syscall.RT_SCOPE_SITE + SCOPE_LINK Scope = syscall.RT_SCOPE_LINK + SCOPE_HOST Scope = syscall.RT_SCOPE_HOST + SCOPE_NOWHERE Scope = syscall.RT_SCOPE_NOWHERE +) + +// Route represents a netlink route. A route is associated with a link, +// has a destination network, an optional source ip, and optional +// gateway. Advanced route parameters and non-main routing tables are +// currently not supported. +type Route struct { + LinkIndex int + Scope Scope + Dst *net.IPNet + Src net.IP + Gw net.IP +} + +func (r Route) String() string { + return fmt.Sprintf("{Ifindex: %d Dst: %s Src: %s Gw: %s}", r.LinkIndex, r.Dst, + r.Src, r.Gw) +} diff --git a/vendor/src/github.com/vishvananda/netlink/route_linux.go b/vendor/src/github.com/vishvananda/netlink/route_linux.go new file mode 100644 index 0000000000..43872aa417 --- /dev/null +++ b/vendor/src/github.com/vishvananda/netlink/route_linux.go @@ -0,0 +1,225 @@ +package netlink + +import ( + "fmt" + "net" + "syscall" + + "github.com/vishvananda/netlink/nl" +) + +// RtAttr is shared so it is in netlink_linux.go + +// RouteAdd will add a route to the system. +// Equivalent to: `ip route add $route` +func RouteAdd(route *Route) error { + req := nl.NewNetlinkRequest(syscall.RTM_NEWROUTE, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK) + return routeHandle(route, req) +} + +// RouteAdd will delete a route from the system. +// Equivalent to: `ip route del $route` +func RouteDel(route *Route) error { + req := nl.NewNetlinkRequest(syscall.RTM_DELROUTE, syscall.NLM_F_ACK) + return routeHandle(route, req) +} + +func routeHandle(route *Route, req *nl.NetlinkRequest) error { + if (route.Dst == nil || route.Dst.IP == nil) && route.Src == nil && route.Gw == nil { + return fmt.Errorf("one of Dst.IP, Src, or Gw must not be nil") + } + + msg := nl.NewRtMsg() + msg.Scope = uint8(route.Scope) + family := -1 + var rtAttrs []*nl.RtAttr + + if route.Dst != nil && route.Dst.IP != nil { + dstLen, _ := route.Dst.Mask.Size() + msg.Dst_len = uint8(dstLen) + dstFamily := nl.GetIPFamily(route.Dst.IP) + family = dstFamily + var dstData []byte + if dstFamily == FAMILY_V4 { + dstData = route.Dst.IP.To4() + } else { + dstData = route.Dst.IP.To16() + } + rtAttrs = append(rtAttrs, nl.NewRtAttr(syscall.RTA_DST, dstData)) + } + + if route.Src != nil { + srcFamily := nl.GetIPFamily(route.Src) + if family != -1 && family != srcFamily { + return fmt.Errorf("source and destination ip are not the same IP family") + } + family = srcFamily + var srcData []byte + if srcFamily == FAMILY_V4 { + srcData = route.Src.To4() + } else { + srcData = route.Src.To16() + } + // The commonly used src ip for routes is actually PREFSRC + rtAttrs = append(rtAttrs, nl.NewRtAttr(syscall.RTA_PREFSRC, srcData)) + } + + if route.Gw != nil { + gwFamily := nl.GetIPFamily(route.Gw) + if family != -1 && family != gwFamily { + return fmt.Errorf("gateway, source, and destination ip are not the same IP family") + } + family = gwFamily + var gwData []byte + if gwFamily == FAMILY_V4 { + gwData = route.Gw.To4() + } else { + gwData = route.Gw.To16() + } + rtAttrs = append(rtAttrs, nl.NewRtAttr(syscall.RTA_GATEWAY, gwData)) + } + + msg.Family = uint8(family) + + req.AddData(msg) + for _, attr := range rtAttrs { + req.AddData(attr) + } + + var ( + b = make([]byte, 4) + native = nl.NativeEndian() + ) + native.PutUint32(b, uint32(route.LinkIndex)) + + req.AddData(nl.NewRtAttr(syscall.RTA_OIF, b)) + + _, err := req.Execute(syscall.NETLINK_ROUTE, 0) + return err +} + +// RouteList gets a list of routes in the system. +// Equivalent to: `ip route show`. +// The list can be filtered by link and ip family. +func RouteList(link Link, family int) ([]Route, error) { + req := nl.NewNetlinkRequest(syscall.RTM_GETROUTE, syscall.NLM_F_DUMP) + msg := nl.NewIfInfomsg(family) + req.AddData(msg) + + msgs, err := req.Execute(syscall.NETLINK_ROUTE, syscall.RTM_NEWROUTE) + if err != nil { + return nil, err + } + + index := 0 + if link != nil { + base := link.Attrs() + ensureIndex(base) + index = base.Index + } + + native := nl.NativeEndian() + res := make([]Route, 0) + for _, m := range msgs { + msg := nl.DeserializeRtMsg(m) + + if msg.Flags&syscall.RTM_F_CLONED != 0 { + // Ignore cloned routes + continue + } + + if msg.Table != syscall.RT_TABLE_MAIN { + // Ignore non-main tables + continue + } + + attrs, err := nl.ParseRouteAttr(m[msg.Len():]) + if err != nil { + return nil, err + } + + route := Route{Scope: Scope(msg.Scope)} + for _, attr := range attrs { + switch attr.Attr.Type { + case syscall.RTA_GATEWAY: + route.Gw = net.IP(attr.Value) + case syscall.RTA_PREFSRC: + route.Src = net.IP(attr.Value) + case syscall.RTA_DST: + route.Dst = &net.IPNet{ + IP: attr.Value, + Mask: net.CIDRMask(int(msg.Dst_len), 8*len(attr.Value)), + } + case syscall.RTA_OIF: + routeIndex := int(native.Uint32(attr.Value[0:4])) + if link != nil && routeIndex != index { + // Ignore routes from other interfaces + continue + } + route.LinkIndex = routeIndex + } + } + res = append(res, route) + } + + return res, nil +} + +// RouteGet gets a route to a specific destination from the host system. +// Equivalent to: 'ip route get'. +func RouteGet(destination net.IP) ([]Route, error) { + req := nl.NewNetlinkRequest(syscall.RTM_GETROUTE, syscall.NLM_F_REQUEST) + family := nl.GetIPFamily(destination) + var destinationData []byte + var bitlen uint8 + if family == FAMILY_V4 { + destinationData = destination.To4() + bitlen = 32 + } else { + destinationData = destination.To16() + bitlen = 128 + } + msg := &nl.RtMsg{} + msg.Family = uint8(family) + msg.Dst_len = bitlen + req.AddData(msg) + + rtaDst := nl.NewRtAttr(syscall.RTA_DST, destinationData) + req.AddData(rtaDst) + + msgs, err := req.Execute(syscall.NETLINK_ROUTE, syscall.RTM_NEWROUTE) + if err != nil { + return nil, err + } + + native := nl.NativeEndian() + res := make([]Route, 0) + for _, m := range msgs { + msg := nl.DeserializeRtMsg(m) + attrs, err := nl.ParseRouteAttr(m[msg.Len():]) + if err != nil { + return nil, err + } + + route := Route{} + for _, attr := range attrs { + switch attr.Attr.Type { + case syscall.RTA_GATEWAY: + route.Gw = net.IP(attr.Value) + case syscall.RTA_PREFSRC: + route.Src = net.IP(attr.Value) + case syscall.RTA_DST: + route.Dst = &net.IPNet{ + IP: attr.Value, + Mask: net.CIDRMask(int(msg.Dst_len), 8*len(attr.Value)), + } + case syscall.RTA_OIF: + routeIndex := int(native.Uint32(attr.Value[0:4])) + route.LinkIndex = routeIndex + } + } + res = append(res, route) + } + return res, nil + +} diff --git a/vendor/src/github.com/vishvananda/netlink/route_test.go b/vendor/src/github.com/vishvananda/netlink/route_test.go new file mode 100644 index 0000000000..f02bef8c87 --- /dev/null +++ b/vendor/src/github.com/vishvananda/netlink/route_test.go @@ -0,0 +1,84 @@ +package netlink + +import ( + "net" + "testing" +) + +func TestRouteAddDel(t *testing.T) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + + // get loopback interface + link, err := LinkByName("lo") + if err != nil { + t.Fatal(err) + } + + // bring the interface up + if err = LinkSetUp(link); err != nil { + t.Fatal(err) + } + + // add a gateway route + _, dst, err := net.ParseCIDR("192.168.0.0/24") + + ip := net.ParseIP("127.1.1.1") + route := Route{LinkIndex: link.Attrs().Index, Dst: dst, Src: ip} + err = RouteAdd(&route) + if err != nil { + t.Fatal(err) + } + routes, err := RouteList(link, FAMILY_V4) + if err != nil { + t.Fatal(err) + } + if len(routes) != 1 { + t.Fatal("Link not added properly") + } + + dstIP := net.ParseIP("192.168.0.42") + routeToDstIP, err := RouteGet(dstIP) + if err != nil { + t.Fatal(err) + } + + if len(routeToDstIP) == 0 { + t.Fatal("Default route not present") + } + + err = RouteDel(&route) + if err != nil { + t.Fatal(err) + } + + routes, err = RouteList(link, FAMILY_V4) + if err != nil { + t.Fatal(err) + } + if len(routes) != 0 { + t.Fatal("Route not removed properly") + } + +} + +func TestRouteAddIncomplete(t *testing.T) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + + // get loopback interface + link, err := LinkByName("lo") + if err != nil { + t.Fatal(err) + } + + // bring the interface up + if err = LinkSetUp(link); err != nil { + t.Fatal(err) + } + + route := Route{LinkIndex: link.Attrs().Index} + if err := RouteAdd(&route); err == nil { + t.Fatal("Adding incomplete route should fail") + } +} diff --git a/vendor/src/github.com/vishvananda/netlink/xfrm.go b/vendor/src/github.com/vishvananda/netlink/xfrm.go new file mode 100644 index 0000000000..621ffb6c68 --- /dev/null +++ b/vendor/src/github.com/vishvananda/netlink/xfrm.go @@ -0,0 +1,64 @@ +package netlink + +import ( + "fmt" + "syscall" +) + +// Proto is an enum representing an ipsec protocol. +type Proto uint8 + +const ( + XFRM_PROTO_ROUTE2 Proto = syscall.IPPROTO_ROUTING + XFRM_PROTO_ESP Proto = syscall.IPPROTO_ESP + XFRM_PROTO_AH Proto = syscall.IPPROTO_AH + XFRM_PROTO_HAO Proto = syscall.IPPROTO_DSTOPTS + XFRM_PROTO_COMP Proto = syscall.IPPROTO_COMP + XFRM_PROTO_IPSEC_ANY Proto = syscall.IPPROTO_RAW +) + +func (p Proto) String() string { + switch p { + case XFRM_PROTO_ROUTE2: + return "route2" + case XFRM_PROTO_ESP: + return "esp" + case XFRM_PROTO_AH: + return "ah" + case XFRM_PROTO_HAO: + return "hao" + case XFRM_PROTO_COMP: + return "comp" + case XFRM_PROTO_IPSEC_ANY: + return "ipsec-any" + } + return fmt.Sprintf("%d", p) +} + +// Mode is an enum representing an ipsec transport. +type Mode uint8 + +const ( + XFRM_MODE_TRANSPORT Mode = iota + XFRM_MODE_TUNNEL + XFRM_MODE_ROUTEOPTIMIZATION + XFRM_MODE_IN_TRIGGER + XFRM_MODE_BEET + XFRM_MODE_MAX +) + +func (m Mode) String() string { + switch m { + case XFRM_MODE_TRANSPORT: + return "transport" + case XFRM_MODE_TUNNEL: + return "tunnel" + case XFRM_MODE_ROUTEOPTIMIZATION: + return "ro" + case XFRM_MODE_IN_TRIGGER: + return "in_trigger" + case XFRM_MODE_BEET: + return "beet" + } + return fmt.Sprintf("%d", m) +} diff --git a/vendor/src/github.com/vishvananda/netlink/xfrm_policy.go b/vendor/src/github.com/vishvananda/netlink/xfrm_policy.go new file mode 100644 index 0000000000..d85c65d2d2 --- /dev/null +++ b/vendor/src/github.com/vishvananda/netlink/xfrm_policy.go @@ -0,0 +1,59 @@ +package netlink + +import ( + "fmt" + "net" +) + +// Dir is an enum representing an ipsec template direction. +type Dir uint8 + +const ( + XFRM_DIR_IN Dir = iota + XFRM_DIR_OUT + XFRM_DIR_FWD + XFRM_SOCKET_IN + XFRM_SOCKET_OUT + XFRM_SOCKET_FWD +) + +func (d Dir) String() string { + switch d { + case XFRM_DIR_IN: + return "dir in" + case XFRM_DIR_OUT: + return "dir out" + case XFRM_DIR_FWD: + return "dir fwd" + case XFRM_SOCKET_IN: + return "socket in" + case XFRM_SOCKET_OUT: + return "socket out" + case XFRM_SOCKET_FWD: + return "socket fwd" + } + return fmt.Sprintf("socket %d", d-XFRM_SOCKET_IN) +} + +// XfrmPolicyTmpl encapsulates a rule for the base addresses of an ipsec +// policy. These rules are matched with XfrmState to determine encryption +// and authentication algorithms. +type XfrmPolicyTmpl struct { + Dst net.IP + Src net.IP + Proto Proto + Mode Mode + Reqid int +} + +// XfrmPolicy represents an ipsec policy. It represents the overlay network +// and has a list of XfrmPolicyTmpls representing the base addresses of +// the policy. +type XfrmPolicy struct { + Dst *net.IPNet + Src *net.IPNet + Dir Dir + Priority int + Index int + Tmpls []XfrmPolicyTmpl +} diff --git a/vendor/src/github.com/vishvananda/netlink/xfrm_policy_linux.go b/vendor/src/github.com/vishvananda/netlink/xfrm_policy_linux.go new file mode 100644 index 0000000000..6fe1b63757 --- /dev/null +++ b/vendor/src/github.com/vishvananda/netlink/xfrm_policy_linux.go @@ -0,0 +1,127 @@ +package netlink + +import ( + "syscall" + + "github.com/vishvananda/netlink/nl" +) + +func selFromPolicy(sel *nl.XfrmSelector, policy *XfrmPolicy) { + sel.Family = uint16(nl.GetIPFamily(policy.Dst.IP)) + sel.Daddr.FromIP(policy.Dst.IP) + sel.Saddr.FromIP(policy.Src.IP) + prefixlenD, _ := policy.Dst.Mask.Size() + sel.PrefixlenD = uint8(prefixlenD) + prefixlenS, _ := policy.Src.Mask.Size() + sel.PrefixlenS = uint8(prefixlenS) +} + +// XfrmPolicyAdd will add an xfrm policy to the system. +// Equivalent to: `ip xfrm policy add $policy` +func XfrmPolicyAdd(policy *XfrmPolicy) error { + req := nl.NewNetlinkRequest(nl.XFRM_MSG_NEWPOLICY, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK) + + msg := &nl.XfrmUserpolicyInfo{} + selFromPolicy(&msg.Sel, policy) + msg.Priority = uint32(policy.Priority) + msg.Index = uint32(policy.Index) + msg.Dir = uint8(policy.Dir) + msg.Lft.SoftByteLimit = nl.XFRM_INF + msg.Lft.HardByteLimit = nl.XFRM_INF + msg.Lft.SoftPacketLimit = nl.XFRM_INF + msg.Lft.HardPacketLimit = nl.XFRM_INF + req.AddData(msg) + + tmplData := make([]byte, nl.SizeofXfrmUserTmpl*len(policy.Tmpls)) + for i, tmpl := range policy.Tmpls { + start := i * nl.SizeofXfrmUserTmpl + userTmpl := nl.DeserializeXfrmUserTmpl(tmplData[start : start+nl.SizeofXfrmUserTmpl]) + userTmpl.XfrmId.Daddr.FromIP(tmpl.Dst) + userTmpl.Saddr.FromIP(tmpl.Src) + userTmpl.XfrmId.Proto = uint8(tmpl.Proto) + userTmpl.Mode = uint8(tmpl.Mode) + userTmpl.Reqid = uint32(tmpl.Reqid) + userTmpl.Aalgos = ^uint32(0) + userTmpl.Ealgos = ^uint32(0) + userTmpl.Calgos = ^uint32(0) + } + if len(tmplData) > 0 { + tmpls := nl.NewRtAttr(nl.XFRMA_TMPL, tmplData) + req.AddData(tmpls) + } + + _, err := req.Execute(syscall.NETLINK_XFRM, 0) + return err +} + +// XfrmPolicyDel will delete an xfrm policy from the system. Note that +// the Tmpls are ignored when matching the policy to delete. +// Equivalent to: `ip xfrm policy del $policy` +func XfrmPolicyDel(policy *XfrmPolicy) error { + req := nl.NewNetlinkRequest(nl.XFRM_MSG_DELPOLICY, syscall.NLM_F_ACK) + + msg := &nl.XfrmUserpolicyId{} + selFromPolicy(&msg.Sel, policy) + msg.Index = uint32(policy.Index) + msg.Dir = uint8(policy.Dir) + req.AddData(msg) + + _, err := req.Execute(syscall.NETLINK_XFRM, 0) + return err +} + +// XfrmPolicyList gets a list of xfrm policies in the system. +// Equivalent to: `ip xfrm policy show`. +// The list can be filtered by ip family. +func XfrmPolicyList(family int) ([]XfrmPolicy, error) { + req := nl.NewNetlinkRequest(nl.XFRM_MSG_GETPOLICY, syscall.NLM_F_DUMP) + + msg := nl.NewIfInfomsg(family) + req.AddData(msg) + + msgs, err := req.Execute(syscall.NETLINK_XFRM, nl.XFRM_MSG_NEWPOLICY) + if err != nil { + return nil, err + } + + res := make([]XfrmPolicy, 0) + for _, m := range msgs { + msg := nl.DeserializeXfrmUserpolicyInfo(m) + + if family != FAMILY_ALL && family != int(msg.Sel.Family) { + continue + } + + var policy XfrmPolicy + + policy.Dst = msg.Sel.Daddr.ToIPNet(msg.Sel.PrefixlenD) + policy.Src = msg.Sel.Saddr.ToIPNet(msg.Sel.PrefixlenS) + policy.Priority = int(msg.Priority) + policy.Index = int(msg.Index) + policy.Dir = Dir(msg.Dir) + + attrs, err := nl.ParseRouteAttr(m[msg.Len():]) + if err != nil { + return nil, err + } + + for _, attr := range attrs { + switch attr.Attr.Type { + case nl.XFRMA_TMPL: + max := len(attr.Value) + for i := 0; i < max; i += nl.SizeofXfrmUserTmpl { + var resTmpl XfrmPolicyTmpl + tmpl := nl.DeserializeXfrmUserTmpl(attr.Value[i : i+nl.SizeofXfrmUserTmpl]) + resTmpl.Dst = tmpl.XfrmId.Daddr.ToIP() + resTmpl.Src = tmpl.Saddr.ToIP() + resTmpl.Proto = Proto(tmpl.XfrmId.Proto) + resTmpl.Mode = Mode(tmpl.Mode) + resTmpl.Reqid = int(tmpl.Reqid) + policy.Tmpls = append(policy.Tmpls, resTmpl) + } + } + } + res = append(res, policy) + } + return res, nil +} diff --git a/vendor/src/github.com/vishvananda/netlink/xfrm_policy_test.go b/vendor/src/github.com/vishvananda/netlink/xfrm_policy_test.go new file mode 100644 index 0000000000..06d178d1f9 --- /dev/null +++ b/vendor/src/github.com/vishvananda/netlink/xfrm_policy_test.go @@ -0,0 +1,49 @@ +package netlink + +import ( + "net" + "testing" +) + +func TestXfrmPolicyAddDel(t *testing.T) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + + src, _ := ParseIPNet("127.1.1.1/32") + dst, _ := ParseIPNet("127.1.1.2/32") + policy := XfrmPolicy{ + Src: src, + Dst: dst, + Dir: XFRM_DIR_OUT, + } + tmpl := XfrmPolicyTmpl{ + Src: net.ParseIP("127.0.0.1"), + Dst: net.ParseIP("127.0.0.2"), + Proto: XFRM_PROTO_ESP, + Mode: XFRM_MODE_TUNNEL, + } + policy.Tmpls = append(policy.Tmpls, tmpl) + if err := XfrmPolicyAdd(&policy); err != nil { + t.Fatal(err) + } + policies, err := XfrmPolicyList(FAMILY_ALL) + if err != nil { + t.Fatal(err) + } + + if len(policies) != 1 { + t.Fatal("Policy not added properly") + } + + if err = XfrmPolicyDel(&policy); err != nil { + t.Fatal(err) + } + + policies, err = XfrmPolicyList(FAMILY_ALL) + if err != nil { + t.Fatal(err) + } + if len(policies) != 0 { + t.Fatal("Policy not removed properly") + } +} diff --git a/vendor/src/github.com/vishvananda/netlink/xfrm_state.go b/vendor/src/github.com/vishvananda/netlink/xfrm_state.go new file mode 100644 index 0000000000..5b8f2df708 --- /dev/null +++ b/vendor/src/github.com/vishvananda/netlink/xfrm_state.go @@ -0,0 +1,53 @@ +package netlink + +import ( + "net" +) + +// XfrmStateAlgo represents the algorithm to use for the ipsec encryption. +type XfrmStateAlgo struct { + Name string + Key []byte + TruncateLen int // Auth only +} + +// EncapType is an enum representing an ipsec template direction. +type EncapType uint8 + +const ( + XFRM_ENCAP_ESPINUDP_NONIKE EncapType = iota + 1 + XFRM_ENCAP_ESPINUDP +) + +func (e EncapType) String() string { + switch e { + case XFRM_ENCAP_ESPINUDP_NONIKE: + return "espinudp-nonike" + case XFRM_ENCAP_ESPINUDP: + return "espinudp" + } + return "unknown" +} + +// XfrmEncap represents the encapsulation to use for the ipsec encryption. +type XfrmStateEncap struct { + Type EncapType + SrcPort int + DstPort int + OriginalAddress net.IP +} + +// XfrmState represents the state of an ipsec policy. It optionally +// contains an XfrmStateAlgo for encryption and one for authentication. +type XfrmState struct { + Dst net.IP + Src net.IP + Proto Proto + Mode Mode + Spi int + Reqid int + ReplayWindow int + Auth *XfrmStateAlgo + Crypt *XfrmStateAlgo + Encap *XfrmStateEncap +} diff --git a/vendor/src/github.com/vishvananda/netlink/xfrm_state_linux.go b/vendor/src/github.com/vishvananda/netlink/xfrm_state_linux.go new file mode 100644 index 0000000000..0f1fbd0e06 --- /dev/null +++ b/vendor/src/github.com/vishvananda/netlink/xfrm_state_linux.go @@ -0,0 +1,181 @@ +package netlink + +import ( + "fmt" + "syscall" + + "github.com/vishvananda/netlink/nl" +) + +func writeStateAlgo(a *XfrmStateAlgo) []byte { + algo := nl.XfrmAlgo{ + AlgKeyLen: uint32(len(a.Key) * 8), + AlgKey: a.Key, + } + end := len(a.Name) + if end > 64 { + end = 64 + } + copy(algo.AlgName[:end], a.Name) + return algo.Serialize() +} + +func writeStateAlgoAuth(a *XfrmStateAlgo) []byte { + algo := nl.XfrmAlgoAuth{ + AlgKeyLen: uint32(len(a.Key) * 8), + AlgTruncLen: uint32(a.TruncateLen), + AlgKey: a.Key, + } + end := len(a.Name) + if end > 64 { + end = 64 + } + copy(algo.AlgName[:end], a.Name) + return algo.Serialize() +} + +// XfrmStateAdd will add an xfrm state to the system. +// Equivalent to: `ip xfrm state add $state` +func XfrmStateAdd(state *XfrmState) error { + // A state with spi 0 can't be deleted so don't allow it to be set + if state.Spi == 0 { + return fmt.Errorf("Spi must be set when adding xfrm state.") + } + req := nl.NewNetlinkRequest(nl.XFRM_MSG_NEWSA, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK) + + msg := &nl.XfrmUsersaInfo{} + msg.Family = uint16(nl.GetIPFamily(state.Dst)) + msg.Id.Daddr.FromIP(state.Dst) + msg.Saddr.FromIP(state.Src) + msg.Id.Proto = uint8(state.Proto) + msg.Mode = uint8(state.Mode) + msg.Id.Spi = nl.Swap32(uint32(state.Spi)) + msg.Reqid = uint32(state.Reqid) + msg.ReplayWindow = uint8(state.ReplayWindow) + msg.Lft.SoftByteLimit = nl.XFRM_INF + msg.Lft.HardByteLimit = nl.XFRM_INF + msg.Lft.SoftPacketLimit = nl.XFRM_INF + msg.Lft.HardPacketLimit = nl.XFRM_INF + req.AddData(msg) + + if state.Auth != nil { + out := nl.NewRtAttr(nl.XFRMA_ALG_AUTH_TRUNC, writeStateAlgoAuth(state.Auth)) + req.AddData(out) + } + if state.Crypt != nil { + out := nl.NewRtAttr(nl.XFRMA_ALG_CRYPT, writeStateAlgo(state.Crypt)) + req.AddData(out) + } + if state.Encap != nil { + encapData := make([]byte, nl.SizeofXfrmEncapTmpl) + encap := nl.DeserializeXfrmEncapTmpl(encapData) + encap.EncapType = uint16(state.Encap.Type) + encap.EncapSport = nl.Swap16(uint16(state.Encap.SrcPort)) + encap.EncapDport = nl.Swap16(uint16(state.Encap.DstPort)) + encap.EncapOa.FromIP(state.Encap.OriginalAddress) + out := nl.NewRtAttr(nl.XFRMA_ENCAP, encapData) + req.AddData(out) + } + + _, err := req.Execute(syscall.NETLINK_XFRM, 0) + return err +} + +// XfrmStateDel will delete an xfrm state from the system. Note that +// the Algos are ignored when matching the state to delete. +// Equivalent to: `ip xfrm state del $state` +func XfrmStateDel(state *XfrmState) error { + req := nl.NewNetlinkRequest(nl.XFRM_MSG_DELSA, syscall.NLM_F_ACK) + + msg := &nl.XfrmUsersaId{} + msg.Daddr.FromIP(state.Dst) + msg.Family = uint16(nl.GetIPFamily(state.Dst)) + msg.Proto = uint8(state.Proto) + msg.Spi = nl.Swap32(uint32(state.Spi)) + req.AddData(msg) + + saddr := nl.XfrmAddress{} + saddr.FromIP(state.Src) + srcdata := nl.NewRtAttr(nl.XFRMA_SRCADDR, saddr.Serialize()) + + req.AddData(srcdata) + + _, err := req.Execute(syscall.NETLINK_XFRM, 0) + return err +} + +// XfrmStateList gets a list of xfrm states in the system. +// Equivalent to: `ip xfrm state show`. +// The list can be filtered by ip family. +func XfrmStateList(family int) ([]XfrmState, error) { + req := nl.NewNetlinkRequest(nl.XFRM_MSG_GETSA, syscall.NLM_F_DUMP) + + msg := nl.NewIfInfomsg(family) + req.AddData(msg) + + msgs, err := req.Execute(syscall.NETLINK_XFRM, nl.XFRM_MSG_NEWSA) + if err != nil { + return nil, err + } + + res := make([]XfrmState, 0) + for _, m := range msgs { + msg := nl.DeserializeXfrmUsersaInfo(m) + + if family != FAMILY_ALL && family != int(msg.Family) { + continue + } + + var state XfrmState + + state.Dst = msg.Id.Daddr.ToIP() + state.Src = msg.Saddr.ToIP() + state.Proto = Proto(msg.Id.Proto) + state.Mode = Mode(msg.Mode) + state.Spi = int(nl.Swap32(msg.Id.Spi)) + state.Reqid = int(msg.Reqid) + state.ReplayWindow = int(msg.ReplayWindow) + + attrs, err := nl.ParseRouteAttr(m[msg.Len():]) + if err != nil { + return nil, err + } + + for _, attr := range attrs { + switch attr.Attr.Type { + case nl.XFRMA_ALG_AUTH, nl.XFRMA_ALG_CRYPT: + var resAlgo *XfrmStateAlgo + if attr.Attr.Type == nl.XFRMA_ALG_AUTH { + if state.Auth == nil { + state.Auth = new(XfrmStateAlgo) + } + resAlgo = state.Auth + } else { + state.Crypt = new(XfrmStateAlgo) + resAlgo = state.Crypt + } + algo := nl.DeserializeXfrmAlgo(attr.Value[:]) + (*resAlgo).Name = nl.BytesToString(algo.AlgName[:]) + (*resAlgo).Key = algo.AlgKey + case nl.XFRMA_ALG_AUTH_TRUNC: + if state.Auth == nil { + state.Auth = new(XfrmStateAlgo) + } + algo := nl.DeserializeXfrmAlgoAuth(attr.Value[:]) + state.Auth.Name = nl.BytesToString(algo.AlgName[:]) + state.Auth.Key = algo.AlgKey + state.Auth.TruncateLen = int(algo.AlgTruncLen) + case nl.XFRMA_ENCAP: + encap := nl.DeserializeXfrmEncapTmpl(attr.Value[:]) + state.Encap = new(XfrmStateEncap) + state.Encap.Type = EncapType(encap.EncapType) + state.Encap.SrcPort = int(nl.Swap16(encap.EncapSport)) + state.Encap.DstPort = int(nl.Swap16(encap.EncapDport)) + state.Encap.OriginalAddress = encap.EncapOa.ToIP() + } + + } + res = append(res, state) + } + return res, nil +} diff --git a/vendor/src/github.com/vishvananda/netlink/xfrm_state_test.go b/vendor/src/github.com/vishvananda/netlink/xfrm_state_test.go new file mode 100644 index 0000000000..df57ef8b7e --- /dev/null +++ b/vendor/src/github.com/vishvananda/netlink/xfrm_state_test.go @@ -0,0 +1,50 @@ +package netlink + +import ( + "net" + "testing" +) + +func TestXfrmStateAddDel(t *testing.T) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + + state := XfrmState{ + Src: net.ParseIP("127.0.0.1"), + Dst: net.ParseIP("127.0.0.2"), + Proto: XFRM_PROTO_ESP, + Mode: XFRM_MODE_TUNNEL, + Spi: 1, + Auth: &XfrmStateAlgo{ + Name: "hmac(sha256)", + Key: []byte("abcdefghijklmnopqrstuvwzyzABCDEF"), + }, + Crypt: &XfrmStateAlgo{ + Name: "cbc(aes)", + Key: []byte("abcdefghijklmnopqrstuvwzyzABCDEF"), + }, + } + if err := XfrmStateAdd(&state); err != nil { + t.Fatal(err) + } + policies, err := XfrmStateList(FAMILY_ALL) + if err != nil { + t.Fatal(err) + } + + if len(policies) != 1 { + t.Fatal("State not added properly") + } + + if err = XfrmStateDel(&state); err != nil { + t.Fatal(err) + } + + policies, err = XfrmStateList(FAMILY_ALL) + if err != nil { + t.Fatal(err) + } + if len(policies) != 0 { + t.Fatal("State not removed properly") + } +} diff --git a/vendor/src/github.com/vishvananda/netns/LICENSE b/vendor/src/github.com/vishvananda/netns/LICENSE new file mode 100644 index 0000000000..9f64db8582 --- /dev/null +++ b/vendor/src/github.com/vishvananda/netns/LICENSE @@ -0,0 +1,192 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + Copyright 2014 Vishvananda Ishaya. + Copyright 2014 Docker, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/src/github.com/vishvananda/netns/README.md b/vendor/src/github.com/vishvananda/netns/README.md new file mode 100644 index 0000000000..24a4003ae6 --- /dev/null +++ b/vendor/src/github.com/vishvananda/netns/README.md @@ -0,0 +1,49 @@ +# netns - network namespaces in go # + +The netns package provides an ultra-simple interface for handling +network namespaces in go. Changing namespaces requires elevated +privileges, so in most cases this code needs to be run as root. + +## Local Build and Test ## + +You can use go get command: + + go get github.com/vishvananda/netns + +Testing (requires root): + + sudo -E go test github.com/vishvananda/netns + +## Example ## + +```go +package main + +import ( + "net" + "runtime" + "github.com/vishvananada/netns" +) + +func main() { + // Lock the OS Thread so we don't accidentally switch namespaces + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + // Save the current network namespace + origns, _ := netns.Get() + defer origns.Close() + + // Create a new network namespace + newns, _ := netns.New() + defer newns.Close() + + // Do something with tne network namespace + ifaces, _ := net.Interfaces() + fmt.Printf("Interfaces: %v\n", ifaces) + + // Switch back to the original namespace + netns.Set(origns) +} + +``` diff --git a/vendor/src/github.com/vishvananda/netns/netns.go b/vendor/src/github.com/vishvananda/netns/netns.go new file mode 100644 index 0000000000..3878da3389 --- /dev/null +++ b/vendor/src/github.com/vishvananda/netns/netns.go @@ -0,0 +1,66 @@ +// Package netns allows ultra-simple network namespace handling. NsHandles +// can be retrieved and set. Note that the current namespace is thread +// local so actions that set and reset namespaces should use LockOSThread +// to make sure the namespace doesn't change due to a goroutine switch. +// It is best to close NsHandles when you are done with them. This can be +// accomplished via a `defer ns.Close()` on the handle. Changing namespaces +// requires elevated privileges, so in most cases this code needs to be run +// as root. +package netns + +import ( + "fmt" + "syscall" +) +// NsHandle is a handle to a network namespace. It can be cast directly +// to an int and used as a file descriptor. +type NsHandle int + +// Equal determines if two network handles refer to the same network +// namespace. This is done by comparing the device and inode that the +// file descripors point to. +func (ns NsHandle) Equal(other NsHandle) bool { + if ns == other { + return true + } + var s1, s2 syscall.Stat_t + if err := syscall.Fstat(int(ns), &s1); err != nil { + return false + } + if err := syscall.Fstat(int(other), &s2); err != nil { + return false + } + return (s1.Dev == s2.Dev) && (s1.Ino == s2.Ino) +} + +// String shows the file descriptor number and its dev and inode. +func (ns NsHandle) String() string { + var s syscall.Stat_t + if ns == -1 { + return "NS(None)" + } + if err := syscall.Fstat(int(ns), &s); err != nil { + return fmt.Sprintf("NS(%d: unknown)", ns) + } + return fmt.Sprintf("NS(%d: %d, %d)", ns, s.Dev, s.Ino) +} + +// IsOpen returns true if Close() has not been called. +func (ns NsHandle) IsOpen() bool { + return ns != -1 +} + +// Close closes the NsHandle and resets its file descriptor to -1. +// It is not safe to use an NsHandle after Close() is called. +func (ns *NsHandle) Close() error { + if err := syscall.Close(int(*ns)); err != nil { + return err + } + (*ns) = -1 + return nil +} + +// Get an empty (closed) NsHandle +func None() NsHandle { + return NsHandle(-1) +} diff --git a/vendor/src/github.com/vishvananda/netns/netns_linux.go b/vendor/src/github.com/vishvananda/netns/netns_linux.go new file mode 100644 index 0000000000..1cf5e136ec --- /dev/null +++ b/vendor/src/github.com/vishvananda/netns/netns_linux.go @@ -0,0 +1,206 @@ +package netns + +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" + "strconv" + "strings" + "syscall" +) + +const ( + // These constants belong in the syscall library but have not been + // added yet. + CLONE_NEWUTS = 0x04000000 /* New utsname group? */ + CLONE_NEWIPC = 0x08000000 /* New ipcs */ + CLONE_NEWUSER = 0x10000000 /* New user namespace */ + CLONE_NEWPID = 0x20000000 /* New pid namespace */ + CLONE_NEWNET = 0x40000000 /* New network namespace */ + CLONE_IO = 0x80000000 /* Get io context */ +) + +// Setns sets namespace using syscall. Note that this should be a method +// in syscall but it has not been added. +func Setns(ns NsHandle, nstype int) (err error) { + _, _, e1 := syscall.Syscall(SYS_SETNS, uintptr(ns), uintptr(nstype), 0) + if e1 != 0 { + err = e1 + } + return +} + +// Set sets the current network namespace to the namespace represented +// by NsHandle. +func Set(ns NsHandle) (err error) { + return Setns(ns, CLONE_NEWNET) +} + +// New creates a new network namespace and returns a handle to it. +func New() (ns NsHandle, err error) { + if err := syscall.Unshare(CLONE_NEWNET); err != nil { + return -1, err + } + return Get() +} + +// Get gets a handle to the current threads network namespace. +func Get() (NsHandle, error) { + return GetFromThread(os.Getpid(), syscall.Gettid()) +} + +// GetFromName gets a handle to a named network namespace such as one +// created by `ip netns add`. +func GetFromName(name string) (NsHandle, error) { + fd, err := syscall.Open(fmt.Sprintf("/var/run/netns/%s", name), syscall.O_RDONLY, 0) + if err != nil { + return -1, err + } + return NsHandle(fd), nil +} + +// GetFromPid gets a handle to the network namespace of a given pid. +func GetFromPid(pid int) (NsHandle, error) { + fd, err := syscall.Open(fmt.Sprintf("/proc/%d/ns/net", pid), syscall.O_RDONLY, 0) + if err != nil { + return -1, err + } + return NsHandle(fd), nil +} + +// GetFromThread gets a handle to the network namespace of a given pid and tid. +func GetFromThread(pid, tid int) (NsHandle, error) { + name := fmt.Sprintf("/proc/%d/task/%d/ns/net", pid, tid) + fd, err := syscall.Open(name, syscall.O_RDONLY, 0) + if err != nil { + return -1, err + } + return NsHandle(fd), nil +} + +// GetFromDocker gets a handle to the network namespace of a docker container. +// Id is prefixed matched against the running docker containers, so a short +// identifier can be used as long as it isn't ambiguous. +func GetFromDocker(id string) (NsHandle, error) { + pid, err := getPidForContainer(id) + if err != nil { + return -1, err + } + return GetFromPid(pid) +} + +// borrowed from docker/utils/utils.go +func findCgroupMountpoint(cgroupType string) (string, error) { + output, err := ioutil.ReadFile("/proc/mounts") + if err != nil { + return "", err + } + + // /proc/mounts has 6 fields per line, one mount per line, e.g. + // cgroup /sys/fs/cgroup/devices cgroup rw,relatime,devices 0 0 + for _, line := range strings.Split(string(output), "\n") { + parts := strings.Split(line, " ") + if len(parts) == 6 && parts[2] == "cgroup" { + for _, opt := range strings.Split(parts[3], ",") { + if opt == cgroupType { + return parts[1], nil + } + } + } + } + + return "", fmt.Errorf("cgroup mountpoint not found for %s", cgroupType) +} + +// Returns the relative path to the cgroup docker is running in. +// borrowed from docker/utils/utils.go +// modified to get the docker pid instead of using /proc/self +func getThisCgroup(cgroupType string) (string, error) { + dockerpid, err := ioutil.ReadFile("/var/run/docker.pid") + if err != nil { + return "", err + } + result := strings.Split(string(dockerpid), "\n") + if len(result) == 0 || len(result[0]) == 0 { + return "", fmt.Errorf("docker pid not found in /var/run/docker.pid") + } + pid, err := strconv.Atoi(result[0]) + + output, err := ioutil.ReadFile(fmt.Sprintf("/proc/%d/cgroup", pid)) + if err != nil { + return "", err + } + for _, line := range strings.Split(string(output), "\n") { + parts := strings.Split(line, ":") + // any type used by docker should work + if parts[1] == cgroupType { + return parts[2], nil + } + } + return "", fmt.Errorf("cgroup '%s' not found in /proc/%d/cgroup", cgroupType, pid) +} + +// Returns the first pid in a container. +// borrowed from docker/utils/utils.go +// modified to only return the first pid +// modified to glob with id +// modified to search for newer docker containers +func getPidForContainer(id string) (int, error) { + pid := 0 + + // memory is chosen randomly, any cgroup used by docker works + cgroupType := "memory" + + cgroupRoot, err := findCgroupMountpoint(cgroupType) + if err != nil { + return pid, err + } + + cgroupThis, err := getThisCgroup(cgroupType) + if err != nil { + return pid, err + } + + id += "*" + + attempts := []string{ + filepath.Join(cgroupRoot, cgroupThis, id, "tasks"), + // With more recent lxc versions use, cgroup will be in lxc/ + filepath.Join(cgroupRoot, cgroupThis, "lxc", id, "tasks"), + // With more recent dockee, cgroup will be in docker/ + filepath.Join(cgroupRoot, cgroupThis, "docker", id, "tasks"), + } + + var filename string + for _, attempt := range attempts { + filenames, _ := filepath.Glob(attempt) + if len(filenames) > 1 { + return pid, fmt.Errorf("Ambiguous id supplied: %v", filenames) + } else if len(filenames) == 1 { + filename = filenames[0] + break + } + } + + if filename == "" { + return pid, fmt.Errorf("Unable to find container: %v", id[:len(id)-1]) + } + + output, err := ioutil.ReadFile(filename) + if err != nil { + return pid, err + } + + result := strings.Split(string(output), "\n") + if len(result) == 0 || len(result[0]) == 0 { + return pid, fmt.Errorf("No pid found for container") + } + + pid, err = strconv.Atoi(result[0]) + if err != nil { + return pid, fmt.Errorf("Invalid pid '%s': %s", result[0], err) + } + + return pid, nil +} diff --git a/vendor/src/github.com/vishvananda/netns/netns_linux_386.go b/vendor/src/github.com/vishvananda/netns/netns_linux_386.go new file mode 100644 index 0000000000..0a6fe49a0b --- /dev/null +++ b/vendor/src/github.com/vishvananda/netns/netns_linux_386.go @@ -0,0 +1,5 @@ +package netns + +const ( + SYS_SETNS = 346 +) diff --git a/vendor/src/github.com/vishvananda/netns/netns_linux_amd.go b/vendor/src/github.com/vishvananda/netns/netns_linux_amd.go new file mode 100644 index 0000000000..bbf3f4de49 --- /dev/null +++ b/vendor/src/github.com/vishvananda/netns/netns_linux_amd.go @@ -0,0 +1,5 @@ +package netns + +const ( + SYS_SETNS = 308 +) diff --git a/vendor/src/github.com/vishvananda/netns/netns_linux_arm.go b/vendor/src/github.com/vishvananda/netns/netns_linux_arm.go new file mode 100644 index 0000000000..e35cb07647 --- /dev/null +++ b/vendor/src/github.com/vishvananda/netns/netns_linux_arm.go @@ -0,0 +1,5 @@ +package netns + +const ( + SYS_SETNS = 374 +) diff --git a/vendor/src/github.com/vishvananda/netns/netns_test.go b/vendor/src/github.com/vishvananda/netns/netns_test.go new file mode 100644 index 0000000000..e51981cc1b --- /dev/null +++ b/vendor/src/github.com/vishvananda/netns/netns_test.go @@ -0,0 +1,66 @@ +package netns + +import ( + "runtime" + "sync" + "testing" +) + +func TestGetNewSetDelete(t *testing.T) { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + origns, err := Get() + if err != nil { + t.Fatal(err) + } + newns, err := New() + if err != nil { + t.Fatal(err) + } + if origns.Equal(newns) { + t.Fatal("New ns failed") + } + if err := Set(origns); err != nil { + t.Fatal(err) + } + newns.Close() + if newns.IsOpen() { + t.Fatal("newns still open after close", newns) + } + ns, err := Get() + if err != nil { + t.Fatal(err) + } + if !ns.Equal(origns) { + t.Fatal("Reset ns failed", origns, newns, ns) + } +} + +func TestNone(t *testing.T) { + ns := None() + if ns.IsOpen() { + t.Fatal("None ns is open", ns) + } +} + +func TestThreaded(t *testing.T) { + ncpu := runtime.GOMAXPROCS(-1) + if ncpu < 2 { + t.Skip("-cpu=2 or larger required") + } + + // Lock this thread simply to ensure other threads get used. + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + wg := &sync.WaitGroup{} + for i := 0; i < ncpu; i++ { + wg.Add(1) + go func() { + defer wg.Done() + TestGetNewSetDelete(t) + }() + } + wg.Wait() +} diff --git a/vendor/src/github.com/vishvananda/netns/netns_unspecified.go b/vendor/src/github.com/vishvananda/netns/netns_unspecified.go new file mode 100644 index 0000000000..42a804fe88 --- /dev/null +++ b/vendor/src/github.com/vishvananda/netns/netns_unspecified.go @@ -0,0 +1,35 @@ +// +build !linux + +package netns + +import ( + "errors" +) + +var ( + ErrNotImplemented = errors.New("not implemented") +) + +func Set(ns Namespace) (err error) { + return ErrNotImplemented +} + +func New() (ns Namespace, err error) { + return -1, ErrNotImplemented +} + +func Get() (Namespace, error) { + return -1, ErrNotImplemented +} + +func GetFromName(name string) (Namespace, error) { + return -1, ErrNotImplemented +} + +func GetFromPid(pid int) (Namespace, error) { + return -1, ErrNotImplemented +} + +func GetFromDocker(id string) (Namespace, error) { + return -1, ErrNotImplemented +}