blob: c772848024c8ef15b2c67e9bbb5e15bd1c68180f [file] [log] [blame]
Cosmos Nicolaouef323db2014-09-07 22:13:28 -07001// +build linux darwin
2
3// Package roaming provides a network-aware Profile that provides appropriate
4// options and configuration for a variety of network configurations, including
5// being behind 1-1 NATs, using dhcp and auto-configuration for being on
6// Google Compute Engine.
7//
8// The config.Publisher mechanism is used for communicating networking
9// settings to the ipc.Server implementation of the runtime and publishes
10// the Settings it expects.
11package roaming
12
13import (
14 "flag"
Cosmos Nicolaouef323db2014-09-07 22:13:28 -070015
Jiri Simsa519c5072014-09-17 21:37:57 -070016 "veyron.io/veyron/veyron2"
17 "veyron.io/veyron/veyron2/config"
18 "veyron.io/veyron/veyron2/ipc"
19 "veyron.io/veyron/veyron2/rt"
Cosmos Nicolaouef323db2014-09-07 22:13:28 -070020
Cosmos Nicolaou39e3ae52014-11-14 13:30:01 -080021 "veyron.io/veyron/veyron/lib/appcycle"
Jiri Simsa519c5072014-09-17 21:37:57 -070022 "veyron.io/veyron/veyron/lib/flags"
23 "veyron.io/veyron/veyron/lib/netconfig"
24 "veyron.io/veyron/veyron/lib/netstate"
Cosmos Nicolaou87c0a552014-12-02 23:05:49 -080025 _ "veyron.io/veyron/veyron/lib/tcp"
26 _ "veyron.io/veyron/veyron/lib/websocket"
Cosmos Nicolaou66bc1202014-09-30 20:42:43 -070027 "veyron.io/veyron/veyron/profiles/internal"
Cosmos Nicolaou87c0a552014-12-02 23:05:49 -080028 "veyron.io/veyron/veyron/profiles/internal/platform"
29 _ "veyron.io/veyron/veyron/runtimes/google/rt"
Cosmos Nicolaou8246a8b2014-11-01 09:32:36 -070030 "veyron.io/veyron/veyron/services/mgmt/debug"
Cosmos Nicolaou87c0a552014-12-02 23:05:49 -080031
Cosmos Nicolaou8246a8b2014-11-01 09:32:36 -070032 // TODO(cnicolaou,ashankar): move this into flags.
33 sflag "veyron.io/veyron/veyron/security/flag"
Cosmos Nicolaouef323db2014-09-07 22:13:28 -070034)
35
36const (
Cosmos Nicolaou778cb7e2014-09-10 15:07:43 -070037 SettingsStreamName = "roaming"
Cosmos Nicolaouef323db2014-09-07 22:13:28 -070038)
39
40var (
Cosmos Nicolaoubdc917c2014-10-24 12:41:47 -070041 commonFlags *flags.Flags
Cosmos Nicolaou9348da62014-10-03 14:21:19 -070042 // ListenSpec is an initialized instance of ipc.ListenSpec that can
43 // be used with ipc.Listen.
Cosmos Nicolaouf8d4c2b2014-10-23 22:36:38 -070044 ListenSpec ipc.ListenSpec
Cosmos Nicolaouef323db2014-09-07 22:13:28 -070045)
46
47func init() {
Cosmos Nicolaoud811b072014-10-28 17:46:27 -070048 commonFlags = flags.CreateAndRegister(flag.CommandLine, flags.Listen)
Cosmos Nicolaouef323db2014-09-07 22:13:28 -070049 rt.RegisterProfile(New())
50}
51
52type profile struct {
Cosmos Nicolaou39e3ae52014-11-14 13:30:01 -080053 gce string
54 ac *appcycle.AppCycle
55 cleanupCh, watcherCh chan struct{}
Cosmos Nicolaouef323db2014-09-07 22:13:28 -070056}
57
Cosmos Nicolaouef323db2014-09-07 22:13:28 -070058func New() veyron2.Profile {
Cosmos Nicolaou767b62d2014-09-19 13:58:40 -070059 return &profile{}
Cosmos Nicolaouef323db2014-09-07 22:13:28 -070060}
61
62func (p *profile) Platform() *veyron2.Platform {
Cosmos Nicolaou87c0a552014-12-02 23:05:49 -080063 pstr, _ := platform.Platform()
64 return pstr
Cosmos Nicolaouef323db2014-09-07 22:13:28 -070065}
66
67func (p *profile) Name() string {
Cosmos Nicolaouc0e4b792014-09-25 10:57:52 -070068 return "roaming" + p.gce
Cosmos Nicolaouef323db2014-09-07 22:13:28 -070069}
70
Cosmos Nicolaou4e8da642014-11-13 08:32:05 -080071func (p *profile) Runtime() (string, []veyron2.ROpt) {
72 return veyron2.GoogleRuntimeName, nil
Cosmos Nicolaouef323db2014-09-07 22:13:28 -070073}
74
75func (p *profile) String() string {
76 return p.Name() + " " + p.Platform().String()
77}
78
Cosmos Nicolaou39e3ae52014-11-14 13:30:01 -080079func (p *profile) Init(rt veyron2.Runtime, publisher *config.Publisher) (veyron2.AppCycle, error) {
Cosmos Nicolaouef323db2014-09-07 22:13:28 -070080 log := rt.Logger()
81
Matt Rosencrantzb30286b2014-11-10 14:52:17 -080082 rt.ConfigureReservedName(debug.NewDispatcher(log.LogDir(), sflag.NewAuthorizerOrDie(), rt.VtraceStore()))
Cosmos Nicolaou8246a8b2014-11-01 09:32:36 -070083
Cosmos Nicolaoud811b072014-10-28 17:46:27 -070084 lf := commonFlags.ListenFlags()
Cosmos Nicolaouf8d4c2b2014-10-23 22:36:38 -070085 ListenSpec = ipc.ListenSpec{
Cosmos Nicolaouae8dd212014-12-13 23:43:08 -080086 Addrs: ipc.ListenAddrs(lf.Addrs),
87 Proxy: lf.ListenProxy,
Robin Thellendae260e42014-09-22 14:46:43 -070088 }
89
Bogdan Caprita3e8f9642014-12-05 14:29:40 -080090 p.ac = appcycle.New()
Cosmos Nicolaou39e3ae52014-11-14 13:30:01 -080091
Cosmos Nicolaouef323db2014-09-07 22:13:28 -070092 // Our address is private, so we test for running on GCE and for its
Cosmos Nicolaou43b95352014-10-14 11:09:52 -070093 // 1:1 NAT configuration.
94 if !internal.HasPublicIP(log) {
95 if addr := internal.GCEPublicAddress(log); addr != nil {
Cosmos Nicolaou66bc1202014-09-30 20:42:43 -070096 ListenSpec.AddressChooser = func(string, []ipc.Address) ([]ipc.Address, error) {
97 return []ipc.Address{&netstate.AddrIfc{addr, "nat", nil}}, nil
Cosmos Nicolaouef323db2014-09-07 22:13:28 -070098 }
99 p.gce = "+gce"
Cosmos Nicolaou39e3ae52014-11-14 13:30:01 -0800100 return p.ac, nil
Cosmos Nicolaouef323db2014-09-07 22:13:28 -0700101 }
102 }
103
104 // Create stream in Init function to avoid a race between any
105 // goroutines started here and consumers started after Init returns.
106 ch := make(chan config.Setting)
Cosmos Nicolaoud6c3c9c2014-09-30 15:42:53 -0700107 stop, err := publisher.CreateStream(SettingsStreamName, SettingsStreamName, ch)
Cosmos Nicolaouef323db2014-09-07 22:13:28 -0700108 if err != nil {
109 log.Errorf("failed to create publisher: %s", err)
Cosmos Nicolaou39e3ae52014-11-14 13:30:01 -0800110 p.ac.Shutdown()
111 return nil, err
Cosmos Nicolaouef323db2014-09-07 22:13:28 -0700112 }
113
Cosmos Nicolaou43b95352014-10-14 11:09:52 -0700114 prev, err := netstate.GetAccessibleIPs()
115 if err != nil {
Cosmos Nicolaou43b95352014-10-14 11:09:52 -0700116 log.VI(2).Infof("failed to determine network state")
Cosmos Nicolaou39e3ae52014-11-14 13:30:01 -0800117 p.ac.Shutdown()
118 return nil, err
Cosmos Nicolaou43b95352014-10-14 11:09:52 -0700119 }
Cosmos Nicolaouef323db2014-09-07 22:13:28 -0700120
121 // Start the dhcp watcher.
122 watcher, err := netconfig.NewNetConfigWatcher()
123 if err != nil {
124 log.VI(2).Infof("Failed to get new config watcher: %s", err)
Cosmos Nicolaou39e3ae52014-11-14 13:30:01 -0800125 p.ac.Shutdown()
126 return nil, err
Cosmos Nicolaouef323db2014-09-07 22:13:28 -0700127 }
128
Cosmos Nicolaou39e3ae52014-11-14 13:30:01 -0800129 p.cleanupCh = make(chan struct{})
130 p.watcherCh = make(chan struct{})
131
132 ListenSpec.StreamPublisher = publisher
133 ListenSpec.StreamName = SettingsStreamName
134 ListenSpec.AddressChooser = internal.IPAddressChooser
135
136 go monitorNetworkSettings(rt, watcher, prev, stop, p.cleanupCh, p.watcherCh, ch, ListenSpec)
137 return p.ac, nil
138}
139
140func (p *profile) Cleanup() {
141 if p.cleanupCh != nil {
142 close(p.cleanupCh)
143 }
144 if p.ac != nil {
145 p.ac.Shutdown()
146 }
147 if p.watcherCh != nil {
148 <-p.watcherCh
149 }
150}
151
152// monitorNetworkSettings will monitor network configuration changes and
153// publish subsequent Settings to reflect any changes detected.
154func monitorNetworkSettings(rt veyron2.Runtime, watcher netconfig.NetConfigWatcher, prev netstate.AddrList, pubStop, cleanup <-chan struct{},
155 watcherLoop chan<- struct{}, ch chan<- config.Setting, listenSpec ipc.ListenSpec) {
156 defer close(ch)
157
158 log := rt.Logger()
159
Cosmos Nicolaouae8dd212014-12-13 23:43:08 -0800160 // TODO(cnicolaou): add support for listening on multiple network addresses.
161
Cosmos Nicolaou39e3ae52014-11-14 13:30:01 -0800162done:
Cosmos Nicolaouef323db2014-09-07 22:13:28 -0700163 for {
164 select {
165 case <-watcher.Channel():
166 cur, err := netstate.GetAccessibleIPs()
167 if err != nil {
168 log.Errorf("failed to read network state: %s", err)
169 continue
170 }
171 removed := netstate.FindRemoved(prev, cur)
172 added := netstate.FindAdded(prev, cur)
173 log.VI(2).Infof("Previous: %d: %s", len(prev), prev)
174 log.VI(2).Infof("Current : %d: %s", len(cur), cur)
175 log.VI(2).Infof("Added : %d: %s", len(added), added)
176 log.VI(2).Infof("Removed : %d: %s", len(removed), removed)
177 if len(removed) == 0 && len(added) == 0 {
178 log.VI(2).Infof("Network event that lead to no address changes since our last 'baseline'")
179 continue
180 }
181 if len(removed) > 0 {
182 log.VI(2).Infof("Sending removed: %s", removed)
183 ch <- ipc.NewRmAddrsSetting(removed)
184 }
185 // We will always send the best currently available address
Cosmos Nicolaouae8dd212014-12-13 23:43:08 -0800186 if chosen, err := listenSpec.AddressChooser(listenSpec.Addrs[0].Protocol, cur); err == nil && chosen != nil {
Cosmos Nicolaou66bc1202014-09-30 20:42:43 -0700187 ch <- ipc.NewAddAddrsSetting(chosen)
Cosmos Nicolaouef323db2014-09-07 22:13:28 -0700188 }
189 prev = cur
Cosmos Nicolaou39e3ae52014-11-14 13:30:01 -0800190 case <-cleanup:
191 break done
192 case <-pubStop:
193 goto done
Cosmos Nicolaouef323db2014-09-07 22:13:28 -0700194 }
195 }
Cosmos Nicolaou39e3ae52014-11-14 13:30:01 -0800196 watcher.Stop()
197 close(watcherLoop)
Cosmos Nicolaouef323db2014-09-07 22:13:28 -0700198}