blob: f09f1c66aeba14c1252e20bd641b86b042dbc985 [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
Todd Wang8c4e5cc2015-04-09 11:30:52 -07007// Package roaming implements a profile suitable for a variety of network
8// configurations, including 1-1 NATs, dhcp auto-configuration, and Google
9// Compute Engine.
Suharsh Sivakumar3f0eaf92015-01-15 09:48:44 -080010//
11// The config.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"
18
Jiri Simsa6ac95222015-02-23 16:11:49 -080019 "v.io/v23"
20 "v.io/v23/config"
21 "v.io/v23/context"
Matt Rosencrantz94502cf2015-03-18 09:43:44 -070022 "v.io/v23/rpc"
Matt Rosencrantz9d3278a2015-03-11 14:58:34 -070023 "v.io/x/lib/netconfig"
24 "v.io/x/lib/netstate"
Todd Wangb3511492015-04-07 23:32:34 -070025 "v.io/x/lib/vlog"
Jiri Simsaffceefa2015-02-28 11:03:34 -080026 "v.io/x/ref/lib/flags"
Todd Wangb3511492015-04-07 23:32:34 -070027 "v.io/x/ref/lib/security/securityflag"
Jiri Simsaffceefa2015-02-28 11:03:34 -080028 "v.io/x/ref/profiles/internal"
Matt Rosencrantz86ba1a12015-03-09 13:19:02 -070029 "v.io/x/ref/profiles/internal/lib/appcycle"
30 "v.io/x/ref/profiles/internal/lib/websocket"
Matt Rosencrantz94502cf2015-03-18 09:43:44 -070031 _ "v.io/x/ref/profiles/internal/rpc/protocols/tcp"
32 _ "v.io/x/ref/profiles/internal/rpc/protocols/ws"
33 _ "v.io/x/ref/profiles/internal/rpc/protocols/wsh"
Todd Wangb3511492015-04-07 23:32:34 -070034 "v.io/x/ref/profiles/internal/rt"
Todd Wang1ea8f192015-04-03 17:31:51 -070035 "v.io/x/ref/services/debug/debuglib"
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -080036)
37
38const (
39 SettingsStreamName = "roaming"
40)
41
Suharsh Sivakumard68949c2015-01-26 10:32:23 -080042var commonFlags *flags.Flags
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -080043
44func init() {
Suharsh Sivakumarc6f38e92015-03-31 13:04:06 -070045 v23.RegisterProfile(Init)
Matt Rosencrantz94502cf2015-03-18 09:43:44 -070046 rpc.RegisterUnknownProtocol("wsh", websocket.HybridDial, websocket.HybridListener)
Suharsh Sivakumard68949c2015-01-26 10:32:23 -080047 commonFlags = flags.CreateAndRegister(flag.CommandLine, flags.Runtime, flags.Listen)
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -080048}
49
Jiri Simsa6ac95222015-02-23 16:11:49 -080050func Init(ctx *context.T) (v23.Runtime, *context.T, v23.Shutdown, error) {
Suharsh Sivakumard68949c2015-01-26 10:32:23 -080051 if err := internal.ParseFlags(commonFlags); err != nil {
Matt Rosencrantz1b793912015-01-23 13:32:53 -080052 return nil, nil, nil, err
53 }
Suharsh Sivakumard68949c2015-01-26 10:32:23 -080054
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -080055 lf := commonFlags.ListenFlags()
Matt Rosencrantz94502cf2015-03-18 09:43:44 -070056 listenSpec := rpc.ListenSpec{
57 Addrs: rpc.ListenAddrs(lf.Addrs),
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -080058 Proxy: lf.ListenProxy,
59 }
Todd Wangb3511492015-04-07 23:32:34 -070060 reservedDispatcher := debuglib.NewDispatcher(vlog.Log.LogDir, securityflag.NewAuthorizerOrDie())
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -080061
Suharsh Sivakumareb0e2962015-01-15 11:24:11 -080062 ac := appcycle.New()
Suharsh Sivakumareb0e2962015-01-15 11:24:11 -080063
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -080064 // Our address is private, so we test for running on GCE and for its
65 // 1:1 NAT configuration.
Suharsh Sivakumard68949c2015-01-26 10:32:23 -080066 if !internal.HasPublicIP(vlog.Log) {
67 if addr := internal.GCEPublicAddress(vlog.Log); addr != nil {
Matt Rosencrantz94502cf2015-03-18 09:43:44 -070068 listenSpec.AddressChooser = func(string, []rpc.Address) ([]rpc.Address, error) {
69 return []rpc.Address{&netstate.AddrIfc{addr, "nat", nil}}, nil
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -080070 }
Todd Wangb3511492015-04-07 23:32:34 -070071 runtime, ctx, shutdown, err := rt.Init(ctx, ac, nil, &listenSpec, commonFlags.RuntimeFlags(), reservedDispatcher)
Suharsh Sivakumard5049b72015-01-21 14:11:35 -080072 if err != nil {
73 return nil, nil, shutdown, err
74 }
75 profileShutdown := func() {
76 ac.Shutdown()
77 shutdown()
78 }
79 return runtime, ctx, profileShutdown, nil
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -080080 }
81 }
82
Suharsh Sivakumard5049b72015-01-21 14:11:35 -080083 publisher := config.NewPublisher()
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -080084
85 // Create stream in Init function to avoid a race between any
86 // goroutines started here and consumers started after Init returns.
87 ch := make(chan config.Setting)
88 stop, err := publisher.CreateStream(SettingsStreamName, SettingsStreamName, ch)
89 if err != nil {
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -080090 ac.Shutdown()
Suharsh Sivakumard5049b72015-01-21 14:11:35 -080091 return nil, nil, nil, err
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -080092 }
93
94 prev, err := netstate.GetAccessibleIPs()
95 if err != nil {
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -080096 ac.Shutdown()
Suharsh Sivakumard5049b72015-01-21 14:11:35 -080097 return nil, nil, nil, err
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -080098 }
99
100 // Start the dhcp watcher.
101 watcher, err := netconfig.NewNetConfigWatcher()
102 if err != nil {
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -0800103 ac.Shutdown()
Suharsh Sivakumard5049b72015-01-21 14:11:35 -0800104 return nil, nil, nil, err
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -0800105 }
106
107 cleanupCh := make(chan struct{})
108 watcherCh := make(chan struct{})
109
Matt Rosencrantz99cc06e2015-01-16 10:25:11 -0800110 listenSpec.StreamPublisher = publisher
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -0800111 listenSpec.StreamName = SettingsStreamName
112 listenSpec.AddressChooser = internal.IPAddressChooser
113
Todd Wangb3511492015-04-07 23:32:34 -0700114 runtime, ctx, shutdown, err := rt.Init(ctx, ac, nil, &listenSpec, commonFlags.RuntimeFlags(), reservedDispatcher)
Suharsh Sivakumard5049b72015-01-21 14:11:35 -0800115 if err != nil {
116 return nil, nil, shutdown, err
117 }
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -0800118
Matt Rosencrantz99cc06e2015-01-16 10:25:11 -0800119 go monitorNetworkSettingsX(runtime, ctx, watcher, prev, stop, cleanupCh, watcherCh, ch)
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -0800120 profileShutdown := func() {
121 close(cleanupCh)
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -0800122 ac.Shutdown()
Suharsh Sivakumard5049b72015-01-21 14:11:35 -0800123 shutdown()
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -0800124 <-watcherCh
125 }
126 return runtime, ctx, profileShutdown, nil
127}
128
129// monitorNetworkSettings will monitor network configuration changes and
130// publish subsequent Settings to reflect any changes detected.
Matt Rosencrantz99cc06e2015-01-16 10:25:11 -0800131func monitorNetworkSettingsX(
Todd Wangb3511492015-04-07 23:32:34 -0700132 runtime *rt.Runtime,
Matt Rosencrantz99cc06e2015-01-16 10:25:11 -0800133 ctx *context.T,
134 watcher netconfig.NetConfigWatcher,
135 prev netstate.AddrList,
136 pubStop, cleanup <-chan struct{},
137 watcherLoop chan<- struct{},
138 ch chan<- config.Setting) {
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -0800139 defer close(ch)
140
Matt Rosencrantz99cc06e2015-01-16 10:25:11 -0800141 listenSpec := runtime.GetListenSpec(ctx)
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -0800142
143 // TODO(cnicolaou): add support for listening on multiple network addresses.
144
145done:
146 for {
147 select {
148 case <-watcher.Channel():
149 cur, err := netstate.GetAccessibleIPs()
150 if err != nil {
Matt Rosencrantz97d67a92015-01-27 21:03:12 -0800151 vlog.Errorf("failed to read network state: %s", err)
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -0800152 continue
153 }
154 removed := netstate.FindRemoved(prev, cur)
155 added := netstate.FindAdded(prev, cur)
Matt Rosencrantz97d67a92015-01-27 21:03:12 -0800156 vlog.VI(2).Infof("Previous: %d: %s", len(prev), prev)
157 vlog.VI(2).Infof("Current : %d: %s", len(cur), cur)
158 vlog.VI(2).Infof("Added : %d: %s", len(added), added)
159 vlog.VI(2).Infof("Removed : %d: %s", len(removed), removed)
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -0800160 if len(removed) == 0 && len(added) == 0 {
Matt Rosencrantz97d67a92015-01-27 21:03:12 -0800161 vlog.VI(2).Infof("Network event that lead to no address changes since our last 'baseline'")
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -0800162 continue
163 }
164 if len(removed) > 0 {
Matt Rosencrantz97d67a92015-01-27 21:03:12 -0800165 vlog.VI(2).Infof("Sending removed: %s", removed)
Matt Rosencrantz94502cf2015-03-18 09:43:44 -0700166 ch <- rpc.NewRmAddrsSetting(removed)
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -0800167 }
168 // We will always send the best currently available address
169 if chosen, err := listenSpec.AddressChooser(listenSpec.Addrs[0].Protocol, cur); err == nil && chosen != nil {
Cosmos Nicolaou1b3594d2015-02-01 10:05:03 -0800170 vlog.VI(2).Infof("Sending added and chosen: %s", chosen)
Matt Rosencrantz94502cf2015-03-18 09:43:44 -0700171 ch <- rpc.NewAddAddrsSetting(chosen)
Cosmos Nicolaou1b3594d2015-02-01 10:05:03 -0800172 } else {
173 vlog.VI(2).Infof("Ignoring added %s", added)
Suharsh Sivakumar98aa48c2015-01-14 16:11:30 -0800174 }
175 prev = cur
176 case <-cleanup:
177 break done
178 case <-pubStop:
179 goto done
180 }
181 }
182 watcher.Stop()
183 close(watcherLoop)
184}