Beispiel #1
0
func (driver *driver) JoinEndpoint(j *api.JoinRequest) (*api.JoinResponse, error) {
	endID := j.EndpointID

	maybeBridge, err := netlink.LinkByName(WeaveBridge)
	if err != nil {
		return nil, errorf(`bridge "%s" not present; did you launch weave?`, WeaveBridge)
	}

	// create and attach local name to the bridge
	local := vethPair(endID[:5])
	local.Attrs().MTU = maybeBridge.Attrs().MTU
	if err := netlink.LinkAdd(local); err != nil {
		return nil, errorf("could not create veth pair: %s", err)
	}

	switch maybeBridge.(type) {
	case *netlink.Bridge:
		if err := netlink.LinkSetMasterByIndex(local, maybeBridge.Attrs().Index); err != nil {
			return nil, errorf(`unable to set master: %s`, err)
		}
	case *netlink.GenericLink:
		if maybeBridge.Type() != "openvswitch" {
			Log.Errorf("device %s is %+v", WeaveBridge, maybeBridge)
			return nil, errorf(`device "%s" is of type "%s"`, WeaveBridge, maybeBridge.Type())
		}
		odp.AddDatapathInterface(WeaveBridge, local.Name)
	case *netlink.Device:
		Log.Warnf("kernel does not report what kind of device %s is, just %+v", WeaveBridge, maybeBridge)
		// Assume it's our openvswitch device, and the kernel has not been updated to report the kind.
		odp.AddDatapathInterface(WeaveBridge, local.Name)
	default:
		Log.Errorf("device %s is %+v", WeaveBridge, maybeBridge)
		return nil, errorf(`device "%s" not a bridge`, WeaveBridge)
	}
	if err := netlink.LinkSetUp(local); err != nil {
		return nil, errorf(`unable to bring veth up: %s`, err)
	}

	ifname := &api.InterfaceName{
		SrcName:   local.PeerName,
		DstPrefix: "ethwe",
	}

	response := &api.JoinResponse{
		InterfaceName: ifname,
	}
	if !driver.noMulticastRoute {
		multicastRoute := api.StaticRoute{
			Destination: "224.0.0.0/4",
			RouteType:   types.CONNECTED,
		}
		response.StaticRoutes = append(response.StaticRoutes, multicastRoute)
	}
	Log.Infof("Join endpoint %s:%s to %s", j.NetworkID, j.EndpointID, j.SandboxKey)
	return response, nil
}
Beispiel #2
0
// create and attach a veth to the Weave bridge
func CreateAndAttachVeth(name, peerName, bridgeName string, mtu int, keepTXOn bool, init func(peer netlink.Link) error) (*netlink.Veth, error) {
	bridge, err := netlink.LinkByName(bridgeName)
	if err != nil {
		return nil, fmt.Errorf(`bridge "%s" not present; did you launch weave?`, bridgeName)
	}

	if mtu == 0 {
		mtu = bridge.Attrs().MTU
	}
	veth := &netlink.Veth{
		LinkAttrs: netlink.LinkAttrs{
			Name: name,
			MTU:  mtu},
		PeerName: peerName,
	}
	if err := netlink.LinkAdd(veth); err != nil {
		return nil, fmt.Errorf(`could not create veth pair %s-%s: %s`, name, peerName, err)
	}

	cleanup := func(format string, a ...interface{}) (*netlink.Veth, error) {
		netlink.LinkDel(veth)
		return nil, fmt.Errorf(format, a...)
	}

	switch bridgeType := DetectBridgeType(bridgeName, DatapathName); bridgeType {
	case Bridge, BridgedFastdp:
		if err := netlink.LinkSetMasterByIndex(veth, bridge.Attrs().Index); err != nil {
			return cleanup(`unable to set master of %s: %s`, name, err)
		}
		if bridgeType == Bridge && !keepTXOn {
			if err := EthtoolTXOff(peerName); err != nil {
				return cleanup(`unable to set tx off on %q: %s`, peerName, err)
			}
		}
	case Fastdp:
		if err := odp.AddDatapathInterface(bridgeName, name); err != nil {
			return cleanup(`failed to attach %s to device "%s": %s`, name, bridgeName, err)
		}
	default:
		return cleanup(`invalid bridge configuration`)
	}

	if init != nil {
		peer, err := netlink.LinkByName(peerName)
		if err != nil {
			return cleanup("unable to find peer veth %s: %s", peerName, err)
		}
		if err := init(peer); err != nil {
			return cleanup("initializing veth: %s", err)
		}
	}

	if err := netlink.LinkSetUp(veth); err != nil {
		return cleanup("unable to bring veth up: %s", err)
	}

	return veth, nil
}
Beispiel #3
0
func (driver *driver) JoinEndpoint(j *api.JoinRequest) (*api.JoinResponse, error) {
	endID := j.EndpointID

	// create and attach local name to the bridge
	local := vethPair(endID[:5])
	if err := netlink.LinkAdd(local); err != nil {
		return nil, errorf("could not create veth pair: %s", err)
	}

	if maybeBridge, err := netlink.LinkByName(WeaveBridge); err != nil {
		return nil, errorf(`bridge "%s" not present; did you launch weave?`, WeaveBridge)
	} else {
		switch maybeBridge.(type) {
		case *netlink.Bridge:
			if err := netlink.LinkSetMasterByIndex(local, maybeBridge.Attrs().Index); err != nil {
				return nil, errorf(`unable to set master: %s`, err)
			}
		case *netlink.GenericLink:
			if maybeBridge.Type() != "openvswitch" {
				Log.Errorf("device %s is %+v", WeaveBridge, maybeBridge)
				return nil, errorf(`device "%s" is of type "%s"`, WeaveBridge, maybeBridge.Type())
			}
			odp.AddDatapathInterface(WeaveBridge, local.Name)
		default:
			Log.Errorf("device %s is %+v", WeaveBridge, maybeBridge)
			return nil, errorf(`device "%s" not a bridge`, WeaveBridge)
		}
	}
	if err := netlink.LinkSetUp(local); err != nil {
		return nil, errorf(`unable to bring veth up: %s`, err)
	}

	ifname := &api.InterfaceName{
		SrcName:   local.PeerName,
		DstPrefix: "ethwe",
	}

	response := &api.JoinResponse{
		InterfaceName: ifname,
	}
	if driver.nameserver != "" {
		routeToDNS := api.StaticRoute{
			Destination: driver.nameserver + "/32",
			RouteType:   types.CONNECTED,
			NextHop:     "",
		}
		response.StaticRoutes = []api.StaticRoute{routeToDNS}
	}
	Log.Infof("Join endpoint %s:%s to %s", j.NetworkID, j.EndpointID, j.SandboxKey)
	return response, nil
}
Beispiel #4
0
func main() {
	procs := runtime.NumCPU()
	// packet sniffing can block an OS thread, so we need one thread
	// for that plus at least one more.
	if procs < 2 {
		procs = 2
	}
	runtime.GOMAXPROCS(procs)

	var (
		// flags that cause immediate exit
		justVersion          bool
		createDatapath       bool
		deleteDatapath       bool
		addDatapathInterface string

		config                    mesh.Config
		networkConfig             weave.NetworkConfig
		protocolMinVersion        int
		ifaceName                 string
		routerName                string
		nickName                  string
		password                  string
		pktdebug                  bool
		logLevel                  string
		prof                      string
		bufSzMB                   int
		noDiscovery               bool
		httpAddr                  string
		iprangeCIDR               string
		ipsubnetCIDR              string
		peerCount                 int
		dockerAPI                 string
		peers                     []string
		noDNS                     bool
		dnsDomain                 string
		dnsListenAddress          string
		dnsTTL                    int
		dnsClientTimeout          time.Duration
		dnsEffectiveListenAddress string
		iface                     *net.Interface
		datapathName              string
		trustedSubnetStr          string

		defaultDockerHost = "unix:///var/run/docker.sock"
	)

	if val := os.Getenv("DOCKER_HOST"); val != "" {
		defaultDockerHost = val
	}

	mflag.BoolVar(&justVersion, []string{"#version", "-version"}, false, "print version and exit")
	mflag.BoolVar(&createDatapath, []string{"-create-datapath"}, false, "create ODP datapath and exit")
	mflag.BoolVar(&deleteDatapath, []string{"-delete-datapath"}, false, "delete ODP datapath and exit")
	mflag.StringVar(&addDatapathInterface, []string{"-add-datapath-iface"}, "", "add a network interface to the ODP datapath and exit")

	mflag.IntVar(&config.Port, []string{"#port", "-port"}, mesh.Port, "router port")
	mflag.IntVar(&protocolMinVersion, []string{"-min-protocol-version"}, mesh.ProtocolMinVersion, "minimum weave protocol version")
	mflag.StringVar(&ifaceName, []string{"#iface", "-iface"}, "", "name of interface to capture/inject from (disabled if blank)")
	mflag.StringVar(&routerName, []string{"#name", "-name"}, "", "name of router (defaults to MAC of interface)")
	mflag.StringVar(&nickName, []string{"#nickname", "-nickname"}, "", "nickname of peer (defaults to hostname)")
	mflag.StringVar(&password, []string{"#password", "-password"}, "", "network password")
	mflag.StringVar(&logLevel, []string{"-log-level"}, "info", "logging level (debug, info, warning, error)")
	mflag.BoolVar(&pktdebug, []string{"#pktdebug", "#-pktdebug", "-pkt-debug"}, false, "enable per-packet debug logging")
	mflag.StringVar(&prof, []string{"#profile", "-profile"}, "", "enable profiling and write profiles to given path")
	mflag.IntVar(&config.ConnLimit, []string{"#connlimit", "#-connlimit", "-conn-limit"}, 30, "connection limit (0 for unlimited)")
	mflag.BoolVar(&noDiscovery, []string{"#nodiscovery", "#-nodiscovery", "-no-discovery"}, false, "disable peer discovery")
	mflag.IntVar(&bufSzMB, []string{"#bufsz", "-bufsz"}, 8, "capture buffer size in MB")
	mflag.StringVar(&httpAddr, []string{"#httpaddr", "#-httpaddr", "-http-addr"}, "", "address to bind HTTP interface to (disabled if blank, absolute path indicates unix domain socket)")
	mflag.StringVar(&iprangeCIDR, []string{"#iprange", "#-iprange", "-ipalloc-range"}, "", "IP address range reserved for automatic allocation, in CIDR notation")
	mflag.StringVar(&ipsubnetCIDR, []string{"#ipsubnet", "#-ipsubnet", "-ipalloc-default-subnet"}, "", "subnet to allocate within by default, in CIDR notation")
	mflag.IntVar(&peerCount, []string{"#initpeercount", "#-initpeercount", "-init-peer-count"}, 0, "number of peers in network (for IP address allocation)")
	mflag.StringVar(&dockerAPI, []string{"#api", "#-api", "-docker-api"}, defaultDockerHost, "Docker API endpoint")
	mflag.BoolVar(&noDNS, []string{"-no-dns"}, false, "disable DNS server")
	mflag.StringVar(&dnsDomain, []string{"-dns-domain"}, nameserver.DefaultDomain, "local domain to server requests for")
	mflag.StringVar(&dnsListenAddress, []string{"-dns-listen-address"}, nameserver.DefaultListenAddress, "address to listen on for DNS requests")
	mflag.IntVar(&dnsTTL, []string{"-dns-ttl"}, nameserver.DefaultTTL, "TTL for DNS request from our domain")
	mflag.DurationVar(&dnsClientTimeout, []string{"-dns-fallback-timeout"}, nameserver.DefaultClientTimeout, "timeout for fallback DNS requests")
	mflag.StringVar(&dnsEffectiveListenAddress, []string{"-dns-effective-listen-address"}, "", "address DNS will actually be listening, after Docker port mapping")
	mflag.StringVar(&datapathName, []string{"-datapath"}, "", "ODP datapath name")

	mflag.StringVar(&trustedSubnetStr, []string{"-trusted-subnets"}, "", "Command separated list of trusted subnets in CIDR notation")

	// crude way of detecting that we probably have been started in a
	// container, with `weave launch` --> suppress misleading paths in
	// mflags error messages.
	if os.Args[0] == "/home/weave/weaver" { // matches the Dockerfile ENTRYPOINT
		os.Args[0] = "weave"
		mflag.CommandLine.Init("weave", mflag.ExitOnError)
	}

	mflag.Parse()

	peers = mflag.Args()

	SetLogLevel(logLevel)

	switch {
	case justVersion:
		fmt.Printf("weave router %s\n", version)
		os.Exit(0)

	case createDatapath:
		odpSupported, err := odp.CreateDatapath(datapathName)
		if !odpSupported {
			if err != nil {
				Log.Error(err)
			}

			// When the kernel lacks ODP support, exit
			// with a special status to distinguish it for
			// the weave script.
			os.Exit(17)
		}

		checkFatal(err)
		os.Exit(0)

	case deleteDatapath:
		checkFatal(odp.DeleteDatapath(datapathName))
		os.Exit(0)

	case addDatapathInterface != "":
		checkFatal(odp.AddDatapathInterface(datapathName, addDatapathInterface))
		os.Exit(0)
	}

	Log.Println("Command line options:", options())
	Log.Println("Command line peers:", peers)

	if protocolMinVersion < mesh.ProtocolMinVersion || protocolMinVersion > mesh.ProtocolMaxVersion {
		Log.Fatalf("--min-protocol-version must be in range [%d,%d]", mesh.ProtocolMinVersion, mesh.ProtocolMaxVersion)
	}
	config.ProtocolMinVersion = byte(protocolMinVersion)

	overlays := weave.NewOverlaySwitch()
	if datapathName != "" {
		// A datapath name implies that "Bridge" and "Overlay"
		// packet handling use fast datapath, although other
		// options can override that below.  Even if both
		// things are overridden, we might need bridging on
		// the datapath.
		fastdp, err := weave.NewFastDatapath(datapathName, config.Port)
		checkFatal(err)
		networkConfig.Bridge = fastdp.Bridge()
		overlays.Add("fastdp", fastdp.Overlay())
	}
	sleeve := weave.NewSleeveOverlay(config.Port)
	overlays.Add("sleeve", sleeve)
	overlays.SetCompatOverlay(sleeve)

	if ifaceName != "" {
		// -iface can coexist with -datapath, because
		// pcap-based packet capture is a bit more efficient
		// than capture via ODP misses, even when using an
		// ODP-based bridge.  So when using weave encyption,
		// it's preferable to use -iface.
		var err error
		iface, err = weavenet.EnsureInterface(ifaceName)
		checkFatal(err)

		// bufsz flag is in MB
		networkConfig.Bridge, err = weave.NewPcap(iface, bufSzMB*1024*1024)
		checkFatal(err)
	}

	if password == "" {
		password = os.Getenv("WEAVE_PASSWORD")
	}

	if password == "" {
		Log.Println("Communication between peers is unencrypted.")
	} else {
		config.Password = []byte(password)
		Log.Println("Communication between peers via untrusted networks is encrypted.")
	}

	if routerName == "" {
		if iface == nil {
			Log.Fatal("Either an interface must be specified with --iface or a name with -name")
		}
		routerName = iface.HardwareAddr.String()
	}

	name, err := mesh.PeerNameFromUserInput(routerName)
	checkFatal(err)

	if nickName == "" {
		nickName, err = os.Hostname()
		checkFatal(err)
	}

	if prof != "" {
		p := *profile.CPUProfile
		p.ProfilePath = prof
		p.NoShutdownHook = true
		defer profile.Start(&p).Stop()
	}

	config.PeerDiscovery = !noDiscovery

	if pktdebug {
		networkConfig.PacketLogging = packetLogging{}
	} else {
		networkConfig.PacketLogging = nopPacketLogging{}
	}

	if config.TrustedSubnets, err = parseTrustedSubnets(trustedSubnetStr); err != nil {
		Log.Fatal("Unable to parse trusted subnets: ", err)
	}

	router := weave.NewNetworkRouter(config, networkConfig, name, nickName, overlays)
	Log.Println("Our name is", router.Ourself)

	var dockerCli *docker.Client
	if dockerAPI != "" {
		dc, err := docker.NewClient(dockerAPI)
		if err != nil {
			Log.Fatal("Unable to start docker client: ", err)
		} else {
			Log.Info(dc.Info())
		}
		dockerCli = dc
	}
	observeContainers := func(o docker.ContainerObserver) {
		if dockerCli != nil {
			if err = dockerCli.AddObserver(o); err != nil {
				Log.Fatal("Unable to start watcher", err)
			}
		}
	}
	isKnownPeer := func(name mesh.PeerName) bool {
		return router.Peers.Fetch(name) != nil
	}
	var allocator *ipam.Allocator
	var defaultSubnet address.CIDR
	if iprangeCIDR != "" {
		allocator, defaultSubnet = createAllocator(router.Router, iprangeCIDR, ipsubnetCIDR, determineQuorum(peerCount, peers), isKnownPeer)
		observeContainers(allocator)
	} else if peerCount > 0 {
		Log.Fatal("--init-peer-count flag specified without --ipalloc-range")
	}

	var (
		ns        *nameserver.Nameserver
		dnsserver *nameserver.DNSServer
	)
	if !noDNS {
		ns = nameserver.New(router.Ourself.Peer.Name, dnsDomain, isKnownPeer)
		router.Peers.OnGC(func(peer *mesh.Peer) { ns.PeerGone(peer.Name) })
		ns.SetGossip(router.NewGossip("nameserver", ns))
		observeContainers(ns)
		ns.Start()
		defer ns.Stop()
		dnsserver, err = nameserver.NewDNSServer(ns, dnsDomain, dnsListenAddress,
			dnsEffectiveListenAddress, uint32(dnsTTL), dnsClientTimeout)
		if err != nil {
			Log.Fatal("Unable to start dns server: ", err)
		}
		listenAddr := dnsListenAddress
		if dnsEffectiveListenAddress != "" {
			listenAddr = dnsEffectiveListenAddress
		}
		Log.Println("Listening for DNS queries on", listenAddr)
		dnsserver.ActivateAndServe()
		defer dnsserver.Stop()
	}

	router.Start()
	if errors := router.ConnectionMaker.InitiateConnections(peers, false); len(errors) > 0 {
		Log.Fatal(ErrorMessages(errors))
	}

	// The weave script always waits for a status call to succeed,
	// so there is no point in doing "weave launch --http-addr ''".
	// This is here to support stand-alone use of weaver.
	if httpAddr != "" {
		muxRouter := mux.NewRouter()
		if allocator != nil {
			allocator.HandleHTTP(muxRouter, defaultSubnet, dockerCli)
		}
		if ns != nil {
			ns.HandleHTTP(muxRouter, dockerCli)
		}
		router.HandleHTTP(muxRouter)
		HandleHTTP(muxRouter, version, router, allocator, defaultSubnet, ns, dnsserver)
		http.Handle("/", muxRouter)
		Log.Println("Listening for HTTP control messages on", httpAddr)
		go listenAndServeHTTP(httpAddr, muxRouter)
	}

	SignalHandlerLoop(router)
}
Beispiel #5
0
func addDatapathInterface(args []string) error {
	if len(args) != 2 {
		cmdUsage("add-datapath-interface", "<datapath> <interface>")
	}
	return odp.AddDatapathInterface(args[0], args[1])
}