Jiri Simsa | d7616c9 | 2015-03-24 23:44:30 -0700 | [diff] [blame] | 1 | // 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 Sivakumar | 3f0eaf9 | 2015-01-15 09:48:44 -0800 | [diff] [blame] | 5 | // +build linux darwin |
| 6 | |
Suharsh Sivakumar | 40e52e9 | 2015-05-11 15:37:00 -0700 | [diff] [blame] | 7 | // Package roaming implements a RuntimeFactory suitable for a variety of network |
Todd Wang | 8c4e5cc | 2015-04-09 11:30:52 -0700 | [diff] [blame] | 8 | // configurations, including 1-1 NATs, dhcp auto-configuration, and Google |
| 9 | // Compute Engine. |
Suharsh Sivakumar | 3f0eaf9 | 2015-01-15 09:48:44 -0800 | [diff] [blame] | 10 | // |
Cosmos Nicolaou | 11c0ca1 | 2015-04-23 16:23:43 -0700 | [diff] [blame] | 11 | // The pubsub.Publisher mechanism is used for communicating networking |
Matt Rosencrantz | 94502cf | 2015-03-18 09:43:44 -0700 | [diff] [blame] | 12 | // settings to the rpc.Server implementation of the runtime and publishes |
Suharsh Sivakumar | 3f0eaf9 | 2015-01-15 09:48:44 -0800 | [diff] [blame] | 13 | // the Settings it expects. |
Suharsh Sivakumar | 98aa48c | 2015-01-14 16:11:30 -0800 | [diff] [blame] | 14 | package roaming |
| 15 | |
| 16 | import ( |
| 17 | "flag" |
Cosmos Nicolaou | aa87e29 | 2015-04-21 22:15:50 -0700 | [diff] [blame] | 18 | "net" |
| 19 | |
| 20 | "v.io/x/lib/netconfig" |
| 21 | "v.io/x/lib/netstate" |
Suharsh Sivakumar | 98aa48c | 2015-01-14 16:11:30 -0800 | [diff] [blame] | 22 | |
Jiri Simsa | 6ac9522 | 2015-02-23 16:11:49 -0800 | [diff] [blame] | 23 | "v.io/v23" |
Jiri Simsa | 6ac9522 | 2015-02-23 16:11:49 -0800 | [diff] [blame] | 24 | "v.io/v23/context" |
Matt Rosencrantz | c1490fe | 2015-10-06 14:02:32 -0700 | [diff] [blame] | 25 | "v.io/v23/flow" |
Matt Rosencrantz | 94502cf | 2015-03-18 09:43:44 -0700 | [diff] [blame] | 26 | "v.io/v23/rpc" |
Cosmos Nicolaou | aa87e29 | 2015-04-21 22:15:50 -0700 | [diff] [blame] | 27 | |
Cosmos Nicolaou | 0e4e392 | 2015-06-10 16:30:09 -0700 | [diff] [blame] | 28 | "v.io/x/ref/internal/logger" |
Jungho Ahn | 1eb602f | 2015-10-08 14:54:30 -0700 | [diff] [blame] | 29 | dfactory "v.io/x/ref/lib/discovery/factory" |
Jiri Simsa | ffceefa | 2015-02-28 11:03:34 -0800 | [diff] [blame] | 30 | "v.io/x/ref/lib/flags" |
Jiri Simsa | 574ec4b | 2015-08-11 09:31:37 -0700 | [diff] [blame] | 31 | "v.io/x/ref/lib/pubsub" |
Todd Wang | b351149 | 2015-04-07 23:32:34 -0700 | [diff] [blame] | 32 | "v.io/x/ref/lib/security/securityflag" |
Suharsh Sivakumar | dcc11d7 | 2015-05-11 12:19:20 -0700 | [diff] [blame] | 33 | "v.io/x/ref/runtime/internal" |
Suharsh Sivakumar | 0219ebb | 2015-08-27 15:04:00 -0700 | [diff] [blame] | 34 | _ "v.io/x/ref/runtime/internal/flow/protocols/tcp" |
Suharsh Sivakumar | 42baa8f | 2015-08-27 18:05:46 -0700 | [diff] [blame] | 35 | _ "v.io/x/ref/runtime/internal/flow/protocols/ws" |
| 36 | _ "v.io/x/ref/runtime/internal/flow/protocols/wsh" |
Suharsh Sivakumar | dcc11d7 | 2015-05-11 12:19:20 -0700 | [diff] [blame] | 37 | "v.io/x/ref/runtime/internal/lib/appcycle" |
| 38 | "v.io/x/ref/runtime/internal/lib/websocket" |
Matt Rosencrantz | c1490fe | 2015-10-06 14:02:32 -0700 | [diff] [blame] | 39 | "v.io/x/ref/runtime/internal/lib/xwebsocket" |
Suharsh Sivakumar | dcc11d7 | 2015-05-11 12:19:20 -0700 | [diff] [blame] | 40 | irpc "v.io/x/ref/runtime/internal/rpc" |
Suharsh Sivakumar | 0219ebb | 2015-08-27 15:04:00 -0700 | [diff] [blame] | 41 | "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 Sivakumar | dcc11d7 | 2015-05-11 12:19:20 -0700 | [diff] [blame] | 45 | _ "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 Sivakumar | 98aa48c | 2015-01-14 16:11:30 -0800 | [diff] [blame] | 48 | ) |
| 49 | |
| 50 | const ( |
| 51 | SettingsStreamName = "roaming" |
Suharsh Sivakumar | 40e52e9 | 2015-05-11 15:37:00 -0700 | [diff] [blame] | 52 | SettingsStreamDesc = "pubsub stream used by the roaming RuntimeFactory" |
Suharsh Sivakumar | 98aa48c | 2015-01-14 16:11:30 -0800 | [diff] [blame] | 53 | ) |
| 54 | |
Suharsh Sivakumar | d68949c | 2015-01-26 10:32:23 -0800 | [diff] [blame] | 55 | var commonFlags *flags.Flags |
Suharsh Sivakumar | 98aa48c | 2015-01-14 16:11:30 -0800 | [diff] [blame] | 56 | |
| 57 | func init() { |
Suharsh Sivakumar | 40e52e9 | 2015-05-11 15:37:00 -0700 | [diff] [blame] | 58 | v23.RegisterRuntimeFactory(Init) |
Suharsh Sivakumar | 7e93ce5 | 2015-05-07 17:46:13 -0700 | [diff] [blame] | 59 | rpc.RegisterUnknownProtocol("wsh", websocket.HybridDial, websocket.HybridResolve, websocket.HybridListener) |
Matt Rosencrantz | c1490fe | 2015-10-06 14:02:32 -0700 | [diff] [blame] | 60 | flow.RegisterUnknownProtocol("wsh", xwebsocket.WSH{}) |
Suharsh Sivakumar | d68949c | 2015-01-26 10:32:23 -0800 | [diff] [blame] | 61 | commonFlags = flags.CreateAndRegister(flag.CommandLine, flags.Runtime, flags.Listen) |
Suharsh Sivakumar | 98aa48c | 2015-01-14 16:11:30 -0800 | [diff] [blame] | 62 | } |
| 63 | |
Jiri Simsa | 6ac9522 | 2015-02-23 16:11:49 -0800 | [diff] [blame] | 64 | func Init(ctx *context.T) (v23.Runtime, *context.T, v23.Shutdown, error) { |
Nicolas Lacasse | 7c45dd3 | 2015-08-28 11:09:24 -0700 | [diff] [blame] | 65 | if err := internal.ParseFlagsAndConfigureGlobalLogger(commonFlags); err != nil { |
Matt Rosencrantz | 1b79391 | 2015-01-23 13:32:53 -0800 | [diff] [blame] | 66 | return nil, nil, nil, err |
| 67 | } |
Suharsh Sivakumar | d68949c | 2015-01-26 10:32:23 -0800 | [diff] [blame] | 68 | |
Jungho Ahn | 1eb602f | 2015-10-08 14:54:30 -0700 | [diff] [blame] | 69 | ac := appcycle.New() |
| 70 | discovery, err := dfactory.New() |
| 71 | if err != nil { |
| 72 | ac.Shutdown() |
| 73 | return nil, nil, nil, err |
| 74 | } |
| 75 | |
Suharsh Sivakumar | 98aa48c | 2015-01-14 16:11:30 -0800 | [diff] [blame] | 76 | lf := commonFlags.ListenFlags() |
Matt Rosencrantz | 94502cf | 2015-03-18 09:43:44 -0700 | [diff] [blame] | 77 | listenSpec := rpc.ListenSpec{ |
| 78 | Addrs: rpc.ListenAddrs(lf.Addrs), |
Todd Wang | e4d8d6c | 2015-10-01 14:45:25 -0700 | [diff] [blame] | 79 | Proxy: lf.Proxy, |
Suharsh Sivakumar | 98aa48c | 2015-01-14 16:11:30 -0800 | [diff] [blame] | 80 | } |
Cosmos Nicolaou | 64d573d | 2015-07-13 16:22:18 -0700 | [diff] [blame] | 81 | reservedDispatcher := debuglib.NewDispatcher(securityflag.NewAuthorizerOrDie()) |
Suharsh Sivakumar | 98aa48c | 2015-01-14 16:11:30 -0800 | [diff] [blame] | 82 | |
Jungho Ahn | 1eb602f | 2015-10-08 14:54:30 -0700 | [diff] [blame] | 83 | ishutdown := func() { |
| 84 | ac.Shutdown() |
| 85 | discovery.Close() |
| 86 | } |
Suharsh Sivakumar | eb0e296 | 2015-01-15 11:24:11 -0800 | [diff] [blame] | 87 | |
Suharsh Sivakumar | 98aa48c | 2015-01-14 16:11:30 -0800 | [diff] [blame] | 88 | // Our address is private, so we test for running on GCE and for its |
| 89 | // 1:1 NAT configuration. |
Cosmos Nicolaou | 0e4e392 | 2015-06-10 16:30:09 -0700 | [diff] [blame] | 90 | if !internal.HasPublicIP(logger.Global()) { |
| 91 | if addr := internal.GCEPublicAddress(logger.Global()); addr != nil { |
James Ring | 318c3fa | 2015-06-17 11:27:23 -0700 | [diff] [blame] | 92 | listenSpec.AddressChooser = netstate.AddressChooserFunc(func(string, []net.Addr) ([]net.Addr, error) { |
Cosmos Nicolaou | aa87e29 | 2015-04-21 22:15:50 -0700 | [diff] [blame] | 93 | // TODO(cnicolaou): the protocol at least should |
Suharsh Sivakumar | 40e52e9 | 2015-05-11 15:37:00 -0700 | [diff] [blame] | 94 | // be configurable, or maybe there's a RuntimeFactory specific |
Cosmos Nicolaou | aa87e29 | 2015-04-21 22:15:50 -0700 | [diff] [blame] | 95 | // flag to configure both the protocol and address. |
| 96 | return []net.Addr{netstate.NewNetAddr("wsh", addr.String())}, nil |
James Ring | 318c3fa | 2015-06-17 11:27:23 -0700 | [diff] [blame] | 97 | }) |
Jungho Ahn | 1eb602f | 2015-10-08 14:54:30 -0700 | [diff] [blame] | 98 | runtime, ctx, shutdown, err := rt.Init(ctx, ac, discovery, nil, &listenSpec, nil, "", commonFlags.RuntimeFlags(), reservedDispatcher) |
Suharsh Sivakumar | d5049b7 | 2015-01-21 14:11:35 -0800 | [diff] [blame] | 99 | if err != nil { |
Jungho Ahn | 1eb602f | 2015-10-08 14:54:30 -0700 | [diff] [blame] | 100 | ishutdown() |
| 101 | return nil, nil, nil, err |
Suharsh Sivakumar | d5049b7 | 2015-01-21 14:11:35 -0800 | [diff] [blame] | 102 | } |
Suharsh Sivakumar | 40e52e9 | 2015-05-11 15:37:00 -0700 | [diff] [blame] | 103 | runtimeFactoryShutdown := func() { |
Jungho Ahn | 1eb602f | 2015-10-08 14:54:30 -0700 | [diff] [blame] | 104 | ishutdown() |
Suharsh Sivakumar | d5049b7 | 2015-01-21 14:11:35 -0800 | [diff] [blame] | 105 | shutdown() |
| 106 | } |
Suharsh Sivakumar | 40e52e9 | 2015-05-11 15:37:00 -0700 | [diff] [blame] | 107 | return runtime, ctx, runtimeFactoryShutdown, nil |
Suharsh Sivakumar | 98aa48c | 2015-01-14 16:11:30 -0800 | [diff] [blame] | 108 | } |
| 109 | } |
| 110 | |
Cosmos Nicolaou | 11c0ca1 | 2015-04-23 16:23:43 -0700 | [diff] [blame] | 111 | publisher := pubsub.NewPublisher() |
Suharsh Sivakumar | 98aa48c | 2015-01-14 16:11:30 -0800 | [diff] [blame] | 112 | |
| 113 | // Create stream in Init function to avoid a race between any |
| 114 | // goroutines started here and consumers started after Init returns. |
Cosmos Nicolaou | 11c0ca1 | 2015-04-23 16:23:43 -0700 | [diff] [blame] | 115 | ch := make(chan pubsub.Setting) |
Suharsh Sivakumar | 40e52e9 | 2015-05-11 15:37:00 -0700 | [diff] [blame] | 116 | // TODO(cnicolaou): use stop to shutdown this stream when the RuntimeFactory shutdowns. |
Cosmos Nicolaou | 00fe9a4 | 2015-04-24 14:18:01 -0700 | [diff] [blame] | 117 | stop, err := publisher.CreateStream(SettingsStreamName, SettingsStreamDesc, ch) |
Suharsh Sivakumar | 98aa48c | 2015-01-14 16:11:30 -0800 | [diff] [blame] | 118 | if err != nil { |
Jungho Ahn | 1eb602f | 2015-10-08 14:54:30 -0700 | [diff] [blame] | 119 | ishutdown() |
Suharsh Sivakumar | d5049b7 | 2015-01-21 14:11:35 -0800 | [diff] [blame] | 120 | return nil, nil, nil, err |
Suharsh Sivakumar | 98aa48c | 2015-01-14 16:11:30 -0800 | [diff] [blame] | 121 | } |
| 122 | |
| 123 | prev, err := netstate.GetAccessibleIPs() |
| 124 | if err != nil { |
Jungho Ahn | 1eb602f | 2015-10-08 14:54:30 -0700 | [diff] [blame] | 125 | ishutdown() |
Suharsh Sivakumar | d5049b7 | 2015-01-21 14:11:35 -0800 | [diff] [blame] | 126 | return nil, nil, nil, err |
Suharsh Sivakumar | 98aa48c | 2015-01-14 16:11:30 -0800 | [diff] [blame] | 127 | } |
| 128 | |
| 129 | // Start the dhcp watcher. |
| 130 | watcher, err := netconfig.NewNetConfigWatcher() |
| 131 | if err != nil { |
Jungho Ahn | 1eb602f | 2015-10-08 14:54:30 -0700 | [diff] [blame] | 132 | ishutdown() |
Suharsh Sivakumar | d5049b7 | 2015-01-21 14:11:35 -0800 | [diff] [blame] | 133 | return nil, nil, nil, err |
Suharsh Sivakumar | 98aa48c | 2015-01-14 16:11:30 -0800 | [diff] [blame] | 134 | } |
| 135 | |
| 136 | cleanupCh := make(chan struct{}) |
| 137 | watcherCh := make(chan struct{}) |
| 138 | |
James Ring | 318c3fa | 2015-06-17 11:27:23 -0700 | [diff] [blame] | 139 | listenSpec.AddressChooser = internal.IPAddressChooser{} |
Suharsh Sivakumar | 98aa48c | 2015-01-14 16:11:30 -0800 | [diff] [blame] | 140 | |
Jungho Ahn | 1eb602f | 2015-10-08 14:54:30 -0700 | [diff] [blame] | 141 | runtime, ctx, shutdown, err := rt.Init(ctx, ac, discovery, nil, &listenSpec, publisher, SettingsStreamName, commonFlags.RuntimeFlags(), reservedDispatcher) |
Suharsh Sivakumar | d5049b7 | 2015-01-21 14:11:35 -0800 | [diff] [blame] | 142 | if err != nil { |
Jungho Ahn | 1eb602f | 2015-10-08 14:54:30 -0700 | [diff] [blame] | 143 | ishutdown() |
| 144 | return nil, nil, nil, err |
Suharsh Sivakumar | d5049b7 | 2015-01-21 14:11:35 -0800 | [diff] [blame] | 145 | } |
Suharsh Sivakumar | 98aa48c | 2015-01-14 16:11:30 -0800 | [diff] [blame] | 146 | |
Matt Rosencrantz | 99cc06e | 2015-01-16 10:25:11 -0800 | [diff] [blame] | 147 | go monitorNetworkSettingsX(runtime, ctx, watcher, prev, stop, cleanupCh, watcherCh, ch) |
Suharsh Sivakumar | 40e52e9 | 2015-05-11 15:37:00 -0700 | [diff] [blame] | 148 | runtimeFactoryShutdown := func() { |
Suharsh Sivakumar | 98aa48c | 2015-01-14 16:11:30 -0800 | [diff] [blame] | 149 | close(cleanupCh) |
Jungho Ahn | 1eb602f | 2015-10-08 14:54:30 -0700 | [diff] [blame] | 150 | ishutdown() |
Suharsh Sivakumar | d5049b7 | 2015-01-21 14:11:35 -0800 | [diff] [blame] | 151 | shutdown() |
Suharsh Sivakumar | 98aa48c | 2015-01-14 16:11:30 -0800 | [diff] [blame] | 152 | <-watcherCh |
| 153 | } |
Suharsh Sivakumar | 40e52e9 | 2015-05-11 15:37:00 -0700 | [diff] [blame] | 154 | return runtime, ctx, runtimeFactoryShutdown, nil |
Suharsh Sivakumar | 98aa48c | 2015-01-14 16:11:30 -0800 | [diff] [blame] | 155 | } |
| 156 | |
| 157 | // monitorNetworkSettings will monitor network configuration changes and |
| 158 | // publish subsequent Settings to reflect any changes detected. |
Matt Rosencrantz | 99cc06e | 2015-01-16 10:25:11 -0800 | [diff] [blame] | 159 | func monitorNetworkSettingsX( |
Todd Wang | b351149 | 2015-04-07 23:32:34 -0700 | [diff] [blame] | 160 | runtime *rt.Runtime, |
Matt Rosencrantz | 99cc06e | 2015-01-16 10:25:11 -0800 | [diff] [blame] | 161 | ctx *context.T, |
| 162 | watcher netconfig.NetConfigWatcher, |
| 163 | prev netstate.AddrList, |
| 164 | pubStop, cleanup <-chan struct{}, |
| 165 | watcherLoop chan<- struct{}, |
Cosmos Nicolaou | 11c0ca1 | 2015-04-23 16:23:43 -0700 | [diff] [blame] | 166 | ch chan<- pubsub.Setting) { |
Suharsh Sivakumar | 98aa48c | 2015-01-14 16:11:30 -0800 | [diff] [blame] | 167 | defer close(ch) |
| 168 | |
Matt Rosencrantz | 99cc06e | 2015-01-16 10:25:11 -0800 | [diff] [blame] | 169 | listenSpec := runtime.GetListenSpec(ctx) |
Suharsh Sivakumar | 98aa48c | 2015-01-14 16:11:30 -0800 | [diff] [blame] | 170 | |
| 171 | // TODO(cnicolaou): add support for listening on multiple network addresses. |
| 172 | |
| 173 | done: |
| 174 | for { |
| 175 | select { |
| 176 | case <-watcher.Channel(): |
Cosmos Nicolaou | aa87e29 | 2015-04-21 22:15:50 -0700 | [diff] [blame] | 177 | netstate.InvalidateCache() |
Suharsh Sivakumar | 98aa48c | 2015-01-14 16:11:30 -0800 | [diff] [blame] | 178 | cur, err := netstate.GetAccessibleIPs() |
| 179 | if err != nil { |
Cosmos Nicolaou | e9c622d | 2015-07-10 11:09:42 -0700 | [diff] [blame] | 180 | ctx.Errorf("failed to read network state: %s", err) |
Suharsh Sivakumar | 98aa48c | 2015-01-14 16:11:30 -0800 | [diff] [blame] | 181 | continue |
| 182 | } |
| 183 | removed := netstate.FindRemoved(prev, cur) |
| 184 | added := netstate.FindAdded(prev, cur) |
Cosmos Nicolaou | e9c622d | 2015-07-10 11:09:42 -0700 | [diff] [blame] | 185 | 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 Sivakumar | 98aa48c | 2015-01-14 16:11:30 -0800 | [diff] [blame] | 189 | if len(removed) == 0 && len(added) == 0 { |
Cosmos Nicolaou | e9c622d | 2015-07-10 11:09:42 -0700 | [diff] [blame] | 190 | ctx.VI(2).Infof("Network event that lead to no address changes since our last 'baseline'") |
Suharsh Sivakumar | 98aa48c | 2015-01-14 16:11:30 -0800 | [diff] [blame] | 191 | continue |
| 192 | } |
| 193 | if len(removed) > 0 { |
Cosmos Nicolaou | e9c622d | 2015-07-10 11:09:42 -0700 | [diff] [blame] | 194 | ctx.VI(2).Infof("Sending removed: %s", removed) |
Cosmos Nicolaou | 00fe9a4 | 2015-04-24 14:18:01 -0700 | [diff] [blame] | 195 | ch <- irpc.NewRmAddrsSetting(removed.AsNetAddrs()) |
Suharsh Sivakumar | 98aa48c | 2015-01-14 16:11:30 -0800 | [diff] [blame] | 196 | } |
| 197 | // We will always send the best currently available address |
Jungho Ahn | 00720b8 | 2015-10-12 15:33:31 -0700 | [diff] [blame^] | 198 | if chosen, err := listenSpec.AddressChooser.ChooseAddresses(listenSpec.Addrs[0].Protocol, cur.AsNetAddrs()); err == nil && chosen != nil { |
Cosmos Nicolaou | e9c622d | 2015-07-10 11:09:42 -0700 | [diff] [blame] | 199 | ctx.VI(2).Infof("Sending added and chosen: %s", chosen) |
Jungho Ahn | 00720b8 | 2015-10-12 15:33:31 -0700 | [diff] [blame^] | 200 | ch <- irpc.NewNewAddrsSetting(chosen) |
Cosmos Nicolaou | 1b3594d | 2015-02-01 10:05:03 -0800 | [diff] [blame] | 201 | } else { |
Cosmos Nicolaou | e9c622d | 2015-07-10 11:09:42 -0700 | [diff] [blame] | 202 | ctx.VI(2).Infof("Ignoring added %s", added) |
Suharsh Sivakumar | 98aa48c | 2015-01-14 16:11:30 -0800 | [diff] [blame] | 203 | } |
| 204 | prev = cur |
| 205 | case <-cleanup: |
| 206 | break done |
| 207 | case <-pubStop: |
| 208 | goto done |
| 209 | } |
| 210 | } |
| 211 | watcher.Stop() |
| 212 | close(watcherLoop) |
| 213 | } |