blob: ee9453a8bc3cccb8e449f44c0833c6d52c166525 [file] [log] [blame]
Jiri Simsad7616c92015-03-24 23:44:30 -07001// Copyright 2015 The Vanadium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
Suharsh Sivakumar3f0eaf92015-01-15 09:48:44 -08005// +build linux darwin
6
Suharsh Sivakumar40e52e92015-05-11 15:37:00 -07007// Package roaming implements a RuntimeFactory suitable for a variety of network
Todd Wang8c4e5cc2015-04-09 11:30:52 -07008// configurations, including 1-1 NATs, dhcp auto-configuration, and Google
9// Compute Engine.
Suharsh Sivakumar3f0eaf92015-01-15 09:48:44 -080010//
Cosmos Nicolaou11c0ca12015-04-23 16:23:43 -070011// The pubsub.Publisher mechanism is used for communicating networking
Matt Rosencrantz94502cf2015-03-18 09:43:44 -070012// settings to the rpc.Server implementation of the runtime and publishes
Suharsh Sivakumar3f0eaf92015-01-15 09:48:44 -080013// the Settings it expects.
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -080014package roaming
15
16import (
17 "flag"
Cosmos Nicolaouaa87e292015-04-21 22:15:50 -070018 "net"
19
20 "v.io/x/lib/netconfig"
21 "v.io/x/lib/netstate"
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -080022
Jiri Simsa6ac95222015-02-23 16:11:49 -080023 "v.io/v23"
Jiri Simsa6ac95222015-02-23 16:11:49 -080024 "v.io/v23/context"
Matt Rosencrantzc1490fe2015-10-06 14:02:32 -070025 "v.io/v23/flow"
Matt Rosencrantz94502cf2015-03-18 09:43:44 -070026 "v.io/v23/rpc"
Cosmos Nicolaouaa87e292015-04-21 22:15:50 -070027
Cosmos Nicolaou0e4e3922015-06-10 16:30:09 -070028 "v.io/x/ref/internal/logger"
Jungho Ahn1eb602f2015-10-08 14:54:30 -070029 dfactory "v.io/x/ref/lib/discovery/factory"
Jiri Simsaffceefa2015-02-28 11:03:34 -080030 "v.io/x/ref/lib/flags"
Jiri Simsa574ec4b2015-08-11 09:31:37 -070031 "v.io/x/ref/lib/pubsub"
Todd Wangb3511492015-04-07 23:32:34 -070032 "v.io/x/ref/lib/security/securityflag"
Suharsh Sivakumardcc11d72015-05-11 12:19:20 -070033 "v.io/x/ref/runtime/internal"
Suharsh Sivakumar0219ebb2015-08-27 15:04:00 -070034 _ "v.io/x/ref/runtime/internal/flow/protocols/tcp"
Suharsh Sivakumar42baa8f2015-08-27 18:05:46 -070035 _ "v.io/x/ref/runtime/internal/flow/protocols/ws"
36 _ "v.io/x/ref/runtime/internal/flow/protocols/wsh"
Suharsh Sivakumardcc11d72015-05-11 12:19:20 -070037 "v.io/x/ref/runtime/internal/lib/appcycle"
38 "v.io/x/ref/runtime/internal/lib/websocket"
Matt Rosencrantzc1490fe2015-10-06 14:02:32 -070039 "v.io/x/ref/runtime/internal/lib/xwebsocket"
Suharsh Sivakumardcc11d72015-05-11 12:19:20 -070040 irpc "v.io/x/ref/runtime/internal/rpc"
Suharsh Sivakumar0219ebb2015-08-27 15:04:00 -070041 "v.io/x/ref/runtime/internal/rt"
42 "v.io/x/ref/services/debug/debuglib"
43
44 // TODO(suharshs): Remove these once we switch to the flow protocols.
Suharsh Sivakumardcc11d72015-05-11 12:19:20 -070045 _ "v.io/x/ref/runtime/internal/rpc/protocols/tcp"
46 _ "v.io/x/ref/runtime/internal/rpc/protocols/ws"
47 _ "v.io/x/ref/runtime/internal/rpc/protocols/wsh"
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -080048)
49
50const (
51 SettingsStreamName = "roaming"
Suharsh Sivakumar40e52e92015-05-11 15:37:00 -070052 SettingsStreamDesc = "pubsub stream used by the roaming RuntimeFactory"
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -080053)
54
Suharsh Sivakumard68949c2015-01-26 10:32:23 -080055var commonFlags *flags.Flags
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -080056
57func init() {
Suharsh Sivakumar40e52e92015-05-11 15:37:00 -070058 v23.RegisterRuntimeFactory(Init)
Suharsh Sivakumar7e93ce52015-05-07 17:46:13 -070059 rpc.RegisterUnknownProtocol("wsh", websocket.HybridDial, websocket.HybridResolve, websocket.HybridListener)
Matt Rosencrantzc1490fe2015-10-06 14:02:32 -070060 flow.RegisterUnknownProtocol("wsh", xwebsocket.WSH{})
Suharsh Sivakumard68949c2015-01-26 10:32:23 -080061 commonFlags = flags.CreateAndRegister(flag.CommandLine, flags.Runtime, flags.Listen)
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -080062}
63
Jiri Simsa6ac95222015-02-23 16:11:49 -080064func Init(ctx *context.T) (v23.Runtime, *context.T, v23.Shutdown, error) {
Nicolas Lacasse7c45dd32015-08-28 11:09:24 -070065 if err := internal.ParseFlagsAndConfigureGlobalLogger(commonFlags); err != nil {
Matt Rosencrantz1b793912015-01-23 13:32:53 -080066 return nil, nil, nil, err
67 }
Suharsh Sivakumard68949c2015-01-26 10:32:23 -080068
Jungho Ahn1eb602f2015-10-08 14:54:30 -070069 ac := appcycle.New()
70 discovery, err := dfactory.New()
71 if err != nil {
72 ac.Shutdown()
73 return nil, nil, nil, err
74 }
75
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -080076 lf := commonFlags.ListenFlags()
Matt Rosencrantz94502cf2015-03-18 09:43:44 -070077 listenSpec := rpc.ListenSpec{
78 Addrs: rpc.ListenAddrs(lf.Addrs),
Todd Wange4d8d6c2015-10-01 14:45:25 -070079 Proxy: lf.Proxy,
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -080080 }
Cosmos Nicolaou64d573d2015-07-13 16:22:18 -070081 reservedDispatcher := debuglib.NewDispatcher(securityflag.NewAuthorizerOrDie())
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -080082
Jungho Ahn1eb602f2015-10-08 14:54:30 -070083 ishutdown := func() {
84 ac.Shutdown()
85 discovery.Close()
86 }
Suharsh Sivakumareb0e2962015-01-15 11:24:11 -080087
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -080088 // Our address is private, so we test for running on GCE and for its
89 // 1:1 NAT configuration.
Cosmos Nicolaou0e4e3922015-06-10 16:30:09 -070090 if !internal.HasPublicIP(logger.Global()) {
91 if addr := internal.GCEPublicAddress(logger.Global()); addr != nil {
James Ring318c3fa2015-06-17 11:27:23 -070092 listenSpec.AddressChooser = netstate.AddressChooserFunc(func(string, []net.Addr) ([]net.Addr, error) {
Cosmos Nicolaouaa87e292015-04-21 22:15:50 -070093 // TODO(cnicolaou): the protocol at least should
Suharsh Sivakumar40e52e92015-05-11 15:37:00 -070094 // be configurable, or maybe there's a RuntimeFactory specific
Cosmos Nicolaouaa87e292015-04-21 22:15:50 -070095 // flag to configure both the protocol and address.
96 return []net.Addr{netstate.NewNetAddr("wsh", addr.String())}, nil
James Ring318c3fa2015-06-17 11:27:23 -070097 })
Jungho Ahn1eb602f2015-10-08 14:54:30 -070098 runtime, ctx, shutdown, err := rt.Init(ctx, ac, discovery, nil, &listenSpec, nil, "", commonFlags.RuntimeFlags(), reservedDispatcher)
Suharsh Sivakumard5049b72015-01-21 14:11:35 -080099 if err != nil {
Jungho Ahn1eb602f2015-10-08 14:54:30 -0700100 ishutdown()
101 return nil, nil, nil, err
Suharsh Sivakumard5049b72015-01-21 14:11:35 -0800102 }
Suharsh Sivakumar40e52e92015-05-11 15:37:00 -0700103 runtimeFactoryShutdown := func() {
Jungho Ahn1eb602f2015-10-08 14:54:30 -0700104 ishutdown()
Suharsh Sivakumard5049b72015-01-21 14:11:35 -0800105 shutdown()
106 }
Suharsh Sivakumar40e52e92015-05-11 15:37:00 -0700107 return runtime, ctx, runtimeFactoryShutdown, nil
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -0800108 }
109 }
110
Cosmos Nicolaou11c0ca12015-04-23 16:23:43 -0700111 publisher := pubsub.NewPublisher()
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -0800112
113 // Create stream in Init function to avoid a race between any
114 // goroutines started here and consumers started after Init returns.
Cosmos Nicolaou11c0ca12015-04-23 16:23:43 -0700115 ch := make(chan pubsub.Setting)
Suharsh Sivakumar40e52e92015-05-11 15:37:00 -0700116 // TODO(cnicolaou): use stop to shutdown this stream when the RuntimeFactory shutdowns.
Cosmos Nicolaou00fe9a42015-04-24 14:18:01 -0700117 stop, err := publisher.CreateStream(SettingsStreamName, SettingsStreamDesc, ch)
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -0800118 if err != nil {
Jungho Ahn1eb602f2015-10-08 14:54:30 -0700119 ishutdown()
Suharsh Sivakumard5049b72015-01-21 14:11:35 -0800120 return nil, nil, nil, err
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -0800121 }
122
123 prev, err := netstate.GetAccessibleIPs()
124 if err != nil {
Jungho Ahn1eb602f2015-10-08 14:54:30 -0700125 ishutdown()
Suharsh Sivakumard5049b72015-01-21 14:11:35 -0800126 return nil, nil, nil, err
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -0800127 }
128
129 // Start the dhcp watcher.
130 watcher, err := netconfig.NewNetConfigWatcher()
131 if err != nil {
Jungho Ahn1eb602f2015-10-08 14:54:30 -0700132 ishutdown()
Suharsh Sivakumard5049b72015-01-21 14:11:35 -0800133 return nil, nil, nil, err
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -0800134 }
135
136 cleanupCh := make(chan struct{})
137 watcherCh := make(chan struct{})
138
James Ring318c3fa2015-06-17 11:27:23 -0700139 listenSpec.AddressChooser = internal.IPAddressChooser{}
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -0800140
Jungho Ahn1eb602f2015-10-08 14:54:30 -0700141 runtime, ctx, shutdown, err := rt.Init(ctx, ac, discovery, nil, &listenSpec, publisher, SettingsStreamName, commonFlags.RuntimeFlags(), reservedDispatcher)
Suharsh Sivakumard5049b72015-01-21 14:11:35 -0800142 if err != nil {
Jungho Ahn1eb602f2015-10-08 14:54:30 -0700143 ishutdown()
144 return nil, nil, nil, err
Suharsh Sivakumard5049b72015-01-21 14:11:35 -0800145 }
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -0800146
Matt Rosencrantz99cc06e2015-01-16 10:25:11 -0800147 go monitorNetworkSettingsX(runtime, ctx, watcher, prev, stop, cleanupCh, watcherCh, ch)
Suharsh Sivakumar40e52e92015-05-11 15:37:00 -0700148 runtimeFactoryShutdown := func() {
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -0800149 close(cleanupCh)
Jungho Ahn1eb602f2015-10-08 14:54:30 -0700150 ishutdown()
Suharsh Sivakumard5049b72015-01-21 14:11:35 -0800151 shutdown()
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -0800152 <-watcherCh
153 }
Suharsh Sivakumar40e52e92015-05-11 15:37:00 -0700154 return runtime, ctx, runtimeFactoryShutdown, nil
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -0800155}
156
157// monitorNetworkSettings will monitor network configuration changes and
158// publish subsequent Settings to reflect any changes detected.
Matt Rosencrantz99cc06e2015-01-16 10:25:11 -0800159func monitorNetworkSettingsX(
Todd Wangb3511492015-04-07 23:32:34 -0700160 runtime *rt.Runtime,
Matt Rosencrantz99cc06e2015-01-16 10:25:11 -0800161 ctx *context.T,
162 watcher netconfig.NetConfigWatcher,
163 prev netstate.AddrList,
164 pubStop, cleanup <-chan struct{},
165 watcherLoop chan<- struct{},
Cosmos Nicolaou11c0ca12015-04-23 16:23:43 -0700166 ch chan<- pubsub.Setting) {
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -0800167 defer close(ch)
168
Matt Rosencrantz99cc06e2015-01-16 10:25:11 -0800169 listenSpec := runtime.GetListenSpec(ctx)
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -0800170
171 // TODO(cnicolaou): add support for listening on multiple network addresses.
172
173done:
174 for {
175 select {
176 case <-watcher.Channel():
Cosmos Nicolaouaa87e292015-04-21 22:15:50 -0700177 netstate.InvalidateCache()
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -0800178 cur, err := netstate.GetAccessibleIPs()
179 if err != nil {
Cosmos Nicolaoue9c622d2015-07-10 11:09:42 -0700180 ctx.Errorf("failed to read network state: %s", err)
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -0800181 continue
182 }
183 removed := netstate.FindRemoved(prev, cur)
184 added := netstate.FindAdded(prev, cur)
Cosmos Nicolaoue9c622d2015-07-10 11:09:42 -0700185 ctx.VI(2).Infof("Previous: %d: %s", len(prev), prev)
186 ctx.VI(2).Infof("Current : %d: %s", len(cur), cur)
187 ctx.VI(2).Infof("Added : %d: %s", len(added), added)
188 ctx.VI(2).Infof("Removed : %d: %s", len(removed), removed)
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -0800189 if len(removed) == 0 && len(added) == 0 {
Cosmos Nicolaoue9c622d2015-07-10 11:09:42 -0700190 ctx.VI(2).Infof("Network event that lead to no address changes since our last 'baseline'")
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -0800191 continue
192 }
193 if len(removed) > 0 {
Cosmos Nicolaoue9c622d2015-07-10 11:09:42 -0700194 ctx.VI(2).Infof("Sending removed: %s", removed)
Cosmos Nicolaou00fe9a42015-04-24 14:18:01 -0700195 ch <- irpc.NewRmAddrsSetting(removed.AsNetAddrs())
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -0800196 }
197 // We will always send the best currently available address
Jungho Ahn00720b82015-10-12 15:33:31 -0700198 if chosen, err := listenSpec.AddressChooser.ChooseAddresses(listenSpec.Addrs[0].Protocol, cur.AsNetAddrs()); err == nil && chosen != nil {
Cosmos Nicolaoue9c622d2015-07-10 11:09:42 -0700199 ctx.VI(2).Infof("Sending added and chosen: %s", chosen)
Jungho Ahn00720b82015-10-12 15:33:31 -0700200 ch <- irpc.NewNewAddrsSetting(chosen)
Cosmos Nicolaou1b3594d2015-02-01 10:05:03 -0800201 } else {
Cosmos Nicolaoue9c622d2015-07-10 11:09:42 -0700202 ctx.VI(2).Infof("Ignoring added %s", added)
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -0800203 }
204 prev = cur
205 case <-cleanup:
206 break done
207 case <-pubStop:
208 goto done
209 }
210 }
211 watcher.Stop()
212 close(watcherLoop)
213}