mirror of
https://github.com/weaveworks/scope.git
synced 2026-02-14 18:09:59 +00:00
We modify the critbitgo library to skip creating a route object we don't use. The weaveworks-local modification can be removed if https://github.com/k-sone/critbitgo/pull/7 is merged.
210 lines
4.6 KiB
Go
210 lines
4.6 KiB
Go
package report
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"net"
|
|
"strings"
|
|
|
|
"github.com/k-sone/critbitgo"
|
|
)
|
|
|
|
// Networks represent a set of subnets
|
|
type Networks struct{ *critbitgo.Net }
|
|
|
|
// LocalNetworks helps in determining which addresses a probe reports
|
|
// as being host-scoped.
|
|
//
|
|
// TODO this design is broken, make it consistent with probe networks.
|
|
var LocalNetworks = MakeNetworks()
|
|
|
|
// MakeNetworks creates a datastructure representing a set of networks.
|
|
func MakeNetworks() Networks {
|
|
return Networks{critbitgo.NewNet()}
|
|
}
|
|
|
|
// Add adds a network.
|
|
func (n Networks) Add(ipnet *net.IPNet) error {
|
|
return n.Net.Add(ipnet, struct{}{})
|
|
}
|
|
|
|
// AddCIDR adds a network, represented as CIDR.
|
|
func (n Networks) AddCIDR(cidr string) error {
|
|
return n.Net.AddCIDR(cidr, struct{}{})
|
|
}
|
|
|
|
// Contains returns true if IP is in Networks.
|
|
func (n Networks) Contains(ip net.IP) bool {
|
|
contained, _ := n.ContainedIP(ip)
|
|
return contained
|
|
}
|
|
|
|
// LocalAddresses returns a list of the local IP addresses.
|
|
func LocalAddresses() ([]net.IP, error) {
|
|
result := []net.IP{}
|
|
|
|
infs, err := net.Interfaces()
|
|
if err != nil {
|
|
return []net.IP{}, err
|
|
}
|
|
|
|
for _, inf := range infs {
|
|
if strings.HasPrefix(inf.Name, "veth") ||
|
|
strings.HasPrefix(inf.Name, "docker") ||
|
|
strings.HasPrefix(inf.Name, "lo") {
|
|
continue
|
|
}
|
|
|
|
addrs, err := inf.Addrs()
|
|
if err != nil {
|
|
return []net.IP{}, err
|
|
}
|
|
|
|
for _, ipnet := range ipv4Nets(addrs) {
|
|
result = append(result, ipnet.IP)
|
|
}
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
// AddLocalBridge records the subnet address associated with the bridge name
|
|
// supplied, such that MakeAddressNodeID will scope addresses in this subnet
|
|
// as local.
|
|
func AddLocalBridge(name string) error {
|
|
inf, err := net.InterfaceByName(name)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
addrs, err := inf.Addrs()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, ipnet := range ipv4Nets(addrs) {
|
|
LocalNetworks.Add(ipnet)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// GetLocalNetworks returns all the local networks.
|
|
func GetLocalNetworks() ([]*net.IPNet, error) {
|
|
addrs, err := net.InterfaceAddrs()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return ipv4Nets(addrs), nil
|
|
}
|
|
|
|
func ipv4Nets(addrs []net.Addr) []*net.IPNet {
|
|
nets := []*net.IPNet{}
|
|
for _, addr := range addrs {
|
|
if ipnet, ok := addr.(*net.IPNet); ok && ipnet.IP.To4() != nil {
|
|
nets = append(nets, ipnet)
|
|
}
|
|
}
|
|
return nets
|
|
}
|
|
|
|
// ContainingIPv4Network determines the smallest network containing
|
|
// the given IPv4 addresses. When no addresses are specified, nil is
|
|
// returned.
|
|
func ContainingIPv4Network(ips []net.IP) *net.IPNet {
|
|
if len(ips) == 0 {
|
|
return nil
|
|
}
|
|
cpl := net.IPv4len * 8
|
|
network := networkFromPrefix(ips[0], cpl)
|
|
for _, ip := range ips[1:] {
|
|
if ncpl := commonIPv4PrefixLen(network.IP, ip); ncpl < cpl {
|
|
cpl = ncpl
|
|
network = networkFromPrefix(network.IP, cpl)
|
|
}
|
|
}
|
|
return network
|
|
}
|
|
|
|
func networkFromPrefix(ip net.IP, prefixLen int) *net.IPNet {
|
|
mask := net.CIDRMask(prefixLen, net.IPv4len*8)
|
|
return &net.IPNet{IP: ip.Mask(mask), Mask: mask}
|
|
}
|
|
|
|
func commonIPv4PrefixLen(a, b net.IP) int {
|
|
x := binary.BigEndian.Uint32(a)
|
|
y := binary.BigEndian.Uint32(b)
|
|
cpl := 32
|
|
for ; x != y; cpl-- {
|
|
x >>= 1
|
|
y >>= 1
|
|
}
|
|
return cpl
|
|
}
|
|
|
|
// ParseIP parses s as an IP address into a byte slice if supplied, returning the result.
|
|
// (mostly copied from net.ParseIP, modified to save memory allocations)
|
|
func ParseIP(s []byte, into []byte) net.IP {
|
|
for i := 0; i < len(s); i++ {
|
|
switch s[i] {
|
|
case '.':
|
|
return parseIPv4(s, into)
|
|
case ':':
|
|
return net.ParseIP(string(s)) // leave IPv6 to the original code since we don't see many of those
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Parse IPv4 address (d.d.d.d).
|
|
// (mostly copied from net.parseIPv4, modified to save memory allocations)
|
|
func parseIPv4(s []byte, into []byte) net.IP {
|
|
var p []byte
|
|
if len(into) >= net.IPv4len { // check if we can use the supplied slice
|
|
p = into[:net.IPv4len]
|
|
} else {
|
|
p = make([]byte, net.IPv4len)
|
|
}
|
|
for i := 0; i < net.IPv4len; i++ {
|
|
if len(s) == 0 {
|
|
// Missing octets.
|
|
return nil
|
|
}
|
|
if i > 0 {
|
|
if s[0] != '.' {
|
|
return nil
|
|
}
|
|
s = s[1:]
|
|
}
|
|
n, c, ok := dtoi(s)
|
|
if !ok || n > 0xFF {
|
|
return nil
|
|
}
|
|
s = s[c:]
|
|
p[i] = byte(n)
|
|
}
|
|
if len(s) != 0 {
|
|
return nil
|
|
}
|
|
return p
|
|
}
|
|
|
|
// Bigger than we need, not too big to worry about overflow
|
|
const big = 0xFFFFFF
|
|
|
|
// Decimal to integer.
|
|
// Returns number, characters consumed, success.
|
|
// (completely copied from net.dtoi, just because it wasn't exported)
|
|
func dtoi(s []byte) (n int, i int, ok bool) {
|
|
n = 0
|
|
for i = 0; i < len(s) && '0' <= s[i] && s[i] <= '9'; i++ {
|
|
n = n*10 + int(s[i]-'0')
|
|
if n >= big {
|
|
return big, i, false
|
|
}
|
|
}
|
|
if i == 0 {
|
|
return 0, 0, false
|
|
}
|
|
return n, i, true
|
|
}
|