veyron/lib/netstate,netconfig: move netconfig out of the runtime and create netstate.
netstate is a set of routines for determining current networking state
and making policy based selections from the set of available addresses.
Change-Id: Iba2e20f046ae6018bd14f4fcb66f3d1c17af1518
diff --git a/lib/netstate/netstate.go b/lib/netstate/netstate.go
new file mode 100644
index 0000000..e35e595
--- /dev/null
+++ b/lib/netstate/netstate.go
@@ -0,0 +1,270 @@
+// Package netstate provides routines to obtain the available set of
+// of network addresess, for determining changes to those addresses and for
+// selecting from amongst them according to some set of policies that are
+// implemented by applying simple predicates (functions with names of the form
+// Is<condition> to filter or find the first matching address from a list
+// of addresses. The intent is to make it easy to create policies that do
+// things like 'find the first IPv4 unicast address that is globally routable,
+// failing that use a private IPv4 address, and failing that, an IPv6 address'.
+//
+// A typical usage would be:
+//
+// state, _ := netstate.GetAccessibleIPs()
+// first := netstate.First(netstate.IsPublicIPv4)
+// // first will contain the first public IPv4 address or be nil.
+//
+// The example policy described above would be implemented using a
+// series of calls to First with appropriate predicates.
+//
+// Although most commercial networking hardware supports IPv6, some consumer
+// devices and more importantly many ISPs do not, so routines are provided
+// to allow developers to easily distinguish between the two and to use
+// whichever is appropriate for their product/situation.
+//
+// The term 'accessible' is used to refer to any non-loopback IP address.
+// The term 'public' is used to refer to any globally routable IP address.
+//
+// All IPv6 addresses are intended to be 'public', but any starting with
+// fc00::/7 are (RFC4193) are reserved for private use, but the go
+// net libraries do not appear to recognise this. Similarly fe80::/10
+// (RFC 4291) are reserved for 'site-local' usage, but again this is not
+// implemented in the go libraries. Any developer who needs to distinguish
+// these cases will need to write their own routines to test for them.
+//
+// When using the go net package it is important to remember that IPv6
+// addresses subsume IPv4 and hence in many cases the same internal
+// representation is used for both, thus testing for the length of the IP
+// address is unreliable. The reliable test is to use the net.To4() which
+// will return a non-nil result if can be used as an IPv4 one. Any address
+// can be used as an IPv6 and hence the only reliable way to test for an IPv6
+// address that is not an IPv4 one also is for the To4 call to return nil for
+// it.
+package netstate
+
+import (
+ "fmt"
+ "net"
+ "strings"
+)
+
+type AddrList []net.Addr
+
+func (al AddrList) String() string {
+ r := ""
+ for _, v := range al {
+ r += fmt.Sprintf("(%s) ", v)
+ }
+ return strings.TrimRight(r, " ")
+}
+
+// GetAll gets all of the available addresses on the device, including
+// loopback addresses.
+func GetAll() (AddrList, error) {
+ interfaces, err := net.Interfaces()
+ if err != nil {
+ return nil, err
+ }
+ var all AddrList
+ for _, ifc := range interfaces {
+ addrs, err := ifc.Addrs()
+ if err != nil {
+ continue
+ }
+ all = append(all, addrs...)
+ }
+ return all, nil
+}
+
+// GetAccessibleIPs returns all of the IP addresses on the device that are
+// accessible to other devices - i.e. excluding loopback etc.
+func GetAccessibleIPs() (AddrList, error) {
+ all, err := GetAll()
+ if err != nil {
+ return nil, err
+ }
+ return all.Filter(IsAccessibleIP), nil
+}
+
+type Predicate func(a net.Addr) bool
+
+// Filter returns all of the addresses for which the predicate
+// function is true.
+func (al AddrList) Filter(predicate Predicate) AddrList {
+ r := AddrList{}
+ for _, a := range al {
+ if predicate(a) {
+ r = append(r, a)
+ }
+ }
+ return r
+}
+
+// Filter returns the first address for which the predicate function is true.
+func (al AddrList) First(predicate Predicate) net.Addr {
+ for _, a := range al {
+ if predicate(a) {
+ return a
+ }
+ }
+ return nil
+}
+
+func IsIPNetwork(n string) bool {
+ switch n {
+ case "ip+net", "tcp", "tcp4", "tcp6", "udp":
+ return true
+ default:
+ return false
+
+ }
+}
+
+// AsIP returns its argument as a net.IP if that's possible.
+func AsIP(a net.Addr) net.IP {
+ ipn, ok := a.(*net.IPNet)
+ if ok {
+ return ipn.IP
+ }
+ ipa, ok := a.(*net.IPAddr)
+ if ok {
+ return ipa.IP
+ }
+ switch a.Network() {
+ default:
+ return nil
+ case "ip+net", "tcp", "tcp4", "tcp6", "udp":
+ }
+ return net.ParseIP(a.String())
+}
+
+// IsUnspecified returns true if its argument is an unspecified IP address
+func IsUnspecifiedIP(a net.Addr) bool {
+ if ip := AsIP(a); ip != nil {
+ return ip.IsUnspecified()
+ }
+ return false
+}
+
+// IsLoopback returns true if its argument is a loopback IP address
+func IsLoopbackIP(a net.Addr) bool {
+ if ip := AsIP(a); ip != nil && !ip.IsUnspecified() {
+ return ip.IsLoopback()
+ }
+ return false
+}
+
+// IsAccessible returns true if its argument is an accessible (non-loopback)
+// IP address.
+func IsAccessibleIP(a net.Addr) bool {
+ if ip := AsIP(a); ip != nil && !ip.IsUnspecified() {
+ return !ip.IsLoopback()
+ }
+ return false
+}
+
+type ma struct {
+ n, a string
+}
+
+func (a *ma) Network() string {
+ return a.n
+}
+
+func (a *ma) String() string {
+ return a.a
+}
+
+// AsAddr returns its argument as a net.Addr.
+func AsAddr(network string, a net.IP) net.Addr {
+ return &ma{n: network, a: a.String()}
+}
+
+// IsUnicastIP returns true if its argument is a unicast IP address.
+func IsUnicastIP(a net.Addr) bool {
+ if ip := AsIP(a); ip != nil && !ip.IsUnspecified() {
+ // ipv4 or v6
+ return !(ip.IsMulticast() || ip.IsLinkLocalMulticast() || ip.IsInterfaceLocalMulticast())
+ }
+ return false
+}
+
+// IsUnicastIPv4 returns true if its argument is a unicast IP4 address
+func IsUnicastIPv4(a net.Addr) bool {
+ if ip := AsIP(a); ip != nil && ip.To4() != nil && !ip.IsMulticast() {
+ return !ip.IsUnspecified() && !ip.IsMulticast()
+ }
+ return false
+}
+
+// IsPublicUnicastIPv4 returns true if its argument is a globally routable,
+// public IPv4 unicast address.
+func IsPublicUnicastIPv4(a net.Addr) bool {
+ if ip := AsIP(a); ip != nil && !ip.IsUnspecified() {
+ if t := ip.To4(); t != nil && IsGloballyRoutableIP(t) {
+ return !ip.IsMulticast()
+ }
+ }
+ return false
+}
+
+// IsUnicastIPv6 returns true if its argument is a unicast IPv6 address
+func IsUnicastIPv6(a net.Addr) bool {
+ if ip := AsIP(a); ip != nil && ip.To4() == nil {
+ return !ip.IsUnspecified() && !(ip.IsLinkLocalMulticast() || ip.IsInterfaceLocalMulticast())
+ }
+ return false
+}
+
+// IsUnicastIPv6 returns true if its argument is a globally routable IP6 address
+func IsPublicUnicastIPv6(a net.Addr) bool {
+ if ip := AsIP(a); ip != nil && ip.To4() == nil {
+ if t := ip.To16(); t != nil && IsGloballyRoutableIP(t) {
+ return true
+ }
+ }
+ return false
+}
+
+// IsPublicUnicastIP returns true if its argument is a global routable IPv4 or 6
+// address.
+func IsPublicUnicastIP(a net.Addr) bool {
+ if ip := AsIP(a); ip != nil {
+ if t := ip.To4(); t != nil && IsGloballyRoutableIP(t) {
+ return true
+ }
+ if t := ip.To16(); t != nil && IsGloballyRoutableIP(t) {
+ return true
+ }
+ }
+ return false
+}
+
+func diffAB(a, b AddrList) AddrList {
+ diff := AddrList{}
+ for _, av := range a {
+ found := false
+ for _, bv := range b {
+ if av.Network() == bv.Network() &&
+ av.String() == bv.String() {
+ found = true
+ break
+ }
+ }
+ if !found {
+ diff = append(diff, av)
+ }
+ }
+ return diff
+}
+
+// FindAdded returns the set of interfaces and/or addresses that are
+// present in b, but not in a - i.e. have been added.
+func FindAdded(a, b AddrList) AddrList {
+ return diffAB(b, a)
+}
+
+// FindRemoved returns the set of interfaces and/or addresses that
+// are present in a, but not in b - i.e. have been removed.
+func FindRemoved(a, b AddrList) AddrList {
+ return diffAB(a, b)
+}