package plugin import ( "fmt" "strconv" "sync" "github.com/docker/libnetwork/drivers/remote/api" "github.com/docker/libnetwork/netlabel" "github.com/docker/libnetwork/types" "golang.org/x/sys/unix" weaveapi "github.com/rajch/weave/api" "github.com/rajch/weave/common" "github.com/rajch/weave/common/docker" weavenet "github.com/rajch/weave/net" "github.com/rajch/weave/plugin/skel" "github.com/vishvananda/netlink" ) const ( MulticastOption = "works.weave.multicast" ) type network struct { isOurs bool hasMulticastRoute bool } type driver struct { sync.RWMutex name string scope string // Docker API is not available for plugin-v2 docker *docker.Client dns bool isPluginV2 bool // Enable multicast regardless whether multicast opt is passed to Docker; // used only by plugin-v2 forceMulticast bool networks map[string]network procPath string } func New(client *docker.Client, weave *weaveapi.Client, name, scope string, dns, isPluginV2, forceMulticast bool, procPath string) (skel.Driver, error) { driver := &driver{ name: name, scope: scope, docker: client, dns: dns, isPluginV2: isPluginV2, // make sure that it's used only by plugin-v2 forceMulticast: isPluginV2 && forceMulticast, networks: make(map[string]network), procPath: procPath, } // Do not start watcher in the case of plugin v2, which prevents us from // configuring arp settings of containers. if client != nil { _, err := NewWatcher(client, weave, driver) if err != nil { return nil, err } } return driver, nil } // === protocol handlers func (driver *driver) GetCapabilities() (*api.GetCapabilityResponse, error) { driver.logReq("GetCapabilities", nil, "") var caps = &api.GetCapabilityResponse{ Scope: driver.scope, } driver.logRes("GetCapabilities", caps) return caps, nil } // In Swarm mode, CreateNetwork is called on each Swarm node when a new service // is created. func (driver *driver) CreateNetwork(create *api.CreateNetworkRequest) error { driver.logReq("CreateNetwork", create, create.NetworkID) _, err := driver.setupNetworkInfo(create.NetworkID, true, stringOptions(create)) return err } // NetworkAllocate is called on a Swarm node (master) which creates the network. // The returned options are passed to CreateNetwork. func (driver *driver) NetworkAllocate(alloc *api.AllocateNetworkRequest) (*api.AllocateNetworkResponse, error) { driver.logReq("NetworkAllocate", alloc, alloc.NetworkID) return &api.AllocateNetworkResponse{Options: alloc.Options}, nil } // NetworkFree is called on a Swarm master node which created the network. func (driver *driver) NetworkFree(free *api.FreeNetworkRequest) (*api.FreeNetworkResponse, error) { driver.logReq("NetworkFree", free, free.NetworkID) return nil, nil } // 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 } // In Swarm mode, DeleteNetwork is called after a service has been removed. 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 } func (driver *driver) CreateEndpoint(create *api.CreateEndpointRequest) (*api.CreateEndpointResponse, error) { driver.logReq("CreateEndpoint", create, create.EndpointID) common.Log.Debugf("interface %+v", create.Interface) if create.Interface == nil { return nil, driver.error("CreateEndpoint", "Not supported: creating an interface from within CreateEndpoint") } // create veths. note we assume endpoint IDs are unique in the first 9 chars name, peerName := vethPair(create.EndpointID) if _, err := weavenet.CreateAndAttachVeth(driver.procPath, name, peerName, weavenet.WeaveBridgeName, 0, false, true, nil); err != nil { return nil, driver.error("JoinEndpoint", "%s", err) } // Send back the MAC address link, _ := netlink.LinkByName(peerName) resp := &api.CreateEndpointResponse{Interface: &api.EndpointInterface{MacAddress: link.Attrs().HardwareAddr.String()}} driver.logRes("CreateEndpoint", resp) return resp, nil } func (driver *driver) DeleteEndpoint(deleteReq *api.DeleteEndpointRequest) error { driver.logReq("DeleteEndpoint", deleteReq, deleteReq.EndpointID) name, _ := vethPair(deleteReq.EndpointID) veth := &netlink.Veth{LinkAttrs: netlink.LinkAttrs{Name: name}} if err := netlink.LinkDel(veth); err != nil { // Try again using the name construction from earlier plugin version, // in case user has upgraded with endpoints still extant veth.Name = "vethwl" + deleteReq.EndpointID[:5] if err2 := netlink.LinkDel(veth); err2 != nil { // Note we report the first error driver.warn("LeaveEndpoint", "unable to delete veth %q: %s", name, err) } } return nil } func (driver *driver) EndpointInfo(req *api.EndpointInfoRequest) (*api.EndpointInfoResponse, error) { driver.logReq("EndpointInfo", req, req.EndpointID) return &api.EndpointInfoResponse{Value: map[string]interface{}{}}, nil } 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) } _, peerName := vethPair(j.EndpointID) response := &api.JoinResponse{ InterfaceName: &api.InterfaceName{ SrcName: peerName, DstPrefix: weavenet.VethName, }, } if network.hasMulticastRoute { multicastRoute := api.StaticRoute{ Destination: "224.0.0.0/4", RouteType: types.CONNECTED, } response.StaticRoutes = append(response.StaticRoutes, multicastRoute) } driver.logRes("JoinEndpoint", response) return response, nil } func (driver *driver) findNetworkInfo(id string) (network, error) { var network network // plugin-v2 does not have access to docker.sock, so we cannot call Docker // API for the network info. if driver.isPluginV2 { // safe to set, as isOurs used only by the watcher which is disabled for plugin-v2 network.isOurs = true network.hasMulticastRoute = driver.forceMulticast return network, nil } driver.Lock() network, found := driver.networks[id] driver.Unlock() if found { return network, nil } if driver.docker == nil { return network, fmt.Errorf("Docker client disabled; unable to get network info") } info, err := driver.docker.NetworkInfo(id) if err != nil { return network, err } return driver.setupNetworkInfo(id, info.Driver == driver.name, info.Options) } func (driver *driver) setupNetworkInfo(id string, isOurs bool, options map[string]string) (network, error) { network := network{isOurs: isOurs} if isOurs { 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)) return nil } func (driver *driver) DiscoverNew(disco *api.DiscoveryNotification) error { driver.logReq("DiscoverNew", disco, "") return nil } func (driver *driver) DiscoverDelete(disco *api.DiscoveryNotification) error { driver.logReq("DiscoverDelete", disco, "") return nil } func vethPair(id string) (string, string) { // IFNAMSIZ is buffer length; subtract 6 for "vethwl" and 1 for terminating nul return "vethwl" + id[:unix.IFNAMSIZ-7], "vethwg" + id[:unix.IFNAMSIZ-7] } // logging func (driver *driver) logReq(fun string, req interface{}, short string) { driver.log(common.Log.Debugf, " %+v", fun, req) common.Log.Infof("[net] %s %s", fun, short) } func (driver *driver) logRes(fun string, res interface{}) { driver.log(common.Log.Debugf, " %+v", fun, res) } func (driver *driver) warn(fun string, format string, a ...interface{}) { driver.log(common.Log.Warnf, ": "+format, fun, a...) } func (driver *driver) debug(fun string, format string, a ...interface{}) { driver.log(common.Log.Debugf, ": "+format, fun, a...) } func (driver *driver) error(fun string, format string, a ...interface{}) error { driver.log(common.Log.Errorf, ": "+format, fun, a...) return fmt.Errorf(format, a...) } func (driver *driver) log(f func(string, ...interface{}), format string, fun string, a ...interface{}) { f("[net] %s"+format, append([]interface{}{fun}, a...)...) }