Skip to content

Commit

Permalink
Merge pull request #2327 from /issues/1960-mcast-option
Browse files Browse the repository at this point in the history
Add an option to disable multicast route per-network

Fixes #1960.
  • Loading branch information
rade committed Jun 6, 2016
2 parents d4de48a + ab74d7b commit 7ffbeea
Show file tree
Hide file tree
Showing 6 changed files with 126 additions and 27 deletions.
94 changes: 85 additions & 9 deletions plugin/net/driver.go
Expand Up @@ -2,9 +2,11 @@ package plugin

import (
"fmt"
"strconv"
"sync"

"github.com/docker/libnetwork/drivers/remote/api"
"github.com/docker/libnetwork/netlabel"
"github.com/docker/libnetwork/types"

"github.com/vishvananda/netlink"
Expand All @@ -15,18 +17,28 @@ import (
"github.com/weaveworks/weave/plugin/skel"
)

const (
MulticastOption = "works.weave.multicast"
)

type network struct {
hasMulticastRoute bool
}

type driver struct {
scope string
noMulticastRoute bool
scope string
docker *docker.Client
sync.RWMutex
endpoints map[string]struct{}
networks map[string]network
}

func New(client *docker.Client, weave *weaveapi.Client, scope string, noMulticastRoute bool) (skel.Driver, error) {
func New(client *docker.Client, weave *weaveapi.Client, scope string) (skel.Driver, error) {
driver := &driver{
noMulticastRoute: noMulticastRoute,
scope: scope,
endpoints: make(map[string]struct{}),
scope: scope,
docker: client,
endpoints: make(map[string]struct{}),
networks: make(map[string]network),
}

_, err := NewWatcher(client, weave, driver)
Expand All @@ -49,11 +61,33 @@ func (driver *driver) GetCapabilities() (*api.GetCapabilityResponse, error) {

func (driver *driver) CreateNetwork(create *api.CreateNetworkRequest) error {
driver.logReq("CreateNetwork", create, create.NetworkID)
_, err := driver.setupNetworkInfo(create.NetworkID, stringOptions(create))
return err
}

// Deal with excessively-generic way the options get decoded from JSON
func stringOptions(create *api.CreateNetworkRequest) map[string]string {
if create.Options != nil {
if data, found := create.Options[netlabel.GenericData]; found {
if options, ok := data.(map[string]interface{}); ok {
out := make(map[string]string, len(options))
for key, value := range options {
if str, ok := value.(string); ok {
out[key] = str
}
}
return out
}
}
}
return nil
}

func (driver *driver) DeleteNetwork(delete *api.DeleteNetworkRequest) error {
driver.logReq("DeleteNetwork", delete, delete.NetworkID)
func (driver *driver) DeleteNetwork(delreq *api.DeleteNetworkRequest) error {
driver.logReq("DeleteNetwork", delreq, delreq.NetworkID)
driver.Lock()
delete(driver.networks, delreq.NetworkID)
driver.Unlock()
return nil
}

Expand Down Expand Up @@ -96,6 +130,10 @@ func (driver *driver) EndpointInfo(req *api.EndpointInfoRequest) (*api.EndpointI
func (driver *driver) JoinEndpoint(j *api.JoinRequest) (*api.JoinResponse, error) {
driver.logReq("JoinEndpoint", j, fmt.Sprintf("%s:%s to %s", j.NetworkID, j.EndpointID, j.SandboxKey))

network, err := driver.findNetworkInfo(j.NetworkID)
if err != nil {
return nil, driver.error("JoinEndpoint", "unable to find network info: %s", err)
}
name, peerName := vethPair(j.EndpointID)
if _, err := weavenet.CreateAndAttachVeth(name, peerName, weavenet.WeaveBridgeName, 0, nil); err != nil {
return nil, driver.error("JoinEndpoint", "%s", err)
Expand All @@ -107,7 +145,7 @@ func (driver *driver) JoinEndpoint(j *api.JoinRequest) (*api.JoinResponse, error
DstPrefix: weavenet.VethName,
},
}
if !driver.noMulticastRoute {
if network.hasMulticastRoute {
multicastRoute := api.StaticRoute{
Destination: "224.0.0.0/4",
RouteType: types.CONNECTED,
Expand All @@ -118,6 +156,44 @@ func (driver *driver) JoinEndpoint(j *api.JoinRequest) (*api.JoinResponse, error
return response, nil
}

func (driver *driver) findNetworkInfo(id string) (network, error) {
driver.Lock()
network, found := driver.networks[id]
driver.Unlock()
if found {
return network, nil
}
info, err := driver.docker.NetworkInfo(id)
if err != nil {
return network, err
}
return driver.setupNetworkInfo(id, info.Options)
}

func (driver *driver) setupNetworkInfo(id string, options map[string]string) (network, error) {
var network network
for key, value := range options {
switch key {
case MulticastOption:
if value == "" { // interpret "--opt works.weave.multicast" as "turn it on"
network.hasMulticastRoute = true
} else {
var err error
if network.hasMulticastRoute, err = strconv.ParseBool(value); err != nil {
return network, fmt.Errorf("unrecognized value %q for option %s", value, key)
}

}
default:
driver.warn("setupNetworkInfo", "unrecognized option: %s", key)
}
}
driver.Lock()
driver.networks[id] = network
driver.Unlock()
return network, nil
}

func (driver *driver) LeaveEndpoint(leave *api.LeaveRequest) error {
driver.logReq("LeaveEndpoint", leave, fmt.Sprintf("%s:%s", leave.NetworkID, leave.EndpointID))

Expand Down
17 changes: 10 additions & 7 deletions prog/plugin/main.go
Expand Up @@ -42,7 +42,7 @@ func main() {
flag.StringVar(&logLevel, "log-level", "info", "logging level (debug, info, warning, error)")
flag.StringVar(&address, "socket", "/run/docker/plugins/weave.sock", "socket on which to listen")
flag.StringVar(&meshAddress, "meshsocket", "/run/docker/plugins/weavemesh.sock", "socket on which to listen in mesh mode")
flag.BoolVar(&noMulticastRoute, "no-multicast-route", false, "do not add a multicast route to network endpoints")
flag.BoolVar(&noMulticastRoute, "no-multicast-route", false, "deprecated (this is now the default)")

flag.Parse()

Expand Down Expand Up @@ -73,26 +73,29 @@ func main() {
}

Log.Println("Weave plugin", version, "Command line options:", os.Args[1:])
if noMulticastRoute {
Log.Warning("--no-multicast-route option has been removed; multicast is off by default")
}
Log.Info(dockerClient.Info())

err = run(dockerClient, weave, address, meshAddress, noMulticastRoute)
err = run(dockerClient, weave, address, meshAddress)
if err != nil {
Log.Fatal(err)
}
}

func run(dockerClient *docker.Client, weave *weaveapi.Client, address, meshAddress string, noMulticastRoute bool) error {
func run(dockerClient *docker.Client, weave *weaveapi.Client, address, meshAddress string) error {
endChan := make(chan error, 1)
if address != "" {
globalListener, err := listenAndServe(dockerClient, weave, address, noMulticastRoute, endChan, "global", false)
globalListener, err := listenAndServe(dockerClient, weave, address, endChan, "global", false)
if err != nil {
return err
}
defer os.Remove(address)
defer globalListener.Close()
}
if meshAddress != "" {
meshListener, err := listenAndServe(dockerClient, weave, meshAddress, noMulticastRoute, endChan, "local", true)
meshListener, err := listenAndServe(dockerClient, weave, meshAddress, endChan, "local", true)
if err != nil {
return err
}
Expand All @@ -117,8 +120,8 @@ func run(dockerClient *docker.Client, weave *weaveapi.Client, address, meshAddre
}
}

func listenAndServe(dockerClient *docker.Client, weave *weaveapi.Client, address string, noMulticastRoute bool, endChan chan<- error, scope string, withIpam bool) (net.Listener, error) {
d, err := netplugin.New(dockerClient, weave, scope, noMulticastRoute)
func listenAndServe(dockerClient *docker.Client, weave *weaveapi.Client, address string, endChan chan<- error, scope string, withIpam bool) (net.Listener, error) {
d, err := netplugin.New(dockerClient, weave, scope)
if err != nil {
return nil, err
}
Expand Down
5 changes: 4 additions & 1 deletion prog/weaveutil/plugin_network.go
Expand Up @@ -4,7 +4,9 @@ package main
import (
"fmt"

"github.com/fsouza/go-dockerclient"
docker "github.com/fsouza/go-dockerclient"

"github.com/weaveworks/weave/plugin/net"
)

func createPluginNetwork(args []string) error {
Expand All @@ -23,6 +25,7 @@ func createPluginNetwork(args []string) error {
CheckDuplicate: true,
Driver: driverName,
IPAM: docker.IPAMOptions{Driver: driverName},
Options: map[string]interface{}{plugin.MulticastOption: "true"},
})
if err != docker.ErrNetworkAlreadyExists && err != nil {
// Despite appearances to the contrary, CreateNetwork does
Expand Down
9 changes: 2 additions & 7 deletions site/plugin.md
Expand Up @@ -22,7 +22,8 @@ After you've launched Weave Net and peered your hosts, you can start containers

$ docker run --net=weave -ti ubuntu

on any of the hosts, and they can all communicate with each other.
on any of the hosts, and they can all communicate with each other
using any protocol, even multicast.

>**Warning!** It is inadvisable to attach containers to the Weave network using the Weave Docker Networking Plugin and Weave Docker API Proxy simultaneously. Such containers will end up with two Weave network interfaces and two IP addresses, which is rarely desirable. To ensure that the proxy is not being used, do not run `eval $(weave env)`, or `docker $(weave config) ...`.
Expand Down Expand Up @@ -82,12 +83,6 @@ The plugin configuration parameters are:
how much information to emit for debugging.
* `--no-restart` -- remove the default policy of `--restart=always`, if
you want to control start-up of the plugin yourself
* `--no-multicast-route` -- stops weave from adding a static IP route for
multicast traffic onto its interface

By default, multicast traffic is routed over the weave network.
To turn this off, e.g. you want to configure your own multicast
route, add the `--no-multicast-route` flag to `weave launch-plugin`.


**See Also**
Expand Down
9 changes: 9 additions & 0 deletions site/plugin/plugin-how-it-works.md
Expand Up @@ -26,6 +26,15 @@ store, but the first part of

>**Note:** In the case of multiple networks using the `weave` driver, all containers are on the same virtual network but Docker allocates their addresses on different subnets so they cannot talk to each other directly.
The plugin accepts the following options via `docker network create ... --opt`:

* `works.weave.multicast` -- tells weave to add a static IP
route for multicast traffic onto its interface.

>**Note:** If you connect a container to multiple Weave networks, at
most one of them can have the multicast route enabled. The `weave`
network created when the plugin is first launched has the multicast
option turned on, but for any networks you create it defaults to off.

**See Also**

Expand Down
19 changes: 16 additions & 3 deletions test/195_no_multicast_route_1_test.sh
Expand Up @@ -8,18 +8,30 @@ show_multicast_route_on() {

start_suite "--no-multicast-route operation"

# In case one is left around from a previous test
$SSH $HOST1 docker network rm testmcasttrue testmcastfalse >/dev/null 2>&1 || true

# Ensure containers run either way have no multicast route
weave_on $HOST1 launch-router
weave_on $HOST1 launch-proxy --no-multicast-route
weave_on $HOST1 launch-plugin --no-multicast-route
weave_on $HOST1 launch-plugin # plugin defaults to multicast off except for 'net=weave'

start_container $HOST1 --no-multicast-route --name c1
proxy_start_container $HOST1 --name c2
start_container_local_plugin $HOST1 --name=c3

assert "show_multicast_route_on $HOST1 c1"
assert "show_multicast_route_on $HOST1 c2"
assert "show_multicast_route_on $HOST1 c3"
assert "show_multicast_route_on $HOST1 c3" "224.0.0.0/4 dev ethwe0 "

# Now try via docker network options
# using ssh rather than docker -H because CircleCI docker client is older
$SSH $HOST1 docker network create --driver weavemesh --ipam-driver weavemesh --opt works.weave.multicast=false testmcastfalse
$SSH $HOST1 docker run --net=testmcastfalse --name c4 -di $SMALL_IMAGE /bin/sh
assert "show_multicast_route_on $HOST1 c4"
$SSH $HOST1 docker network create --driver weavemesh --ipam-driver weavemesh --opt works.weave.multicast testmcasttrue
$SSH $HOST1 docker run --net=testmcasttrue --name c5 -di $SMALL_IMAGE /bin/sh
assert "show_multicast_route_on $HOST1 c5" "224.0.0.0/4 dev ethwe0 "

# Ensure current proxy options are obeyed on container start
docker_on $HOST1 stop -t 1 c2
Expand All @@ -30,6 +42,7 @@ proxy docker_on $HOST1 start c2

assert "show_multicast_route_on $HOST1 c2" "224.0.0.0/4 dev ethwe "

docker_on $HOST1 rm -f c3
docker_on $HOST1 rm -f c3 c4 c5
$SSH $HOST1 docker network rm testmcasttrue testmcastfalse

end_suite

0 comments on commit 7ffbeea

Please sign in to comment.