Forráskód Böngészése

Merge pull request #31710 from sanimej/drillerrr

Add verbose flag to network inspect to show all services & tasks in swarm mode
Madhu Venugopal 8 éve
szülő
commit
cdf66ba715
37 módosított fájl, 697 hozzáadás és 93 törlés
  1. 39 5
      api/server/router/network/network_routes.go
  2. 5 0
      api/swagger.yaml
  3. 16 0
      api/types/network/network.go
  4. 14 13
      api/types/types.go
  5. 5 3
      cli/command/network/inspect.go
  6. 1 1
      cli/command/stack/deploy_composefile.go
  7. 1 1
      cli/command/system/inspect.go
  8. 2 2
      client/interface.go
  9. 14 5
      client/network_inspect.go
  10. 33 6
      client/network_inspect_test.go
  11. 1 0
      docs/api/version-history.md
  12. 96 1
      docs/reference/commandline/network_inspect.md
  13. 93 0
      man/src/network/inspect.md
  14. 1 1
      vendor.conf
  15. 114 7
      vendor/github.com/docker/libnetwork/agent.go
  16. 36 1
      vendor/github.com/docker/libnetwork/driverapi/driverapi.go
  17. 4 0
      vendor/github.com/docker/libnetwork/drivers/bridge/bridge.go
  18. 0 6
      vendor/github.com/docker/libnetwork/drivers/bridge/setup_ip_tables.go
  19. 4 0
      vendor/github.com/docker/libnetwork/drivers/host/host.go
  20. 4 0
      vendor/github.com/docker/libnetwork/drivers/ipvlan/ipvlan.go
  21. 4 0
      vendor/github.com/docker/libnetwork/drivers/macvlan/macvlan.go
  22. 4 0
      vendor/github.com/docker/libnetwork/drivers/null/null.go
  23. 55 15
      vendor/github.com/docker/libnetwork/drivers/overlay/encryption.go
  24. 17 0
      vendor/github.com/docker/libnetwork/drivers/overlay/joinleave.go
  25. 1 1
      vendor/github.com/docker/libnetwork/drivers/overlay/ov_network.go
  26. 4 0
      vendor/github.com/docker/libnetwork/drivers/overlay/ovmanager/ovmanager.go
  27. 4 0
      vendor/github.com/docker/libnetwork/drivers/remote/driver.go
  28. 4 0
      vendor/github.com/docker/libnetwork/drivers/solaris/bridge/bridge.go
  29. 4 0
      vendor/github.com/docker/libnetwork/drivers/solaris/overlay/joinleave.go
  30. 1 1
      vendor/github.com/docker/libnetwork/drivers/solaris/overlay/ov_network.go
  31. 4 0
      vendor/github.com/docker/libnetwork/drivers/windows/overlay/joinleave_windows.go
  32. 1 1
      vendor/github.com/docker/libnetwork/drivers/windows/overlay/ov_network_windows.go
  33. 4 0
      vendor/github.com/docker/libnetwork/drivers/windows/windows.go
  34. 52 19
      vendor/github.com/docker/libnetwork/iptables/iptables.go
  35. 19 4
      vendor/github.com/docker/libnetwork/network.go
  36. 16 0
      vendor/github.com/docker/libnetwork/networkdb/networkdb.go
  37. 20 0
      vendor/github.com/docker/libnetwork/service_common.go

+ 39 - 5
api/server/router/network/network_routes.go

@@ -4,10 +4,12 @@ import (
 	"encoding/json"
 	"fmt"
 	"net/http"
+	"strconv"
 	"strings"
 
 	"golang.org/x/net/context"
 
+	"github.com/docker/docker/api/errors"
 	"github.com/docker/docker/api/server/httputils"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/filters"
@@ -65,7 +67,7 @@ SKIP:
 		// run across all the networks. Starting API version 1.27, this detailed
 		// info is available for network specific GET API (equivalent to inspect)
 		if versions.LessThan(httputils.VersionFromContext(ctx), "1.27") {
-			nr = n.buildDetailedNetworkResources(nw)
+			nr = n.buildDetailedNetworkResources(nw, false)
 		} else {
 			nr = n.buildNetworkResource(nw)
 		}
@@ -85,6 +87,16 @@ func (n *networkRouter) getNetwork(ctx context.Context, w http.ResponseWriter, r
 	}
 
 	term := vars["id"]
+	var (
+		verbose bool
+		err     error
+	)
+	if v := r.URL.Query().Get("verbose"); v != "" {
+		if verbose, err = strconv.ParseBool(v); err != nil {
+			err = fmt.Errorf("invalid value for verbose: %s", v)
+			return errors.NewBadRequestError(err)
+		}
+	}
 
 	// In case multiple networks have duplicate names, return error.
 	// TODO (yongtang): should we wrap with version here for backward compatibility?
@@ -100,17 +112,17 @@ func (n *networkRouter) getNetwork(ctx context.Context, w http.ResponseWriter, r
 	nw := n.backend.GetNetworks()
 	for _, network := range nw {
 		if network.ID() == term {
-			return httputils.WriteJSON(w, http.StatusOK, *n.buildDetailedNetworkResources(network))
+			return httputils.WriteJSON(w, http.StatusOK, *n.buildDetailedNetworkResources(network, verbose))
 		}
 		if network.Name() == term {
 			// No need to check the ID collision here as we are still in
 			// local scope and the network ID is unique in this scope.
-			listByFullName[network.ID()] = *n.buildDetailedNetworkResources(network)
+			listByFullName[network.ID()] = *n.buildDetailedNetworkResources(network, verbose)
 		}
 		if strings.HasPrefix(network.ID(), term) {
 			// No need to check the ID collision here as we are still in
 			// local scope and the network ID is unique in this scope.
-			listByPartialID[network.ID()] = *n.buildDetailedNetworkResources(network)
+			listByPartialID[network.ID()] = *n.buildDetailedNetworkResources(network, verbose)
 		}
 	}
 
@@ -294,7 +306,7 @@ func (n *networkRouter) buildNetworkResource(nw libnetwork.Network) *types.Netwo
 	return r
 }
 
-func (n *networkRouter) buildDetailedNetworkResources(nw libnetwork.Network) *types.NetworkResource {
+func (n *networkRouter) buildDetailedNetworkResources(nw libnetwork.Network, verbose bool) *types.NetworkResource {
 	if nw == nil {
 		return &types.NetworkResource{}
 	}
@@ -315,6 +327,28 @@ func (n *networkRouter) buildDetailedNetworkResources(nw libnetwork.Network) *ty
 
 		r.Containers[key] = buildEndpointResource(tmpID, e.Name(), ei)
 	}
+	if !verbose {
+		return r
+	}
+	services := nw.Info().Services()
+	r.Services = make(map[string]network.ServiceInfo)
+	for name, service := range services {
+		tasks := []network.Task{}
+		for _, t := range service.Tasks {
+			tasks = append(tasks, network.Task{
+				Name:       t.Name,
+				EndpointID: t.EndpointID,
+				EndpointIP: t.EndpointIP,
+				Info:       t.Info,
+			})
+		}
+		r.Services[name] = network.ServiceInfo{
+			VIP:          service.VIP,
+			Ports:        service.Ports,
+			Tasks:        tasks,
+			LocalLBIndex: service.LocalLBIndex,
+		}
+	}
 	return r
 }
 

+ 5 - 0
api/swagger.yaml

@@ -6271,6 +6271,11 @@ paths:
           description: "Network ID or name"
           required: true
           type: "string"
+        - name: "verbose"
+          in: "query"
+          description: "Detailed inspect output for troubleshooting"
+          type: "boolean"
+          default: false
       tags: ["Network"]
 
     delete:

+ 16 - 0
api/types/network/network.go

@@ -60,6 +60,22 @@ type EndpointSettings struct {
 	MacAddress          string
 }
 
+// Task carries the information about one backend task
+type Task struct {
+	Name       string
+	EndpointID string
+	EndpointIP string
+	Info       map[string]string
+}
+
+// ServiceInfo represents service parameters with the list of service's tasks
+type ServiceInfo struct {
+	VIP          string
+	Ports        []string
+	LocalLBIndex int
+	Tasks        []Task
+}
+
 // Copy makes a deep copy of `EndpointSettings`
 func (es *EndpointSettings) Copy() *EndpointSettings {
 	epCopy := *es

+ 14 - 13
api/types/types.go

@@ -391,19 +391,20 @@ type MountPoint struct {
 
 // NetworkResource is the body of the "get network" http response message
 type NetworkResource struct {
-	Name       string                      // Name is the requested name of the network
-	ID         string                      `json:"Id"` // ID uniquely identifies a network on a single machine
-	Created    time.Time                   // Created is the time the network created
-	Scope      string                      // Scope describes the level at which the network exists (e.g. `global` for cluster-wide or `local` for machine level)
-	Driver     string                      // Driver is the Driver name used to create the network (e.g. `bridge`, `overlay`)
-	EnableIPv6 bool                        // EnableIPv6 represents whether to enable IPv6
-	IPAM       network.IPAM                // IPAM is the network's IP Address Management
-	Internal   bool                        // Internal represents if the network is used internal only
-	Attachable bool                        // Attachable represents if the global scope is manually attachable by regular containers from workers in swarm mode.
-	Containers map[string]EndpointResource // Containers contains endpoints belonging to the network
-	Options    map[string]string           // Options holds the network specific options to use for when creating the network
-	Labels     map[string]string           // Labels holds metadata specific to the network being created
-	Peers      []network.PeerInfo          `json:",omitempty"` // List of peer nodes for an overlay network
+	Name       string                         // Name is the requested name of the network
+	ID         string                         `json:"Id"` // ID uniquely identifies a network on a single machine
+	Created    time.Time                      // Created is the time the network created
+	Scope      string                         // Scope describes the level at which the network exists (e.g. `global` for cluster-wide or `local` for machine level)
+	Driver     string                         // Driver is the Driver name used to create the network (e.g. `bridge`, `overlay`)
+	EnableIPv6 bool                           // EnableIPv6 represents whether to enable IPv6
+	IPAM       network.IPAM                   // IPAM is the network's IP Address Management
+	Internal   bool                           // Internal represents if the network is used internal only
+	Attachable bool                           // Attachable represents if the global scope is manually attachable by regular containers from workers in swarm mode.
+	Containers map[string]EndpointResource    // Containers contains endpoints belonging to the network
+	Options    map[string]string              // Options holds the network specific options to use for when creating the network
+	Labels     map[string]string              // Labels holds metadata specific to the network being created
+	Peers      []network.PeerInfo             `json:",omitempty"` // List of peer nodes for an overlay network
+	Services   map[string]network.ServiceInfo `json:",omitempty"`
 }
 
 // EndpointResource contains network resources allocated and used for a container in a network

+ 5 - 3
cli/command/network/inspect.go

@@ -10,8 +10,9 @@ import (
 )
 
 type inspectOptions struct {
-	format string
-	names  []string
+	format  string
+	names   []string
+	verbose bool
 }
 
 func newInspectCommand(dockerCli *command.DockerCli) *cobra.Command {
@@ -28,6 +29,7 @@ func newInspectCommand(dockerCli *command.DockerCli) *cobra.Command {
 	}
 
 	cmd.Flags().StringVarP(&opts.format, "format", "f", "", "Format the output using the given Go template")
+	cmd.Flags().BoolVarP(&opts.verbose, "verbose", "v", false, "Verbose output for diagnostics")
 
 	return cmd
 }
@@ -38,7 +40,7 @@ func runInspect(dockerCli *command.DockerCli, opts inspectOptions) error {
 	ctx := context.Background()
 
 	getNetFunc := func(name string) (interface{}, []byte, error) {
-		return client.NetworkInspectWithRaw(ctx, name)
+		return client.NetworkInspectWithRaw(ctx, name, opts.verbose)
 	}
 
 	return inspect.Inspect(dockerCli.Out(), opts.names, opts.format, getNetFunc)

+ 1 - 1
cli/command/stack/deploy_composefile.go

@@ -140,7 +140,7 @@ func validateExternalNetworks(
 	client := dockerCli.Client()
 
 	for _, networkName := range externalNetworks {
-		network, err := client.NetworkInspect(ctx, networkName)
+		network, err := client.NetworkInspect(ctx, networkName, false)
 		if err != nil {
 			if dockerclient.IsErrNetworkNotFound(err) {
 				return fmt.Errorf("network %q is declared as external, but could not be found. You need to create the network before the stack is deployed (with overlay driver)", networkName)

+ 1 - 1
cli/command/system/inspect.go

@@ -67,7 +67,7 @@ func inspectImages(ctx context.Context, dockerCli *command.DockerCli) inspect.Ge
 
 func inspectNetwork(ctx context.Context, dockerCli *command.DockerCli) inspect.GetRefFunc {
 	return func(ref string) (interface{}, []byte, error) {
-		return dockerCli.Client().NetworkInspectWithRaw(ctx, ref)
+		return dockerCli.Client().NetworkInspectWithRaw(ctx, ref, false)
 	}
 }
 

+ 2 - 2
client/interface.go

@@ -91,8 +91,8 @@ type NetworkAPIClient interface {
 	NetworkConnect(ctx context.Context, networkID, container string, config *network.EndpointSettings) error
 	NetworkCreate(ctx context.Context, name string, options types.NetworkCreate) (types.NetworkCreateResponse, error)
 	NetworkDisconnect(ctx context.Context, networkID, container string, force bool) error
-	NetworkInspect(ctx context.Context, networkID string) (types.NetworkResource, error)
-	NetworkInspectWithRaw(ctx context.Context, networkID string) (types.NetworkResource, []byte, error)
+	NetworkInspect(ctx context.Context, networkID string, verbose bool) (types.NetworkResource, error)
+	NetworkInspectWithRaw(ctx context.Context, networkID string, verbose bool) (types.NetworkResource, []byte, error)
 	NetworkList(ctx context.Context, options types.NetworkListOptions) ([]types.NetworkResource, error)
 	NetworkRemove(ctx context.Context, networkID string) error
 	NetworksPrune(ctx context.Context, pruneFilter filters.Args) (types.NetworksPruneReport, error)

+ 14 - 5
client/network_inspect.go

@@ -5,21 +5,30 @@ import (
 	"encoding/json"
 	"io/ioutil"
 	"net/http"
+	"net/url"
 
 	"github.com/docker/docker/api/types"
 	"golang.org/x/net/context"
 )
 
 // NetworkInspect returns the information for a specific network configured in the docker host.
-func (cli *Client) NetworkInspect(ctx context.Context, networkID string) (types.NetworkResource, error) {
-	networkResource, _, err := cli.NetworkInspectWithRaw(ctx, networkID)
+func (cli *Client) NetworkInspect(ctx context.Context, networkID string, verbose bool) (types.NetworkResource, error) {
+	networkResource, _, err := cli.NetworkInspectWithRaw(ctx, networkID, verbose)
 	return networkResource, err
 }
 
 // NetworkInspectWithRaw returns the information for a specific network configured in the docker host and its raw representation.
-func (cli *Client) NetworkInspectWithRaw(ctx context.Context, networkID string) (types.NetworkResource, []byte, error) {
-	var networkResource types.NetworkResource
-	resp, err := cli.get(ctx, "/networks/"+networkID, nil, nil)
+func (cli *Client) NetworkInspectWithRaw(ctx context.Context, networkID string, verbose bool) (types.NetworkResource, []byte, error) {
+	var (
+		networkResource types.NetworkResource
+		resp            serverResponse
+		err             error
+	)
+	query := url.Values{}
+	if verbose {
+		query.Set("verbose", "true")
+	}
+	resp, err = cli.get(ctx, "/networks/"+networkID, query, nil)
 	if err != nil {
 		if resp.statusCode == http.StatusNotFound {
 			return networkResource, nil, networkNotFoundError{networkID}

+ 33 - 6
client/network_inspect_test.go

@@ -10,6 +10,7 @@ import (
 	"testing"
 
 	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/api/types/network"
 	"golang.org/x/net/context"
 )
 
@@ -18,7 +19,7 @@ func TestNetworkInspectError(t *testing.T) {
 		client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")),
 	}
 
-	_, err := client.NetworkInspect(context.Background(), "nothing")
+	_, err := client.NetworkInspect(context.Background(), "nothing", false)
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
@@ -29,7 +30,7 @@ func TestNetworkInspectContainerNotFound(t *testing.T) {
 		client: newMockClient(errorMock(http.StatusNotFound, "Server error")),
 	}
 
-	_, err := client.NetworkInspect(context.Background(), "unknown")
+	_, err := client.NetworkInspect(context.Background(), "unknown", false)
 	if err == nil || !IsErrNetworkNotFound(err) {
 		t.Fatalf("expected a networkNotFound error, got %v", err)
 	}
@@ -46,9 +47,23 @@ func TestNetworkInspect(t *testing.T) {
 				return nil, fmt.Errorf("expected GET method, got %s", req.Method)
 			}
 
-			content, err := json.Marshal(types.NetworkResource{
-				Name: "mynetwork",
-			})
+			var (
+				content []byte
+				err     error
+			)
+			if strings.HasPrefix(req.URL.RawQuery, "verbose=true") {
+				s := map[string]network.ServiceInfo{
+					"web": {},
+				}
+				content, err = json.Marshal(types.NetworkResource{
+					Name:     "mynetwork",
+					Services: s,
+				})
+			} else {
+				content, err = json.Marshal(types.NetworkResource{
+					Name: "mynetwork",
+				})
+			}
 			if err != nil {
 				return nil, err
 			}
@@ -59,11 +74,23 @@ func TestNetworkInspect(t *testing.T) {
 		}),
 	}
 
-	r, err := client.NetworkInspect(context.Background(), "network_id")
+	r, err := client.NetworkInspect(context.Background(), "network_id", false)
 	if err != nil {
 		t.Fatal(err)
 	}
 	if r.Name != "mynetwork" {
 		t.Fatalf("expected `mynetwork`, got %s", r.Name)
 	}
+
+	r, err = client.NetworkInspect(context.Background(), "network_id", true)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if r.Name != "mynetwork" {
+		t.Fatalf("expected `mynetwork`, got %s", r.Name)
+	}
+	_, ok := r.Services["web"]
+	if !ok {
+		t.Fatalf("expected service `web` missing in the verbose output")
+	}
 }

+ 1 - 0
docs/api/version-history.md

@@ -17,6 +17,7 @@ keywords: "API, Docker, rcli, REST, documentation"
 
 [Docker Engine API v1.27](https://docs.docker.com/engine/api/v1.27/) documentation
 
+* Optional query parameter `verbose` for `GET /networks/(id or name)` will now list all services with all the tasks, including the non-local tasks on the given network.
 * `GET /containers/(id or name)/attach/ws` now returns WebSocket in binary frame format for API version >= v1.27, and returns WebSocket in text frame format for API version< v1.27, for the purpose of backward-compatibility.
 * `GET /networks` is optimised only to return list of all networks and network specific information. List of all containers attached to a specific network is removed from this API and is only available using the network specific `GET /networks/{network-id}.
 * `GET /containers/json` now supports `publish` and `expose` filters to filter containers that expose or publish certain ports.

+ 96 - 1
docs/reference/commandline/network_inspect.md

@@ -48,7 +48,7 @@ The `network inspect` command shows the containers, by id, in its
 results. For networks backed by multi-host network driver, such as Overlay,
 this command also shows the container endpoints in other hosts in the
 cluster. These endpoints are represented as "ep-{endpoint-id}" in the output.
-However, for swarm-scoped networks, only the endpoints that are local to the
+However, for swarm mode networks, only the endpoints that are local to the
 node are shown.
 
 You can specify an alternate format to execute a given
@@ -201,6 +201,101 @@ $ docker network inspect ingress
 ]
 ```
 
+### Using `verbose` option for `network inspect`
+
+`docker network inspect --verbose` for swarm mode overlay networks shows service-specific
+details such as the service's VIP and port mappings. It also shows IPs of service tasks,
+and the IPs of the nodes where the tasks are running.
+
+Following is an example output for a overlay network `ov1` that has one service `s1`
+attached to. service `s1` in this case has three replicas.
+
+```bash
+$ docker network inspect --verbose ov1
+[
+    {
+        "Name": "ov1",
+        "Id": "ybmyjvao9vtzy3oorxbssj13b",
+        "Created": "2017-03-13T17:04:39.776106792Z",
+        "Scope": "swarm",
+        "Driver": "overlay",
+        "EnableIPv6": false,
+        "IPAM": {
+            "Driver": "default",
+            "Options": null,
+            "Config": [
+                {
+                    "Subnet": "10.0.0.0/24",
+                    "Gateway": "10.0.0.1"
+                }
+            ]
+        },
+        "Internal": false,
+        "Attachable": false,
+        "Containers": {
+            "020403bd88a15f60747fd25d1ad5fa1272eb740e8a97fc547d8ad07b2f721c5e": {
+                "Name": "s1.1.pjn2ik0sfgkfzed3h0s00gs9o",
+                "EndpointID": "ad16946f416562d658f3bb30b9830d73ad91ccf6feae44411269cd0ff674714e",
+                "MacAddress": "02:42:0a:00:00:04",
+                "IPv4Address": "10.0.0.4/24",
+                "IPv6Address": ""
+            }
+        },
+        "Options": {
+            "com.docker.network.driver.overlay.vxlanid_list": "4097"
+        },
+        "Labels": {},
+        "Peers": [
+            {
+                "Name": "net-3-5d3cfd30a58c",
+                "IP": "192.168.33.13"
+            },
+            {
+                "Name": "net-1-6ecbc0040a73",
+                "IP": "192.168.33.11"
+            },
+            {
+                "Name": "net-2-fb80208efd75",
+                "IP": "192.168.33.12"
+            }
+        ],
+        "Services": {
+            "s1": {
+                "VIP": "10.0.0.2",
+                "Ports": [],
+                "LocalLBIndex": 257,
+                "Tasks": [
+                    {
+                        "Name": "s1.2.q4hcq2aiiml25ubtrtg4q1txt",
+                        "EndpointID": "040879b027e55fb658e8b60ae3b87c6cdac7d291e86a190a3b5ac6567b26511a",
+                        "EndpointIP": "10.0.0.5",
+                        "Info": {
+                            "Host IP": "192.168.33.11"
+                        }
+                    },
+                    {
+                        "Name": "s1.3.yawl4cgkp7imkfx469kn9j6lm",
+                        "EndpointID": "106edff9f120efe44068b834e1cddb5b39dd4a3af70211378b2f7a9e562bbad8",
+                        "EndpointIP": "10.0.0.3",
+                        "Info": {
+                            "Host IP": "192.168.33.12"
+                        }
+                    },
+                    {
+                        "Name": "s1.1.pjn2ik0sfgkfzed3h0s00gs9o",
+                        "EndpointID": "ad16946f416562d658f3bb30b9830d73ad91ccf6feae44411269cd0ff674714e",
+                        "EndpointIP": "10.0.0.4",
+                        "Info": {
+                            "Host IP": "192.168.33.13"
+                        }
+                    }
+                ]
+            }
+        }
+    }
+]
+```
+
 ## Related commands
 
 * [network disconnect ](network_disconnect.md)

+ 93 - 0
man/src/network/inspect.md

@@ -86,3 +86,96 @@ $ docker network inspect simple-network
     }
 ]
 ```
+
+`docker network inspect --verbose` for swarm mode overlay networks shows service-specific
+details such as the service's VIP and port mappings. It also shows IPs of service tasks,
+and the IPs of the nodes where the tasks are running.
+
+Following is an example output for a overlay network `ov1` that has one service `s1`
+attached to. service `s1` in this case has three replicas.
+
+```bash
+$ docker network inspect --verbose ov1
+[
+    {
+        "Name": "ov1",
+        "Id": "ybmyjvao9vtzy3oorxbssj13b",
+        "Created": "2017-03-13T17:04:39.776106792Z",
+        "Scope": "swarm",
+        "Driver": "overlay",
+        "EnableIPv6": false,
+        "IPAM": {
+            "Driver": "default",
+            "Options": null,
+            "Config": [
+                {
+                    "Subnet": "10.0.0.0/24",
+                    "Gateway": "10.0.0.1"
+                }
+            ]
+        },
+        "Internal": false,
+        "Attachable": false,
+        "Containers": {
+            "020403bd88a15f60747fd25d1ad5fa1272eb740e8a97fc547d8ad07b2f721c5e": {
+                "Name": "s1.1.pjn2ik0sfgkfzed3h0s00gs9o",
+                "EndpointID": "ad16946f416562d658f3bb30b9830d73ad91ccf6feae44411269cd0ff674714e",
+                "MacAddress": "02:42:0a:00:00:04",
+                "IPv4Address": "10.0.0.4/24",
+                "IPv6Address": ""
+            }
+        },
+        "Options": {
+            "com.docker.network.driver.overlay.vxlanid_list": "4097"
+        },
+        "Labels": {},
+        "Peers": [
+            {
+                "Name": "net-3-5d3cfd30a58c",
+                "IP": "192.168.33.13"
+            },
+            {
+                "Name": "net-1-6ecbc0040a73",
+                "IP": "192.168.33.11"
+            },
+            {
+                "Name": "net-2-fb80208efd75",
+                "IP": "192.168.33.12"
+            }
+        ],
+        "Services": {
+            "s1": {
+                "VIP": "10.0.0.2",
+                "Ports": [],
+                "LocalLBIndex": 257,
+                "Tasks": [
+                    {
+                        "Name": "s1.2.q4hcq2aiiml25ubtrtg4q1txt",
+                        "EndpointID": "040879b027e55fb658e8b60ae3b87c6cdac7d291e86a190a3b5ac6567b26511a",
+                        "EndpointIP": "10.0.0.5",
+                        "Info": {
+                            "Host IP": "192.168.33.11"
+                        }
+                    },
+                    {
+                        "Name": "s1.3.yawl4cgkp7imkfx469kn9j6lm",
+                        "EndpointID": "106edff9f120efe44068b834e1cddb5b39dd4a3af70211378b2f7a9e562bbad8",
+                        "EndpointIP": "10.0.0.3",
+                        "Info": {
+                            "Host IP": "192.168.33.12"
+                        }
+                    },
+                    {
+                        "Name": "s1.1.pjn2ik0sfgkfzed3h0s00gs9o",
+                        "EndpointID": "ad16946f416562d658f3bb30b9830d73ad91ccf6feae44411269cd0ff674714e",
+                        "EndpointIP": "10.0.0.4",
+                        "Info": {
+                            "Host IP": "192.168.33.13"
+                        }
+                    }
+                ]
+            }
+        }
+    }
+]
+```

+ 1 - 1
vendor.conf

@@ -23,7 +23,7 @@ github.com/RackSec/srslog 456df3a81436d29ba874f3590eeeee25d666f8a5
 github.com/imdario/mergo 0.2.1
 
 #get libnetwork packages
-github.com/docker/libnetwork 1a019214c9cb80bd56219e5d6994a22caf302895
+github.com/docker/libnetwork 4610dd67c7b9828bb4719d8aa2ac53a7f1f739d2
 github.com/docker/go-events 18b43f1bc85d9cdd42c05a6cd2d444c7a200a894
 github.com/armon/go-radix e39d623f12e8e41c7b5529e9a9dd67a1e2261f80
 github.com/armon/go-metrics eb0af217e5e9747e41dd5303755356b62d28e3ec

+ 114 - 7
vendor/github.com/docker/libnetwork/agent.go

@@ -44,6 +44,8 @@ type agent struct {
 	sync.Mutex
 }
 
+const libnetworkEPTable = "endpoint_table"
+
 func getBindAddr(ifaceName string) (string, error) {
 	iface, err := net.InterfaceByName(ifaceName)
 	if err != nil {
@@ -285,7 +287,7 @@ func (c *controller) agentInit(listenAddr, bindAddrOrInterface, advertiseAddr st
 		return err
 	}
 
-	ch, cancel := nDB.Watch("endpoint_table", "", "")
+	ch, cancel := nDB.Watch(libnetworkEPTable, "", "")
 	nodeCh, cancel := nDB.Watch(networkdb.NodeTable, "", "")
 
 	c.Lock()
@@ -385,6 +387,111 @@ func (c *controller) agentClose() {
 	agent.networkDB.Close()
 }
 
+// Task has the backend container details
+type Task struct {
+	Name       string
+	EndpointID string
+	EndpointIP string
+	Info       map[string]string
+}
+
+// ServiceInfo has service specific details along with the list of backend tasks
+type ServiceInfo struct {
+	VIP          string
+	LocalLBIndex int
+	Tasks        []Task
+	Ports        []string
+}
+
+type epRecord struct {
+	ep      EndpointRecord
+	info    map[string]string
+	lbIndex int
+}
+
+func (n *network) Services() map[string]ServiceInfo {
+	eps := make(map[string]epRecord)
+
+	if !n.isClusterEligible() {
+		return nil
+	}
+	agent := n.getController().getAgent()
+	if agent == nil {
+		return nil
+	}
+
+	// Walk through libnetworkEPTable and fetch the driver agnostic endpoint info
+	entries := agent.networkDB.GetTableByNetwork(libnetworkEPTable, n.id)
+	for eid, value := range entries {
+		var epRec EndpointRecord
+		nid := n.ID()
+		if err := proto.Unmarshal(value.([]byte), &epRec); err != nil {
+			logrus.Errorf("Unmarshal of libnetworkEPTable failed for endpoint %s in network %s, %v", eid, nid, err)
+			continue
+		}
+		i := n.getController().getLBIndex(epRec.ServiceID, nid, epRec.IngressPorts)
+		eps[eid] = epRecord{
+			ep:      epRec,
+			lbIndex: i,
+		}
+	}
+
+	// Walk through the driver's tables, have the driver decode the entries
+	// and return the tuple {ep ID, value}. value is a string that coveys
+	// relevant info about the endpoint.
+	d, err := n.driver(true)
+	if err != nil {
+		logrus.Errorf("Could not resolve driver for network %s/%s while fetching services: %v", n.networkType, n.ID(), err)
+		return nil
+	}
+	for _, table := range n.driverTables {
+		if table.objType != driverapi.EndpointObject {
+			continue
+		}
+		entries := agent.networkDB.GetTableByNetwork(table.name, n.id)
+		for key, value := range entries {
+			epID, info := d.DecodeTableEntry(table.name, key, value.([]byte))
+			if ep, ok := eps[epID]; !ok {
+				logrus.Errorf("Inconsistent driver and libnetwork state for endpoint %s", epID)
+			} else {
+				ep.info = info
+				eps[epID] = ep
+			}
+		}
+	}
+
+	// group the endpoints into a map keyed by the service name
+	sinfo := make(map[string]ServiceInfo)
+	for ep, epr := range eps {
+		var (
+			s  ServiceInfo
+			ok bool
+		)
+		if s, ok = sinfo[epr.ep.ServiceName]; !ok {
+			s = ServiceInfo{
+				VIP:          epr.ep.VirtualIP,
+				LocalLBIndex: epr.lbIndex,
+			}
+		}
+		ports := []string{}
+		if s.Ports == nil {
+			for _, port := range epr.ep.IngressPorts {
+				p := fmt.Sprintf("Target: %d, Publish: %d", port.TargetPort, port.PublishedPort)
+				ports = append(ports, p)
+			}
+			s.Ports = ports
+		}
+		s.Tasks = append(s.Tasks, Task{
+			Name:       epr.ep.Name,
+			EndpointID: ep,
+			EndpointIP: epr.ep.EndpointIP,
+			Info:       epr.info,
+		})
+		sinfo[epr.ep.ServiceName] = s
+	}
+	return sinfo
+}
+
 func (n *network) isClusterEligible() bool {
 	if n.driverScope() != datastore.GlobalScope {
 		return false
@@ -508,7 +615,7 @@ func (ep *endpoint) addServiceInfoToCluster() error {
 	}
 
 	if agent != nil {
-		if err := agent.networkDB.CreateEntry("endpoint_table", n.ID(), ep.ID(), buf); err != nil {
+		if err := agent.networkDB.CreateEntry(libnetworkEPTable, n.ID(), ep.ID(), buf); err != nil {
 			return err
 		}
 	}
@@ -541,7 +648,7 @@ func (ep *endpoint) deleteServiceInfoFromCluster() error {
 	}
 
 	if agent != nil {
-		if err := agent.networkDB.DeleteEntry("endpoint_table", n.ID(), ep.ID()); err != nil {
+		if err := agent.networkDB.DeleteEntry(libnetworkEPTable, n.ID(), ep.ID()); err != nil {
 			return err
 		}
 	}
@@ -559,8 +666,8 @@ func (n *network) addDriverWatches() {
 	if agent == nil {
 		return
 	}
-	for _, tableName := range n.driverTables {
-		ch, cancel := agent.networkDB.Watch(tableName, n.ID(), "")
+	for _, table := range n.driverTables {
+		ch, cancel := agent.networkDB.Watch(table.name, n.ID(), "")
 		agent.Lock()
 		agent.driverCancelFuncs[n.ID()] = append(agent.driverCancelFuncs[n.ID()], cancel)
 		agent.Unlock()
@@ -571,9 +678,9 @@ func (n *network) addDriverWatches() {
 			return
 		}
 
-		agent.networkDB.WalkTable(tableName, func(nid, key string, value []byte) bool {
+		agent.networkDB.WalkTable(table.name, func(nid, key string, value []byte) bool {
 			if nid == n.ID() {
-				d.EventNotify(driverapi.Create, nid, tableName, key, value)
+				d.EventNotify(driverapi.Create, nid, table.name, key, value)
 			}
 
 			return false

+ 36 - 1
vendor/github.com/docker/libnetwork/driverapi/driverapi.go

@@ -72,6 +72,16 @@ type Driver interface {
 	// only invoked for the global scope driver.
 	EventNotify(event EventType, nid string, tableName string, key string, value []byte)
 
+	// DecodeTableEntry passes the driver a key, value pair from table it registered
+	// with libnetwork. Driver should return {object ID, map[string]string} tuple.
+	// If DecodeTableEntry is called for a table associated with NetworkObject or
+	// EndpointObject the return object ID should be the network id or endppoint id
+	// associated with that entry. map should have information about the object that
+	// can be presented to the user.
+	// For exampe: overlay driver returns the VTEP IP of the host that has the endpoint
+	// which is shown in 'network inspect --verbose'
+	DecodeTableEntry(tablename string, key string, value []byte) (string, map[string]string)
+
 	// Type returns the type of this driver, the network type this driver manages
 	Type() string
 
@@ -84,7 +94,7 @@ type Driver interface {
 type NetworkInfo interface {
 	// TableEventRegister registers driver interest in a given
 	// table name.
-	TableEventRegister(tableName string) error
+	TableEventRegister(tableName string, objType ObjectType) error
 }
 
 // InterfaceInfo provides a go interface for drivers to retrive
@@ -175,3 +185,28 @@ const (
 	// Delete event is generated when a table entry is deleted.
 	Delete
 )
+
+// ObjectType represents the type of object driver wants to store in libnetwork's networkDB
+type ObjectType int
+
+const (
+	// EndpointObject should be set for libnetwork endpoint object related data
+	EndpointObject ObjectType = 1 + iota
+	// NetworkObject should be set for libnetwork network object related data
+	NetworkObject
+	// OpaqueObject is for driver specific data with no corresponding libnetwork object
+	OpaqueObject
+)
+
+// IsValidType validates the passed in type against the valid object types
+func IsValidType(objType ObjectType) bool {
+	switch objType {
+	case EndpointObject:
+		fallthrough
+	case NetworkObject:
+		fallthrough
+	case OpaqueObject:
+		return true
+	}
+	return false
+}

+ 4 - 0
vendor/github.com/docker/libnetwork/drivers/bridge/bridge.go

@@ -575,6 +575,10 @@ func (d *driver) NetworkFree(id string) error {
 func (d *driver) EventNotify(etype driverapi.EventType, nid, tableName, key string, value []byte) {
 }
 
+func (d *driver) DecodeTableEntry(tablename string, key string, value []byte) (string, map[string]string) {
+	return "", nil
+}
+
 // Create a new network using bridge plugin
 func (d *driver) CreateNetwork(id string, option map[string]interface{}, nInfo driverapi.NetworkInfo, ipV4Data, ipV6Data []driverapi.IPAMData) error {
 	if len(ipV4Data) == 0 || ipV4Data[0].Pool.String() == "0.0.0.0/0" {

+ 0 - 6
vendor/github.com/docker/libnetwork/drivers/bridge/setup_ip_tables.go

@@ -140,7 +140,6 @@ func setupIPTablesInternal(bridgeIface string, addr net.Addr, icc, ipmasq, hairp
 		hpNatRule = iptRule{table: iptables.Nat, chain: "POSTROUTING", preArgs: []string{"-t", "nat"}, args: []string{"-m", "addrtype", "--src-type", "LOCAL", "-o", bridgeIface, "-j", "MASQUERADE"}}
 		skipDNAT  = iptRule{table: iptables.Nat, chain: DockerChain, preArgs: []string{"-t", "nat"}, args: []string{"-i", bridgeIface, "-j", "RETURN"}}
 		outRule   = iptRule{table: iptables.Filter, chain: "FORWARD", args: []string{"-i", bridgeIface, "!", "-o", bridgeIface, "-j", "ACCEPT"}}
-		inRule    = iptRule{table: iptables.Filter, chain: "FORWARD", args: []string{"-o", bridgeIface, "-m", "conntrack", "--ctstate", "RELATED,ESTABLISHED", "-j", "ACCEPT"}}
 	)
 
 	// Set NAT.
@@ -173,11 +172,6 @@ func setupIPTablesInternal(bridgeIface string, addr net.Addr, icc, ipmasq, hairp
 		return err
 	}
 
-	// Set Accept on incoming packets for existing connections.
-	if err := programChainRule(inRule, "ACCEPT INCOMING", enable); err != nil {
-		return err
-	}
-
 	return nil
 }
 

+ 4 - 0
vendor/github.com/docker/libnetwork/drivers/host/host.go

@@ -35,6 +35,10 @@ func (d *driver) NetworkFree(id string) error {
 func (d *driver) EventNotify(etype driverapi.EventType, nid, tableName, key string, value []byte) {
 }
 
+func (d *driver) DecodeTableEntry(tablename string, key string, value []byte) (string, map[string]string) {
+	return "", nil
+}
+
 func (d *driver) CreateNetwork(id string, option map[string]interface{}, nInfo driverapi.NetworkInfo, ipV4Data, ipV6Data []driverapi.IPAMData) error {
 	d.Lock()
 	defer d.Unlock()

+ 4 - 0
vendor/github.com/docker/libnetwork/drivers/ipvlan/ipvlan.go

@@ -108,3 +108,7 @@ func (d *driver) DiscoverDelete(dType discoverapi.DiscoveryType, data interface{
 
 func (d *driver) EventNotify(etype driverapi.EventType, nid, tableName, key string, value []byte) {
 }
+
+func (d *driver) DecodeTableEntry(tablename string, key string, value []byte) (string, map[string]string) {
+	return "", nil
+}

+ 4 - 0
vendor/github.com/docker/libnetwork/drivers/macvlan/macvlan.go

@@ -110,3 +110,7 @@ func (d *driver) DiscoverDelete(dType discoverapi.DiscoveryType, data interface{
 
 func (d *driver) EventNotify(etype driverapi.EventType, nid, tableName, key string, value []byte) {
 }
+
+func (d *driver) DecodeTableEntry(tablename string, key string, value []byte) (string, map[string]string) {
+	return "", nil
+}

+ 4 - 0
vendor/github.com/docker/libnetwork/drivers/null/null.go

@@ -35,6 +35,10 @@ func (d *driver) NetworkFree(id string) error {
 func (d *driver) EventNotify(etype driverapi.EventType, nid, tableName, key string, value []byte) {
 }
 
+func (d *driver) DecodeTableEntry(tablename string, key string, value []byte) (string, map[string]string) {
+	return "", nil
+}
+
 func (d *driver) CreateNetwork(id string, option map[string]interface{}, nInfo driverapi.NetworkInfo, ipV4Data, ipV6Data []driverapi.IPAMData) error {
 	d.Lock()
 	defer d.Unlock()

+ 55 - 15
vendor/github.com/docker/libnetwork/drivers/overlay/encryption.go

@@ -20,7 +20,7 @@ import (
 )
 
 const (
-	mark         = uint32(0xD0C4E3)
+	r            = 0xD0C4E3
 	timeout      = 30
 	pktExpansion = 26 // SPI(4) + SeqN(4) + IV(8) + PadLength(1) + NextHeader(1) + ICV(8)
 )
@@ -31,6 +31,8 @@ const (
 	bidir
 )
 
+var spMark = netlink.XfrmMark{Value: uint32(r), Mask: 0xffffffff}
+
 type key struct {
 	value []byte
 	tag   uint32
@@ -201,7 +203,7 @@ func programMangle(vni uint32, add bool) (err error) {
 	var (
 		p      = strconv.FormatUint(uint64(vxlanPort), 10)
 		c      = fmt.Sprintf("0>>22&0x3C@12&0xFFFFFF00=%d", int(vni)<<8)
-		m      = strconv.FormatUint(uint64(mark), 10)
+		m      = strconv.FormatUint(uint64(r), 10)
 		chain  = "OUTPUT"
 		rule   = []string{"-p", "udp", "--dport", p, "-m", "u32", "--u32", c, "-j", "MARK", "--set-mark", m}
 		a      = "-A"
@@ -271,6 +273,7 @@ func programSA(localIP, remoteIP net.IP, spi *spi, k *key, dir int, add bool) (f
 			Proto: netlink.XFRM_PROTO_ESP,
 			Spi:   spi.reverse,
 			Mode:  netlink.XFRM_MODE_TRANSPORT,
+			Reqid: r,
 		}
 		if add {
 			rSA.Aead = buildAeadAlgo(k, spi.reverse)
@@ -296,6 +299,7 @@ func programSA(localIP, remoteIP net.IP, spi *spi, k *key, dir int, add bool) (f
 			Proto: netlink.XFRM_PROTO_ESP,
 			Spi:   spi.forward,
 			Mode:  netlink.XFRM_MODE_TRANSPORT,
+			Reqid: r,
 		}
 		if add {
 			fSA.Aead = buildAeadAlgo(k, spi.forward)
@@ -325,17 +329,18 @@ func programSP(fSA *netlink.XfrmState, rSA *netlink.XfrmState, add bool) error {
 		xfrmProgram = ns.NlHandle().XfrmPolicyAdd
 	}
 
-	fullMask := net.CIDRMask(8*len(fSA.Src), 8*len(fSA.Src))
+	// Create a congruent cidr
+	s := types.GetMinimalIP(fSA.Src)
+	d := types.GetMinimalIP(fSA.Dst)
+	fullMask := net.CIDRMask(8*len(s), 8*len(s))
 
 	fPol := &netlink.XfrmPolicy{
-		Src:     &net.IPNet{IP: fSA.Src, Mask: fullMask},
-		Dst:     &net.IPNet{IP: fSA.Dst, Mask: fullMask},
+		Src:     &net.IPNet{IP: s, Mask: fullMask},
+		Dst:     &net.IPNet{IP: d, Mask: fullMask},
 		Dir:     netlink.XFRM_DIR_OUT,
 		Proto:   17,
 		DstPort: 4789,
-		Mark: &netlink.XfrmMark{
-			Value: mark,
-		},
+		Mark:    &spMark,
 		Tmpls: []netlink.XfrmPolicyTmpl{
 			{
 				Src:   fSA.Src,
@@ -343,6 +348,7 @@ func programSP(fSA *netlink.XfrmState, rSA *netlink.XfrmState, add bool) error {
 				Proto: netlink.XFRM_PROTO_ESP,
 				Mode:  netlink.XFRM_MODE_TRANSPORT,
 				Spi:   fSA.Spi,
+				Reqid: r,
 			},
 		},
 	}
@@ -426,6 +432,8 @@ func (d *driver) secMapWalk(f func(string, []*spi) ([]*spi, bool)) error {
 }
 
 func (d *driver) setKeys(keys []*key) error {
+	// Remove any stale policy, state
+	clearEncryptionStates()
 	// Accept the encryption keys and clear any stale encryption map
 	d.Lock()
 	d.keys = keys
@@ -526,7 +534,7 @@ func updateNodeKey(lIP, aIP, rIP net.IP, idxs []*spi, curKeys []*key, newIdx, pr
 	}
 
 	if newIdx > -1 {
-		// +RSA2
+		// +rSA2
 		programSA(lIP, rIP, spis[newIdx], curKeys[newIdx], reverse, true)
 	}
 
@@ -535,16 +543,17 @@ func updateNodeKey(lIP, aIP, rIP net.IP, idxs []*spi, curKeys []*key, newIdx, pr
 		fSA2, _, _ := programSA(lIP, rIP, spis[priIdx], curKeys[priIdx], forward, true)
 
 		// +fSP2, -fSP1
-		fullMask := net.CIDRMask(8*len(fSA2.Src), 8*len(fSA2.Src))
+		s := types.GetMinimalIP(fSA2.Src)
+		d := types.GetMinimalIP(fSA2.Dst)
+		fullMask := net.CIDRMask(8*len(s), 8*len(s))
+
 		fSP1 := &netlink.XfrmPolicy{
-			Src:     &net.IPNet{IP: fSA2.Src, Mask: fullMask},
-			Dst:     &net.IPNet{IP: fSA2.Dst, Mask: fullMask},
+			Src:     &net.IPNet{IP: s, Mask: fullMask},
+			Dst:     &net.IPNet{IP: d, Mask: fullMask},
 			Dir:     netlink.XFRM_DIR_OUT,
 			Proto:   17,
 			DstPort: 4789,
-			Mark: &netlink.XfrmMark{
-				Value: mark,
-			},
+			Mark:    &spMark,
 			Tmpls: []netlink.XfrmPolicyTmpl{
 				{
 					Src:   fSA2.Src,
@@ -552,6 +561,7 @@ func updateNodeKey(lIP, aIP, rIP net.IP, idxs []*spi, curKeys []*key, newIdx, pr
 					Proto: netlink.XFRM_PROTO_ESP,
 					Mode:  netlink.XFRM_MODE_TRANSPORT,
 					Spi:   fSA2.Spi,
+					Reqid: r,
 				},
 			},
 		}
@@ -597,3 +607,33 @@ func (n *network) maxMTU() int {
 	}
 	return mtu
 }
+
+func clearEncryptionStates() {
+	nlh := ns.NlHandle()
+	spList, err := nlh.XfrmPolicyList(netlink.FAMILY_ALL)
+	if err != nil {
+		logrus.Warnf("Failed to retrieve SP list for cleanup: %v", err)
+	}
+	saList, err := nlh.XfrmStateList(netlink.FAMILY_ALL)
+	if err != nil {
+		logrus.Warnf("Failed to retrieve SA list for cleanup: %v", err)
+	}
+	for _, sp := range spList {
+		if sp.Mark != nil && sp.Mark.Value == spMark.Value {
+			if err := nlh.XfrmPolicyDel(&sp); err != nil {
+				logrus.Warnf("Failed to delete stale SP %s: %v", sp, err)
+				continue
+			}
+			logrus.Debugf("Removed stale SP: %s", sp)
+		}
+	}
+	for _, sa := range saList {
+		if sa.Reqid == r {
+			if err := nlh.XfrmStateDel(&sa); err != nil {
+				logrus.Warnf("Failed to delete stale SA %s: %v", sa, err)
+				continue
+			}
+			logrus.Debugf("Removed stale SA: %s", sa)
+		}
+	}
+}

+ 17 - 0
vendor/github.com/docker/libnetwork/drivers/overlay/joinleave.go

@@ -145,6 +145,23 @@ func (d *driver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo,
 	return nil
 }
 
+func (d *driver) DecodeTableEntry(tablename string, key string, value []byte) (string, map[string]string) {
+	if tablename != ovPeerTable {
+		logrus.Errorf("DecodeTableEntry: unexpected table name %s", tablename)
+		return "", nil
+	}
+
+	var peer PeerRecord
+	if err := proto.Unmarshal(value, &peer); err != nil {
+		logrus.Errorf("DecodeTableEntry: failed to unmarshal peer record for key %s: %v", key, err)
+		return "", nil
+	}
+
+	return key, map[string]string{
+		"Host IP": peer.TunnelEndpointIP,
+	}
+}
+
 func (d *driver) EventNotify(etype driverapi.EventType, nid, tableName, key string, value []byte) {
 	if tableName != ovPeerTable {
 		logrus.Errorf("Unexpected table notification for table %s received", tableName)

+ 1 - 1
vendor/github.com/docker/libnetwork/drivers/overlay/ov_network.go

@@ -159,7 +159,7 @@ func (d *driver) CreateNetwork(id string, option map[string]interface{}, nInfo d
 	}
 
 	if nInfo != nil {
-		if err := nInfo.TableEventRegister(ovPeerTable); err != nil {
+		if err := nInfo.TableEventRegister(ovPeerTable, driverapi.EndpointObject); err != nil {
 			return err
 		}
 	}

+ 4 - 0
vendor/github.com/docker/libnetwork/drivers/overlay/ovmanager/ovmanager.go

@@ -199,6 +199,10 @@ func (d *driver) CreateNetwork(id string, option map[string]interface{}, nInfo d
 func (d *driver) EventNotify(etype driverapi.EventType, nid, tableName, key string, value []byte) {
 }
 
+func (d *driver) DecodeTableEntry(tablename string, key string, value []byte) (string, map[string]string) {
+	return "", nil
+}
+
 func (d *driver) DeleteNetwork(nid string) error {
 	return types.NotImplementedErrorf("not implemented")
 }

+ 4 - 0
vendor/github.com/docker/libnetwork/drivers/remote/driver.go

@@ -116,6 +116,10 @@ func (d *driver) NetworkFree(id string) error {
 func (d *driver) EventNotify(etype driverapi.EventType, nid, tableName, key string, value []byte) {
 }
 
+func (d *driver) DecodeTableEntry(tablename string, key string, value []byte) (string, map[string]string) {
+	return "", nil
+}
+
 func (d *driver) CreateNetwork(id string, options map[string]interface{}, nInfo driverapi.NetworkInfo, ipV4Data, ipV6Data []driverapi.IPAMData) error {
 	create := &api.CreateNetworkRequest{
 		NetworkID: id,

+ 4 - 0
vendor/github.com/docker/libnetwork/drivers/solaris/bridge/bridge.go

@@ -175,6 +175,10 @@ func (d *driver) NetworkFree(id string) error {
 func (d *driver) EventNotify(etype driverapi.EventType, nid, tableName, key string, value []byte) {
 }
 
+func (d *driver) DecodeTableEntry(tablename string, key string, value []byte) (string, map[string]string) {
+	return "", nil
+}
+
 func (d *driver) CreateNetwork(id string, option map[string]interface{}, nInfo driverapi.NetworkInfo, 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")

+ 4 - 0
vendor/github.com/docker/libnetwork/drivers/solaris/overlay/joinleave.go

@@ -149,6 +149,10 @@ func (d *driver) EventNotify(etype driverapi.EventType, nid, tableName, key stri
 	d.peerAdd(nid, eid, addr.IP, addr.Mask, mac, vtep, true)
 }
 
+func (d *driver) DecodeTableEntry(tablename string, key string, value []byte) (string, map[string]string) {
+	return "", nil
+}
+
 // Leave method is invoked when a Sandbox detaches from an endpoint.
 func (d *driver) Leave(nid, eid string) error {
 	if err := validateID(nid, eid); err != nil {

+ 1 - 1
vendor/github.com/docker/libnetwork/drivers/solaris/overlay/ov_network.go

@@ -153,7 +153,7 @@ func (d *driver) CreateNetwork(id string, option map[string]interface{}, nInfo d
 	}
 
 	if nInfo != nil {
-		if err := nInfo.TableEventRegister(ovPeerTable); err != nil {
+		if err := nInfo.TableEventRegister(ovPeerTable, driverapi.EndpointObject); err != nil {
 			return err
 		}
 	}

+ 4 - 0
vendor/github.com/docker/libnetwork/drivers/windows/overlay/joinleave_windows.go

@@ -93,6 +93,10 @@ func (d *driver) EventNotify(etype driverapi.EventType, nid, tableName, key stri
 	d.peerAdd(nid, eid, addr.IP, addr.Mask, mac, vtep, true)
 }
 
+func (d *driver) DecodeTableEntry(tablename string, key string, value []byte) (string, map[string]string) {
+	return "", nil
+}
+
 // Leave method is invoked when a Sandbox detaches from an endpoint.
 func (d *driver) Leave(nid, eid string) error {
 	if err := validateID(nid, eid); err != nil {

+ 1 - 1
vendor/github.com/docker/libnetwork/drivers/windows/overlay/ov_network_windows.go

@@ -169,7 +169,7 @@ func (d *driver) CreateNetwork(id string, option map[string]interface{}, nInfo d
 	n.interfaceName = interfaceName
 
 	if nInfo != nil {
-		if err := nInfo.TableEventRegister(ovPeerTable); err != nil {
+		if err := nInfo.TableEventRegister(ovPeerTable, driverapi.EndpointObject); err != nil {
 			return err
 		}
 	}

+ 4 - 0
vendor/github.com/docker/libnetwork/drivers/windows/windows.go

@@ -183,6 +183,10 @@ func (c *networkConfiguration) processIPAM(id string, ipamV4Data, ipamV6Data []d
 func (d *driver) EventNotify(etype driverapi.EventType, nid, tableName, key string, value []byte) {
 }
 
+func (d *driver) DecodeTableEntry(tablename string, key string, value []byte) (string, map[string]string) {
+	return "", nil
+}
+
 // Create a new network
 func (d *driver) CreateNetwork(id string, option map[string]interface{}, nInfo driverapi.NetworkInfo, ipV4Data, ipV6Data []driverapi.IPAMData) error {
 	if _, err := d.getNetwork(id); err == nil {

+ 52 - 19
vendor/github.com/docker/libnetwork/iptables/iptables.go

@@ -50,8 +50,7 @@ var (
 	bestEffortLock sync.Mutex
 	// ErrIptablesNotFound is returned when the rule is not found.
 	ErrIptablesNotFound = errors.New("Iptables not found")
-	probeOnce           sync.Once
-	firewalldOnce       sync.Once
+	initOnce            sync.Once
 )
 
 // ChainInfo defines the iptables chain.
@@ -86,22 +85,32 @@ func initFirewalld() {
 	}
 }
 
+func detectIptables() {
+	path, err := exec.LookPath("iptables")
+	if err != nil {
+		return
+	}
+	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
+	}
+	supportsCOpt = supportsCOption(mj, mn, mc)
+}
+
+func initIptables() {
+	probe()
+	initFirewalld()
+	detectIptables()
+}
+
 func initCheck() error {
+	initOnce.Do(initIptables)
+
 	if iptablesPath == "" {
-		probeOnce.Do(probe)
-		firewalldOnce.Do(initFirewalld)
-		path, err := exec.LookPath("iptables")
-		if err != nil {
-			return ErrIptablesNotFound
-		}
-		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 ErrIptablesNotFound
 	}
 	return nil
 }
@@ -189,6 +198,26 @@ func ProgramChain(c *ChainInfo, bridgeName string, hairpinMode, enable bool) err
 			}
 
 		}
+		establish := []string{
+			"-o", bridgeName,
+			"-m", "conntrack",
+			"--ctstate", "RELATED,ESTABLISHED",
+			"-j", "ACCEPT"}
+		if !Exists(Filter, "FORWARD", establish...) && enable {
+			insert := append([]string{string(Insert), "FORWARD"}, establish...)
+			if output, err := Raw(insert...); err != nil {
+				return err
+			} else if len(output) != 0 {
+				return fmt.Errorf("Could not create establish rule to %s: %s", c.Table, output)
+			}
+		} else if Exists(Filter, "FORWARD", establish...) && !enable {
+			del := append([]string{string(Delete), "FORWARD"}, establish...)
+			if output, err := Raw(del...); err != nil {
+				return err
+			} else if len(output) != 0 {
+				return fmt.Errorf("Could not delete establish rule from %s: %s", c.Table, output)
+			}
+		}
 	}
 	return nil
 }
@@ -353,7 +382,11 @@ func exists(native bool, table Table, chain string, rule ...string) bool {
 		table = Filter
 	}
 
-	initCheck()
+	if err := initCheck(); err != nil {
+		// The exists() signature does not allow us to return an error, but at least
+		// we can skip the (likely invalid) exec invocation.
+		return false
+	}
 
 	if supportsCOpt {
 		// if exit status is 0 then return true, the rule exists
@@ -436,9 +469,9 @@ func ExistChain(chain string, table Table) bool {
 	return false
 }
 
-// GetVersion reads the iptables version numbers
+// GetVersion reads the iptables version numbers during initialization
 func GetVersion() (major, minor, micro int, err error) {
-	out, err := Raw("--version")
+	out, err := exec.Command(iptablesPath, "--version").CombinedOutput()
 	if err == nil {
 		major, minor, micro = parseVersionNumbers(string(out))
 	}

+ 19 - 4
vendor/github.com/docker/libnetwork/network.go

@@ -74,6 +74,9 @@ type NetworkInfo interface {
 	// gossip cluster. For non-dynamic overlay networks and bridge networks it returns an
 	// empty slice
 	Peers() []networkdb.PeerInfo
+	//Services returns a map of services keyed by the service name with the details
+	//of all the tasks that belong to the service. Applicable only in swarm mode.
+	Services() map[string]ServiceInfo
 }
 
 // EndpointWalker is a client provided function which will be used to walk the Endpoints.
@@ -108,6 +111,11 @@ type servicePorts struct {
 	target   []serviceTarget
 }
 
+type networkDBTable struct {
+	name    string
+	objType driverapi.ObjectType
+}
+
 // IpamConf contains all the ipam related configurations for a network
 type IpamConf struct {
 	// The master address pool for containers and network interfaces
@@ -208,7 +216,7 @@ type network struct {
 	attachable   bool
 	inDelete     bool
 	ingress      bool
-	driverTables []string
+	driverTables []networkDBTable
 	dynamic      bool
 	sync.Mutex
 }
@@ -1607,11 +1615,18 @@ func (n *network) Labels() map[string]string {
 	return lbls
 }
 
-func (n *network) TableEventRegister(tableName string) error {
+func (n *network) TableEventRegister(tableName string, objType driverapi.ObjectType) error {
+	if !driverapi.IsValidType(objType) {
+		return fmt.Errorf("invalid object type %v in registering table, %s", objType, tableName)
+	}
+
+	t := networkDBTable{
+		name:    tableName,
+		objType: objType,
+	}
 	n.Lock()
 	defer n.Unlock()
-
-	n.driverTables = append(n.driverTables, tableName)
+	n.driverTables = append(n.driverTables, t)
 	return nil
 }
 

+ 16 - 0
vendor/github.com/docker/libnetwork/networkdb/networkdb.go

@@ -307,6 +307,22 @@ func (nDB *NetworkDB) UpdateEntry(tname, nid, key string, value []byte) error {
 	return nil
 }
 
+// GetTableByNetwork walks the networkdb by the give table and network id and
+// returns a map of keys and values
+func (nDB *NetworkDB) GetTableByNetwork(tname, nid string) map[string]interface{} {
+	entries := make(map[string]interface{})
+	nDB.indexes[byTable].WalkPrefix(fmt.Sprintf("/%s/%s", tname, nid), func(k string, v interface{}) bool {
+		entry := v.(*entry)
+		if entry.deleting {
+			return false
+		}
+		key := k[strings.LastIndex(k, "/")+1:]
+		entries[key] = entry.value
+		return false
+	})
+	return entries
+}
+
 // DeleteEntry deletes a table entry in NetworkDB for given (network,
 // table, key) tuple and if the NetworkDB is part of the cluster
 // propagates this event to the cluster.

+ 20 - 0
vendor/github.com/docker/libnetwork/service_common.go

@@ -18,6 +18,26 @@ func newService(name string, id string, ingressPorts []*PortConfig, aliases []st
 	}
 }
 
+func (c *controller) getLBIndex(sid, nid string, ingressPorts []*PortConfig) int {
+	skey := serviceKey{
+		id:    sid,
+		ports: portConfigs(ingressPorts).String(),
+	}
+	c.Lock()
+	s, ok := c.serviceBindings[skey]
+	c.Unlock()
+
+	if !ok {
+		return 0
+	}
+
+	s.Lock()
+	lb := s.loadBalancers[nid]
+	s.Unlock()
+
+	return int(lb.fwMark)
+}
+
 func (c *controller) cleanupServiceBindings(cleanupNID string) {
 	var cleanupFuncs []func()