| // 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 simple usage would be: |
| // |
| // state, _ := netstate.GetAccessibleIPs() |
| // ipv4 := state.Filter(netstate.IsPublicUnicastIPv4) |
| // // ipv4 will contain all of the public IPv4 addresses, if any. |
| // |
| // The example policy described above would be implemented using a |
| // series of calls to Filter with appropriate predicates. |
| // |
| // In some cases, it may be necessary to take IP routing information |
| // into account and hence interface hosting the address. The interface |
| // hosting each address is provided in the AddrIfc structure used to represent |
| // addresses and the IP routing information is provided by the GetAccessibleIPs |
| // function which will typically be used to obtain the available IP addresses. |
| // |
| // 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 (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" |
| |
| "veyron.io/veyron/veyron2/ipc" |
| |
| "veyron.io/veyron/veyron/lib/netconfig" |
| ) |
| |
| // AddrIfc represents a network address and the network interface that |
| // hosts it. |
| type AddrIfc struct { |
| // Network address |
| Addr net.Addr |
| |
| // The name of the network interface this address is hosted on, empty |
| // if this information is not available. |
| Name string |
| |
| // The IPRoutes of the network interface this address is hosted on, |
| // nil if this information is not available. |
| IPRoutes []*netconfig.IPRoute |
| } |
| |
| func (a *AddrIfc) String() string { |
| if a.IPRoutes != nil { |
| r := fmt.Sprintf("%s: %s[", a.Addr, a.Name) |
| for _, rt := range a.IPRoutes { |
| src := "" |
| if rt.PreferredSource != nil { |
| src = ", src: " + rt.PreferredSource.String() |
| } |
| r += fmt.Sprintf("{%d: net: %s, gw: %s%s}, ", rt.IfcIndex, rt.Net, rt.Gateway, src) |
| } |
| r = strings.TrimSuffix(r, ", ") |
| r += "]" |
| return r |
| } |
| return a.Addr.String() |
| } |
| |
| func (a *AddrIfc) Address() net.Addr { |
| return a.Addr |
| } |
| |
| func (a *AddrIfc) InterfaceIndex() int { |
| if len(a.IPRoutes) == 0 { |
| return -1 |
| } |
| return a.IPRoutes[0].IfcIndex |
| } |
| |
| func (a *AddrIfc) InterfaceName() string { |
| return a.Name |
| } |
| |
| func (a *AddrIfc) Networks() []net.Addr { |
| nets := []net.Addr{} |
| for _, r := range a.IPRoutes { |
| nets = append(nets, &r.Net) |
| } |
| return nets |
| } |
| |
| type AddrList []ipc.Address |
| |
| 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, non-IP protocols etc. |
| func GetAll() (AddrList, error) { |
| interfaces, err := net.Interfaces() |
| if err != nil { |
| return nil, err |
| } |
| routes := netconfig.GetIPRoutes(false) |
| routeTable := make(map[int][]*netconfig.IPRoute) |
| for _, r := range routes { |
| routeTable[r.IfcIndex] = append(routeTable[r.IfcIndex], r) |
| } |
| var all AddrList |
| for _, ifc := range interfaces { |
| addrs, err := ifc.Addrs() |
| if err != nil { |
| continue |
| } |
| for _, a := range addrs { |
| all = append(all, &AddrIfc{a, ifc.Name, routeTable[ifc.Index]}) |
| } |
| } |
| return all, nil |
| } |
| |
| // GetAccessibleIPs returns all of the accessible IP addresses on the device |
| // - i.e. excluding loopback and unspecified addresses. |
| // The IP addresses returned will be host addresses. |
| func GetAccessibleIPs() (AddrList, error) { |
| all, err := GetAll() |
| 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 |
| } |
| |
| // AddressPredicate defines the function signature for predicate functions |
| // to be used with AddrList |
| type AddressPredicate func(a ipc.Address) bool |
| |
| // Filter returns all of the addresses for which the predicate |
| // function is true. |
| func (al AddrList) Filter(predicate AddressPredicate) AddrList { |
| r := AddrList{} |
| for _, a := range al { |
| if predicate(a) { |
| r = append(r, a) |
| } |
| } |
| return r |
| } |
| |
| type Mapper func(a ipc.Address) ipc.Address |
| |
| // Map will apply the Mapper function to all of the items in its receiver |
| // and return a new AddrList containing all of the non-nil results from |
| // said calls. |
| func (al AddrList) Map(mapper Mapper) AddrList { |
| var ral AddrList |
| for _, a := range al { |
| if na := mapper(a); na != nil { |
| ral = append(ral, na) |
| } |
| } |
| 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 |
| // network CIDR for example). |
| func ConvertToIPHost(a ipc.Address) ipc.Address { |
| aifc, ok := a.(*AddrIfc) |
| if !ok { |
| return nil |
| } |
| aifc.Addr = AsIPAddr(aifc.Addr) |
| return aifc |
| } |
| |
| // IsIPProtocol returns true if its parameter is one of the allowed |
| // network/protocol values for IP. |
| func IsIPProtocol(n string) bool { |
| switch n { |
| case "ip+net", "ip", "tcp", "tcp4", "tcp6", "udp": |
| return true |
| default: |
| return false |
| } |
| } |
| |
| // AsIPAddr returns its argument as a net.IPAddr if that's possible. |
| func AsIPAddr(a net.Addr) *net.IPAddr { |
| if v, ok := a.(*net.IPAddr); ok { |
| return v |
| } |
| if ipn, ok := a.(*net.IPNet); ok { |
| return &net.IPAddr{IP: ipn.IP} |
| } |
| if IsIPProtocol(a.Network()) { |
| if r := net.ParseIP(a.String()); r != nil { |
| return &net.IPAddr{IP: r} |
| } |
| } |
| return nil |
| } |
| |
| // AsIP returns its argument as a net.IP if that's possible. |
| func AsIP(a net.Addr) net.IP { |
| ipAddr := AsIPAddr(a) |
| if ipAddr == nil { |
| return nil |
| } |
| return ipAddr.IP |
| } |
| |
| // IsUnspecified returns true if its argument is an unspecified IP address |
| func IsUnspecifiedIP(a ipc.Address) bool { |
| if ip := AsIP(a.Address()); ip != nil { |
| return ip.IsUnspecified() |
| } |
| return false |
| } |
| |
| // IsLoopback returns true if its argument is a loopback IP address |
| func IsLoopbackIP(a ipc.Address) bool { |
| if ip := AsIP(a.Address()); 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 ipc.Address) bool { |
| if ip := AsIP(a.Address()); ip != nil && !ip.IsUnspecified() { |
| return !ip.IsLoopback() |
| } |
| return false |
| } |
| |
| // IsUnicastIP returns true if its argument is a unicast IP address. |
| func IsUnicastIP(a ipc.Address) bool { |
| if ip := AsIP(a.Address()); 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 ipc.Address) bool { |
| if ip := AsIP(a.Address()); ip != nil && ip.To4() != nil { |
| return !ip.IsUnspecified() && !ip.IsMulticast() |
| } |
| return false |
| } |
| |
| // IsPublicUnicastIPv4 returns true if its argument is a globally routable, |
| // public IPv4 unicast address. |
| func IsPublicUnicastIPv4(a ipc.Address) bool { |
| if ip := AsIP(a.Address()); 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 ipc.Address) bool { |
| if ip := AsIP(a.Address()); 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 ipc.Address) bool { |
| if ip := AsIP(a.Address()); 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 ipc.Address) bool { |
| if ip := AsIP(a.Address()); 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.Address().Network() == bv.Address().Network() && |
| av.Address().String() == bv.Address().String() { |
| found = true |
| break |
| } |
| } |
| if !found { |
| diff = append(diff, av) |
| } |
| } |
| return diff |
| } |
| |
| // FindAdded returns the set 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 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) |
| } |
| |
| // SameMachine returns true if the provided addr is on the node |
| // executing this function. |
| func SameMachine(addr net.Addr) (bool, error) { |
| // The available interfaces may change between calls. |
| addrs, err := GetAll() |
| if err != nil { |
| return false, err |
| } |
| ips := make(map[string]struct{}) |
| for _, a := range addrs { |
| ip, _, err := net.ParseCIDR(a.Address().String()) |
| if err != nil { |
| return false, err |
| } |
| ips[ip.String()] = struct{}{} |
| } |
| |
| client, _, err := net.SplitHostPort(addr.String()) |
| if err != nil { |
| return false, err |
| } |
| _, islocal := ips[client] |
| return islocal, nil |
| } |