package main import ( "fmt" "net" "net/http" _ "net/http/pprof" "os" "runtime" "strconv" "strings" "time" "github.com/gorilla/mux" "github.com/pkg/profile" "github.com/weaveworks/common/mflag" "github.com/weaveworks/common/mflagext" "github.com/weaveworks/common/signals" "github.com/weaveworks/mesh" "github.com/weaveworks/weave/common" "github.com/weaveworks/weave/common/docker" "github.com/weaveworks/weave/db" "github.com/weaveworks/weave/ipam" "github.com/weaveworks/weave/ipam/tracker" "github.com/weaveworks/weave/nameserver" weavenet "github.com/weaveworks/weave/net" "github.com/weaveworks/weave/net/address" "github.com/weaveworks/weave/plugin" weaveproxy "github.com/weaveworks/weave/proxy" weave "github.com/weaveworks/weave/router" ) var version = "unreleased" var Log = common.Log type ipamConfig struct { IPRangeCIDR string IPSubnetCIDR string PeerCount int Mode string Observer bool SeedPeerNames []mesh.PeerName } type dnsConfig struct { Domain string ListenAddress string TTL int ClientTimeout time.Duration ResolvConf string } // return the address part of the DNS listen address, without ":53" on the end func (c dnsConfig) addressOnly() string { addr, _, err := net.SplitHostPort(c.ListenAddress) if err != nil { Log.Fatalf("Error when parsing DNS listen address %q: %s", c.ListenAddress, err) } return addr } func (c *ipamConfig) Enabled() bool { var ( hasPeerCount = c.PeerCount > 0 hasMode = c.HasMode() hasRange = c.IPRangeCIDR != "" hasSubnet = c.IPSubnetCIDR != "" ) switch { case !(hasPeerCount || hasMode || hasRange || hasSubnet): return false case !hasRange && hasSubnet: Log.Fatal("--ipalloc-default-subnet specified without --ipalloc-range.") case !hasRange: Log.Fatal("--ipalloc-init specified without --ipalloc-range.") } if hasMode { if err := c.parseMode(); err != nil { Log.Fatalf("Unable to parse --ipalloc-init: %s", err) } } return true } func (c ipamConfig) HasMode() bool { return len(c.Mode) > 0 } func (c *ipamConfig) parseMode() error { modeAndParam := strings.SplitN(c.Mode, "=", 2) switch modeAndParam[0] { case "consensus": if len(modeAndParam) == 2 { peerCount, err := strconv.Atoi(modeAndParam[1]) if err != nil { return fmt.Errorf("bad consensus parameter: %s", err) } c.PeerCount = peerCount } case "seed": if len(modeAndParam) != 2 { return fmt.Errorf("seed mode requires peer name list") } seedPeerNames, err := parsePeerNames(modeAndParam[1]) if err != nil { return fmt.Errorf("bad seed parameter: %s", err) } c.SeedPeerNames = seedPeerNames case "observer": if len(modeAndParam) != 1 { return fmt.Errorf("observer mode takes no parameter") } c.Observer = true default: return fmt.Errorf("unknown mode: %s", modeAndParam[0]) } return nil } func getenvOrDefault(key, defaultVal string) string { if v := os.Getenv(key); v != "" { return v } return defaultVal } 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 ( justVersion bool justPeerName bool config mesh.Config bridgeConfig weavenet.BridgeConfig networkConfig weave.NetworkConfig protocolMinVersion int resume bool routerName string nickName string password string pktdebug bool logLevel = "info" prof string bufSzMB int noDiscovery bool httpAddr string statusAddr string metricsAddr string ipamConfig ipamConfig dockerAPI string peers []string noDNS bool dnsConfig dnsConfig trustedSubnetStr string dbPrefix string hostRoot string procPath string discoveryEndpoint string token string advertiseAddress string pluginConfig plugin.Config defaultDockerHost = getenvOrDefault("DOCKER_HOST", "unix:///var/run/docker.sock") ) mflag.BoolVar(&justVersion, []string{"-version"}, false, "print version and exit") mflag.BoolVar(&justPeerName, []string{"-print-peer-name"}, false, "print peer name and exit") mflag.StringVar(&config.Host, []string{"-host"}, "", "router host") mflag.IntVar(&config.Port, []string{"-port"}, mesh.Port, "router port") mflag.IntVar(&protocolMinVersion, []string{"-min-protocol-version"}, mesh.ProtocolMinVersion, "minimum weave protocol version") mflag.BoolVar(&resume, []string{"-resume"}, false, "resume connections to previous peers") mflag.StringVar(&bridgeConfig.WeaveBridgeName, []string{"-weave-bridge"}, "weave", "name of weave bridge") mflag.StringVar(&bridgeConfig.DockerBridgeName, []string{"-docker-bridge"}, "", "name of Docker bridge") mflag.BoolVar(&bridgeConfig.NPC, []string{"-expect-npc"}, false, "set up iptables rules for npc") mflag.StringVar(&routerName, []string{"-name"}, "", "name of router (defaults to MAC of interface)") mflag.StringVar(&nickName, []string{"-nickname"}, "", "nickname of peer (defaults to hostname)") mflag.StringVar(&password, []string{"-password"}, "", "network password") mflag.StringVar(&logLevel, []string{"-log-level"}, "info", "logging level (debug, info, warning, error)") mflag.BoolVar(&pktdebug, []string{"-pkt-debug"}, false, "enable per-packet debug logging") mflag.StringVar(&prof, []string{"-profile"}, "", "enable profiling and write profiles to given path") mflag.IntVar(&config.ConnLimit, []string{"-conn-limit"}, 100, "connection limit (0 for unlimited)") mflag.BoolVar(&noDiscovery, []string{"-no-discovery"}, false, "disable peer discovery") mflag.IntVar(&bufSzMB, []string{"-bufsz"}, 8, "capture buffer size in MB") mflag.IntVar(&bridgeConfig.MTU, []string{"-mtu"}, 0, "MTU size") mflag.StringVar(&httpAddr, []string{"-http-addr"}, "", "address to bind HTTP interface to (disabled if blank, absolute path indicates unix domain socket)") mflag.StringVar(&statusAddr, []string{"-status-addr"}, "", "address to bind status+metrics interface to (disabled if blank, absolute path indicates unix domain socket)") mflag.StringVar(&metricsAddr, []string{"-metrics-addr"}, "", "address to bind metrics interface to (disabled if blank, absolute path indicates unix domain socket)") mflag.StringVar(&ipamConfig.Mode, []string{"-ipalloc-init"}, "", "allocator initialisation strategy (consensus, seed or observer)") mflag.StringVar(&ipamConfig.IPRangeCIDR, []string{"-ipalloc-range"}, "", "IP address range reserved for automatic allocation, in CIDR notation") mflag.StringVar(&ipamConfig.IPSubnetCIDR, []string{"-ipalloc-default-subnet"}, "", "subnet to allocate within by default, in CIDR notation") mflag.StringVar(&dockerAPI, []string{"-docker-api"}, defaultDockerHost, "Docker API endpoint") mflag.BoolVar(&noDNS, []string{"-no-dns"}, false, "disable DNS server") mflag.StringVar(&dnsConfig.Domain, []string{"-dns-domain"}, nameserver.DefaultDomain, "local domain to server requests for") mflag.StringVar(&dnsConfig.ListenAddress, []string{"-dns-listen-address"}, nameserver.DefaultListenAddress, "address to listen on for DNS requests") mflag.IntVar(&dnsConfig.TTL, []string{"-dns-ttl"}, nameserver.DefaultTTL, "TTL for DNS request from our domain") mflag.DurationVar(&dnsConfig.ClientTimeout, []string{"-dns-fallback-timeout"}, nameserver.DefaultClientTimeout, "timeout for fallback DNS requests") mflag.StringVar(&dnsConfig.ResolvConf, []string{"-resolv-conf"}, "", "path to resolver configuration for fallback DNS lookups") mflag.StringVar(&bridgeConfig.DatapathName, []string{"-datapath"}, "", "ODP datapath name") mflag.BoolVar(&bridgeConfig.NoFastdp, []string{"-no-fastdp"}, false, "Disable Fast Datapath") mflag.BoolVar(&bridgeConfig.NoBridgedFastdp, []string{"-no-bridged-fastdp"}, false, "Disable Bridged Fast Datapath") mflag.StringVar(&trustedSubnetStr, []string{"-trusted-subnets"}, "", "comma-separated list of trusted subnets in CIDR notation") mflag.StringVar(&dbPrefix, []string{"-db-prefix"}, "/weavedb/weave", "pathname/prefix of filename to store data") mflag.StringVar(&procPath, []string{"-proc-path"}, "/proc", "path to reach host /proc filesystem") mflag.BoolVar(&bridgeConfig.AWSVPC, []string{"-awsvpc"}, false, "use AWS VPC for routing") mflag.StringVar(&hostRoot, []string{"-host-root"}, "", "path to reach host filesystem") mflag.StringVar(&discoveryEndpoint, []string{"-peer-discovery-url"}, "https://cloud.weave.works/api/net", "url for peer discovery") mflag.StringVar(&token, []string{"-token"}, "", "token for peer discovery") mflag.StringVar(&advertiseAddress, []string{"-advertise-address"}, "", "address to advertise for peer discovery") mflag.BoolVar(&pluginConfig.Enable, []string{"-plugin"}, false, "enable Docker plugin (v1)") mflag.BoolVar(&pluginConfig.EnableV2, []string{"-plugin-v2"}, false, "enable Docker plugin (v2)") mflag.BoolVar(&pluginConfig.EnableV2Multicast, []string{"-plugin-v2-multicast"}, false, "enable multicast for Docker plugin (v2)") mflag.StringVar(&pluginConfig.Socket, []string{"-plugin-socket"}, "/run/docker/plugins/weave.sock", "plugin socket on which to listen") mflag.StringVar(&pluginConfig.MeshSocket, []string{"-plugin-mesh-socket"}, "/run/docker/plugins/weavemesh.sock", "plugin socket on which to listen in mesh mode") proxyConfig := newProxyConfig() // 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() if justVersion { fmt.Printf("weave %s\n", version) os.Exit(0) } name := peerName(routerName, bridgeConfig.WeaveBridgeName, dbPrefix, hostRoot) if justPeerName { fmt.Printf("%s\n", name) os.Exit(0) } proxyConfig.DockerHost = dockerAPI if bridgeConfig.AWSVPC { proxyConfig.NoMulticastRoute = true proxyConfig.KeepTXOn = true } peers = mflag.Args() if resume && len(peers) > 0 { Log.Fatalf("You must not specify an initial peer list in conjunction with --resume") } common.SetLogLevel(logLevel) Log.Println("Command line options:", options()) Log.Infoln("weave ", version) if prof != "" { defer profile.Start(profile.CPUProfile, profile.ProfilePath(prof), profile.NoShutdownHook).Stop() } 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) var waitReady common.WaitGroup var proxy *weaveproxy.Proxy var err error if proxyConfig.Enabled { proxyConfig.DNSListenAddress = dnsConfig.addressOnly() proxyConfig.DNSDomain = dnsConfig.Domain if noDNS { proxyConfig.WithoutDNS = true } // Start Weave Proxy: proxy, err = weaveproxy.NewProxy(*proxyConfig) if err != nil { Log.Fatalf("Could not start proxy: %s", err) } defer proxy.Stop() listeners := proxy.Listen() proxy.AttachExistingContainers() go proxy.Serve(listeners, waitReady.Add()) } if pktdebug { networkConfig.PacketLogging = packetLogging{} } else { networkConfig.PacketLogging = nopPacketLogging{} } if bridgeConfig.DockerBridgeName != "" { if setAddr, err := weavenet.EnforceAddrAssignType(bridgeConfig.DockerBridgeName); err != nil { Log.Errorf("While checking address assignment type of %s: %s", bridgeConfig.DockerBridgeName, err) } else if setAddr { Log.Warningf("Setting %s MAC (mitigate https://github.com/docker/docker/issues/14908)", bridgeConfig.DockerBridgeName) } } bridgeConfig.Mac = name.String() bridgeConfig.Port = config.Port bridgeType, err := weavenet.EnsureBridge(procPath, &bridgeConfig, Log) checkFatal(err) Log.Println("Bridge type is", bridgeType) config.Password = determinePassword(password) overlay, injectorConsumer := createOverlay(bridgeType, bridgeConfig, config.Host, config.Port, bufSzMB, config.Password != nil) networkConfig.InjectorConsumer = injectorConsumer if injectorConsumer != nil { if err := weavenet.DetectHairpin("vethwe-bridge", Log); err != nil { Log.Errorf("Setting may cause connectivity issues : %s", err) Log.Infof("Hairpin mode may have been enabled by other software on this machine") } } if nickName == "" { var err error nickName, err = os.Hostname() checkFatal(err) } config.TrustedSubnets = parseTrustedSubnets(trustedSubnetStr) config.PeerDiscovery = !noDiscovery if bridgeConfig.AWSVPC && len(config.Password) > 0 { Log.Fatalf("--awsvpc mode is not compatible with the --password option") } if bridgeConfig.AWSVPC && !ipamConfig.Enabled() { Log.Fatalf("--awsvpc mode requires IPAM enabled") } db, err := db.NewBoltDB(dbPrefix) checkFatal(err) defer db.Close() router, err := weave.NewNetworkRouter(config, networkConfig, bridgeConfig, name, nickName, overlay, db) checkFatal(err) Log.Println("Our name is", router.Ourself) if token != "" { var addresses []string if advertiseAddress == "" { localAddrs, err := weavenet.LocalAddresses() checkFatal(err) for _, addr := range localAddrs { addresses = append(addresses, addr.IP.String()) } } else { addresses = strings.Split(advertiseAddress, ",") } discoveredPeers, count, err := peerDiscoveryUpdate(discoveryEndpoint, token, name.String(), nickName, addresses) checkFatal(err) if !ipamConfig.HasMode() { ipamConfig.PeerCount = len(peers) + count } peers = append(peers, discoveredPeers...) } else if peers, err = router.InitialPeers(resume, peers); err != nil { Log.Fatal("Unable to get initial peer set: ", err) } var dockerCli *docker.Client dockerVersion := "none" 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 dockerVersion = dockerCli.DockerVersion() } checkForUpdates(dockerVersion, router) 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 defaultSubnet address.CIDR trackerName string ) if ipamConfig.Enabled() { var t tracker.LocalRangeTracker if bridgeConfig.AWSVPC { Log.Infoln("Creating AWSVPC LocalRangeTracker") t, err = tracker.NewAWSVPCTracker(bridgeConfig.WeaveBridgeName) if err != nil { Log.Fatalf("Cannot create AWSVPC LocalRangeTracker: %s", err) } trackerName = "awsvpc" } preClaims, err := findExistingAddresses(dockerCli, bridgeConfig.WeaveBridgeName) checkFatal(err) allocator, defaultSubnet = createAllocator(router, ipamConfig, preClaims, db, t, isKnownPeer) observeContainers(allocator) if dockerCli != nil { allContainerIDs, err := dockerCli.RunningContainerIDs() checkFatal(err) allocator.PruneOwned(allContainerIDs) } } var ( ns *nameserver.Nameserver dnsserver *nameserver.DNSServer ) if !noDNS { ns, dnsserver = createDNSServer(dnsConfig, router.Router, isKnownPeer) observeContainers(ns) ns.Start() defer ns.Stop() dnsserver.ActivateAndServe() if dockerCli != nil { populateDNS(ns, dockerCli, name, bridgeConfig.WeaveBridgeName) } defer dnsserver.Stop() } router.Start() if errors := router.InitiateConnections(peers, false); len(errors) > 0 { Log.Fatal(common.ErrorMessages(errors)) } checkFatal(router.CreateRestartSentinel()) pluginConfig.DNS = !noDNS pluginConfig.DefaultSubnet = defaultSubnet.String() plugin := plugin.NewPlugin(pluginConfig) // 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, trackerName, dockerCli) } if ns != nil { ns.HandleHTTP(muxRouter, dockerCli) } router.HandleHTTP(muxRouter) HandleHTTP(muxRouter, version, router, allocator, defaultSubnet, ns, dnsserver, proxy, plugin, &waitReady) HandleHTTPPeer(muxRouter, allocator, discoveryEndpoint, token, name.String()) muxRouter.Methods("GET").Path("/metrics").Handler(metricsHandler(router, allocator, ns, dnsserver)) if proxy != nil { muxRouter.Methods("GET").Path("/proxyaddrs").HandlerFunc(proxy.StatusHTTP) } http.Handle("/", common.LoggingHTTPHandler(muxRouter)) Log.Println("Listening for HTTP control messages on", httpAddr) go listenAndServeHTTP(httpAddr, nil) } if statusAddr != "" { muxRouter := mux.NewRouter() HandleHTTP(muxRouter, version, router, allocator, defaultSubnet, ns, dnsserver, proxy, plugin, &waitReady) muxRouter.Methods("GET").Path("/metrics").Handler(metricsHandler(router, allocator, ns, dnsserver)) statusMux := http.NewServeMux() statusMux.Handle("/", muxRouter) Log.Println("Listening for status+metrics requests on", statusAddr) go listenAndServeHTTP(statusAddr, statusMux) } if metricsAddr != "" { metricsMux := http.NewServeMux() metricsMux.Handle("/metrics", metricsHandler(router, allocator, ns, dnsserver)) Log.Println("Listening for metrics requests on", metricsAddr) go listenAndServeHTTP(metricsAddr, metricsMux) } if plugin != nil { go plugin.Start(httpAddr, dockerCli, waitReady.Add()) } if bridgeConfig.AWSVPC { // Run this on its own goroutine because the allocator can block // We remove the default route installed by the kernel, // because awsvpc has installed it as well go exposeForAWSVPC(allocator, defaultSubnet, bridgeConfig.WeaveBridgeName, waitReady.Add()) } signals.SignalHandlerLoop(common.Log, router) } func exposeForAWSVPC(alloc *ipam.Allocator, subnet address.CIDR, bridgeName string, ready func()) { addr, err := alloc.Allocate("weave:expose", subnet, false, func() bool { return false }) checkFatal(err) cidr := address.MakeCIDR(subnet, addr) err = weavenet.Expose(bridgeName, cidr.IPNet(), true, false) checkFatal(err) Log.Printf("Bridge %q exposed on address %v", bridgeName, cidr) ready() } func options() map[string]string { options := make(map[string]string) mflag.Visit(func(f *mflag.Flag) { value := f.Value.String() name := canonicalName(f) if name == "password" || name == "token" { value = "" } options[name] = value }) return options } func canonicalName(f *mflag.Flag) string { for _, n := range f.Names { if n[0] != '#' { return strings.TrimLeft(n, "#-") } } return "" } type packetLogging struct{} func (packetLogging) LogPacket(msg string, key weave.PacketKey) { Log.Println(msg, key.SrcMAC, "->", key.DstMAC) } func (packetLogging) LogForwardPacket(msg string, key weave.ForwardPacketKey) { Log.Println(msg, key.SrcPeer, key.SrcMAC, "->", key.DstPeer, key.DstMAC) } type nopPacketLogging struct{} func (nopPacketLogging) LogPacket(string, weave.PacketKey) { } func (nopPacketLogging) LogForwardPacket(string, weave.ForwardPacketKey) { } func newProxyConfig() *weaveproxy.Config { proxyConfig := weaveproxy.Config{ Image: getenvOrDefault("EXEC_IMAGE", "weaveworks/weaveexec"), DockerBridge: getenvOrDefault("DOCKER_BRIDGE", "docker0"), } mflag.BoolVar(&proxyConfig.Enabled, []string{"-proxy"}, false, "instruct Weave Net to start its Docker proxy") mflagext.ListVar(&proxyConfig.ListenAddrs, []string{"H"}, nil, "addresses on which to listen for Docker proxy") mflag.StringVar(&proxyConfig.HostnameFromLabel, []string{"-hostname-from-label"}, "", "Key of container label from which to obtain the container's hostname") mflag.StringVar(&proxyConfig.HostnameMatch, []string{"-hostname-match"}, "(.*)", "Regexp pattern to apply on container names (e.g. '^aws-[0-9]+-(.*)$')") mflag.StringVar(&proxyConfig.HostnameReplacement, []string{"-hostname-replacement"}, "$1", "Expression to generate hostnames based on matches from --hostname-match (e.g. 'my-app-$1')") mflag.BoolVar(&proxyConfig.RewriteInspect, []string{"-rewrite-inspect"}, false, "Rewrite 'inspect' calls to return the weave network settings (if attached)") mflag.BoolVar(&proxyConfig.NoDefaultIPAM, []string{"-no-default-ipalloc"}, false, "proxy: do not automatically allocate addresses for containers without a WEAVE_CIDR") mflag.BoolVar(&proxyConfig.NoRewriteHosts, []string{"-no-rewrite-hosts"}, false, "proxy: do not automatically rewrite /etc/hosts. Use if you need the docker IP to remain in /etc/hosts") mflag.StringVar(&proxyConfig.TLSConfig.CACert, []string{"-tlscacert"}, "", "Trust certs signed only by this CA") mflag.StringVar(&proxyConfig.TLSConfig.Cert, []string{"-tlscert"}, "", "Path to TLS certificate file") mflag.BoolVar(&proxyConfig.TLSConfig.Enabled, []string{"-tls"}, false, "Use TLS; implied by --tlsverify") mflag.StringVar(&proxyConfig.TLSConfig.Key, []string{"-tlskey"}, "", "Path to TLS key file") mflag.BoolVar(&proxyConfig.TLSConfig.Verify, []string{"-tlsverify"}, false, "Use TLS and verify the remote") mflag.BoolVar(&proxyConfig.WithoutDNS, []string{"-without-dns"}, false, "proxy: instruct created containers to never use weaveDNS as their nameserver") mflag.BoolVar(&proxyConfig.NoMulticastRoute, []string{"-no-multicast-route"}, false, "proxy: do not add a multicast route via the weave interface when attaching containers") return &proxyConfig } func createOverlay(bridgeType weavenet.Bridge, config weavenet.BridgeConfig, host string, port int, bufSzMB int, enableEncryption bool) (weave.NetworkOverlay, weave.InjectorConsumer) { overlay := weave.NewOverlaySwitch() var injectorConsumer weave.InjectorConsumer var ignoreSleeve bool switch { case config.AWSVPC: vpc := weave.NewAWSVPC() overlay.Add("awsvpc", vpc) injectorConsumer = weave.NullInjectorConsumer{} // Currently, we do not support any overlay with AWSVPC ignoreSleeve = true case bridgeType == nil: injectorConsumer = weave.NullInjectorConsumer{} case bridgeType.IsFastdp(): iface, err := weavenet.EnsureInterface(config.DatapathName) checkFatal(err) fastdp, err := weave.NewFastDatapath(iface, port, enableEncryption) checkFatal(err) injectorConsumer = fastdp.InjectorConsumer() overlay.Add("fastdp", fastdp.Overlay()) case !bridgeType.IsFastdp(): iface, err := weavenet.EnsureInterface(weavenet.PcapIfName) checkFatal(err) injectorConsumer, err = weave.NewPcap(iface, bufSzMB*1024*1024) // bufsz flag is in MB checkFatal(err) } if !ignoreSleeve { sleeve := weave.NewSleeveOverlay(host, port) overlay.Add("sleeve", sleeve) overlay.SetCompatOverlay(sleeve) } return overlay, injectorConsumer } func createAllocator(router *weave.NetworkRouter, config ipamConfig, preClaims []ipam.PreClaim, db db.DB, track tracker.LocalRangeTracker, isKnownPeer func(mesh.PeerName) bool) (*ipam.Allocator, address.CIDR) { ipRange, err := ipam.ParseCIDRSubnet(config.IPRangeCIDR) checkFatal(err) defaultSubnet := ipRange if config.IPSubnetCIDR != "" { defaultSubnet, err = ipam.ParseCIDRSubnet(config.IPSubnetCIDR) checkFatal(err) if !ipRange.Range().Overlaps(defaultSubnet.Range()) { Log.Fatalf("IP address allocation default subnet %s does not overlap with allocation range %s", defaultSubnet, ipRange) } } c := ipam.Config{ OurName: router.Ourself.Peer.Name, OurUID: router.Ourself.Peer.UID, OurNickname: router.Ourself.Peer.NickName, Seed: config.SeedPeerNames, Universe: ipRange, IsObserver: config.Observer, PreClaims: preClaims, Quorum: func() uint { return determineQuorum(config.PeerCount, router) }, Db: db, IsKnownPeer: isKnownPeer, Tracker: track, } allocator := ipam.NewAllocator(c) gossip, err := router.NewGossip("IPallocation", allocator) checkFatal(err) allocator.SetInterfaces(gossip) allocator.Start() router.Peers.OnGC(func(peer *mesh.Peer) { allocator.PeerGone(peer.Name) }) return allocator, defaultSubnet } func createDNSServer(config dnsConfig, router *mesh.Router, isKnownPeer func(mesh.PeerName) bool) (*nameserver.Nameserver, *nameserver.DNSServer) { ns := nameserver.New(router.Ourself.Peer.Name, config.Domain, isKnownPeer) router.Peers.OnGC(func(peer *mesh.Peer) { ns.PeerGone(peer.Name) }) gossip, err := router.NewGossip("nameserver", ns) checkFatal(err) ns.SetGossip(gossip) upstream := nameserver.NewUpstream(config.ResolvConf, config.addressOnly()) dnsserver, err := nameserver.NewDNSServer(ns, config.Domain, config.ListenAddress, upstream, uint32(config.TTL), config.ClientTimeout) if err != nil { Log.Fatal("Unable to start dns server: ", err) } Log.Println("Listening for DNS queries on", config.ListenAddress) return ns, dnsserver } // Pick a quorum size based on the number of peer addresses. func determineQuorum(initPeerCountFlag int, router *weave.NetworkRouter) uint { if initPeerCountFlag > 0 { return uint(initPeerCountFlag/2 + 1) } peers := router.ConnectionMaker.Targets(true) // Guess a suitable quorum size based on the list of peer // addresses. The peer list might or might not contain an // address for this peer, so the conservative assumption is // that it doesn't. The list might contain multiple addresses // that resolve to the same peer, in which case the quorum // might be larger than it needs to be. But the user can // specify it explicitly if that becomes a problem. clusterSize := uint(len(peers) + 1) quorum := clusterSize/2 + 1 Log.Println("Assuming quorum size of", quorum) return quorum } func determinePassword(password string) []byte { if password == "" { password = os.Getenv("WEAVE_PASSWORD") } if password == "" { Log.Println("Communication between peers is unencrypted.") return nil } Log.Println("Communication between peers via untrusted networks is encrypted.") return []byte(password) } func peerName(routerName, bridgeName, dbPrefix, hostRoot string) mesh.PeerName { if routerName == "" { iface, err := net.InterfaceByName(bridgeName) if err == nil { routerName = iface.HardwareAddr.String() } else { routerName, err = weavenet.GetSystemPeerName(dbPrefix, hostRoot) checkFatal(err) } } name, err := mesh.PeerNameFromUserInput(routerName) checkFatal(err) return name } func parseTrustedSubnets(trustedSubnetStr string) []*net.IPNet { trustedSubnets := []*net.IPNet{} if trustedSubnetStr == "" { return trustedSubnets } for _, subnetStr := range strings.Split(trustedSubnetStr, ",") { _, subnet, err := net.ParseCIDR(subnetStr) if err != nil { Log.Fatal("Unable to parse trusted subnets: ", err) } trustedSubnets = append(trustedSubnets, subnet) } return trustedSubnets } func parsePeerNames(s string) ([]mesh.PeerName, error) { peerNames := []mesh.PeerName{} if s == "" { return peerNames, nil } for _, peerNameStr := range strings.Split(s, ",") { peerName, err := mesh.PeerNameFromUserInput(peerNameStr) if err != nil { return nil, fmt.Errorf("error parsing peer names: %s", err) } peerNames = append(peerNames, peerName) } return peerNames, nil } func listenAndServeHTTP(httpAddr string, handler http.Handler) { protocol := "tcp" if strings.HasPrefix(httpAddr, "/") { os.Remove(httpAddr) // in case it's there from last time protocol = "unix" } l, err := net.Listen(protocol, httpAddr) if err != nil { Log.Fatal("Unable to create http listener socket: ", err) } err = http.Serve(l, handler) if err != nil { Log.Fatal("Unable to create http server", err) } } func checkFatal(e error) { if e != nil { Log.Fatal(e) } }