blob: e78dc3c48753ca3fc75011c3d61ff8d59cc98328 [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"
15 "fmt"
16 "net"
17
Jiri Simsa519c5072014-09-17 21:37:57 -070018 "veyron.io/veyron/veyron2"
19 "veyron.io/veyron/veyron2/config"
20 "veyron.io/veyron/veyron2/ipc"
21 "veyron.io/veyron/veyron2/rt"
Cosmos Nicolaouef323db2014-09-07 22:13:28 -070022
Jiri Simsa519c5072014-09-17 21:37:57 -070023 "veyron.io/veyron/veyron/lib/flags"
24 "veyron.io/veyron/veyron/lib/netconfig"
25 "veyron.io/veyron/veyron/lib/netstate"
26 "veyron.io/veyron/veyron/profiles"
Cosmos Nicolaouef323db2014-09-07 22:13:28 -070027)
28
29const (
Cosmos Nicolaou778cb7e2014-09-10 15:07:43 -070030 SettingsStreamName = "roaming"
Cosmos Nicolaouef323db2014-09-07 22:13:28 -070031)
32
33var (
Cosmos Nicolaou778cb7e2014-09-10 15:07:43 -070034 listenProtocolFlag = flags.TCPProtocolFlag{"tcp"}
35 listenAddressFlag = flags.IPHostPortFlag{Port: "0"}
36 listenProxyFlag string
37 ListenSpec *ipc.ListenSpec
Cosmos Nicolaouef323db2014-09-07 22:13:28 -070038)
39
40func init() {
41 flag.Var(&listenProtocolFlag, "veyron.tcp.protocol", "protocol to listen with")
Cosmos Nicolaou778cb7e2014-09-10 15:07:43 -070042 flag.Var(&listenAddressFlag, "veyron.tcp.address", "address to listen on")
43 flag.StringVar(&listenProxyFlag, "veyron.proxy", "", "proxy to use")
44
Cosmos Nicolaouef323db2014-09-07 22:13:28 -070045 rt.RegisterProfile(New())
46}
47
48type profile struct {
Cosmos Nicolaou767b62d2014-09-19 13:58:40 -070049 gce string
Cosmos Nicolaouef323db2014-09-07 22:13:28 -070050}
51
52func preferredIPAddress(network string, addrs []net.Addr) (net.Addr, error) {
53 if !netstate.IsIPProtocol(network) {
54 return nil, fmt.Errorf("can't support network protocol %q", network)
55 }
56 al := netstate.AddrList(addrs).Map(netstate.ConvertToIPHost)
57 for _, predicate := range []netstate.Predicate{netstate.IsPublicUnicastIPv4,
58 netstate.IsUnicastIPv4, netstate.IsPublicUnicastIPv6} {
59 if a := al.First(predicate); a != nil {
60 return a, nil
61 }
62 }
63 return nil, fmt.Errorf("failed to find any usable address for %q", network)
64}
65
66func New() veyron2.Profile {
Cosmos Nicolaou767b62d2014-09-19 13:58:40 -070067 return &profile{}
Cosmos Nicolaouef323db2014-09-07 22:13:28 -070068}
69
70func (p *profile) Platform() *veyron2.Platform {
71 platform, _ := profiles.Platform()
72 return platform
73}
74
75func (p *profile) Name() string {
Cosmos Nicolaouc0e4b792014-09-25 10:57:52 -070076 return "roaming" + p.gce
Cosmos Nicolaouef323db2014-09-07 22:13:28 -070077}
78
79func (p *profile) Runtime() string {
80 return ""
81}
82
83func (p *profile) String() string {
84 return p.Name() + " " + p.Platform().String()
85}
86
Cosmos Nicolaou682d7fd2014-09-24 22:54:16 -070087func (p *profile) Init(rt veyron2.Runtime, publisher *config.Publisher) error {
Cosmos Nicolaouef323db2014-09-07 22:13:28 -070088 log := rt.Logger()
89
Robin Thellendae260e42014-09-22 14:46:43 -070090 ListenSpec = &ipc.ListenSpec{
91 Protocol: listenProtocolFlag.Protocol,
92 Address: listenAddressFlag.String(),
93 Proxy: listenProxyFlag,
94 }
95
Cosmos Nicolaouef323db2014-09-07 22:13:28 -070096 state, err := netstate.GetAccessibleIPs()
97 if err != nil {
98 log.Infof("failed to determine network state")
Cosmos Nicolaou682d7fd2014-09-24 22:54:16 -070099 return err
Cosmos Nicolaouef323db2014-09-07 22:13:28 -0700100 }
101 first := state.First(netstate.IsUnicastIP)
102 if first == nil {
103 log.Infof("failed to find any usable IP addresses at startup")
104 }
105 public := netstate.IsPublicUnicastIPv4(first)
106
107 // We now know that there is an IP address to listen on, and whether
108 // it's public or private.
109
110 // Our address is private, so we test for running on GCE and for its
111 // 1:1 NAT configuration. handleGCE returns a non-nil addr
112 // if we are indeed running on GCE.
113 if !public {
114 if addr := handleGCE(rt, publisher); addr != nil {
Cosmos Nicolaou767b62d2014-09-19 13:58:40 -0700115 ListenSpec.AddressChooser = func(string, []net.Addr) (net.Addr, error) {
Cosmos Nicolaouef323db2014-09-07 22:13:28 -0700116 return addr, nil
117 }
118 p.gce = "+gce"
Cosmos Nicolaou682d7fd2014-09-24 22:54:16 -0700119 return nil
Cosmos Nicolaouef323db2014-09-07 22:13:28 -0700120 }
121 }
122
123 // Create stream in Init function to avoid a race between any
124 // goroutines started here and consumers started after Init returns.
125 ch := make(chan config.Setting)
126 stop, err := publisher.CreateStream(SettingsStreamName, "dhcp", ch)
127 if err != nil {
128 log.Errorf("failed to create publisher: %s", err)
Cosmos Nicolaou682d7fd2014-09-24 22:54:16 -0700129 return err
Cosmos Nicolaouef323db2014-09-07 22:13:28 -0700130 }
131
132 protocol := listenProtocolFlag.Protocol
Cosmos Nicolaou767b62d2014-09-19 13:58:40 -0700133 ListenSpec.StreamPublisher = publisher
134 ListenSpec.StreamName = "dhcp"
135 ListenSpec.AddressChooser = preferredIPAddress
Cosmos Nicolaou778cb7e2014-09-10 15:07:43 -0700136 log.VI(2).Infof("Initial Network Settings: %s %s available: %s", protocol, listenAddressFlag, state)
Cosmos Nicolaou767b62d2014-09-19 13:58:40 -0700137 go monitorNetworkSettings(rt, stop, ch, state, ListenSpec)
Cosmos Nicolaou682d7fd2014-09-24 22:54:16 -0700138 return nil
Cosmos Nicolaouef323db2014-09-07 22:13:28 -0700139}
140
Cosmos Nicolaouef323db2014-09-07 22:13:28 -0700141// monitorNetworkSettings will monitor network configuration changes and
142// publish subsequent Settings to reflect any changes detected.
143func monitorNetworkSettings(rt veyron2.Runtime, stop <-chan struct{},
Cosmos Nicolaou767b62d2014-09-19 13:58:40 -0700144 ch chan<- config.Setting, prev netstate.AddrList, listenSpec *ipc.ListenSpec) {
Cosmos Nicolaouef323db2014-09-07 22:13:28 -0700145 defer close(ch)
146
147 log := rt.Logger()
148
149 // Start the dhcp watcher.
150 watcher, err := netconfig.NewNetConfigWatcher()
151 if err != nil {
152 log.VI(2).Infof("Failed to get new config watcher: %s", err)
153 // TODO(cnicolaou): add support for shutting down profiles
154 //<-stop
155 return
156 }
157
158 for {
159 select {
160 case <-watcher.Channel():
161 cur, err := netstate.GetAccessibleIPs()
162 if err != nil {
163 log.Errorf("failed to read network state: %s", err)
164 continue
165 }
166 removed := netstate.FindRemoved(prev, cur)
167 added := netstate.FindAdded(prev, cur)
168 log.VI(2).Infof("Previous: %d: %s", len(prev), prev)
169 log.VI(2).Infof("Current : %d: %s", len(cur), cur)
170 log.VI(2).Infof("Added : %d: %s", len(added), added)
171 log.VI(2).Infof("Removed : %d: %s", len(removed), removed)
172 if len(removed) == 0 && len(added) == 0 {
173 log.VI(2).Infof("Network event that lead to no address changes since our last 'baseline'")
174 continue
175 }
176 if len(removed) > 0 {
177 log.VI(2).Infof("Sending removed: %s", removed)
178 ch <- ipc.NewRmAddrsSetting(removed)
179 }
180 // We will always send the best currently available address
Cosmos Nicolaou767b62d2014-09-19 13:58:40 -0700181 if chosen, err := listenSpec.AddressChooser(listenSpec.Protocol, cur); err == nil && chosen != nil {
Cosmos Nicolaouef323db2014-09-07 22:13:28 -0700182 ch <- ipc.NewAddAddrsSetting([]net.Addr{chosen})
183 }
184 prev = cur
185 // TODO(cnicolaou): add support for shutting down profiles.
186 //case <-stop:
187 // return
188 }
189 }
190}