veyron/runtimes/google/ipc: cache network interfaces
Cache available network interfaces on the device, since it is not
that cheap. (In my local machine, this saves about 1ms per RPC call.)
Will add monitoring the network interface changes once it's ready.
Change-Id: Iff75c67bdfd7eab7582ffa2b7a93d5485937269f
diff --git a/lib/netstate/netstate.go b/lib/netstate/netstate.go
index e188525..fbc9940 100644
--- a/lib/netstate/netstate.go
+++ b/lib/netstate/netstate.go
@@ -155,23 +155,7 @@
if err != nil {
return nil, err
}
- return all.Map(accessibleIPHost), nil
-}
-
-func accessibleIPHost(a ipc.Address) ipc.Address {
- if !IsAccessibleIP(a) {
- return nil
- }
- aifc, ok := a.(*AddrIfc)
- if !ok {
- return nil
- }
- ip := AsIPAddr(aifc.Addr)
- if ip == nil {
- return aifc
- }
- aifc.Addr = ip
- return aifc
+ return all.Map(ConvertAccessibleIPHost), nil
}
// AddressPredicate defines the function signature for predicate functions
@@ -205,8 +189,8 @@
return ral
}
-// Convert the network address component of an ipc.Address into an instance
-// with a net.Addr that contains an IP host address (as opposed to a
+// ConvertToIPHost converts the network address component of an ipc.Address into
+// an instance with a net.Addr that contains an IP host address (as opposed to a
// network CIDR for example).
func ConvertToIPHost(a ipc.Address) ipc.Address {
aifc, ok := a.(*AddrIfc)
@@ -217,6 +201,23 @@
return aifc
}
+// ConvertAccessibleIPHost converts the network address component of an ipc.Address
+// into an instance with a net.Addr that contains an IP host address (as opposed to a
+// network CIDR for example) with filtering out a loopback or non-accessible IPs.
+func ConvertAccessibleIPHost(a ipc.Address) ipc.Address {
+ if !IsAccessibleIP(a) {
+ return nil
+ }
+ aifc, ok := a.(*AddrIfc)
+ if !ok {
+ return nil
+ }
+ if ip := AsIPAddr(aifc.Addr); ip != nil {
+ aifc.Addr = ip
+ }
+ return aifc
+}
+
// IsIPProtocol returns true if its parameter is one of the allowed
// network/protocol values for IP.
func IsIPProtocol(n string) bool {
diff --git a/runtimes/google/ipc/client.go b/runtimes/google/ipc/client.go
index cfc6bdc..de89583 100644
--- a/runtimes/google/ipc/client.go
+++ b/runtimes/google/ipc/client.go
@@ -95,6 +95,11 @@
vcOpts []stream.VCOpt // vc opts passed to dial
preferredProtocols []string
+ // We cache the IP networks on the device since it is not that cheap to read
+ // network interfaces through os syscall.
+ // TODO(jhahn): Add monitoring the network interface changes.
+ ipNets []*net.IPNet
+
// We support concurrent calls to StartCall and Close, so we must protect the
// vcMap. Everything else is initialized upon client construction, and safe
// to use concurrently.
@@ -121,6 +126,7 @@
c := &client{
streamMgr: streamMgr,
ns: ns,
+ ipNets: ipNetworks(),
vcMap: make(map[vcMapKey]*vcInfo),
}
c.dc = InternalNewDischargeClient(nil, c)
@@ -421,7 +427,7 @@
return nil, verror.RetryRefetch, verror.Make(verror.NoServers, ctx, name)
}
// An empty set of protocols means all protocols...
- if resolved.Servers, err = filterAndOrderServers(resolved.Servers, c.preferredProtocols); err != nil {
+ if resolved.Servers, err = filterAndOrderServers(resolved.Servers, c.preferredProtocols, c.ipNets); err != nil {
return nil, verror.RetryRefetch, verror.Make(verror.NoServers, ctx, name, err)
}
}
diff --git a/runtimes/google/ipc/server.go b/runtimes/google/ipc/server.go
index abf18a3..26fd9f0 100644
--- a/runtimes/google/ipc/server.go
+++ b/runtimes/google/ipc/server.go
@@ -58,8 +58,12 @@
active sync.WaitGroup // active goroutines we've spawned.
stoppedChan chan struct{} // closed when the server has been stopped.
preferredProtocols []string // protocols to use when resolving proxy name to endpoint.
- ns naming.Namespace
- servesMountTable bool
+ // We cache the IP networks on the device since it is not that cheap to read
+ // network interfaces through os syscall.
+ // TODO(jhahn): Add monitoring the network interface changes.
+ ipNets []*net.IPNet
+ ns naming.Namespace
+ servesMountTable bool
// TODO(cnicolaou): add roaming stats to ipcStats
stats *ipcStats // stats for this server.
}
@@ -145,6 +149,7 @@
proxies: make(map[string]proxyState),
dhcpListeners: make(map[*dhcpListener]struct{}),
stoppedChan: make(chan struct{}),
+ ipNets: ipNetworks(),
ns: ns,
stats: newIPCStats(statsPrefix),
}
@@ -222,7 +227,7 @@
}}
}
// An empty set of protocols means all protocols...
- if resolved.Servers, err = filterAndOrderServers(resolved.Servers, s.preferredProtocols); err != nil {
+ if resolved.Servers, err = filterAndOrderServers(resolved.Servers, s.preferredProtocols, s.ipNets); err != nil {
return "", err
}
for _, n := range resolved.Names() {
diff --git a/runtimes/google/ipc/sort_endpoints.go b/runtimes/google/ipc/sort_endpoints.go
index 93b8af0..53817fa 100644
--- a/runtimes/google/ipc/sort_endpoints.go
+++ b/runtimes/google/ipc/sort_endpoints.go
@@ -92,13 +92,12 @@
// will be used, but unlike the previous case, any servers that don't support
// these protocols will be returned also, but following the default
// preferences.
-func filterAndOrderServers(servers []naming.MountedServer, protocols []string) ([]naming.MountedServer, error) {
+func filterAndOrderServers(servers []naming.MountedServer, protocols []string, ipnets []*net.IPNet) ([]naming.MountedServer, error) {
vlog.VI(3).Infof("filterAndOrderServers%v: %v", protocols, servers)
var (
errs = newErrorAccumulator()
list = make(sortableServerList, 0, len(servers))
protoRanks = mkProtocolRankMap(protocols)
- ipnets = ipNetworks()
)
if len(protoRanks) == 0 {
protoRanks = defaultPreferredProtocolOrder
@@ -175,6 +174,30 @@
return 0, fmt.Errorf("undesired protocol %q", protocol)
}
+// locality returns the serverLocality to use given an endpoint and the
+// set of IP networks configured on this machine.
+func locality(ep naming.Endpoint, ipnets []*net.IPNet) serverLocality {
+ if len(ipnets) < 1 {
+ return unknownNetwork // 0 IP networks, locality doesn't matter.
+
+ }
+ host, _, err := net.SplitHostPort(ep.Addr().String())
+ if err != nil {
+ host = ep.Addr().String()
+ }
+ ip := net.ParseIP(host)
+ if ip == nil {
+ // Not an IP address (possibly not an IP network).
+ return unknownNetwork
+ }
+ for _, ipnet := range ipnets {
+ if ipnet.Contains(ip) {
+ return localNetwork
+ }
+ }
+ return remoteNetwork
+}
+
// ipNetworks returns the IP networks on this machine.
func ipNetworks() []*net.IPNet {
ifcs, err := netstate.GetAll()
@@ -193,27 +216,3 @@
}
return ret
}
-
-// locality returns the serverLocality to use given an endpoint and the
-// set of IP networks configured on this machine.
-func locality(ep naming.Endpoint, networks []*net.IPNet) serverLocality {
- if len(networks) < 1 {
- return unknownNetwork // 0 IP networks, locality doesn't matter.
-
- }
- host, _, err := net.SplitHostPort(ep.Addr().String())
- if err != nil {
- host = ep.Addr().String()
- }
- ip := net.ParseIP(host)
- if ip == nil {
- // Not an IP address (possibly not an IP network).
- return unknownNetwork
- }
- for _, ipnet := range networks {
- if ipnet.Contains(ip) {
- return localNetwork
- }
- }
- return remoteNetwork
-}
diff --git a/runtimes/google/ipc/sort_internal_test.go b/runtimes/google/ipc/sort_internal_test.go
index 041cacd..f7a1419 100644
--- a/runtimes/google/ipc/sort_internal_test.go
+++ b/runtimes/google/ipc/sort_internal_test.go
@@ -1,6 +1,7 @@
package ipc
import (
+ "net"
"reflect"
"strings"
"testing"
@@ -17,7 +18,7 @@
func TestIncompatible(t *testing.T) {
servers := []naming.MountedServer{}
- _, err := filterAndOrderServers(servers, []string{"tcp"})
+ _, err := filterAndOrderServers(servers, []string{"tcp"}, nil)
if err == nil || err.Error() != "failed to find any compatible servers: " {
t.Errorf("expected a different error: %v", err)
}
@@ -28,7 +29,7 @@
servers = append(servers, naming.MountedServer{Server: name})
}
- _, err = filterAndOrderServers(servers, []string{"tcp"})
+ _, err = filterAndOrderServers(servers, []string{"tcp"}, nil)
if err == nil || (!strings.HasPrefix(err.Error(), "failed to find any compatible servers:") && !strings.Contains(err.Error(), "No compatible IPC versions available")) {
t.Errorf("expected a different error to: %v", err)
}
@@ -38,7 +39,7 @@
servers = append(servers, naming.MountedServer{Server: name})
}
- _, err = filterAndOrderServers(servers, []string{"foobar"})
+ _, err = filterAndOrderServers(servers, []string{"foobar"}, nil)
if err == nil || !strings.HasSuffix(err.Error(), "undesired protocol \"tcp\")") {
t.Errorf("expected a different error to: %v", err)
}
@@ -47,6 +48,9 @@
func TestOrderingByProtocol(t *testing.T) {
servers := []naming.MountedServer{}
+ _, ipnet, _ := net.ParseCIDR("127.0.0.0/8")
+ ipnets := []*net.IPNet{ipnet}
+
for _, a := range []string{"127.0.0.3", "127.0.0.4"} {
name := naming.JoinAddressName(naming.FormatEndpoint("tcp", a), "")
servers = append(servers, naming.MountedServer{Server: name})
@@ -63,7 +67,7 @@
name := naming.JoinAddressName(naming.FormatEndpoint("tcp6", a), "")
servers = append(servers, naming.MountedServer{Server: name})
}
- if _, err := filterAndOrderServers(servers, []string{"batman"}); err == nil {
+ if _, err := filterAndOrderServers(servers, []string{"batman"}, ipnets); err == nil {
t.Fatalf("expected an error")
}
@@ -82,7 +86,7 @@
"/@2@tcp4@127.0.0.2@@@@@",
"/127.0.0.12:14141",
}
- result, err := filterAndOrderServers(servers, []string{"foobar", "tcp4"})
+ result, err := filterAndOrderServers(servers, []string{"foobar", "tcp4"}, ipnets)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
@@ -106,14 +110,14 @@
"/@2@foobar@127.0.0.11@@@@@",
"/127.0.0.12:14141",
}
- if result, err = filterAndOrderServers(servers, nil); err != nil {
+ if result, err = filterAndOrderServers(servers, nil, ipnets); err != nil {
t.Fatalf("unexpected error: %s", err)
}
if got := servers2names(result); !reflect.DeepEqual(got, want) {
t.Errorf("got: %v, want %v", got, want)
}
- if result, err = filterAndOrderServers(servers, []string{}); err != nil {
+ if result, err = filterAndOrderServers(servers, []string{}, ipnets); err != nil {
t.Fatalf("unexpected error: %s", err)
}
if got := servers2names(result); !reflect.DeepEqual(got, want) {
@@ -130,7 +134,7 @@
"/@2@tcp6@127.0.0.8@@@@@",
"/127.0.0.12:14141",
}
- if result, err = filterAndOrderServers(servers, []string{"tcp"}); err != nil {
+ if result, err = filterAndOrderServers(servers, []string{"tcp"}, ipnets); err != nil {
t.Fatalf("unexpected error: %s", err)
}
if got := servers2names(result); !reflect.DeepEqual(got, want) {
@@ -159,7 +163,7 @@
name := naming.JoinAddressName(naming.FormatEndpoint("foobar", a), "")
servers = append(servers, naming.MountedServer{Server: name})
}
- if result, err = filterAndOrderServers(servers, []string{}); err != nil {
+ if result, err = filterAndOrderServers(servers, []string{}, ipnets); err != nil {
t.Fatalf("unexpected error: %s", err)
}
if got := servers2names(result); !reflect.DeepEqual(got, want) {
@@ -169,11 +173,14 @@
func TestOrderingByLocality(t *testing.T) {
servers := []naming.MountedServer{}
+ _, ipnet, _ := net.ParseCIDR("127.0.0.0/8")
+ ipnets := []*net.IPNet{ipnet}
+
for _, a := range []string{"74.125.69.139", "127.0.0.3", "127.0.0.1", "192.168.1.10", "74.125.142.83"} {
name := naming.JoinAddressName(naming.FormatEndpoint("tcp", a), "")
servers = append(servers, naming.MountedServer{Server: name})
}
- result, err := filterAndOrderServers(servers, []string{"tcp"})
+ result, err := filterAndOrderServers(servers, []string{"tcp"}, ipnets)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
@@ -192,7 +199,7 @@
servers = append(servers, naming.MountedServer{Server: name})
}
- if result, err = filterAndOrderServers(servers, []string{"ws", "tcp"}); err != nil {
+ if result, err = filterAndOrderServers(servers, []string{"ws", "tcp"}, ipnets); err != nil {
t.Fatalf("unexpected error: %s", err)
}
want = []string{