blob: b117402115172bc07354ca7f60cd1555dac193e6 [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
7// Package roaming provides a network-aware Profile that provides appropriate
8// options and configuration for a variety of network configurations, including
9// being behind 1-1 NATs, using dhcp and auto-configuration for being on
10// Google Compute Engine.
11//
12// The config.Publisher mechanism is used for communicating networking
Matt Rosencrantz94502cf2015-03-18 09:43:44 -070013// settings to the rpc.Server implementation of the runtime and publishes
Suharsh Sivakumar3f0eaf92015-01-15 09:48:44 -080014// the Settings it expects.
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -080015package roaming
16
17import (
18 "flag"
19
Jiri Simsa6ac95222015-02-23 16:11:49 -080020 "v.io/v23"
21 "v.io/v23/config"
22 "v.io/v23/context"
Matt Rosencrantz94502cf2015-03-18 09:43:44 -070023 "v.io/v23/rpc"
Jiri Simsa337af232015-02-27 14:36:46 -080024 "v.io/x/lib/vlog"
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -080025
Matt Rosencrantz9d3278a2015-03-11 14:58:34 -070026 "v.io/x/lib/netconfig"
27 "v.io/x/lib/netstate"
Jiri Simsaffceefa2015-02-28 11:03:34 -080028 "v.io/x/ref/lib/flags"
Jiri Simsaffceefa2015-02-28 11:03:34 -080029 "v.io/x/ref/profiles/internal"
Matt Rosencrantz86ba1a12015-03-09 13:19:02 -070030 "v.io/x/ref/profiles/internal/lib/appcycle"
31 "v.io/x/ref/profiles/internal/lib/websocket"
Matt Rosencrantz94502cf2015-03-18 09:43:44 -070032 _ "v.io/x/ref/profiles/internal/rpc/protocols/tcp"
33 _ "v.io/x/ref/profiles/internal/rpc/protocols/ws"
34 _ "v.io/x/ref/profiles/internal/rpc/protocols/wsh"
Matt Rosencrantzdbc1be22015-02-28 15:15:49 -080035 grt "v.io/x/ref/profiles/internal/rt"
Jiri Simsaffceefa2015-02-28 11:03:34 -080036 "v.io/x/ref/services/mgmt/debug"
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -080037
38 // TODO(cnicolaou,ashankar): move this into flags.
Jiri Simsaffceefa2015-02-28 11:03:34 -080039 sflag "v.io/x/ref/security/flag"
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -080040)
41
42const (
43 SettingsStreamName = "roaming"
44)
45
Suharsh Sivakumard68949c2015-01-26 10:32:23 -080046var commonFlags *flags.Flags
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -080047
48func init() {
Suharsh Sivakumarc6f38e92015-03-31 13:04:06 -070049 v23.RegisterProfile(Init)
Matt Rosencrantz94502cf2015-03-18 09:43:44 -070050 rpc.RegisterUnknownProtocol("wsh", websocket.HybridDial, websocket.HybridListener)
Suharsh Sivakumard68949c2015-01-26 10:32:23 -080051 commonFlags = flags.CreateAndRegister(flag.CommandLine, flags.Runtime, flags.Listen)
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -080052}
53
Jiri Simsa6ac95222015-02-23 16:11:49 -080054func Init(ctx *context.T) (v23.Runtime, *context.T, v23.Shutdown, error) {
Suharsh Sivakumard68949c2015-01-26 10:32:23 -080055 if err := internal.ParseFlags(commonFlags); err != nil {
Matt Rosencrantz1b793912015-01-23 13:32:53 -080056 return nil, nil, nil, err
57 }
Suharsh Sivakumard68949c2015-01-26 10:32:23 -080058
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -080059 lf := commonFlags.ListenFlags()
Matt Rosencrantz94502cf2015-03-18 09:43:44 -070060 listenSpec := rpc.ListenSpec{
61 Addrs: rpc.ListenAddrs(lf.Addrs),
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -080062 Proxy: lf.ListenProxy,
63 }
Robin Thellend67d95cb2015-02-13 11:02:14 -080064 reservedDispatcher := debug.NewDispatcher(vlog.Log.LogDir, sflag.NewAuthorizerOrDie())
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -080065
Suharsh Sivakumareb0e2962015-01-15 11:24:11 -080066 ac := appcycle.New()
Suharsh Sivakumareb0e2962015-01-15 11:24:11 -080067
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -080068 // Our address is private, so we test for running on GCE and for its
69 // 1:1 NAT configuration.
Suharsh Sivakumard68949c2015-01-26 10:32:23 -080070 if !internal.HasPublicIP(vlog.Log) {
71 if addr := internal.GCEPublicAddress(vlog.Log); addr != nil {
Matt Rosencrantz94502cf2015-03-18 09:43:44 -070072 listenSpec.AddressChooser = func(string, []rpc.Address) ([]rpc.Address, error) {
73 return []rpc.Address{&netstate.AddrIfc{addr, "nat", nil}}, nil
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -080074 }
Suharsh Sivakumard68949c2015-01-26 10:32:23 -080075 runtime, ctx, shutdown, err := grt.Init(ctx, ac, nil, &listenSpec, commonFlags.RuntimeFlags(), reservedDispatcher)
Suharsh Sivakumard5049b72015-01-21 14:11:35 -080076 if err != nil {
77 return nil, nil, shutdown, err
78 }
79 profileShutdown := func() {
80 ac.Shutdown()
81 shutdown()
82 }
83 return runtime, ctx, profileShutdown, nil
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -080084 }
85 }
86
Suharsh Sivakumard5049b72015-01-21 14:11:35 -080087 publisher := config.NewPublisher()
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -080088
89 // Create stream in Init function to avoid a race between any
90 // goroutines started here and consumers started after Init returns.
91 ch := make(chan config.Setting)
92 stop, err := publisher.CreateStream(SettingsStreamName, SettingsStreamName, ch)
93 if err != nil {
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -080094 ac.Shutdown()
Suharsh Sivakumard5049b72015-01-21 14:11:35 -080095 return nil, nil, nil, err
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -080096 }
97
98 prev, err := netstate.GetAccessibleIPs()
99 if err != nil {
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -0800100 ac.Shutdown()
Suharsh Sivakumard5049b72015-01-21 14:11:35 -0800101 return nil, nil, nil, err
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -0800102 }
103
104 // Start the dhcp watcher.
105 watcher, err := netconfig.NewNetConfigWatcher()
106 if err != nil {
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -0800107 ac.Shutdown()
Suharsh Sivakumard5049b72015-01-21 14:11:35 -0800108 return nil, nil, nil, err
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -0800109 }
110
111 cleanupCh := make(chan struct{})
112 watcherCh := make(chan struct{})
113
Matt Rosencrantz99cc06e2015-01-16 10:25:11 -0800114 listenSpec.StreamPublisher = publisher
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -0800115 listenSpec.StreamName = SettingsStreamName
116 listenSpec.AddressChooser = internal.IPAddressChooser
117
Suharsh Sivakumard68949c2015-01-26 10:32:23 -0800118 runtime, ctx, shutdown, err := grt.Init(ctx, ac, nil, &listenSpec, commonFlags.RuntimeFlags(), reservedDispatcher)
Suharsh Sivakumard5049b72015-01-21 14:11:35 -0800119 if err != nil {
120 return nil, nil, shutdown, err
121 }
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -0800122
Matt Rosencrantz99cc06e2015-01-16 10:25:11 -0800123 go monitorNetworkSettingsX(runtime, ctx, watcher, prev, stop, cleanupCh, watcherCh, ch)
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -0800124 profileShutdown := func() {
125 close(cleanupCh)
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -0800126 ac.Shutdown()
Suharsh Sivakumard5049b72015-01-21 14:11:35 -0800127 shutdown()
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -0800128 <-watcherCh
129 }
130 return runtime, ctx, profileShutdown, nil
131}
132
133// monitorNetworkSettings will monitor network configuration changes and
134// publish subsequent Settings to reflect any changes detected.
Matt Rosencrantz99cc06e2015-01-16 10:25:11 -0800135func monitorNetworkSettingsX(
Matt Rosencrantzba470a52015-01-26 13:36:13 -0800136 runtime *grt.Runtime,
Matt Rosencrantz99cc06e2015-01-16 10:25:11 -0800137 ctx *context.T,
138 watcher netconfig.NetConfigWatcher,
139 prev netstate.AddrList,
140 pubStop, cleanup <-chan struct{},
141 watcherLoop chan<- struct{},
142 ch chan<- config.Setting) {
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -0800143 defer close(ch)
144
Matt Rosencrantz99cc06e2015-01-16 10:25:11 -0800145 listenSpec := runtime.GetListenSpec(ctx)
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -0800146
147 // TODO(cnicolaou): add support for listening on multiple network addresses.
148
149done:
150 for {
151 select {
152 case <-watcher.Channel():
153 cur, err := netstate.GetAccessibleIPs()
154 if err != nil {
Matt Rosencrantz97d67a92015-01-27 21:03:12 -0800155 vlog.Errorf("failed to read network state: %s", err)
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -0800156 continue
157 }
158 removed := netstate.FindRemoved(prev, cur)
159 added := netstate.FindAdded(prev, cur)
Matt Rosencrantz97d67a92015-01-27 21:03:12 -0800160 vlog.VI(2).Infof("Previous: %d: %s", len(prev), prev)
161 vlog.VI(2).Infof("Current : %d: %s", len(cur), cur)
162 vlog.VI(2).Infof("Added : %d: %s", len(added), added)
163 vlog.VI(2).Infof("Removed : %d: %s", len(removed), removed)
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -0800164 if len(removed) == 0 && len(added) == 0 {
Matt Rosencrantz97d67a92015-01-27 21:03:12 -0800165 vlog.VI(2).Infof("Network event that lead to no address changes since our last 'baseline'")
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -0800166 continue
167 }
168 if len(removed) > 0 {
Matt Rosencrantz97d67a92015-01-27 21:03:12 -0800169 vlog.VI(2).Infof("Sending removed: %s", removed)
Matt Rosencrantz94502cf2015-03-18 09:43:44 -0700170 ch <- rpc.NewRmAddrsSetting(removed)
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -0800171 }
172 // We will always send the best currently available address
173 if chosen, err := listenSpec.AddressChooser(listenSpec.Addrs[0].Protocol, cur); err == nil && chosen != nil {
Cosmos Nicolaou1b3594d2015-02-01 10:05:03 -0800174 vlog.VI(2).Infof("Sending added and chosen: %s", chosen)
Matt Rosencrantz94502cf2015-03-18 09:43:44 -0700175 ch <- rpc.NewAddAddrsSetting(chosen)
Cosmos Nicolaou1b3594d2015-02-01 10:05:03 -0800176 } else {
177 vlog.VI(2).Infof("Ignoring added %s", added)
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -0800178 }
179 prev = cur
180 case <-cleanup:
181 break done
182 case <-pubStop:
183 goto done
184 }
185 }
186 watcher.Stop()
187 close(watcherLoop)
188}