diff --git a/container/container_unix.go b/container/container_unix.go index 61daa177b1..3cffb8a1d0 100644 --- a/container/container_unix.go +++ b/container/container_unix.go @@ -290,7 +290,6 @@ func (container *Container) BuildJoinOptions(n libnetwork.Network) ([]libnetwork // BuildCreateEndpointOptions builds endpoint options from a given network. func (container *Container) BuildCreateEndpointOptions(n libnetwork.Network, epConfig *network.EndpointSettings, sb libnetwork.Sandbox) ([]libnetwork.EndpointOption, error) { var ( - portSpecs = make(nat.PortSet) bindings = make(nat.PortMap) pbList []types.PortBinding exposeList []types.TransportPort @@ -343,10 +342,6 @@ func (container *Container) BuildCreateEndpointOptions(n libnetwork.Network, epC return createOptions, nil } - if container.Config.ExposedPorts != nil { - portSpecs = container.Config.ExposedPorts - } - if container.HostConfig.PortBindings != nil { for p, b := range container.HostConfig.PortBindings { bindings[p] = []nat.PortBinding{} @@ -359,6 +354,7 @@ func (container *Container) BuildCreateEndpointOptions(n libnetwork.Network, epC } } + portSpecs := container.Config.ExposedPorts ports := make([]nat.Port, len(portSpecs)) var i int for p := range portSpecs { diff --git a/daemon/container_operations_unix.go b/daemon/container_operations_unix.go index 2731ce3409..e93f2dad4c 100644 --- a/daemon/container_operations_unix.go +++ b/daemon/container_operations_unix.go @@ -4,6 +4,7 @@ package daemon import ( "fmt" + "net" "os" "path" "path/filepath" @@ -25,10 +26,12 @@ import ( "github.com/docker/docker/runconfig" containertypes "github.com/docker/engine-api/types/container" networktypes "github.com/docker/engine-api/types/network" + "github.com/docker/go-connections/nat" "github.com/docker/go-units" "github.com/docker/libnetwork" "github.com/docker/libnetwork/netlabel" "github.com/docker/libnetwork/options" + "github.com/docker/libnetwork/types" "github.com/opencontainers/runc/libcontainer/configs" "github.com/opencontainers/runc/libcontainer/devices" "github.com/opencontainers/runc/libcontainer/label" @@ -320,6 +323,9 @@ func (daemon *Daemon) buildSandboxOptions(container *container.Container, n libn dns []string dnsSearch []string dnsOptions []string + bindings = make(nat.PortMap) + pbList []types.PortBinding + exposeList []types.TransportPort ) sboxOptions = append(sboxOptions, libnetwork.OptionHostname(container.Config.Hostname), @@ -394,6 +400,59 @@ func (daemon *Daemon) buildSandboxOptions(container *container.Container, n libn sboxOptions = append(sboxOptions, libnetwork.OptionExtraHost(parts[0], parts[1])) } + if container.HostConfig.PortBindings != nil { + for p, b := range container.HostConfig.PortBindings { + bindings[p] = []nat.PortBinding{} + for _, bb := range b { + bindings[p] = append(bindings[p], nat.PortBinding{ + HostIP: bb.HostIP, + HostPort: bb.HostPort, + }) + } + } + } + + portSpecs := container.Config.ExposedPorts + ports := make([]nat.Port, len(portSpecs)) + var i int + for p := range portSpecs { + ports[i] = p + i++ + } + nat.SortPortMap(ports, bindings) + for _, port := range ports { + expose := types.TransportPort{} + expose.Proto = types.ParseProtocol(port.Proto()) + expose.Port = uint16(port.Int()) + exposeList = append(exposeList, expose) + + pb := types.PortBinding{Port: expose.Port, Proto: expose.Proto} + binding := bindings[port] + for i := 0; i < len(binding); i++ { + pbCopy := pb.GetCopy() + newP, err := nat.NewPort(nat.SplitProtoPort(binding[i].HostPort)) + var portStart, portEnd int + if err == nil { + portStart, portEnd, err = newP.Range() + } + if err != nil { + return nil, fmt.Errorf("Error parsing HostPort value(%s):%v", binding[i].HostPort, err) + } + pbCopy.HostPort = uint16(portStart) + pbCopy.HostPortEnd = uint16(portEnd) + pbCopy.HostIP = net.ParseIP(binding[i].HostIP) + pbList = append(pbList, pbCopy) + } + + if container.HostConfig.PublishAllPorts && len(binding) == 0 { + pbList = append(pbList, pb) + } + } + + sboxOptions = append(sboxOptions, + libnetwork.OptionPortMapping(pbList), + libnetwork.OptionExposedPorts(exposeList)) + // Link feature is supported only for the default bridge network. // return if this call to build join options is not for default bridge network if n.Name() != "bridge" { diff --git a/hack/vendor.sh b/hack/vendor.sh index 68db3d5517..8117d9534f 100755 --- a/hack/vendor.sh +++ b/hack/vendor.sh @@ -29,14 +29,14 @@ clone git github.com/RackSec/srslog 6eb773f331e46fbba8eecb8e794e635e75fc04de clone git github.com/imdario/mergo 0.2.1 #get libnetwork packages -clone git github.com/docker/libnetwork v0.7.0-dev.3 +clone git github.com/docker/libnetwork v0.7.0-dev.5 clone git github.com/armon/go-metrics eb0af217e5e9747e41dd5303755356b62d28e3ec clone git github.com/hashicorp/go-msgpack 71c2886f5a673a35f909803f38ece5810165097b clone git github.com/hashicorp/memberlist 9a1e242e454d2443df330bdd51a436d5a9058fc4 clone git github.com/hashicorp/serf 7151adcef72687bf95f451a2e0ba15cb19412bf2 clone git github.com/docker/libkv c2aac5dbbaa5c872211edea7c0f32b3bd67e7410 clone git github.com/vishvananda/netns 604eaf189ee867d8c147fafc28def2394e878d25 -clone git github.com/vishvananda/netlink bfd70f556483c008636b920dda142fdaa0d59ef9 +clone git github.com/vishvananda/netlink 631962935bff4f3d20ff32a72e8944f6d2836a26 clone git github.com/BurntSushi/toml f706d00e3de6abe700c994cdd545a1a4915af060 clone git github.com/samuel/go-zookeeper d0e0d8e11f318e000a8cc434616d69e329edc374 clone git github.com/deckarep/golang-set ef32fa3046d9f249d399f98ebaf9be944430fd1d diff --git a/integration-cli/docker_cli_network_unix_test.go b/integration-cli/docker_cli_network_unix_test.go index 84349b7c3c..fd6da66883 100644 --- a/integration-cli/docker_cli_network_unix_test.go +++ b/integration-cli/docker_cli_network_unix_test.go @@ -1432,7 +1432,7 @@ func (s *DockerSuite) TestDockerNetworkInternalMode(c *check.C) { c.Assert(waitRun("second"), check.IsNil) out, _, err := dockerCmdWithError("exec", "first", "ping", "-W", "4", "-c", "1", "www.google.com") c.Assert(err, check.NotNil) - c.Assert(out, checker.Contains, "100% packet loss") + c.Assert(out, checker.Contains, "ping: bad address") _, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "first") c.Assert(err, check.IsNil) } diff --git a/vendor/src/github.com/docker/libnetwork/CHANGELOG.md b/vendor/src/github.com/docker/libnetwork/CHANGELOG.md index 35fe418a43..c2ebca7730 100644 --- a/vendor/src/github.com/docker/libnetwork/CHANGELOG.md +++ b/vendor/src/github.com/docker/libnetwork/CHANGELOG.md @@ -1,5 +1,25 @@ # Changelog +## 0.7.0-dev.5 (2016-03-08) +- Fixes https://github.com/docker/docker/issues/20847 +- Fixes https://github.com/docker/docker/issues/20997 +- Fixes issues unveiled by docker integ test over 0.7.0-dev.4 + +## 0.7.0-dev.4 (2016-03-07) +- Changed ownership of exposed ports and port-mapping options from Endpoint to Sandbox +- Implement DNS RR in the Docker embedded DNS server +- Fixes https://github.com/docker/libnetwork/issues/984 (multi container overlay veth leak) +- Libnetwork to program container's interface MAC address +- Fixed bug in iptables.Exists() logic +- Fixes https://github.com/docker/docker/issues/20694 +- Source external DNS queries from container namespace +- Added inbuilt nil IPAM driver +- Windows drivers integration fixes +- Extract hostname from (hostname.domainname). Related to https://github.com/docker/docker/issues/14282 +- Fixed race in sandbox statistics read +- Fixes https://github.com/docker/libnetwork/issues/892 (docker start fails when ipv6.disable=1) +- Fixed error message on bridge network creation conflict + ## 0.7.0-dev.3 (2016-02-17) - Fixes https://github.com/docker/docker/issues/20350 - Fixes https://github.com/docker/docker/issues/20145 @@ -90,7 +110,7 @@ - DEPRECATE service discovery from default bridge network - Introduced new network UX - Support for multiple networks in bridge driver -- Local persistance with boltdb +- Local persistence with boltdb ## 0.4.0 (2015-07-24) diff --git a/vendor/src/github.com/docker/libnetwork/MAINTAINERS b/vendor/src/github.com/docker/libnetwork/MAINTAINERS index 1e68125010..da991614c3 100644 --- a/vendor/src/github.com/docker/libnetwork/MAINTAINERS +++ b/vendor/src/github.com/docker/libnetwork/MAINTAINERS @@ -17,6 +17,7 @@ "mrjana", "mavenugo", "sanimej", + "chenchun", ] [people] @@ -37,6 +38,11 @@ Email = "lk4d4@docker.com" GitHub = "LK4D4" + [people.chenchun] + Name = "Chun Chen" + Email = "ramichen@tencent.com" + GitHub = "chenchun" + [people.icecrime] Name = "Arnaud Porterie" Email = "arnaud@docker.com" diff --git a/vendor/src/github.com/docker/libnetwork/bitseq/sequence.go b/vendor/src/github.com/docker/libnetwork/bitseq/sequence.go index 270a36aa63..0dc1bc4ad0 100644 --- a/vendor/src/github.com/docker/libnetwork/bitseq/sequence.go +++ b/vendor/src/github.com/docker/libnetwork/bitseq/sequence.go @@ -163,7 +163,7 @@ func (s *sequence) toByteArray() ([]byte, error) { func (s *sequence) fromByteArray(data []byte) error { l := len(data) if l%12 != 0 { - return fmt.Errorf("cannot deserialize byte sequence of lenght %d (%v)", l, data) + return fmt.Errorf("cannot deserialize byte sequence of length %d (%v)", l, data) } p := s diff --git a/vendor/src/github.com/docker/libnetwork/controller.go b/vendor/src/github.com/docker/libnetwork/controller.go index 3a5e188cec..6abc4d1ddf 100644 --- a/vendor/src/github.com/docker/libnetwork/controller.go +++ b/vendor/src/github.com/docker/libnetwork/controller.go @@ -170,7 +170,7 @@ func New(cfgOptions ...config.Option) (NetworkController, error) { if c.cfg != nil && c.cfg.Cluster.Watcher != nil { if err := c.initDiscovery(c.cfg.Cluster.Watcher); err != nil { - // Failing to initalize discovery is a bad situation to be in. + // Failing to initialize discovery is a bad situation to be in. // But it cannot fail creating the Controller log.Errorf("Failed to Initialize Discovery : %v", err) } diff --git a/vendor/src/github.com/docker/libnetwork/datastore/datastore.go b/vendor/src/github.com/docker/libnetwork/datastore/datastore.go index 687473d275..c15cd620c1 100644 --- a/vendor/src/github.com/docker/libnetwork/datastore/datastore.go +++ b/vendor/src/github.com/docker/libnetwork/datastore/datastore.go @@ -31,7 +31,7 @@ type DataStore interface { DeleteObjectAtomic(kvObject KVObject) error // DeleteTree deletes a record DeleteTree(kvObject KVObject) error - // Watchable returns whether the store is watchable are not + // Watchable returns whether the store is watchable or not Watchable() bool // Watch for changes on a KVObject Watch(kvObject KVObject, stopCh <-chan struct{}) (<-chan KVObject, error) diff --git a/vendor/src/github.com/docker/libnetwork/default_gateway.go b/vendor/src/github.com/docker/libnetwork/default_gateway.go index 2df047a348..d8eb732701 100644 --- a/vendor/src/github.com/docker/libnetwork/default_gateway.go +++ b/vendor/src/github.com/docker/libnetwork/default_gateway.go @@ -3,7 +3,6 @@ package libnetwork import ( "fmt" - "github.com/docker/libnetwork/netlabel" "github.com/docker/libnetwork/types" ) @@ -28,15 +27,15 @@ var procGwNetwork = make(chan (bool), 1) - its deleted when an endpoint with GW joins the container */ -func (sb *sandbox) setupDefaultGW(srcEp *endpoint) error { - var createOptions []EndpointOption - c := srcEp.getNetwork().getController() +func (sb *sandbox) setupDefaultGW() error { // check if the conitainer already has a GW endpoint if ep := sb.getEndpointInGWNetwork(); ep != nil { return nil } + c := sb.controller + // Look for default gw network. In case of error (includes not found), // retry and create it if needed in a serialized execution. n, err := c.NetworkByName(libnGWNetwork) @@ -46,19 +45,7 @@ func (sb *sandbox) setupDefaultGW(srcEp *endpoint) error { } } - if opt, ok := srcEp.generic[netlabel.PortMap]; ok { - if pb, ok := opt.([]types.PortBinding); ok { - createOptions = append(createOptions, CreateOptionPortMapping(pb)) - } - } - - if opt, ok := srcEp.generic[netlabel.ExposedPorts]; ok { - if exp, ok := opt.([]types.TransportPort); ok { - createOptions = append(createOptions, CreateOptionExposedPorts(exp)) - } - } - - createOptions = append(createOptions, CreateOptionAnonymous()) + createOptions := []EndpointOption{CreateOptionAnonymous()} eplen := gwEPlen if len(sb.containerID) < gwEPlen { @@ -74,9 +61,13 @@ func (sb *sandbox) setupDefaultGW(srcEp *endpoint) error { if err := epLocal.sbJoin(sb); err != nil { return fmt.Errorf("container %s: endpoint join on GW Network failed: %v", sb.containerID, err) } + return nil } +// If present, removes the endpoint connecting the sandbox to the default gw network. +// Unless it is the endpoint designated to provide the external connectivity. +// If the sandbox is being deleted, removes the endpoint unconditionally. func (sb *sandbox) clearDefaultGW() error { var ep *endpoint @@ -84,6 +75,10 @@ func (sb *sandbox) clearDefaultGW() error { return nil } + if ep == sb.getGatewayEndpoint() && !sb.inDelete { + return nil + } + if err := ep.sbLeave(sb, false); err != nil { return fmt.Errorf("container %s: endpoint leaving GW Network failed: %v", sb.containerID, err) } @@ -98,7 +93,7 @@ func (sb *sandbox) needDefaultGW() bool { for _, ep := range sb.getConnectedEndpoints() { if ep.endpointInGWNetwork() { - continue + return false } if ep.getNetwork().Type() == "null" || ep.getNetwork().Type() == "host" { continue @@ -165,3 +160,16 @@ func (c *controller) defaultGwNetwork() (Network, error) { } return n, err } + +// Returns the endpoint which is providing external connectivity to the sandbox +func (sb *sandbox) getGatewayEndpoint() *endpoint { + for _, ep := range sb.getConnectedEndpoints() { + if ep.getNetwork().Type() == "null" || ep.getNetwork().Type() == "host" { + continue + } + if len(ep.Gateway()) != 0 { + return ep + } + } + return nil +} diff --git a/vendor/src/github.com/docker/libnetwork/discoverapi/discoverapi.go b/vendor/src/github.com/docker/libnetwork/discoverapi/discoverapi.go index 03e9e909cf..eeacc3204e 100644 --- a/vendor/src/github.com/docker/libnetwork/discoverapi/discoverapi.go +++ b/vendor/src/github.com/docker/libnetwork/discoverapi/discoverapi.go @@ -16,7 +16,7 @@ type DiscoveryType int const ( // NodeDiscovery represents Node join/leave events provided by discovery NodeDiscovery = iota + 1 - // DatastoreConfig represents a add/remove datastore event + // DatastoreConfig represents an add/remove datastore event DatastoreConfig ) diff --git a/vendor/src/github.com/docker/libnetwork/driverapi/driverapi.go b/vendor/src/github.com/docker/libnetwork/driverapi/driverapi.go index 884e23e914..4ea5e11278 100644 --- a/vendor/src/github.com/docker/libnetwork/driverapi/driverapi.go +++ b/vendor/src/github.com/docker/libnetwork/driverapi/driverapi.go @@ -42,6 +42,14 @@ type Driver interface { // Leave method is invoked when a Sandbox detaches from an endpoint. Leave(nid, eid string) error + // ProgramExternalConnectivity invokes the driver method which does the necessary + // programming to allow the external connectivity dictated by the passed options + ProgramExternalConnectivity(nid, eid string, options map[string]interface{}) error + + // RevokeExternalConnectivity aks the driver to remove any external connectivity + // programming that was done so far + RevokeExternalConnectivity(nid, eid string) error + // Type returns the the type of this driver, the network type this driver manages Type() string } @@ -88,8 +96,8 @@ type JoinInfo interface { // SetGatewayIPv6 sets the default IPv6 gateway when a container joins the endpoint. SetGatewayIPv6(net.IP) error - // AddStaticRoute adds a routes to the sandbox. - // It may be used in addtion to or instead of a default gateway (as above). + // AddStaticRoute adds a route to the sandbox. + // It may be used in addition to or instead of a default gateway (as above). AddStaticRoute(destination *net.IPNet, routeType int, nextHop net.IP) error // DisableGatewayService tells libnetwork not to provide Default GW for the container diff --git a/vendor/src/github.com/docker/libnetwork/driverapi/ipamdata.go b/vendor/src/github.com/docker/libnetwork/driverapi/ipamdata.go index 9a2375bf8a..fc1c2af441 100644 --- a/vendor/src/github.com/docker/libnetwork/driverapi/ipamdata.go +++ b/vendor/src/github.com/docker/libnetwork/driverapi/ipamdata.go @@ -64,7 +64,7 @@ func (i *IPAMData) UnmarshalJSON(data []byte) error { return nil } -// Validate checks wheter the IPAMData structure contains congruent data +// Validate checks whether the IPAMData structure contains congruent data func (i *IPAMData) Validate() error { var isV6 bool if i.Pool == nil { @@ -93,7 +93,7 @@ func (i *IPAMData) Validate() error { return nil } -// IsV6 returns wheter this is an IPv6 IPAMData structure +// IsV6 returns whether this is an IPv6 IPAMData structure func (i *IPAMData) IsV6() bool { return nil == i.Pool.IP.To4() } diff --git a/vendor/src/github.com/docker/libnetwork/drivers.go b/vendor/src/github.com/docker/libnetwork/drivers.go index 1a4b348303..566d330ff4 100644 --- a/vendor/src/github.com/docker/libnetwork/drivers.go +++ b/vendor/src/github.com/docker/libnetwork/drivers.go @@ -9,6 +9,7 @@ import ( "github.com/docker/libnetwork/netlabel" builtinIpam "github.com/docker/libnetwork/ipams/builtin" + nullIpam "github.com/docker/libnetwork/ipams/null" remoteIpam "github.com/docker/libnetwork/ipams/remote" ) @@ -73,6 +74,7 @@ func initIpams(ic ipamapi.Callback, lDs, gDs interface{}) error { for _, fn := range [](func(ipamapi.Callback, interface{}, interface{}) error){ builtinIpam.Init, remoteIpam.Init, + nullIpam.Init, } { if err := fn(ic, lDs, gDs); err != nil { return err diff --git a/vendor/src/github.com/docker/libnetwork/drivers/bridge/bridge.go b/vendor/src/github.com/docker/libnetwork/drivers/bridge/bridge.go index 55acf8ac7f..00e16e1e5b 100644 --- a/vendor/src/github.com/docker/libnetwork/drivers/bridge/bridge.go +++ b/vendor/src/github.com/docker/libnetwork/drivers/bridge/bridge.go @@ -74,9 +74,7 @@ type networkConfiguration struct { // endpointConfiguration represents the user specified configuration for the sandbox endpoint type endpointConfiguration struct { - MacAddress net.HardwareAddr - PortBindings []types.PortBinding - ExposedPorts []types.TransportPort + MacAddress net.HardwareAddr } // containerConfiguration represents the user specified configuration for a container @@ -85,6 +83,12 @@ type containerConfiguration struct { ChildEndpoints []string } +// cnnectivityConfiguration represents the user specified configuration regarding the external connectivity +type connectivityConfiguration struct { + PortBindings []types.PortBinding + ExposedPorts []types.TransportPort +} + type bridgeEndpoint struct { id string srcName string @@ -93,6 +97,7 @@ type bridgeEndpoint struct { macAddress net.HardwareAddr config *endpointConfiguration // User specified parameters containerConfig *containerConfiguration + extConnConfig *connectivityConfiguration portMapping []types.PortBinding // Operation port bindings } @@ -183,7 +188,7 @@ func (c *networkConfiguration) Conflicts(o *networkConfiguration) error { return fmt.Errorf("same configuration") } - // Also empty, becasue only one network with empty name is allowed + // Also empty, because only one network with empty name is allowed if c.BridgeName == o.BridgeName { return fmt.Errorf("networks have same bridge name") } @@ -450,7 +455,7 @@ func parseNetworkGenericOptions(data interface{}) (*networkConfiguration, error) func (c *networkConfiguration) processIPAM(id string, ipamV4Data, ipamV6Data []driverapi.IPAMData) error { if len(ipamV4Data) > 1 || len(ipamV6Data) > 1 { - return types.ForbiddenErrorf("bridge driver doesnt support multiple subnets") + return types.ForbiddenErrorf("bridge driver doesn't support multiple subnets") } if len(ipamV4Data) == 0 { @@ -543,6 +548,9 @@ func (d *driver) getNetworks() []*bridgeNetwork { // Create a new network using bridge plugin func (d *driver) CreateNetwork(id string, option map[string]interface{}, ipV4Data, ipV6Data []driverapi.IPAMData) error { + if len(ipV4Data) == 0 || ipV4Data[0].Pool.String() == "0.0.0.0/0" { + return types.BadRequestErrorf("ipv4 pool is empty") + } // Sanity checks d.Lock() if _, ok := d.networks[id]; ok { @@ -581,7 +589,7 @@ func (d *driver) createNetwork(config *networkConfiguration) error { nw.Unlock() if err := nwConfig.Conflicts(config); err != nil { return types.ForbiddenErrorf("cannot create network %s (%s): conflicts with network %s (%s): %s", - nwConfig.BridgeName, config.ID, nw.id, nw.config.BridgeName, err.Error()) + config.ID, config.BridgeName, nwConfig.ID, nwConfig.BridgeName, err.Error()) } } @@ -948,28 +956,19 @@ func (d *driver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo, } } - // Create the sandbox side pipe interface + // Store the sandbox side pipe interface parameters endpoint.srcName = containerIfName endpoint.macAddress = ifInfo.MacAddress() endpoint.addr = ifInfo.Address() endpoint.addrv6 = ifInfo.AddressIPv6() - // Down the interface before configuring mac address. - if err = netlink.LinkSetDown(sbox); err != nil { - return fmt.Errorf("could not set link down for container interface %s: %v", containerIfName, err) - } - - // Set the sbox's MAC. If specified, use the one configured by user, otherwise generate one based on IP. + // Set the sbox's MAC if not provided. If specified, use the one configured by user, otherwise generate one based on IP. if endpoint.macAddress == nil { endpoint.macAddress = electMacAddress(epConfig, endpoint.addr.IP) - if err := ifInfo.SetMacAddress(endpoint.macAddress); err != nil { + if err = ifInfo.SetMacAddress(endpoint.macAddress); err != nil { return err } } - err = netlink.LinkSetHardwareAddr(sbox, endpoint.macAddress) - if err != nil { - return fmt.Errorf("could not set mac address for container interface %s: %v", containerIfName, err) - } // Up the host interface after finishing all netlink configuration if err = netlink.LinkSetUp(host); err != nil { @@ -996,17 +995,11 @@ func (d *driver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo, } endpoint.addrv6 = &net.IPNet{IP: ip6, Mask: network.Mask} - if err := ifInfo.SetIPAddress(endpoint.addrv6); err != nil { + if err = ifInfo.SetIPAddress(endpoint.addrv6); err != nil { return err } } - // Program any required port mapping and store them in the endpoint - endpoint.portMapping, err = n.allocatePorts(epConfig, endpoint, config.DefaultBindingIP, d.config.EnableUserlandProxy) - if err != nil { - return err - } - return nil } @@ -1061,9 +1054,6 @@ func (d *driver) DeleteEndpoint(nid, eid string) error { } }() - // Remove port mappings. Do not stop endpoint delete on unmap failure - n.releasePorts(ep) - // Try removal of link. Discard error: it is a best effort. // Also make sure defer does not see this error either. if link, err := netlink.LinkByName(ep.srcName); err == nil { @@ -1104,10 +1094,10 @@ func (d *driver) EndpointOperInfo(nid, eid string) (map[string]interface{}, erro m := make(map[string]interface{}) - if ep.config.ExposedPorts != nil { + if ep.extConnConfig != nil && ep.extConnConfig.ExposedPorts != nil { // Return a copy of the config data - epc := make([]types.TransportPort, 0, len(ep.config.ExposedPorts)) - for _, tp := range ep.config.ExposedPorts { + epc := make([]types.TransportPort, 0, len(ep.extConnConfig.ExposedPorts)) + for _, tp := range ep.extConnConfig.ExposedPorts { epc = append(epc, tp.GetCopy()) } m[netlabel.ExposedPorts] = epc @@ -1147,6 +1137,11 @@ func (d *driver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo, return EndpointNotFoundError(eid) } + endpoint.containerConfig, err = parseContainerOptions(options) + if err != nil { + return err + } + iNames := jinfo.InterfaceName() err = iNames.SetNames(endpoint.srcName, containerVethPrefix) if err != nil { @@ -1163,10 +1158,6 @@ func (d *driver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo, return err } - if !network.config.EnableICC { - return d.link(network, endpoint, options, true) - } - return nil } @@ -1189,32 +1180,87 @@ func (d *driver) Leave(nid, eid string) error { } if !network.config.EnableICC { - return d.link(network, endpoint, nil, false) + if err = d.link(network, endpoint, false); err != nil { + return err + } } return nil } -func (d *driver) link(network *bridgeNetwork, endpoint *bridgeEndpoint, options map[string]interface{}, enable bool) error { - var ( - cc *containerConfiguration - err error - ) +func (d *driver) ProgramExternalConnectivity(nid, eid string, options map[string]interface{}) error { + defer osl.InitOSContext()() - if enable { - cc, err = parseContainerOptions(options) - if err != nil { - return err - } - } else { - cc = endpoint.containerConfig + 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) + } + + endpoint.extConnConfig, err = parseConnectivityOptions(options) + if err != nil { + return err + } + + // Program any required port mapping and store them in the endpoint + endpoint.portMapping, err = network.allocatePorts(endpoint, network.config.DefaultBindingIP, d.config.EnableUserlandProxy) + if err != nil { + return err + } + + if !network.config.EnableICC { + return d.link(network, endpoint, true) + } + + return nil +} + +func (d *driver) RevokeExternalConnectivity(nid, eid string) error { + defer osl.InitOSContext()() + + 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) + } + + err = network.releasePorts(endpoint) + if err != nil { + logrus.Warn(err) + } + + return nil +} + +func (d *driver) link(network *bridgeNetwork, endpoint *bridgeEndpoint, enable bool) error { + var err error + + cc := endpoint.containerConfig if cc == nil { return nil } + ec := endpoint.extConnConfig + if ec == nil { + return nil + } - if endpoint.config != nil && endpoint.config.ExposedPorts != nil { + if ec.ExposedPorts != nil { for _, p := range cc.ParentEndpoints { var parentEndpoint *bridgeEndpoint parentEndpoint, err = network.getEndpoint(p) @@ -1228,7 +1274,7 @@ func (d *driver) link(network *bridgeNetwork, endpoint *bridgeEndpoint, options l := newLink(parentEndpoint.addr.IP.String(), endpoint.addr.IP.String(), - endpoint.config.ExposedPorts, network.config.BridgeName) + ec.ExposedPorts, network.config.BridgeName) if enable { err = l.Enable() if err != nil { @@ -1255,13 +1301,13 @@ func (d *driver) link(network *bridgeNetwork, endpoint *bridgeEndpoint, options err = InvalidEndpointIDError(c) return err } - if childEndpoint.config == nil || childEndpoint.config.ExposedPorts == nil { + if childEndpoint.extConnConfig == nil || childEndpoint.extConnConfig.ExposedPorts == nil { continue } l := newLink(endpoint.addr.IP.String(), childEndpoint.addr.IP.String(), - childEndpoint.config.ExposedPorts, network.config.BridgeName) + childEndpoint.extConnConfig.ExposedPorts, network.config.BridgeName) if enable { err = l.Enable() if err != nil { @@ -1277,10 +1323,6 @@ func (d *driver) link(network *bridgeNetwork, endpoint *bridgeEndpoint, options } } - if enable { - endpoint.containerConfig = cc - } - return nil } @@ -1313,22 +1355,6 @@ func parseEndpointOptions(epOptions map[string]interface{}) (*endpointConfigurat } } - if opt, ok := epOptions[netlabel.PortMap]; ok { - if bs, ok := opt.([]types.PortBinding); ok { - ec.PortBindings = bs - } else { - return nil, &ErrInvalidEndpointConfig{} - } - } - - if opt, ok := epOptions[netlabel.ExposedPorts]; ok { - if ports, ok := opt.([]types.TransportPort); ok { - ec.ExposedPorts = ports - } else { - return nil, &ErrInvalidEndpointConfig{} - } - } - return ec, nil } @@ -1354,6 +1380,32 @@ func parseContainerOptions(cOptions map[string]interface{}) (*containerConfigura } } +func parseConnectivityOptions(cOptions map[string]interface{}) (*connectivityConfiguration, error) { + if cOptions == nil { + return nil, nil + } + + cc := &connectivityConfiguration{} + + if opt, ok := cOptions[netlabel.PortMap]; ok { + if pb, ok := opt.([]types.PortBinding); ok { + cc.PortBindings = pb + } else { + return nil, types.BadRequestErrorf("Invalid port mapping data in connectivity configuration: %v", opt) + } + } + + if opt, ok := cOptions[netlabel.ExposedPorts]; ok { + if ports, ok := opt.([]types.TransportPort); ok { + cc.ExposedPorts = ports + } else { + return nil, types.BadRequestErrorf("Invalid exposed ports data in connectivity configuration: %v", opt) + } + } + + return cc, nil +} + func electMacAddress(epConfig *endpointConfiguration, ip net.IP) net.HardwareAddr { if epConfig != nil && epConfig.MacAddress != nil { return epConfig.MacAddress 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 index 4dab8a0c89..965cc9a039 100644 --- a/vendor/src/github.com/docker/libnetwork/drivers/bridge/port_mapping.go +++ b/vendor/src/github.com/docker/libnetwork/drivers/bridge/port_mapping.go @@ -14,8 +14,8 @@ var ( defaultBindingIP = net.IPv4(0, 0, 0, 0) ) -func (n *bridgeNetwork) allocatePorts(epConfig *endpointConfiguration, ep *bridgeEndpoint, reqDefBindIP net.IP, ulPxyEnabled bool) ([]types.PortBinding, error) { - if epConfig == nil || epConfig.PortBindings == nil { +func (n *bridgeNetwork) allocatePorts(ep *bridgeEndpoint, reqDefBindIP net.IP, ulPxyEnabled bool) ([]types.PortBinding, error) { + if ep.extConnConfig == nil || ep.extConnConfig.PortBindings == nil { return nil, nil } @@ -24,7 +24,7 @@ func (n *bridgeNetwork) allocatePorts(epConfig *endpointConfiguration, ep *bridg defHostIP = reqDefBindIP } - return n.allocatePortsInternal(epConfig.PortBindings, ep.addr.IP, defHostIP, ulPxyEnabled) + return n.allocatePortsInternal(ep.extConnConfig.PortBindings, ep.addr.IP, defHostIP, ulPxyEnabled) } func (n *bridgeNetwork) allocatePortsInternal(bindings []types.PortBinding, containerIP, defHostIP net.IP, ulPxyEnabled bool) ([]types.PortBinding, error) { diff --git a/vendor/src/github.com/docker/libnetwork/drivers/host/host.go b/vendor/src/github.com/docker/libnetwork/drivers/host/host.go index 66fd9ebdb8..bbf59c204c 100644 --- a/vendor/src/github.com/docker/libnetwork/drivers/host/host.go +++ b/vendor/src/github.com/docker/libnetwork/drivers/host/host.go @@ -63,6 +63,14 @@ func (d *driver) Leave(nid, eid string) error { return nil } +func (d *driver) ProgramExternalConnectivity(nid, eid string, options map[string]interface{}) error { + return nil +} + +func (d *driver) RevokeExternalConnectivity(nid, eid string) 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 index b64c9e995d..ecc64d2db3 100644 --- a/vendor/src/github.com/docker/libnetwork/drivers/null/null.go +++ b/vendor/src/github.com/docker/libnetwork/drivers/null/null.go @@ -63,6 +63,14 @@ func (d *driver) Leave(nid, eid string) error { return nil } +func (d *driver) ProgramExternalConnectivity(nid, eid string, options map[string]interface{}) error { + return nil +} + +func (d *driver) RevokeExternalConnectivity(nid, eid string) error { + return nil +} + func (d *driver) Type() string { return networkType } diff --git a/vendor/src/github.com/docker/libnetwork/drivers/overlay/filter.go b/vendor/src/github.com/docker/libnetwork/drivers/overlay/filter.go index 87b0c48aa0..0a69c6715b 100644 --- a/vendor/src/github.com/docker/libnetwork/drivers/overlay/filter.go +++ b/vendor/src/github.com/docker/libnetwork/drivers/overlay/filter.go @@ -78,7 +78,7 @@ func setFilters(cname, brName string, remove bool) error { opt = "-D" } - // Everytime we set filters for a new subnet make sure to move the global overlay hook to the top of the both the OUTPUT and forward chains + // Every time we set filters for a new subnet make sure to move the global overlay hook to the top of the both the OUTPUT and forward chains if !remove { for _, chain := range []string{"OUTPUT", "FORWARD"} { exists := iptables.Exists(iptables.Filter, chain, "-j", globalChain) diff --git a/vendor/src/github.com/docker/libnetwork/drivers/overlay/joinleave.go b/vendor/src/github.com/docker/libnetwork/drivers/overlay/joinleave.go index d87c032dfc..5b6792da6b 100644 --- a/vendor/src/github.com/docker/libnetwork/drivers/overlay/joinleave.go +++ b/vendor/src/github.com/docker/libnetwork/drivers/overlay/joinleave.go @@ -54,7 +54,7 @@ func (d *driver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo, return err } - ep.ifName = overlayIfName + ep.ifName = containerIfName // Set the container interface and its peer MTU to 1450 to allow // for 50 bytes vxlan encap (inner eth header(14) + outer IP(20) + diff --git a/vendor/src/github.com/docker/libnetwork/drivers/overlay/ov_network.go b/vendor/src/github.com/docker/libnetwork/drivers/overlay/ov_network.go index f0b9b2b1f5..1bf91e3f21 100644 --- a/vendor/src/github.com/docker/libnetwork/drivers/overlay/ov_network.go +++ b/vendor/src/github.com/docker/libnetwork/drivers/overlay/ov_network.go @@ -63,6 +63,9 @@ func (d *driver) CreateNetwork(id string, option map[string]interface{}, ipV4Dat if id == "" { return fmt.Errorf("invalid network id") } + if len(ipV4Data) == 0 || ipV4Data[0].Pool.String() == "0.0.0.0/0" { + return types.BadRequestErrorf("ipv4 pool is empty") + } // Since we perform lazy configuration make sure we try // configuring the driver when we enter CreateNetwork @@ -111,6 +114,14 @@ func (d *driver) DeleteNetwork(nid string) error { return n.releaseVxlanID() } +func (d *driver) ProgramExternalConnectivity(nid, eid string, options map[string]interface{}) error { + return nil +} + +func (d *driver) RevokeExternalConnectivity(nid, eid string) error { + return nil +} + func (n *network) incEndpointCount() { n.Lock() defer n.Unlock() diff --git a/vendor/src/github.com/docker/libnetwork/drivers/remote/api/api.go b/vendor/src/github.com/docker/libnetwork/drivers/remote/api/api.go index 7dc877fc66..c40a80bb87 100644 --- a/vendor/src/github.com/docker/libnetwork/drivers/remote/api/api.go +++ b/vendor/src/github.com/docker/libnetwork/drivers/remote/api/api.go @@ -153,6 +153,29 @@ type LeaveResponse struct { Response } +// ProgramExternalConnectivityRequest describes the API for programming the external connectivity for the given endpoint. +type ProgramExternalConnectivityRequest struct { + NetworkID string + EndpointID string + Options map[string]interface{} +} + +// ProgramExternalConnectivityResponse is the answer to ProgramExternalConnectivityRequest. +type ProgramExternalConnectivityResponse struct { + Response +} + +// RevokeExternalConnectivityRequest describes the API for revoking the external connectivity for the given endpoint. +type RevokeExternalConnectivityRequest struct { + NetworkID string + EndpointID string +} + +// RevokeExternalConnectivityResponse is the answer to RevokeExternalConnectivityRequest. +type RevokeExternalConnectivityResponse struct { + Response +} + // DiscoveryNotification represents a discovery notification type DiscoveryNotification struct { DiscoveryType discoverapi.DiscoveryType diff --git a/vendor/src/github.com/docker/libnetwork/drivers/remote/driver.go b/vendor/src/github.com/docker/libnetwork/drivers/remote/driver.go index c55915ce97..32533533dd 100644 --- a/vendor/src/github.com/docker/libnetwork/drivers/remote/driver.go +++ b/vendor/src/github.com/docker/libnetwork/drivers/remote/driver.go @@ -3,6 +3,7 @@ package remote import ( "fmt" "net" + "strings" log "github.com/Sirupsen/logrus" "github.com/docker/docker/pkg/plugins" @@ -13,6 +14,10 @@ import ( "github.com/docker/libnetwork/types" ) +const ( + missingMethod = "404 page not found" +) + type driver struct { endpoint *plugins.Client networkType string @@ -247,6 +252,35 @@ func (d *driver) Leave(nid, eid string) error { return d.call("Leave", leave, &api.LeaveResponse{}) } +// ProgramExternalConnectivity is invoked to program the rules to allow external connectivity for the endpoint. +func (d *driver) ProgramExternalConnectivity(nid, eid string, options map[string]interface{}) error { + data := &api.ProgramExternalConnectivityRequest{ + NetworkID: nid, + EndpointID: eid, + Options: options, + } + err := d.call("ProgramExternalConnectivity", data, &api.ProgramExternalConnectivityResponse{}) + if err != nil && strings.Contains(err.Error(), missingMethod) { + // It is not mandatory yet to support this method + return nil + } + return err +} + +// RevokeExternalConnectivity method is invoked to remove any external connectivity programming related to the endpoint. +func (d *driver) RevokeExternalConnectivity(nid, eid string) error { + data := &api.RevokeExternalConnectivityRequest{ + NetworkID: nid, + EndpointID: eid, + } + err := d.call("RevokeExternalConnectivity", data, &api.RevokeExternalConnectivityResponse{}) + if err != nil && strings.Contains(err.Error(), missingMethod) { + // It is not mandatory yet to support this method + return nil + } + return err +} + func (d *driver) Type() string { return d.networkType } diff --git a/vendor/src/github.com/docker/libnetwork/drivers/windows/windows.go b/vendor/src/github.com/docker/libnetwork/drivers/windows/windows.go index 9cb9faaeb2..231e6a2d68 100644 --- a/vendor/src/github.com/docker/libnetwork/drivers/windows/windows.go +++ b/vendor/src/github.com/docker/libnetwork/drivers/windows/windows.go @@ -36,11 +36,20 @@ type networkConfiguration struct { RDID string } +// endpointConfiguration represents the user specified configuration for the sandbox endpoint +type endpointConfiguration struct { + MacAddress net.HardwareAddr + PortBindings []types.PortBinding + ExposedPorts []types.TransportPort +} + type hnsEndpoint struct { - id string - profileID string - macAddress net.HardwareAddr - addr *net.IPNet + id string + profileID string + macAddress net.HardwareAddr + config *endpointConfiguration // User specified parameters + portMapping []types.PortBinding // Operation port bindings + addr *net.IPNet } type hnsNetwork struct { @@ -58,7 +67,7 @@ type driver struct { } func isValidNetworkType(networkType string) bool { - if "L2Bridge" == networkType || "L2Tunnel" == networkType || "NAT" == networkType || "Transparent" == networkType { + if "l2bridge" == networkType || "l2tunnel" == networkType || "nat" == networkType || "transparent" == networkType { return true } @@ -126,7 +135,7 @@ func (d *driver) parseNetworkOptions(id string, genericOptions map[string]string func (c *networkConfiguration) processIPAM(id string, ipamV4Data, ipamV6Data []driverapi.IPAMData) error { if len(ipamV6Data) > 0 { - return types.ForbiddenErrorf("windowsshim driver doesnt support v6 subnets") + return types.ForbiddenErrorf("windowsshim driver doesn't support v6 subnets") } if len(ipamV4Data) == 0 { @@ -177,8 +186,11 @@ func (d *driver) CreateNetwork(id string, option map[string]interface{}, ipV4Dat for _, ipData := range ipV4Data { subnet := hcsshim.Subnet{ - AddressPrefix: ipData.Pool.String(), - GatewayAddress: ipData.Gateway.IP.String(), + AddressPrefix: ipData.Pool.String(), + } + + if ipData.Gateway != nil { + subnet.GatewayAddress = ipData.Gateway.IP.String() } subnets = append(subnets, subnet) @@ -276,6 +288,64 @@ func convertPortBindings(portBindings []types.PortBinding) ([]json.RawMessage, e return pbs, nil } +func parsePortBindingPolicies(policies []json.RawMessage) ([]types.PortBinding, error) { + var bindings []types.PortBinding + hcsPolicy := &hcsshim.NatPolicy{} + + for _, elem := range policies { + + if err := json.Unmarshal([]byte(elem), &hcsPolicy); err != nil || hcsPolicy.Type != "NAT" { + continue + } + + binding := types.PortBinding{ + HostPort: hcsPolicy.ExternalPort, + HostPortEnd: hcsPolicy.ExternalPort, + Port: hcsPolicy.InternalPort, + Proto: types.ParseProtocol(hcsPolicy.Protocol), + HostIP: net.IPv4(0, 0, 0, 0), + } + + bindings = append(bindings, binding) + } + + return bindings, nil +} + +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, fmt.Errorf("Invalid endpoint configuration") + } + } + + if opt, ok := epOptions[netlabel.PortMap]; ok { + if bs, ok := opt.([]types.PortBinding); ok { + ec.PortBindings = bs + } else { + return nil, fmt.Errorf("Invalid endpoint configuration") + } + } + + if opt, ok := epOptions[netlabel.ExposedPorts]; ok { + if ports, ok := opt.([]types.TransportPort); ok { + ec.ExposedPorts = ports + } else { + return nil, fmt.Errorf("Invalid endpoint configuration") + } + } + + return ec, nil +} + func (d *driver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo, epOptions map[string]interface{}) error { n, err := d.getNetwork(nid) if err != nil { @@ -292,16 +362,16 @@ func (d *driver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo, VirtualNetwork: n.config.HnsID, } - // Convert the port mapping for the network - if opt, ok := epOptions[netlabel.PortMap]; ok { - if bs, ok := opt.([]types.PortBinding); ok { - endpointStruct.Policies, err = convertPortBindings(bs) - if err != nil { - return err - } - } else { - return fmt.Errorf("Invalid endpoint configuration for endpoint id%s", eid) - } + ec, err := parseEndpointOptions(epOptions) + + if err != nil { + return err + } + + endpointStruct.Policies, err = convertPortBindings(ec.PortBindings) + + if err != nil { + return err } configurationb, err := json.Marshal(endpointStruct) @@ -325,7 +395,16 @@ func (d *driver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo, addr: &net.IPNet{IP: hnsresponse.IPAddress, Mask: hnsresponse.IPAddress.DefaultMask()}, macAddress: mac, } + endpoint.profileID = hnsresponse.Id + endpoint.config = ec + endpoint.portMapping, err = parsePortBindingPolicies(hnsresponse.Policies) + + if err != nil { + hcsshim.HNSEndpointRequest("DELETE", hnsresponse.Id, "") + return err + } + n.Lock() n.endpoints[eid] = endpoint n.Unlock() @@ -365,13 +444,34 @@ func (d *driver) EndpointOperInfo(nid, eid string) (map[string]interface{}, erro return nil, err } - endpoint, err := network.getEndpoint(eid) + ep, err := network.getEndpoint(eid) if err != nil { return nil, err } data := make(map[string]interface{}, 1) - data["hnsid"] = endpoint.profileID + data["hnsid"] = ep.profileID + if ep.config.ExposedPorts != nil { + // Return a copy of the config data + epc := make([]types.TransportPort, 0, len(ep.config.ExposedPorts)) + for _, tp := range ep.config.ExposedPorts { + epc = append(epc, tp.GetCopy()) + } + data[netlabel.ExposedPorts] = epc + } + + if ep.portMapping != nil { + // Return a copy of the operational data + pmc := make([]types.PortBinding, 0, len(ep.portMapping)) + for _, pm := range ep.portMapping { + pmc = append(pmc, pm.GetCopy()) + } + data[netlabel.PortMap] = pmc + } + + if len(ep.macAddress) != 0 { + data[netlabel.MacAddress] = ep.macAddress + } return data, nil } @@ -412,6 +512,14 @@ func (d *driver) Leave(nid, eid string) error { return nil } +func (d *driver) ProgramExternalConnectivity(nid, eid string, options map[string]interface{}) error { + return nil +} + +func (d *driver) RevokeExternalConnectivity(nid, eid string) error { + return nil +} + func (d *driver) Type() string { return d.name } diff --git a/vendor/src/github.com/docker/libnetwork/drivers_windows.go b/vendor/src/github.com/docker/libnetwork/drivers_windows.go index d4769aec9b..1e44b626d4 100644 --- a/vendor/src/github.com/docker/libnetwork/drivers_windows.go +++ b/vendor/src/github.com/docker/libnetwork/drivers_windows.go @@ -8,9 +8,9 @@ import ( func getInitializers() []initializer { return []initializer{ {null.Init, "null"}, - {windows.GetInit("Transparent"), "Transparent"}, - {windows.GetInit("L2Bridge"), "L2Bridge"}, - {windows.GetInit("L2Tunnel"), "L2Tunnel"}, - {windows.GetInit("NAT"), "NAT"}, + {windows.GetInit("transparent"), "transparent"}, + {windows.GetInit("l2bridge"), "l2bridge"}, + {windows.GetInit("l2tunnel"), "l2tunnel"}, + {windows.GetInit("nat"), "nat"}, } } diff --git a/vendor/src/github.com/docker/libnetwork/endpoint.go b/vendor/src/github.com/docker/libnetwork/endpoint.go index 38506c82a8..b0aacb9f44 100644 --- a/vendor/src/github.com/docker/libnetwork/endpoint.go +++ b/vendor/src/github.com/docker/libnetwork/endpoint.go @@ -359,22 +359,16 @@ func (ep *endpoint) Join(sbox Sandbox, options ...EndpointOption) error { sb.joinLeaveStart() defer sb.joinLeaveEnd() - return ep.sbJoin(sbox, options...) + return ep.sbJoin(sb, options...) } -func (ep *endpoint) sbJoin(sbox Sandbox, options ...EndpointOption) error { - var err error - sb, ok := sbox.(*sandbox) - if !ok { - return types.BadRequestErrorf("not a valid Sandbox interface") - } - - network, err := ep.getNetworkFromStore() +func (ep *endpoint) sbJoin(sb *sandbox, options ...EndpointOption) error { + n, err := ep.getNetworkFromStore() if err != nil { return fmt.Errorf("failed to get network from store during join: %v", err) } - ep, err = network.getEndpointFromStore(ep.ID()) + ep, err = n.getEndpointFromStore(ep.ID()) if err != nil { return fmt.Errorf("failed to get endpoint from store during join: %v", err) } @@ -384,11 +378,8 @@ func (ep *endpoint) sbJoin(sbox Sandbox, options ...EndpointOption) error { ep.Unlock() return types.ForbiddenErrorf("another container is attached to the same network endpoint") } - ep.Unlock() - - ep.Lock() - ep.network = network - ep.sandboxID = sbox.ID() + ep.network = n + ep.sandboxID = sb.ID() ep.joinInfo = &endpointJoinInfo{} epid := ep.id ep.Unlock() @@ -400,32 +391,29 @@ func (ep *endpoint) sbJoin(sbox Sandbox, options ...EndpointOption) error { } }() - network.Lock() - nid := network.id - network.Unlock() + nid := n.ID() ep.processOptions(options...) - driver, err := network.driver(true) + d, err := n.driver(true) if err != nil { return fmt.Errorf("failed to join endpoint: %v", err) } - err = driver.Join(nid, epid, sbox.Key(), ep, sbox.Labels()) + err = d.Join(nid, epid, sb.Key(), ep, sb.Labels()) if err != nil { return err } defer func() { if err != nil { - // Do not alter global err variable, it's needed by the previous defer - if err := driver.Leave(nid, epid); err != nil { + if err := d.Leave(nid, epid); err != nil { log.Warnf("driver leave failed while rolling back join: %v", err) } } }() // Watch for service records - network.getController().watchSvcRecord(ep) + n.getController().watchSvcRecord(ep) address := "" if ip := ep.getFirstInterfaceAddress(); ip != nil { @@ -434,27 +422,23 @@ func (ep *endpoint) sbJoin(sbox Sandbox, options ...EndpointOption) error { if err = sb.updateHostsFile(address); err != nil { return err } - if err = sb.updateDNS(network.enableIPv6); err != nil { + if err = sb.updateDNS(n.enableIPv6); err != nil { return err } - if err = network.getController().updateToStore(ep); err != nil { + if err = n.getController().updateToStore(ep); err != nil { return err } + // Current endpoint providing external connectivity for the sandbox + extEp := sb.getGatewayEndpoint() + sb.Lock() heap.Push(&sb.endpoints, ep) sb.Unlock() defer func() { if err != nil { - for i, e := range sb.getConnectedEndpoints() { - if e == ep { - sb.Lock() - heap.Remove(&sb.endpoints, i) - sb.Unlock() - return - } - } + sb.removeEndpoint(ep) } }() @@ -463,9 +447,39 @@ func (ep *endpoint) sbJoin(sbox Sandbox, options ...EndpointOption) error { } if sb.needDefaultGW() { - return sb.setupDefaultGW(ep) + return sb.setupDefaultGW() } - return nil + + moveExtConn := sb.getGatewayEndpoint() != extEp + + if moveExtConn { + if extEp != nil { + log.Debugf("Revoking external connectivity on endpoint %s (%s)", extEp.Name(), extEp.ID()) + if err = d.RevokeExternalConnectivity(extEp.network.ID(), extEp.ID()); err != nil { + return types.InternalErrorf( + "driver failed revoking external connectivity on endpoint %s (%s): %v", + extEp.Name(), extEp.ID(), err) + } + defer func() { + if err != nil { + if e := d.ProgramExternalConnectivity(extEp.network.ID(), extEp.ID(), sb.Labels()); e != nil { + log.Warnf("Failed to roll-back external connectivity on endpoint %s (%s): %v", + extEp.Name(), extEp.ID(), e) + } + } + }() + } + if !n.internal { + log.Debugf("Programming external connectivity on endpoint %s (%s)", ep.Name(), ep.ID()) + if err = d.ProgramExternalConnectivity(n.ID(), ep.ID(), sb.Labels()); err != nil { + return types.InternalErrorf( + "driver failed programming external connectivity on endpoint %s (%s): %v", + ep.Name(), ep.ID(), err) + } + } + } + + return sb.clearDefaultGW() } func (ep *endpoint) rename(name string) error { @@ -533,15 +547,10 @@ func (ep *endpoint) Leave(sbox Sandbox, options ...EndpointOption) error { sb.joinLeaveStart() defer sb.joinLeaveEnd() - return ep.sbLeave(sbox, false, options...) + return ep.sbLeave(sb, false, options...) } -func (ep *endpoint) sbLeave(sbox Sandbox, force bool, options ...EndpointOption) error { - sb, ok := sbox.(*sandbox) - if !ok { - return types.BadRequestErrorf("not a valid Sandbox interface") - } - +func (ep *endpoint) sbLeave(sb *sandbox, force bool, options ...EndpointOption) error { n, err := ep.getNetworkFromStore() if err != nil { return fmt.Errorf("failed to get network from store during leave: %v", err) @@ -559,8 +568,8 @@ func (ep *endpoint) sbLeave(sbox Sandbox, force bool, options ...EndpointOption) if sid == "" { return types.ForbiddenErrorf("cannot leave endpoint with no attached sandbox") } - if sid != sbox.ID() { - return types.ForbiddenErrorf("unexpected sandbox ID in leave request. Expected %s. Got %s", ep.sandboxID, sbox.ID()) + if sid != sb.ID() { + return types.ForbiddenErrorf("unexpected sandbox ID in leave request. Expected %s. Got %s", ep.sandboxID, sb.ID()) } ep.processOptions(options...) @@ -575,7 +584,19 @@ func (ep *endpoint) sbLeave(sbox Sandbox, force bool, options ...EndpointOption) ep.network = n ep.Unlock() + // Current endpoint providing external connectivity to the sandbox + extEp := sb.getGatewayEndpoint() + moveExtConn := extEp != nil && (extEp.ID() == ep.ID()) + if d != nil { + if moveExtConn { + log.Debugf("Revoking external connectivity on endpoint %s (%s)", ep.Name(), ep.ID()) + if err := d.RevokeExternalConnectivity(n.id, ep.id); err != nil { + log.Warnf("driver failed revoking external connectivity on endpoint %s (%s): %v", + ep.Name(), ep.ID(), err) + } + } + if err := d.Leave(n.id, ep.id); err != nil { if _, ok := err.(types.MaskableError); !ok { log.Warnf("driver error disconnecting container %s : %v", ep.name, err) @@ -597,7 +618,24 @@ func (ep *endpoint) sbLeave(sbox Sandbox, force bool, options ...EndpointOption) } sb.deleteHostsEntries(n.getSvcRecords(ep)) - return nil + if !sb.inDelete && sb.needDefaultGW() { + if sb.getEPwithoutGateway() == nil { + return fmt.Errorf("endpoint without GW expected, but not found") + } + return sb.setupDefaultGW() + } + + // New endpoint providing external connectivity for the sandbox + extEp = sb.getGatewayEndpoint() + if moveExtConn && extEp != nil { + log.Debugf("Programming external connectivity on endpoint %s (%s)", extEp.Name(), extEp.ID()) + if err := d.ProgramExternalConnectivity(extEp.network.ID(), extEp.ID(), sb.Labels()); err != nil { + log.Warnf("driver failed programming external connectivity on endpoint %s: (%s) %v", + extEp.Name(), extEp.ID(), err) + } + } + + return sb.clearDefaultGW() } func (n *network) validateForceDelete(locator string) error { @@ -643,7 +681,7 @@ func (ep *endpoint) Delete(force bool) error { } if sb != nil { - if e := ep.sbLeave(sb, force); e != nil { + if e := ep.sbLeave(sb.(*sandbox), force); e != nil { log.Warnf("failed to leave sandbox for endpoint %s : %v", name, e) } } @@ -929,9 +967,13 @@ func (ep *endpoint) releaseAddress() { log.Warnf("Failed to retrieve ipam driver to release interface address on delete of endpoint %s (%s): %v", ep.Name(), ep.ID(), err) return } - if err := ipam.ReleaseAddress(ep.iface.v4PoolID, ep.iface.addr.IP); err != nil { - log.Warnf("Failed to release ip address %s on delete of endpoint %s (%s): %v", ep.iface.addr.IP, ep.Name(), ep.ID(), err) + + if ep.iface.addr != nil { + if err := ipam.ReleaseAddress(ep.iface.v4PoolID, ep.iface.addr.IP); err != nil { + log.Warnf("Failed to release ip address %s on delete of endpoint %s (%s): %v", ep.iface.addr.IP, ep.Name(), ep.ID(), err) + } } + if ep.iface.addrv6 != nil && ep.iface.addrv6.IP.IsGlobalUnicast() { if err := ipam.ReleaseAddress(ep.iface.v6PoolID, ep.iface.addrv6.IP); err != nil { log.Warnf("Failed to release ip address %s on delete of endpoint %s (%s): %v", ep.iface.addrv6.IP, ep.Name(), ep.ID(), err) diff --git a/vendor/src/github.com/docker/libnetwork/etchosts/etchosts.go b/vendor/src/github.com/docker/libnetwork/etchosts/etchosts.go index 5f68372b90..4526532593 100644 --- a/vendor/src/github.com/docker/libnetwork/etchosts/etchosts.go +++ b/vendor/src/github.com/docker/libnetwork/etchosts/etchosts.go @@ -8,6 +8,7 @@ import ( "io/ioutil" "os" "regexp" + "strings" "sync" ) @@ -78,10 +79,17 @@ func Build(path, IP, hostname, domainname string, extraContent []Record) error { //set main record var mainRec Record mainRec.IP = IP + // User might have provided a FQDN in hostname or split it across hostname + // and domainname. We want the FQDN and the bare hostname. + fqdn := hostname if domainname != "" { - mainRec.Hosts = fmt.Sprintf("%s.%s %s", hostname, domainname, hostname) + fqdn = fmt.Sprintf("%s.%s", fqdn, domainname) + } + parts := strings.SplitN(fqdn, ".", 2) + if len(parts) == 2 { + mainRec.Hosts = fmt.Sprintf("%s %s", fqdn, parts[0]) } else { - mainRec.Hosts = hostname + mainRec.Hosts = fqdn } if _, err := mainRec.WriteTo(content); err != nil { return err @@ -151,6 +159,10 @@ func Delete(path string, recs []Record) error { loop: for s.Scan() { b := s.Bytes() + if len(b) == 0 { + continue + } + if b[0] == '#' { buf.Write(b) buf.Write(eol) diff --git a/vendor/src/github.com/docker/libnetwork/idm/idm.go b/vendor/src/github.com/docker/libnetwork/idm/idm.go index 37ae79e558..3ed820ca91 100644 --- a/vendor/src/github.com/docker/libnetwork/idm/idm.go +++ b/vendor/src/github.com/docker/libnetwork/idm/idm.go @@ -8,7 +8,7 @@ import ( "github.com/docker/libnetwork/datastore" ) -// Idm manages the reservation/release of numerical ids from a contiguos set +// Idm manages the reservation/release of numerical ids from a contiguous set type Idm struct { start uint64 end uint64 diff --git a/vendor/src/github.com/docker/libnetwork/ipam/structures.go b/vendor/src/github.com/docker/libnetwork/ipam/structures.go index 601eda4fba..09a77695dd 100644 --- a/vendor/src/github.com/docker/libnetwork/ipam/structures.go +++ b/vendor/src/github.com/docker/libnetwork/ipam/structures.go @@ -40,7 +40,7 @@ type addrSpace struct { } // AddressRange specifies first and last ip ordinal which -// identify a range in a a pool of addresses +// identifies a range in a pool of addresses type AddressRange struct { Sub *net.IPNet Start, End uint64 @@ -85,7 +85,7 @@ func (s *SubnetKey) String() string { return k } -// FromString populate the SubnetKey object reading it from string +// FromString populates the SubnetKey object reading it from string func (s *SubnetKey) FromString(str string) error { if str == "" || !strings.Contains(str, "/") { return types.BadRequestErrorf("invalid string form for subnetkey: %s", str) diff --git a/vendor/src/github.com/docker/libnetwork/ipam/utils.go b/vendor/src/github.com/docker/libnetwork/ipam/utils.go index d524b47830..5117c55cc7 100644 --- a/vendor/src/github.com/docker/libnetwork/ipam/utils.go +++ b/vendor/src/github.com/docker/libnetwork/ipam/utils.go @@ -62,7 +62,7 @@ func getAddressVersion(ip net.IP) ipVersion { } // Adds the ordinal IP to the current array -// 192.168.0.0 + 53 => 192.168.53 +// 192.168.0.0 + 53 => 192.168.0.53 func addIntToIP(array []byte, ordinal uint64) { for i := len(array) - 1; i >= 0; i-- { array[i] |= (byte)(ordinal & 0xff) diff --git a/vendor/src/github.com/docker/libnetwork/ipamapi/contract.go b/vendor/src/github.com/docker/libnetwork/ipamapi/contract.go index ae6ecc8990..513e482349 100644 --- a/vendor/src/github.com/docker/libnetwork/ipamapi/contract.go +++ b/vendor/src/github.com/docker/libnetwork/ipamapi/contract.go @@ -15,6 +15,8 @@ import ( const ( // DefaultIPAM is the name of the built-in default ipam driver DefaultIPAM = "default" + // NullIPAM is the name of the built-in null ipam driver + NullIPAM = "null" // PluginEndpointType represents the Endpoint Type used by Plugin system PluginEndpointType = "IpamDriver" // RequestAddressType represents the Address Type used when requesting an address @@ -33,7 +35,7 @@ type Callback interface { * IPAM Errors **************/ -// Weel-known errors returned by IPAM +// Well-known errors returned by IPAM var ( ErrIpamInternalError = types.InternalErrorf("IPAM Internal Error") ErrInvalidAddressSpace = types.BadRequestErrorf("Invalid Address Space") diff --git a/vendor/src/github.com/docker/libnetwork/ipams/null/null.go b/vendor/src/github.com/docker/libnetwork/ipams/null/null.go new file mode 100644 index 0000000000..60119a36ab --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/ipams/null/null.go @@ -0,0 +1,71 @@ +// Package null implements the null ipam driver. Null ipam driver satisfies ipamapi contract, +// but does not effectively reserve/allocate any address pool or address +package null + +import ( + "fmt" + "net" + + "github.com/docker/libnetwork/discoverapi" + "github.com/docker/libnetwork/ipamapi" + "github.com/docker/libnetwork/types" +) + +var ( + defaultAS = "null" + defaultPool, _ = types.ParseCIDR("0.0.0.0/0") + defaultPoolID = fmt.Sprintf("%s/%s", defaultAS, defaultPool.String()) +) + +type allocator struct{} + +func (a *allocator) GetDefaultAddressSpaces() (string, string, error) { + return defaultAS, defaultAS, nil +} + +func (a *allocator) RequestPool(addressSpace, pool, subPool string, options map[string]string, v6 bool) (string, *net.IPNet, map[string]string, error) { + if addressSpace != defaultAS { + return "", nil, nil, types.BadRequestErrorf("unknown address space: %s", addressSpace) + } + if pool != "" { + return "", nil, nil, types.BadRequestErrorf("null ipam driver does not handle specific address pool requests") + } + if subPool != "" { + return "", nil, nil, types.BadRequestErrorf("null ipam driver does not handle specific address subpool requests") + } + if v6 { + return "", nil, nil, types.BadRequestErrorf("null ipam driver does not handle IPv6 address pool pool requests") + } + return defaultPoolID, defaultPool, nil, nil +} + +func (a *allocator) ReleasePool(poolID string) error { + return nil +} + +func (a *allocator) RequestAddress(poolID string, ip net.IP, opts map[string]string) (*net.IPNet, map[string]string, error) { + if poolID != defaultPoolID { + return nil, nil, types.BadRequestErrorf("unknown pool id: %s", poolID) + } + return nil, nil, nil +} + +func (a *allocator) ReleaseAddress(poolID string, ip net.IP) error { + if poolID != defaultPoolID { + return types.BadRequestErrorf("unknown pool id: %s", poolID) + } + return nil +} + +func (a *allocator) DiscoverNew(dType discoverapi.DiscoveryType, data interface{}) error { + return nil +} + +func (a *allocator) DiscoverDelete(dType discoverapi.DiscoveryType, data interface{}) error { + return nil +} + +// Init registers a remote ipam when its plugin is activated +func Init(ic ipamapi.Callback, l, g interface{}) error { + return ic.RegisterIpamDriver(ipamapi.NullIPAM, &allocator{}) +} diff --git a/vendor/src/github.com/docker/libnetwork/ipams/windowsipam/windowsipam.go b/vendor/src/github.com/docker/libnetwork/ipams/windowsipam/windowsipam.go index 6c112d2536..a92d5ccd77 100644 --- a/vendor/src/github.com/docker/libnetwork/ipams/windowsipam/windowsipam.go +++ b/vendor/src/github.com/docker/libnetwork/ipams/windowsipam/windowsipam.go @@ -6,6 +6,7 @@ import ( log "github.com/Sirupsen/logrus" "github.com/docker/libnetwork/discoverapi" "github.com/docker/libnetwork/ipamapi" + "github.com/docker/libnetwork/netlabel" "github.com/docker/libnetwork/types" ) @@ -33,7 +34,7 @@ func (a *allocator) GetDefaultAddressSpaces() (string, string, error) { } // RequestPool returns an address pool along with its unique id. This is a null ipam driver. It allocates the -// subnet user asked and does not validate anything. Doesnt support subpool allocation +// subnet user asked and does not validate anything. Doesn't support subpool allocation func (a *allocator) RequestPool(addressSpace, pool, subPool string, options map[string]string, v6 bool) (string, *net.IPNet, map[string]string, error) { log.Debugf("RequestPool(%s, %s, %s, %v, %t)", addressSpace, pool, subPool, options, v6) if subPool != "" || v6 { @@ -64,14 +65,19 @@ func (a *allocator) ReleasePool(poolID string) error { // RequestAddress returns an address from the specified pool ID. // Always allocate the 0.0.0.0/32 ip if no preferred address was specified func (a *allocator) RequestAddress(poolID string, prefAddress net.IP, opts map[string]string) (*net.IPNet, map[string]string, error) { - log.Debugf("RequestAddress(%s, %v, %v) %s", poolID, prefAddress, opts, opts["RequestAddressType"]) + log.Debugf("RequestAddress(%s, %v, %v)", poolID, prefAddress, opts) _, ipNet, err := net.ParseCIDR(poolID) if err != nil { return nil, nil, err } - if prefAddress == nil { + + // TODO Windows: Remove this once the bug in docker daemon is fixed + // that causes it to throw an exception on nil gateway + if opts[ipamapi.RequestAddressType] == netlabel.Gateway { return ipNet, nil, nil + } else if prefAddress == nil { + return nil, nil, nil } return &net.IPNet{IP: prefAddress, Mask: ipNet.Mask}, nil, nil } diff --git a/vendor/src/github.com/docker/libnetwork/iptables/iptables.go b/vendor/src/github.com/docker/libnetwork/iptables/iptables.go index ca07893888..298c5bf472 100644 --- a/vendor/src/github.com/docker/libnetwork/iptables/iptables.go +++ b/vendor/src/github.com/docker/libnetwork/iptables/iptables.go @@ -5,6 +5,7 @@ import ( "fmt" "net" "os/exec" + "regexp" "strconv" "strings" "sync" @@ -36,6 +37,7 @@ const ( var ( iptablesPath string supportsXlock = false + supportsCOpt = false // used to lock iptables commands if xtables lock is not supported bestEffortLock sync.Mutex // ErrIptablesNotFound is returned when the rule is not found. @@ -60,7 +62,6 @@ func (e ChainError) Error() string { } func initCheck() error { - if iptablesPath == "" { path, err := exec.LookPath("iptables") if err != nil { @@ -68,6 +69,12 @@ func initCheck() error { } iptablesPath = path supportsXlock = exec.Command(iptablesPath, "--wait", "-L", "-n").Run() == nil + mj, mn, mc, err := GetVersion() + if err != nil { + logrus.Warnf("Failed to read iptables version: %v", err) + return nil + } + supportsCOpt = supportsCOption(mj, mn, mc) } return nil } @@ -299,20 +306,21 @@ func Exists(table Table, chain string, rule ...string) bool { table = Filter } - // iptables -C, --check option was added in v.1.4.11 - // http://ftp.netfilter.org/pub/iptables/changes-iptables-1.4.11.txt + initCheck() - // try -C - // if exit status is 0 then return true, the rule exists - if _, err := Raw(append([]string{ - "-t", string(table), "-C", chain}, rule...)...); err == nil { - return true + if supportsCOpt { + // if exit status is 0 then return true, the rule exists + _, err := Raw(append([]string{"-t", string(table), "-C", chain}, rule...)...) + return err == nil } - // parse "iptables -S" for the rule (this checks rules in a specific chain - // in a specific table) - ruleString := strings.Join(rule, " ") - ruleString = chain + " " + ruleString + // parse "iptables -S" for the rule (it checks rules in a specific chain + // in a specific table and it is very unreliable) + return existsRaw(table, chain, rule...) +} + +func existsRaw(table Table, chain string, rule ...string) bool { + ruleString := fmt.Sprintf("%s %s\n", chain, strings.Join(rule, " ")) existingRules, _ := exec.Command(iptablesPath, "-t", string(table), "-S", chain).Output() return strings.Contains(string(existingRules), ruleString) @@ -380,3 +388,25 @@ func ExistChain(chain string, table Table) bool { } return false } + +// GetVersion reads the iptables version numbers +func GetVersion() (major, minor, micro int, err error) { + out, err := Raw("--version") + if err == nil { + major, minor, micro = parseVersionNumbers(string(out)) + } + return +} + +func parseVersionNumbers(input string) (major, minor, micro int) { + re := regexp.MustCompile(`v\d*.\d*.\d*`) + line := re.FindString(input) + fmt.Sscanf(line, "v%d.%d.%d", &major, &minor, µ) + return +} + +// iptables -C, --check option was added in v.1.4.11 +// http://ftp.netfilter.org/pub/iptables/changes-iptables-1.4.11.txt +func supportsCOption(mj, mn, mc int) bool { + return mj > 1 || (mj == 1 && (mn > 4 || (mn == 4 && mc >= 11))) +} diff --git a/vendor/src/github.com/docker/libnetwork/network.go b/vendor/src/github.com/docker/libnetwork/network.go index 1ef4e569a0..25dc39c3f5 100644 --- a/vendor/src/github.com/docker/libnetwork/network.go +++ b/vendor/src/github.com/docker/libnetwork/network.go @@ -600,7 +600,7 @@ func (n *network) driver(load bool) (driverapi.Driver, error) { return nil, err } } else if !ok { - // dont fail if driver loading is not required + // don't fail if driver loading is not required return nil, nil } @@ -851,14 +851,25 @@ func (n *network) updateSvcRecord(ep *endpoint, localEps []*endpoint, isAdd bool if iface := ep.Iface(); iface.Address() != nil { myAliases := ep.MyAliases() if isAdd { - if !ep.isAnonymous() { + // If anonymous endpoint has an alias use the first alias + // for ip->name mapping. Not having the reverse mapping + // breaks some apps + if ep.isAnonymous() { + if len(myAliases) > 0 { + n.addSvcRecords(myAliases[0], iface.Address().IP, true) + } + } else { n.addSvcRecords(epName, iface.Address().IP, true) } for _, alias := range myAliases { n.addSvcRecords(alias, iface.Address().IP, false) } } else { - if !ep.isAnonymous() { + if ep.isAnonymous() { + if len(myAliases) > 0 { + n.deleteSvcRecords(myAliases[0], iface.Address().IP, true) + } + } else { n.deleteSvcRecords(epName, iface.Address().IP, true) } for _, alias := range myAliases { diff --git a/vendor/src/github.com/docker/libnetwork/osl/interface_linux.go b/vendor/src/github.com/docker/libnetwork/osl/interface_linux.go index de74ee4852..205e3a3909 100644 --- a/vendor/src/github.com/docker/libnetwork/osl/interface_linux.go +++ b/vendor/src/github.com/docker/libnetwork/osl/interface_linux.go @@ -21,6 +21,7 @@ type nwIface struct { dstName string master string dstMaster string + mac net.HardwareAddr address *net.IPNet addressIPv6 *net.IPNet routes []*net.IPNet @@ -64,6 +65,13 @@ func (i *nwIface) Master() string { return i.master } +func (i *nwIface) MacAddress() net.HardwareAddr { + i.Lock() + defer i.Unlock() + + return types.GetMacCopy(i.mac) +} + func (i *nwIface) Address() *net.IPNet { i.Lock() defer i.Unlock() @@ -304,6 +312,7 @@ func configureInterface(iface netlink.Link, i *nwIface) error { ErrMessage string }{ {setInterfaceName, fmt.Sprintf("error renaming interface %q to %q", ifaceName, i.DstName())}, + {setInterfaceMAC, fmt.Sprintf("error setting interface %q MAC to %q", ifaceName, i.MacAddress())}, {setInterfaceIP, fmt.Sprintf("error setting interface %q IP to %q", ifaceName, i.Address())}, {setInterfaceIPv6, fmt.Sprintf("error setting interface %q IPv6 to %q", ifaceName, i.AddressIPv6())}, {setInterfaceMaster, fmt.Sprintf("error setting interface %q master to %q", ifaceName, i.DstMaster())}, @@ -326,6 +335,13 @@ func setInterfaceMaster(iface netlink.Link, i *nwIface) error { LinkAttrs: netlink.LinkAttrs{Name: i.DstMaster()}}) } +func setInterfaceMAC(iface netlink.Link, i *nwIface) error { + if i.MacAddress() == nil { + return nil + } + return netlink.LinkSetHardwareAddr(iface, i.MacAddress()) +} + func setInterfaceIP(iface netlink.Link, i *nwIface) error { if i.Address() == nil { return nil diff --git a/vendor/src/github.com/docker/libnetwork/osl/options_linux.go b/vendor/src/github.com/docker/libnetwork/osl/options_linux.go index 5295eb85c5..ea28e8b6be 100644 --- a/vendor/src/github.com/docker/libnetwork/osl/options_linux.go +++ b/vendor/src/github.com/docker/libnetwork/osl/options_linux.go @@ -42,6 +42,12 @@ func (n *networkNamespace) Master(name string) IfaceOption { } } +func (n *networkNamespace) MacAddress(mac net.HardwareAddr) IfaceOption { + return func(i *nwIface) { + i.mac = mac + } +} + func (n *networkNamespace) Address(addr *net.IPNet) IfaceOption { return func(i *nwIface) { i.address = addr diff --git a/vendor/src/github.com/docker/libnetwork/osl/sandbox.go b/vendor/src/github.com/docker/libnetwork/osl/sandbox.go index 3a824ae6ad..db49d43dce 100644 --- a/vendor/src/github.com/docker/libnetwork/osl/sandbox.go +++ b/vendor/src/github.com/docker/libnetwork/osl/sandbox.go @@ -76,6 +76,9 @@ type IfaceOptionSetter interface { // Bridge returns an option setter to set if the interface is a bridge. Bridge(bool) IfaceOption + // MacAddress returns an option setter to set the MAC address. + MacAddress(net.HardwareAddr) IfaceOption + // Address returns an option setter to set IPv4 address. Address(*net.IPNet) IfaceOption diff --git a/vendor/src/github.com/docker/libnetwork/resolver.go b/vendor/src/github.com/docker/libnetwork/resolver.go index ed17a2d9ef..a4dde792c7 100644 --- a/vendor/src/github.com/docker/libnetwork/resolver.go +++ b/vendor/src/github.com/docker/libnetwork/resolver.go @@ -2,8 +2,11 @@ package libnetwork import ( "fmt" + "math/rand" "net" "strings" + "sync" + "time" log "github.com/Sirupsen/logrus" "github.com/docker/libnetwork/iptables" @@ -31,23 +34,35 @@ type Resolver interface { } const ( - resolverIP = "127.0.0.11" - dnsPort = "53" - ptrIPv4domain = ".in-addr.arpa." - ptrIPv6domain = ".ip6.arpa." - respTTL = 600 - maxExtDNS = 3 //max number of external servers to try + resolverIP = "127.0.0.11" + dnsPort = "53" + ptrIPv4domain = ".in-addr.arpa." + ptrIPv6domain = ".ip6.arpa." + respTTL = 600 + maxExtDNS = 3 //max number of external servers to try + extIOTimeout = 3 * time.Second + defaultRespSize = 512 ) +type extDNSEntry struct { + ipStr string + extConn net.Conn + extOnce sync.Once +} + // resolver implements the Resolver interface type resolver struct { - sb *sandbox - extDNS []string - server *dns.Server - conn *net.UDPConn - tcpServer *dns.Server - tcpListen *net.TCPListener - err error + sb *sandbox + extDNSList [maxExtDNS]extDNSEntry + server *dns.Server + conn *net.UDPConn + tcpServer *dns.Server + tcpListen *net.TCPListener + err error +} + +func init() { + rand.Seed(time.Now().Unix()) } // NewResolver creates a new instance of the Resolver @@ -136,7 +151,13 @@ func (r *resolver) Stop() { } func (r *resolver) SetExtServers(dns []string) { - r.extDNS = dns + l := len(dns) + if l > maxExtDNS { + l = maxExtDNS + } + for i := 0; i < l; i++ { + r.extDNSList[i].ipStr = dns[i] + } } func (r *resolver) NameServer() string { @@ -151,22 +172,36 @@ func setCommonFlags(msg *dns.Msg) { msg.RecursionAvailable = true } +func shuffleAddr(addr []net.IP) []net.IP { + for i := len(addr) - 1; i > 0; i-- { + r := rand.Intn(i + 1) + addr[i], addr[r] = addr[r], addr[i] + } + return addr +} + func (r *resolver) handleIPv4Query(name string, query *dns.Msg) (*dns.Msg, error) { addr := r.sb.ResolveName(name) if addr == nil { return nil, nil } - log.Debugf("Lookup for %s: IP %s", name, addr.String()) + log.Debugf("Lookup for %s: IP %v", name, addr) resp := new(dns.Msg) resp.SetReply(query) setCommonFlags(resp) - rr := new(dns.A) - rr.Hdr = dns.RR_Header{Name: name, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: respTTL} - rr.A = addr - resp.Answer = append(resp.Answer, rr) + if len(addr) > 1 { + addr = shuffleAddr(addr) + } + + for _, ip := range addr { + rr := new(dns.A) + rr.Hdr = dns.RR_Header{Name: name, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: respTTL} + rr.A = ip + resp.Answer = append(resp.Answer, rr) + } return resp, nil } @@ -200,10 +235,23 @@ func (r *resolver) handlePTRQuery(ptr string, query *dns.Msg) (*dns.Msg, error) return resp, nil } +func truncateResp(resp *dns.Msg, maxSize int, isTCP bool) { + if !isTCP { + resp.Truncated = true + } + + // trim the Answer RRs one by one till the whole message fits + // within the reply size + for resp.Len() > maxSize { + resp.Answer = resp.Answer[:len(resp.Answer)-1] + } +} + func (r *resolver) ServeDNS(w dns.ResponseWriter, query *dns.Msg) { var ( - resp *dns.Msg - err error + extConn net.Conn + resp *dns.Msg + err error ) if query == nil || len(query.Question) == 0 { @@ -221,28 +269,82 @@ func (r *resolver) ServeDNS(w dns.ResponseWriter, query *dns.Msg) { return } - if resp == nil { - if len(r.extDNS) == 0 { - return + proto := w.LocalAddr().Network() + maxSize := 0 + if proto == "tcp" { + maxSize = dns.MaxMsgSize - 1 + } else if proto == "udp" { + optRR := query.IsEdns0() + if optRR != nil { + maxSize = int(optRR.UDPSize()) } - - num := maxExtDNS - if len(r.extDNS) < maxExtDNS { - num = len(r.extDNS) + if maxSize < defaultRespSize { + maxSize = defaultRespSize } - for i := 0; i < num; i++ { - log.Debugf("Querying ext dns %s:%s for %s[%d]", w.LocalAddr().Network(), r.extDNS[i], name, query.Question[0].Qtype) + } - c := &dns.Client{Net: w.LocalAddr().Network()} - addr := fmt.Sprintf("%s:%d", r.extDNS[i], 53) - - resp, _, err = c.Exchange(query, addr) - if err == nil { - resp.Compress = true + if resp != nil { + if resp.Len() > maxSize { + truncateResp(resp, maxSize, proto == "tcp") + } + } else { + for i := 0; i < maxExtDNS; i++ { + extDNS := &r.extDNSList[i] + if extDNS.ipStr == "" { break } - log.Errorf("external resolution failed, %s", err) + log.Debugf("Querying ext dns %s:%s for %s[%d]", proto, extDNS.ipStr, name, query.Question[0].Qtype) + + extConnect := func() { + addr := fmt.Sprintf("%s:%d", extDNS.ipStr, 53) + extConn, err = net.DialTimeout(proto, addr, extIOTimeout) + } + + // For udp clients connection is persisted to reuse for further queries. + // Accessing extDNS.extConn be a race here between go rouines. Hence the + // connection setup is done in a Once block and fetch the extConn again + extConn = extDNS.extConn + if extConn == nil || proto == "tcp" { + if proto == "udp" { + extDNS.extOnce.Do(func() { + r.sb.execFunc(extConnect) + extDNS.extConn = extConn + }) + extConn = extDNS.extConn + } else { + r.sb.execFunc(extConnect) + } + if err != nil { + log.Debugf("Connect failed, %s", err) + continue + } + } + + // Timeout has to be set for every IO operation. + extConn.SetDeadline(time.Now().Add(extIOTimeout)) + co := &dns.Conn{Conn: extConn} + + defer func() { + if proto == "tcp" { + co.Close() + } + }() + err = co.WriteMsg(query) + if err != nil { + log.Debugf("Send to DNS server failed, %s", err) + continue + } + + resp, err = co.ReadMsg() + if err != nil { + log.Debugf("Read from DNS server failed, %s", err) + continue + } + + resp.Compress = true + break } + if resp == nil { return } diff --git a/vendor/src/github.com/docker/libnetwork/sandbox.go b/vendor/src/github.com/docker/libnetwork/sandbox.go index c33f4e68da..5733deac18 100644 --- a/vendor/src/github.com/docker/libnetwork/sandbox.go +++ b/vendor/src/github.com/docker/libnetwork/sandbox.go @@ -10,6 +10,7 @@ import ( log "github.com/Sirupsen/logrus" "github.com/docker/libnetwork/etchosts" + "github.com/docker/libnetwork/netlabel" "github.com/docker/libnetwork/osl" "github.com/docker/libnetwork/types" ) @@ -37,7 +38,7 @@ type Sandbox interface { Delete() error // ResolveName searches for the service name in the networks to which the sandbox // is connected to. - ResolveName(name string) net.IP + ResolveName(name string) []net.IP // ResolveIP returns the service name for the passed in IP. IP is in reverse dotted // notation; the format used for DNS PTR records ResolveIP(name string) string @@ -118,6 +119,7 @@ type containerConfig struct { useDefaultSandBox bool useExternalKey bool prio int // higher the value, more the priority + exposedPorts []types.TransportPort } func (sb *sandbox) ID() string { @@ -136,18 +138,27 @@ func (sb *sandbox) Key() string { } func (sb *sandbox) Labels() map[string]interface{} { - return sb.config.generic + sb.Lock() + sb.Unlock() + opts := make(map[string]interface{}, len(sb.config.generic)) + for k, v := range sb.config.generic { + opts[k] = v + } + return opts } func (sb *sandbox) Statistics() (map[string]*types.InterfaceStatistics, error) { m := make(map[string]*types.InterfaceStatistics) - if sb.osSbox == nil { + sb.Lock() + osb := sb.osSbox + sb.Unlock() + if osb == nil { return m, nil } var err error - for _, i := range sb.osSbox.Info().Interfaces() { + for _, i := range osb.Info().Interfaces() { if m[i.DstName()], err = i.Statistics(); err != nil { return m, err } @@ -326,6 +337,18 @@ func (sb *sandbox) getConnectedEndpoints() []*endpoint { return eps } +func (sb *sandbox) removeEndpoint(ep *endpoint) { + sb.Lock() + defer sb.Unlock() + + for i, e := range sb.endpoints { + if e == ep { + heap.Remove(&sb.endpoints, i) + return + } + } +} + func (sb *sandbox) getEndpoint(id string) *endpoint { sb.Lock() defer sb.Unlock() @@ -391,8 +414,12 @@ func (sb *sandbox) ResolveIP(ip string) string { return svc } -func (sb *sandbox) ResolveName(name string) net.IP { - var ip net.IP +func (sb *sandbox) execFunc(f func()) { + sb.osSbox.InvokeFunc(f) +} + +func (sb *sandbox) ResolveName(name string) []net.IP { + var ip []net.IP // Embedded server owns the docker network domain. Resolution should work // for both container_name and container_name.network_name @@ -440,7 +467,7 @@ func (sb *sandbox) ResolveName(name string) net.IP { return nil } -func (sb *sandbox) resolveName(req string, networkName string, epList []*endpoint, alias bool) net.IP { +func (sb *sandbox) resolveName(req string, networkName string, epList []*endpoint, alias bool) []net.IP { for _, ep := range epList { name := req n := ep.getNetwork() @@ -463,7 +490,7 @@ func (sb *sandbox) resolveName(req string, networkName string, epList []*endpoin } } else { // If it is a regular lookup and if the requested name is an alias - // dont perform a svc lookup for this endpoint. + // don't perform a svc lookup for this endpoint. ep.Lock() if _, ok := ep.aliases[req]; ok { ep.Unlock() @@ -481,7 +508,7 @@ func (sb *sandbox) resolveName(req string, networkName string, epList []*endpoin ip, ok := sr.svcMap[name] n.Unlock() if ok { - return ip[0] + return ip } } return nil @@ -606,6 +633,9 @@ func (sb *sandbox) populateNetworkResources(ep *endpoint) error { if i.addrv6 != nil && i.addrv6.IP.To16() != nil { ifaceOptions = append(ifaceOptions, sb.osSbox.InterfaceOptions().AddressIPv6(i.addrv6)) } + if i.mac != nil { + ifaceOptions = append(ifaceOptions, sb.osSbox.InterfaceOptions().MacAddress(i.mac)) + } if err := sb.osSbox.AddInterface(i.srcName, i.dstPrefix, ifaceOptions...); err != nil { return fmt.Errorf("failed to add interface %s to sandbox: %v", i.srcName, err) @@ -621,14 +651,9 @@ func (sb *sandbox) populateNetworkResources(ep *endpoint) error { } } - for _, gwep := range sb.getConnectedEndpoints() { - if len(gwep.Gateway()) > 0 { - if gwep != ep { - break - } - if err := sb.updateGateway(gwep); err != nil { - return err - } + if ep == sb.getGatewayEndpoint() { + if err := sb.updateGateway(ep); err != nil { + return err } } @@ -647,7 +672,7 @@ func (sb *sandbox) clearNetworkResources(origEp *endpoint) error { ep := sb.getEndpoint(origEp.id) if ep == nil { return fmt.Errorf("could not find the sandbox endpoint data for endpoint %s", - ep.name) + origEp.id) } sb.Lock() @@ -739,6 +764,13 @@ func (sb *sandbox) joinLeaveEnd() { } } +func (sb *sandbox) hasPortConfigs() bool { + opts := sb.Labels() + _, hasExpPorts := opts[netlabel.ExposedPorts] + _, hasPortMaps := opts[netlabel.PortMap] + return hasExpPorts || hasPortMaps +} + // OptionHostname function returns an option setter for hostname option to // be passed to NewSandbox method. func OptionHostname(name string) SandboxOption { @@ -848,7 +880,42 @@ func OptionUseExternalKey() SandboxOption { // net container creation method. Container Labels are a good example. func OptionGeneric(generic map[string]interface{}) SandboxOption { return func(sb *sandbox) { - sb.config.generic = generic + if sb.config.generic == nil { + sb.config.generic = make(map[string]interface{}, len(generic)) + } + for k, v := range generic { + sb.config.generic[k] = v + } + } +} + +// OptionExposedPorts function returns an option setter for the container exposed +// ports option to be passed to container Create method. +func OptionExposedPorts(exposedPorts []types.TransportPort) SandboxOption { + return func(sb *sandbox) { + if sb.config.generic == nil { + sb.config.generic = make(map[string]interface{}) + } + // Defensive copy + eps := make([]types.TransportPort, len(exposedPorts)) + copy(eps, exposedPorts) + // Store endpoint label and in generic because driver needs it + sb.config.exposedPorts = eps + sb.config.generic[netlabel.ExposedPorts] = eps + } +} + +// OptionPortMapping function returns an option setter for the mapping +// ports option to be passed to container Create method. +func OptionPortMapping(portBindings []types.PortBinding) SandboxOption { + return func(sb *sandbox) { + if sb.config.generic == nil { + sb.config.generic = make(map[string]interface{}) + } + // Store a copy of the bindings as generic data to pass to the driver + pbs := make([]types.PortBinding, len(portBindings)) + copy(pbs, portBindings) + sb.config.generic[netlabel.PortMap] = pbs } } diff --git a/vendor/src/github.com/docker/libnetwork/sandbox_externalkey_unix.go b/vendor/src/github.com/docker/libnetwork/sandbox_externalkey_unix.go index d0682c2f17..3e5cee1c28 100644 --- a/vendor/src/github.com/docker/libnetwork/sandbox_externalkey_unix.go +++ b/vendor/src/github.com/docker/libnetwork/sandbox_externalkey_unix.go @@ -130,7 +130,7 @@ func (c *controller) acceptClientConnections(sock string, l net.Listener) { conn, err := l.Accept() if err != nil { if _, err1 := os.Stat(sock); os.IsNotExist(err1) { - logrus.Debugf("Unix socket %s doesnt exist. cannot accept client connections", sock) + logrus.Debugf("Unix socket %s doesn't exist. cannot accept client connections", sock) return } logrus.Errorf("Error accepting connection %v", err) diff --git a/vendor/src/github.com/docker/libnetwork/types/types.go b/vendor/src/github.com/docker/libnetwork/types/types.go index 7ada9643c0..44ee563e69 100644 --- a/vendor/src/github.com/docker/libnetwork/types/types.go +++ b/vendor/src/github.com/docker/libnetwork/types/types.go @@ -389,7 +389,7 @@ const ( // NEXTHOP indicates a StaticRoute with an IP next hop. NEXTHOP = iota - // CONNECTED indicates a StaticRoute with a interface for directly connected peers. + // CONNECTED indicates a StaticRoute with an interface for directly connected peers. CONNECTED ) @@ -458,25 +458,25 @@ type NotFoundError interface { NotFound() } -// ForbiddenError is an interface for errors which denote an valid request that cannot be honored +// ForbiddenError is an interface for errors which denote a valid request that cannot be honored type ForbiddenError interface { // Forbidden makes implementer into ForbiddenError type Forbidden() } -// NoServiceError is an interface for errors returned when the required service is not available +// NoServiceError is an interface for errors returned when the required service is not available type NoServiceError interface { // NoService makes implementer into NoServiceError type NoService() } -// TimeoutError is an interface for errors raised because of timeout +// TimeoutError is an interface for errors raised because of timeout type TimeoutError interface { // Timeout makes implementer into TimeoutError type Timeout() } -// NotImplementedError is an interface for errors raised because of requested functionality is not yet implemented +// NotImplementedError is an interface for errors raised because of requested functionality is not yet implemented type NotImplementedError interface { // NotImplemented makes implementer into NotImplementedError type NotImplemented() diff --git a/vendor/src/github.com/vishvananda/netlink/addr_linux.go b/vendor/src/github.com/vishvananda/netlink/addr_linux.go index 9373e9c5a7..9e4f62f1d5 100644 --- a/vendor/src/github.com/vishvananda/netlink/addr_linux.go +++ b/vendor/src/github.com/vishvananda/netlink/addr_linux.go @@ -101,6 +101,10 @@ func AddrList(link Link, family int) ([]Addr, error) { continue } + if family != FAMILY_ALL && msg.Family != uint8(family) { + continue + } + attrs, err := nl.ParseRouteAttr(m[msg.Len():]) if err != nil { return nil, err diff --git a/vendor/src/github.com/vishvananda/netlink/class.go b/vendor/src/github.com/vishvananda/netlink/class.go index 35bdb33100..264e3ad003 100644 --- a/vendor/src/github.com/vishvananda/netlink/class.go +++ b/vendor/src/github.com/vishvananda/netlink/class.go @@ -56,6 +56,7 @@ func NewHtbClass(attrs ClassAttrs, cattrs HtbClassAttrs) *HtbClass { ceil := cattrs.Ceil / 8 buffer := cattrs.Buffer cbuffer := cattrs.Cbuffer + if ceil == 0 { ceil = rate } diff --git a/vendor/src/github.com/vishvananda/netlink/class_linux.go b/vendor/src/github.com/vishvananda/netlink/class_linux.go index 84828da101..4a52d2b997 100644 --- a/vendor/src/github.com/vishvananda/netlink/class_linux.go +++ b/vendor/src/github.com/vishvananda/netlink/class_linux.go @@ -1,6 +1,7 @@ package netlink import ( + "errors" "syscall" "github.com/vishvananda/netlink/nl" @@ -65,15 +66,32 @@ func classPayload(req *nl.NetlinkRequest, class Class) error { options := nl.NewRtAttr(nl.TCA_OPTIONS, nil) if htb, ok := class.(*HtbClass); ok { opt := nl.TcHtbCopt{} - opt.Rate.Rate = uint32(htb.Rate) - opt.Ceil.Rate = uint32(htb.Ceil) opt.Buffer = htb.Buffer opt.Cbuffer = htb.Cbuffer opt.Quantum = htb.Quantum opt.Level = htb.Level opt.Prio = htb.Prio // TODO: Handle Debug properly. For now default to 0 + /* Calculate {R,C}Tab and set Rate and Ceil */ + cell_log := -1 + ccell_log := -1 + linklayer := nl.LINKLAYER_ETHERNET + mtu := 1600 + var rtab [256]uint32 + var ctab [256]uint32 + tcrate := nl.TcRateSpec{Rate: uint32(htb.Rate)} + if CalcRtable(&tcrate, rtab, cell_log, uint32(mtu), linklayer) < 0 { + return errors.New("HTB: failed to calculate rate table.") + } + opt.Rate = tcrate + tcceil := nl.TcRateSpec{Rate: uint32(htb.Ceil)} + if CalcRtable(&tcceil, ctab, ccell_log, uint32(mtu), linklayer) < 0 { + return errors.New("HTB: failed to calculate ceil rate table.") + } + opt.Ceil = tcceil nl.NewRtAttrChild(options, nl.TCA_HTB_PARMS, opt.Serialize()) + nl.NewRtAttrChild(options, nl.TCA_HTB_RTAB, SerializeRtab(rtab)) + nl.NewRtAttrChild(options, nl.TCA_HTB_CTAB, SerializeRtab(ctab)) } req.AddData(options) return nil diff --git a/vendor/src/github.com/vishvananda/netlink/link.go b/vendor/src/github.com/vishvananda/netlink/link.go index 544a97cb46..2934c0fb2a 100644 --- a/vendor/src/github.com/vishvananda/netlink/link.go +++ b/vendor/src/github.com/vishvananda/netlink/link.go @@ -204,6 +204,7 @@ type Vxlan struct { RSC bool L2miss bool L3miss bool + UDPCSum bool NoAge bool GBP bool Age int diff --git a/vendor/src/github.com/vishvananda/netlink/link_linux.go b/vendor/src/github.com/vishvananda/netlink/link_linux.go index 3aa9124881..b3d0472004 100644 --- a/vendor/src/github.com/vishvananda/netlink/link_linux.go +++ b/vendor/src/github.com/vishvananda/netlink/link_linux.go @@ -142,6 +142,54 @@ func LinkSetHardwareAddr(link Link, hwaddr net.HardwareAddr) error { return err } +// LinkSetVfHardwareAddr sets the hardware address of a vf for the link. +// Equivalent to: `ip link set $link vf $vf mac $hwaddr` +func LinkSetVfHardwareAddr(link Link, vf int, 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.Index = int32(base.Index) + req.AddData(msg) + + data := nl.NewRtAttr(nl.IFLA_VFINFO_LIST, nil) + info := nl.NewRtAttrChild(data, nl.IFLA_VF_INFO, nil) + vfmsg := nl.VfMac{ + Vf: uint32(vf), + } + copy(vfmsg.Mac[:], []byte(hwaddr)) + nl.NewRtAttrChild(info, nl.IFLA_VF_MAC, vfmsg.Serialize()) + req.AddData(data) + + _, err := req.Execute(syscall.NETLINK_ROUTE, 0) + return err +} + +// LinkSetVfVlan sets the vlan of a vf for the link. +// Equivalent to: `ip link set $link vf $vf vlan $vlan` +func LinkSetVfVlan(link Link, vf, vlan int) error { + base := link.Attrs() + ensureIndex(base) + req := nl.NewNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK) + + msg := nl.NewIfInfomsg(syscall.AF_UNSPEC) + msg.Index = int32(base.Index) + req.AddData(msg) + + data := nl.NewRtAttr(nl.IFLA_VFINFO_LIST, nil) + info := nl.NewRtAttrChild(data, nl.IFLA_VF_INFO, nil) + vfmsg := nl.VfVlan{ + Vf: uint32(vf), + Vlan: uint32(vlan), + } + nl.NewRtAttrChild(info, nl.IFLA_VF_VLAN, vfmsg.Serialize()) + 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 { @@ -277,10 +325,12 @@ func addVxlanAttrs(vxlan *Vxlan, linkInfo *nl.RtAttr) { nl.NewRtAttrChild(data, nl.IFLA_VXLAN_L2MISS, boolAttr(vxlan.L2miss)) nl.NewRtAttrChild(data, nl.IFLA_VXLAN_L3MISS, boolAttr(vxlan.L3miss)) + if vxlan.UDPCSum { + nl.NewRtAttrChild(data, nl.IFLA_VXLAN_UDP_CSUM, boolAttr(vxlan.UDPCSum)) + } if vxlan.GBP { nl.NewRtAttrChild(data, nl.IFLA_VXLAN_GBP, boolAttr(vxlan.GBP)) } - if vxlan.NoAge { nl.NewRtAttrChild(data, nl.IFLA_VXLAN_AGEING, nl.Uint32Attr(0)) } else if vxlan.Age > 0 { @@ -815,6 +865,7 @@ func LinkList() ([]Link, error) { // LinkUpdate is used to pass information back from LinkSubscribe() type LinkUpdate struct { nl.IfInfomsg + Header syscall.NlMsghdr Link } @@ -844,7 +895,7 @@ func LinkSubscribe(ch chan<- LinkUpdate, done <-chan struct{}) error { if err != nil { return } - ch <- LinkUpdate{IfInfomsg: *ifmsg, Link: link} + ch <- LinkUpdate{IfInfomsg: *ifmsg, Header: m.Header, Link: link} } } }() @@ -935,6 +986,8 @@ func parseVxlanData(link Link, data []syscall.NetlinkRouteAttr) { vxlan.L2miss = int8(datum.Value[0]) != 0 case nl.IFLA_VXLAN_L3MISS: vxlan.L3miss = int8(datum.Value[0]) != 0 + case nl.IFLA_VXLAN_UDP_CSUM: + vxlan.UDPCSum = int8(datum.Value[0]) != 0 case nl.IFLA_VXLAN_GBP: vxlan.GBP = int8(datum.Value[0]) != 0 case nl.IFLA_VXLAN_AGEING: diff --git a/vendor/src/github.com/vishvananda/netlink/nl/link_linux.go b/vendor/src/github.com/vishvananda/netlink/nl/link_linux.go index 8554a5d4a0..b7f50646d2 100644 --- a/vendor/src/github.com/vishvananda/netlink/nl/link_linux.go +++ b/vendor/src/github.com/vishvananda/netlink/nl/link_linux.go @@ -1,7 +1,13 @@ package nl +import ( + "unsafe" +) + const ( DEFAULT_CHANGE = 0xFFFFFFFF + // doesn't exist in syscall + IFLA_VFINFO_LIST = 0x16 ) const ( @@ -182,3 +188,209 @@ const ( GRE_FLAGS = 0x00F8 GRE_VERSION = 0x0007 ) + +const ( + IFLA_VF_INFO_UNSPEC = iota + IFLA_VF_INFO + IFLA_VF_INFO_MAX = IFLA_VF_INFO +) + +const ( + IFLA_VF_UNSPEC = iota + IFLA_VF_MAC /* Hardware queue specific attributes */ + IFLA_VF_VLAN + IFLA_VF_TX_RATE /* Max TX Bandwidth Allocation */ + IFLA_VF_SPOOFCHK /* Spoof Checking on/off switch */ + IFLA_VF_LINK_STATE /* link state enable/disable/auto switch */ + IFLA_VF_RATE /* Min and Max TX Bandwidth Allocation */ + IFLA_VF_RSS_QUERY_EN /* RSS Redirection Table and Hash Key query + * on/off switch + */ + IFLA_VF_STATS /* network device statistics */ + IFLA_VF_MAX = IFLA_VF_STATS +) + +const ( + IFLA_VF_LINK_STATE_AUTO = iota /* link state of the uplink */ + IFLA_VF_LINK_STATE_ENABLE /* link always up */ + IFLA_VF_LINK_STATE_DISABLE /* link always down */ + IFLA_VF_LINK_STATE_MAX = IFLA_VF_LINK_STATE_DISABLE +) + +const ( + IFLA_VF_STATS_RX_PACKETS = iota + IFLA_VF_STATS_TX_PACKETS + IFLA_VF_STATS_RX_BYTES + IFLA_VF_STATS_TX_BYTES + IFLA_VF_STATS_BROADCAST + IFLA_VF_STATS_MULTICAST + IFLA_VF_STATS_MAX = IFLA_VF_STATS_MULTICAST +) + +const ( + SizeofVfMac = 0x24 + SizeofVfVlan = 0x0c + SizeofVfTxRate = 0x08 + SizeofVfRate = 0x0c + SizeofVfSpoofchk = 0x08 + SizeofVfLinkState = 0x08 + SizeofVfRssQueryEn = 0x08 +) + +// struct ifla_vf_mac { +// __u32 vf; +// __u8 mac[32]; /* MAX_ADDR_LEN */ +// }; + +type VfMac struct { + Vf uint32 + Mac [32]byte +} + +func (msg *VfMac) Len() int { + return SizeofVfMac +} + +func DeserializeVfMac(b []byte) *VfMac { + return (*VfMac)(unsafe.Pointer(&b[0:SizeofVfMac][0])) +} + +func (msg *VfMac) Serialize() []byte { + return (*(*[SizeofVfMac]byte)(unsafe.Pointer(msg)))[:] +} + +// struct ifla_vf_vlan { +// __u32 vf; +// __u32 vlan; /* 0 - 4095, 0 disables VLAN filter */ +// __u32 qos; +// }; + +type VfVlan struct { + Vf uint32 + Vlan uint32 + Qos uint32 +} + +func (msg *VfVlan) Len() int { + return SizeofVfVlan +} + +func DeserializeVfVlan(b []byte) *VfVlan { + return (*VfVlan)(unsafe.Pointer(&b[0:SizeofVfVlan][0])) +} + +func (msg *VfVlan) Serialize() []byte { + return (*(*[SizeofVfVlan]byte)(unsafe.Pointer(msg)))[:] +} + +// struct ifla_vf_tx_rate { +// __u32 vf; +// __u32 rate; /* Max TX bandwidth in Mbps, 0 disables throttling */ +// }; + +type VfTxRate struct { + Vf uint32 + Rate uint32 +} + +func (msg *VfTxRate) Len() int { + return SizeofVfTxRate +} + +func DeserializeVfTxRate(b []byte) *VfTxRate { + return (*VfTxRate)(unsafe.Pointer(&b[0:SizeofVfTxRate][0])) +} + +func (msg *VfTxRate) Serialize() []byte { + return (*(*[SizeofVfTxRate]byte)(unsafe.Pointer(msg)))[:] +} + +// struct ifla_vf_rate { +// __u32 vf; +// __u32 min_tx_rate; /* Min Bandwidth in Mbps */ +// __u32 max_tx_rate; /* Max Bandwidth in Mbps */ +// }; + +type VfRate struct { + Vf uint32 + MinTxRate uint32 + MaxTxRate uint32 +} + +func (msg *VfRate) Len() int { + return SizeofVfRate +} + +func DeserializeVfRate(b []byte) *VfRate { + return (*VfRate)(unsafe.Pointer(&b[0:SizeofVfRate][0])) +} + +func (msg *VfRate) Serialize() []byte { + return (*(*[SizeofVfRate]byte)(unsafe.Pointer(msg)))[:] +} + +// struct ifla_vf_spoofchk { +// __u32 vf; +// __u32 setting; +// }; + +type VfSpoofchk struct { + Vf uint32 + Setting uint32 +} + +func (msg *VfSpoofchk) Len() int { + return SizeofVfSpoofchk +} + +func DeserializeVfSpoofchk(b []byte) *VfSpoofchk { + return (*VfSpoofchk)(unsafe.Pointer(&b[0:SizeofVfSpoofchk][0])) +} + +func (msg *VfSpoofchk) Serialize() []byte { + return (*(*[SizeofVfSpoofchk]byte)(unsafe.Pointer(msg)))[:] +} + +// struct ifla_vf_link_state { +// __u32 vf; +// __u32 link_state; +// }; + +type VfLinkState struct { + Vf uint32 + LinkState uint32 +} + +func (msg *VfLinkState) Len() int { + return SizeofVfLinkState +} + +func DeserializeVfLinkState(b []byte) *VfLinkState { + return (*VfLinkState)(unsafe.Pointer(&b[0:SizeofVfLinkState][0])) +} + +func (msg *VfLinkState) Serialize() []byte { + return (*(*[SizeofVfLinkState]byte)(unsafe.Pointer(msg)))[:] +} + +// struct ifla_vf_rss_query_en { +// __u32 vf; +// __u32 setting; +// }; + +type VfRssQueryEn struct { + Vf uint32 + Setting uint32 +} + +func (msg *VfRssQueryEn) Len() int { + return SizeofVfRssQueryEn +} + +func DeserializeVfRssQueryEn(b []byte) *VfRssQueryEn { + return (*VfRssQueryEn)(unsafe.Pointer(&b[0:SizeofVfRssQueryEn][0])) +} + +func (msg *VfRssQueryEn) Serialize() []byte { + return (*(*[SizeofVfRssQueryEn]byte)(unsafe.Pointer(msg)))[:] +} diff --git a/vendor/src/github.com/vishvananda/netlink/xfrm_state_linux.go b/vendor/src/github.com/vishvananda/netlink/xfrm_state_linux.go index 5f44ec8525..fc8604b9d1 100644 --- a/vendor/src/github.com/vishvananda/netlink/xfrm_state_linux.go +++ b/vendor/src/github.com/vishvananda/netlink/xfrm_state_linux.go @@ -110,9 +110,6 @@ func XfrmStateDel(state *XfrmState) error { 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