blob: bad1750864bc838523ee3a6ce5b23c2b37a475b9 [file] [log] [blame]
Cosmos Nicolaou6c6fa112014-08-19 13:22:33 -07001// +build linux darwin
2
3// Package net 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 other components of the application. The Settings stream
10// is called "net" (net.StreamName) and the Settings sent over it are
11// those defined by the ipc package.
12// TODO(cnicolaou): define the settings in the ipc package, not here.
13package net
14
15import (
16 "flag"
17 "net"
18
19 "veyron2"
20 "veyron2/config"
21 "veyron2/rt"
22
23 "veyron/profiles"
24
25 // TODO(cnicolaou): move this to profiles/internal
26 "veyron/runtimes/google/lib/netconfig"
27)
28
29const (
30 StreamName = "net"
31
32 // TODO(cnicolaou): these will eventually be defined in the veyron2/ipc
33 // package.
34 ProtocolSetting = "Protocol"
35 ListenSpecSetting = "ListenSpec"
36 AddPublishAddressSetting = "AddPublishAddr"
37 RmPublishAddressSetting = "RmPublishAddr"
38)
39
40var (
41 listen_protocol string
42 listen_addr config.IPFlag
43)
44
45func init() {
46 flag.StringVar(&listen_protocol, "veyron.protocol", "tcp4", "protocol to listen with")
47 flag.Var(&listen_addr, "veyron.address", "address to listen on")
48 rt.RegisterProfile(&profile{})
49}
50
51type profile struct{}
52
53func (p *profile) Platform() *veyron2.Platform {
54 platform, _ := profiles.Platform()
55 return platform
56}
57
58func (p *profile) Name() string {
59 return "net"
60}
61
62func (p *profile) Runtime() string {
63 return ""
64}
65
66func (p *profile) String() string {
67 return "net " + p.Platform().String()
68}
69
70func (p *profile) Init(rt veyron2.Runtime, publisher *config.Publisher) {
71 log := rt.Logger()
72
73 // TODO(cnicolaou): figure out the correct heuristics for using IPv6.
74 _, _, first := firstUsableIPv4()
75 if first == nil {
76 log.Infof("failed to find any usable IP addresses at startup")
77 }
78 public := publicIP(first)
79
80 // We now know that there is an IP address to listen on, and whether
81 // it's public or private.
82
83 // Our address is private, so we test for running on GCE
84 // and for its 1:1 NAT configuration. handleGCE returns true
85 // if we are indeed running on GCE.
86 if !public && handleGCE(rt, publisher) {
87 return
88 }
89
90 // Create stream in Init function to avoid a race between any
91 // goroutines started here and consumers started after Init returns.
92 ch := make(chan config.Setting)
93 stop, err := publisher.CreateStream(StreamName, "network configuration", ch)
94 if err != nil {
95 log.Errorf("failed to create publisher: %s", err)
96 return
97 }
98 go monitorAndPublishNetworkSettings(rt, stop, ch, listen_protocol, listen_addr.IP.String())
99}
100
101func publishInitialSettings(ch chan<- config.Setting, protocol, listenSpec string, addr net.IP) {
102 for _, setting := range []config.Setting{
103 config.NewString(ProtocolSetting, "protocol to listen with", protocol),
104 config.NewString(ListenSpecSetting, "address spec to listen on", listenSpec),
105 config.NewIP(AddPublishAddressSetting, "address to publish", addr),
106 } {
107 ch <- setting
108 }
109}
110
111// monitorNetworkSettings will publish initial Settings and then
112// monitor network configuration changes and publish subsequent
113// Settings to reflect any changes detected. It will never publish an
114// RmPublishAddressSetting without first sending an AddPublishAddressSetting.
115func monitorAndPublishNetworkSettings(rt veyron2.Runtime, stop <-chan struct{},
116 ch chan<- config.Setting,
117 listenProtocol string, listenSpec string) {
118 defer close(ch)
119
120 log := rt.Logger()
121 prev4, _, prevAddr := firstUsableIPv4()
122 // prevAddr may be nil if we are currently offline.
123
124 publishInitialSettings(ch, listenProtocol, listenSpec, prevAddr)
125
126 // Start the dhcp watcher.
127 watcher, err := netconfig.NewNetConfigWatcher()
128 if err != nil {
129 log.VI(1).Infof("Failed to get new config watcher: %s", err)
130 <-stop
131 return
132 }
133
134 for {
135 select {
136 case <-watcher.Channel():
137 cur4, _, _ := ipState()
138 added := findAdded(prev4, cur4)
139 ifc, newAddr := added.first()
140 log.VI(1).Infof("new address found: %s:%s", ifc, newAddr)
141 removed := findRemoved(prev4, cur4)
142 if prevAddr == nil || (removed.has(prevAddr) && newAddr != nil) {
143 log.VI(1).Infof("address change from %s to %s:%s",
144 prevAddr, ifc, newAddr)
145 ch <- config.NewIP(AddPublishAddressSetting, "new dhcp address to publish", newAddr)
146 ch <- config.NewIP(RmPublishAddressSetting, "remove address", prevAddr)
147 prevAddr = newAddr
148 }
149 case <-stop:
150 return
151 }
152 }
153}
154
155func firstUsableIPv4() (ipAndIf, string, net.IP) {
156 v4, _, _ := publicIPState()
157 if v4.empty() {
158 v4, _, _ = ipState()
159 }
160 ifc, first := v4.first()
161 return v4, ifc, first
162}