Cosmos Nicolaou | ef323db | 2014-09-07 22:13:28 -0700 | [diff] [blame] | 1 | // +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. |
| 11 | package roaming |
| 12 | |
| 13 | import ( |
| 14 | "flag" |
Cosmos Nicolaou | ef323db | 2014-09-07 22:13:28 -0700 | [diff] [blame] | 15 | |
Jiri Simsa | 519c507 | 2014-09-17 21:37:57 -0700 | [diff] [blame] | 16 | "veyron.io/veyron/veyron2" |
| 17 | "veyron.io/veyron/veyron2/config" |
| 18 | "veyron.io/veyron/veyron2/ipc" |
| 19 | "veyron.io/veyron/veyron2/rt" |
Cosmos Nicolaou | ef323db | 2014-09-07 22:13:28 -0700 | [diff] [blame] | 20 | |
Cosmos Nicolaou | 39e3ae5 | 2014-11-14 13:30:01 -0800 | [diff] [blame] | 21 | "veyron.io/veyron/veyron/lib/appcycle" |
Jiri Simsa | 519c507 | 2014-09-17 21:37:57 -0700 | [diff] [blame] | 22 | "veyron.io/veyron/veyron/lib/flags" |
| 23 | "veyron.io/veyron/veyron/lib/netconfig" |
| 24 | "veyron.io/veyron/veyron/lib/netstate" |
Cosmos Nicolaou | 87c0a55 | 2014-12-02 23:05:49 -0800 | [diff] [blame] | 25 | _ "veyron.io/veyron/veyron/lib/tcp" |
| 26 | _ "veyron.io/veyron/veyron/lib/websocket" |
Cosmos Nicolaou | 66bc120 | 2014-09-30 20:42:43 -0700 | [diff] [blame] | 27 | "veyron.io/veyron/veyron/profiles/internal" |
Cosmos Nicolaou | 87c0a55 | 2014-12-02 23:05:49 -0800 | [diff] [blame] | 28 | "veyron.io/veyron/veyron/profiles/internal/platform" |
| 29 | _ "veyron.io/veyron/veyron/runtimes/google/rt" |
Cosmos Nicolaou | 8246a8b | 2014-11-01 09:32:36 -0700 | [diff] [blame] | 30 | "veyron.io/veyron/veyron/services/mgmt/debug" |
Cosmos Nicolaou | 87c0a55 | 2014-12-02 23:05:49 -0800 | [diff] [blame] | 31 | |
Cosmos Nicolaou | 8246a8b | 2014-11-01 09:32:36 -0700 | [diff] [blame] | 32 | // TODO(cnicolaou,ashankar): move this into flags. |
| 33 | sflag "veyron.io/veyron/veyron/security/flag" |
Cosmos Nicolaou | ef323db | 2014-09-07 22:13:28 -0700 | [diff] [blame] | 34 | ) |
| 35 | |
| 36 | const ( |
Cosmos Nicolaou | 778cb7e | 2014-09-10 15:07:43 -0700 | [diff] [blame] | 37 | SettingsStreamName = "roaming" |
Cosmos Nicolaou | ef323db | 2014-09-07 22:13:28 -0700 | [diff] [blame] | 38 | ) |
| 39 | |
| 40 | var ( |
Cosmos Nicolaou | bdc917c | 2014-10-24 12:41:47 -0700 | [diff] [blame] | 41 | commonFlags *flags.Flags |
Cosmos Nicolaou | 9348da6 | 2014-10-03 14:21:19 -0700 | [diff] [blame] | 42 | // ListenSpec is an initialized instance of ipc.ListenSpec that can |
| 43 | // be used with ipc.Listen. |
Cosmos Nicolaou | f8d4c2b | 2014-10-23 22:36:38 -0700 | [diff] [blame] | 44 | ListenSpec ipc.ListenSpec |
Cosmos Nicolaou | ef323db | 2014-09-07 22:13:28 -0700 | [diff] [blame] | 45 | ) |
| 46 | |
| 47 | func init() { |
Cosmos Nicolaou | d811b07 | 2014-10-28 17:46:27 -0700 | [diff] [blame] | 48 | commonFlags = flags.CreateAndRegister(flag.CommandLine, flags.Listen) |
Cosmos Nicolaou | ef323db | 2014-09-07 22:13:28 -0700 | [diff] [blame] | 49 | rt.RegisterProfile(New()) |
| 50 | } |
| 51 | |
| 52 | type profile struct { |
Cosmos Nicolaou | 39e3ae5 | 2014-11-14 13:30:01 -0800 | [diff] [blame] | 53 | gce string |
| 54 | ac *appcycle.AppCycle |
| 55 | cleanupCh, watcherCh chan struct{} |
Cosmos Nicolaou | ef323db | 2014-09-07 22:13:28 -0700 | [diff] [blame] | 56 | } |
| 57 | |
Cosmos Nicolaou | ef323db | 2014-09-07 22:13:28 -0700 | [diff] [blame] | 58 | func New() veyron2.Profile { |
Cosmos Nicolaou | 767b62d | 2014-09-19 13:58:40 -0700 | [diff] [blame] | 59 | return &profile{} |
Cosmos Nicolaou | ef323db | 2014-09-07 22:13:28 -0700 | [diff] [blame] | 60 | } |
| 61 | |
| 62 | func (p *profile) Platform() *veyron2.Platform { |
Cosmos Nicolaou | 87c0a55 | 2014-12-02 23:05:49 -0800 | [diff] [blame] | 63 | pstr, _ := platform.Platform() |
| 64 | return pstr |
Cosmos Nicolaou | ef323db | 2014-09-07 22:13:28 -0700 | [diff] [blame] | 65 | } |
| 66 | |
| 67 | func (p *profile) Name() string { |
Cosmos Nicolaou | c0e4b79 | 2014-09-25 10:57:52 -0700 | [diff] [blame] | 68 | return "roaming" + p.gce |
Cosmos Nicolaou | ef323db | 2014-09-07 22:13:28 -0700 | [diff] [blame] | 69 | } |
| 70 | |
Cosmos Nicolaou | 4e8da64 | 2014-11-13 08:32:05 -0800 | [diff] [blame] | 71 | func (p *profile) Runtime() (string, []veyron2.ROpt) { |
| 72 | return veyron2.GoogleRuntimeName, nil |
Cosmos Nicolaou | ef323db | 2014-09-07 22:13:28 -0700 | [diff] [blame] | 73 | } |
| 74 | |
| 75 | func (p *profile) String() string { |
| 76 | return p.Name() + " " + p.Platform().String() |
| 77 | } |
| 78 | |
Cosmos Nicolaou | 39e3ae5 | 2014-11-14 13:30:01 -0800 | [diff] [blame] | 79 | func (p *profile) Init(rt veyron2.Runtime, publisher *config.Publisher) (veyron2.AppCycle, error) { |
Cosmos Nicolaou | ef323db | 2014-09-07 22:13:28 -0700 | [diff] [blame] | 80 | log := rt.Logger() |
| 81 | |
Matt Rosencrantz | b30286b | 2014-11-10 14:52:17 -0800 | [diff] [blame] | 82 | rt.ConfigureReservedName(debug.NewDispatcher(log.LogDir(), sflag.NewAuthorizerOrDie(), rt.VtraceStore())) |
Cosmos Nicolaou | 8246a8b | 2014-11-01 09:32:36 -0700 | [diff] [blame] | 83 | |
Cosmos Nicolaou | d811b07 | 2014-10-28 17:46:27 -0700 | [diff] [blame] | 84 | lf := commonFlags.ListenFlags() |
Cosmos Nicolaou | f8d4c2b | 2014-10-23 22:36:38 -0700 | [diff] [blame] | 85 | ListenSpec = ipc.ListenSpec{ |
Cosmos Nicolaou | ae8dd21 | 2014-12-13 23:43:08 -0800 | [diff] [blame] | 86 | Addrs: ipc.ListenAddrs(lf.Addrs), |
| 87 | Proxy: lf.ListenProxy, |
Robin Thellend | ae260e4 | 2014-09-22 14:46:43 -0700 | [diff] [blame] | 88 | } |
| 89 | |
Bogdan Caprita | 3e8f964 | 2014-12-05 14:29:40 -0800 | [diff] [blame] | 90 | p.ac = appcycle.New() |
Cosmos Nicolaou | 39e3ae5 | 2014-11-14 13:30:01 -0800 | [diff] [blame] | 91 | |
Cosmos Nicolaou | ef323db | 2014-09-07 22:13:28 -0700 | [diff] [blame] | 92 | // Our address is private, so we test for running on GCE and for its |
Cosmos Nicolaou | 43b9535 | 2014-10-14 11:09:52 -0700 | [diff] [blame] | 93 | // 1:1 NAT configuration. |
| 94 | if !internal.HasPublicIP(log) { |
| 95 | if addr := internal.GCEPublicAddress(log); addr != nil { |
Cosmos Nicolaou | 66bc120 | 2014-09-30 20:42:43 -0700 | [diff] [blame] | 96 | ListenSpec.AddressChooser = func(string, []ipc.Address) ([]ipc.Address, error) { |
| 97 | return []ipc.Address{&netstate.AddrIfc{addr, "nat", nil}}, nil |
Cosmos Nicolaou | ef323db | 2014-09-07 22:13:28 -0700 | [diff] [blame] | 98 | } |
| 99 | p.gce = "+gce" |
Cosmos Nicolaou | 39e3ae5 | 2014-11-14 13:30:01 -0800 | [diff] [blame] | 100 | return p.ac, nil |
Cosmos Nicolaou | ef323db | 2014-09-07 22:13:28 -0700 | [diff] [blame] | 101 | } |
| 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 Nicolaou | d6c3c9c | 2014-09-30 15:42:53 -0700 | [diff] [blame] | 107 | stop, err := publisher.CreateStream(SettingsStreamName, SettingsStreamName, ch) |
Cosmos Nicolaou | ef323db | 2014-09-07 22:13:28 -0700 | [diff] [blame] | 108 | if err != nil { |
| 109 | log.Errorf("failed to create publisher: %s", err) |
Cosmos Nicolaou | 39e3ae5 | 2014-11-14 13:30:01 -0800 | [diff] [blame] | 110 | p.ac.Shutdown() |
| 111 | return nil, err |
Cosmos Nicolaou | ef323db | 2014-09-07 22:13:28 -0700 | [diff] [blame] | 112 | } |
| 113 | |
Cosmos Nicolaou | 43b9535 | 2014-10-14 11:09:52 -0700 | [diff] [blame] | 114 | prev, err := netstate.GetAccessibleIPs() |
| 115 | if err != nil { |
Cosmos Nicolaou | 43b9535 | 2014-10-14 11:09:52 -0700 | [diff] [blame] | 116 | log.VI(2).Infof("failed to determine network state") |
Cosmos Nicolaou | 39e3ae5 | 2014-11-14 13:30:01 -0800 | [diff] [blame] | 117 | p.ac.Shutdown() |
| 118 | return nil, err |
Cosmos Nicolaou | 43b9535 | 2014-10-14 11:09:52 -0700 | [diff] [blame] | 119 | } |
Cosmos Nicolaou | ef323db | 2014-09-07 22:13:28 -0700 | [diff] [blame] | 120 | |
| 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 Nicolaou | 39e3ae5 | 2014-11-14 13:30:01 -0800 | [diff] [blame] | 125 | p.ac.Shutdown() |
| 126 | return nil, err |
Cosmos Nicolaou | ef323db | 2014-09-07 22:13:28 -0700 | [diff] [blame] | 127 | } |
| 128 | |
Cosmos Nicolaou | 39e3ae5 | 2014-11-14 13:30:01 -0800 | [diff] [blame] | 129 | 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 | |
| 140 | func (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. |
| 154 | func 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 Nicolaou | ae8dd21 | 2014-12-13 23:43:08 -0800 | [diff] [blame] | 160 | // TODO(cnicolaou): add support for listening on multiple network addresses. |
| 161 | |
Cosmos Nicolaou | 39e3ae5 | 2014-11-14 13:30:01 -0800 | [diff] [blame] | 162 | done: |
Cosmos Nicolaou | ef323db | 2014-09-07 22:13:28 -0700 | [diff] [blame] | 163 | 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 Nicolaou | ae8dd21 | 2014-12-13 23:43:08 -0800 | [diff] [blame] | 186 | if chosen, err := listenSpec.AddressChooser(listenSpec.Addrs[0].Protocol, cur); err == nil && chosen != nil { |
Cosmos Nicolaou | 66bc120 | 2014-09-30 20:42:43 -0700 | [diff] [blame] | 187 | ch <- ipc.NewAddAddrsSetting(chosen) |
Cosmos Nicolaou | ef323db | 2014-09-07 22:13:28 -0700 | [diff] [blame] | 188 | } |
| 189 | prev = cur |
Cosmos Nicolaou | 39e3ae5 | 2014-11-14 13:30:01 -0800 | [diff] [blame] | 190 | case <-cleanup: |
| 191 | break done |
| 192 | case <-pubStop: |
| 193 | goto done |
Cosmos Nicolaou | ef323db | 2014-09-07 22:13:28 -0700 | [diff] [blame] | 194 | } |
| 195 | } |
Cosmos Nicolaou | 39e3ae5 | 2014-11-14 13:30:01 -0800 | [diff] [blame] | 196 | watcher.Stop() |
| 197 | close(watcherLoop) |
Cosmos Nicolaou | ef323db | 2014-09-07 22:13:28 -0700 | [diff] [blame] | 198 | } |