github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/network/network.go (about)

     1  // Copyright 2014 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package network
     5  
     6  import (
     7  	"fmt"
     8  	"net"
     9  	"strings"
    10  
    11  	"github.com/juju/collections/set"
    12  	"github.com/juju/errors"
    13  	"github.com/juju/loggo"
    14  
    15  	corenetwork "github.com/juju/juju/core/network"
    16  )
    17  
    18  var logger = loggo.GetLogger("juju.network")
    19  
    20  // UnknownId can be used whenever an Id is needed but not known.
    21  const UnknownId = ""
    22  
    23  // DefaultLXDBridge is the bridge that gets used for LXD containers
    24  const DefaultLXDBridge = "lxdbr0"
    25  
    26  // DefaultKVMBridge is the bridge that is set up by installing libvirt-bin
    27  // Note: we don't import this from 'container' to avoid import loops
    28  const DefaultKVMBridge = "virbr0"
    29  
    30  // DefaultDockerBridge is the bridge that is set up by Docker.
    31  const DefaultDockerBridge = "docker0"
    32  
    33  // DeviceToBridge gives the information about a particular device that
    34  // should be bridged.
    35  type DeviceToBridge struct {
    36  	// DeviceName is the name of the device on the machine that should
    37  	// be bridged.
    38  	DeviceName string
    39  
    40  	// BridgeName is the name of the bridge that we want created.
    41  	BridgeName string
    42  
    43  	// MACAddress is the MAC address of the device to be bridged
    44  	MACAddress string
    45  }
    46  
    47  // AddressesForInterfaceName returns the addresses in string form for the
    48  // given interface name. It's exported to facilitate cross-package testing.
    49  var AddressesForInterfaceName = func(name string) ([]string, error) {
    50  	iface, err := net.InterfaceByName(name)
    51  	if err != nil {
    52  		return nil, errors.Trace(err)
    53  	}
    54  
    55  	addrs, err := iface.Addrs()
    56  	if err != nil {
    57  		return nil, errors.Trace(err)
    58  	}
    59  
    60  	res := make([]string, len(addrs))
    61  	for i, addr := range addrs {
    62  		res[i] = addr.String()
    63  	}
    64  	return res, nil
    65  }
    66  
    67  type ipNetAndName struct {
    68  	ipnet *net.IPNet
    69  	name  string
    70  }
    71  
    72  func addrMapToIPNetAndName(bridgeToAddrs map[string][]string) []ipNetAndName {
    73  	ipNets := make([]ipNetAndName, 0, len(bridgeToAddrs))
    74  	for bridgeName, addrList := range bridgeToAddrs {
    75  		for _, addr := range addrList {
    76  			ip, ipNet, err := net.ParseCIDR(addr)
    77  			if err != nil {
    78  				// Not a valid CIDR, check as an IP
    79  				ip = net.ParseIP(addr)
    80  			}
    81  			if ip == nil {
    82  				logger.Debugf("cannot parse %q as IP, ignoring", addr)
    83  				continue
    84  			}
    85  			if ipNet == nil {
    86  				// convert the IP into an IPNet
    87  				if ip.To4() != nil {
    88  					_, ipNet, err = net.ParseCIDR(ip.String() + "/32")
    89  					if err != nil {
    90  						logger.Debugf("error creating a /32 CIDR for %q", addr)
    91  					}
    92  				} else if ip.To16() != nil {
    93  					_, ipNet, err = net.ParseCIDR(ip.String() + "/128")
    94  					if err != nil {
    95  						logger.Debugf("error creating a /128 CIDR for %q", addr)
    96  					}
    97  				} else {
    98  					logger.Debugf("failed to convert %q to a v4 or v6 address, ignoring", addr)
    99  				}
   100  			}
   101  			ipNets = append(ipNets, ipNetAndName{ipnet: ipNet, name: bridgeName})
   102  		}
   103  	}
   104  	return ipNets
   105  }
   106  
   107  // filterAddrs looks at all of the addresses in allAddresses and removes ones
   108  // that line up with removeAddresses. Note that net.Addr may be just an IP or
   109  // may be a CIDR.  removeAddresses should be a map of 'bridge name' to list of
   110  // addresses, so that we can report why the address was filtered.
   111  func filterAddrs(
   112  	allAddresses []corenetwork.ProviderAddress, removeAddresses map[string][]string,
   113  ) []corenetwork.ProviderAddress {
   114  	filtered := make([]corenetwork.ProviderAddress, 0, len(allAddresses))
   115  	// Convert all
   116  	ipNets := addrMapToIPNetAndName(removeAddresses)
   117  	for _, addr := range allAddresses {
   118  		bridgeName := ""
   119  		// Then check if it is in one of the CIDRs
   120  		ip := net.ParseIP(addr.Value)
   121  		if ip == nil {
   122  			logger.Debugf("not filtering invalid IP: %q", addr.Value)
   123  		} else {
   124  			for _, ipNetName := range ipNets {
   125  				if ipNetName.ipnet.Contains(ip) {
   126  					bridgeName = ipNetName.name
   127  					break
   128  				}
   129  			}
   130  		}
   131  		if bridgeName == "" {
   132  			logger.Debugf("including address %v for machine", addr)
   133  			filtered = append(filtered, addr)
   134  		} else {
   135  			logger.Debugf("filtering %q address %s for machine", bridgeName, addr.String())
   136  		}
   137  	}
   138  	return filtered
   139  }
   140  
   141  func gatherBridgeAddresses(bridgeName string, toRemove map[string][]string) {
   142  	addrs, err := AddressesForInterfaceName(bridgeName)
   143  	if err != nil {
   144  		logger.Debugf("cannot get %q addresses: %v (ignoring)", bridgeName, err)
   145  		return
   146  	}
   147  	logger.Debugf("%q has addresses %v", bridgeName, addrs)
   148  	toRemove[bridgeName] = addrs
   149  }
   150  
   151  // FilterBridgeAddresses removes addresses seen as a Bridge address
   152  // (the IP address used only to connect to local containers),
   153  // rather than a remote accessible address.
   154  // This includes addresses used by the local Fan network.
   155  func FilterBridgeAddresses(addresses corenetwork.ProviderAddresses) corenetwork.ProviderAddresses {
   156  	addressesToRemove := make(map[string][]string)
   157  	gatherBridgeAddresses(DefaultLXDBridge, addressesToRemove)
   158  	gatherBridgeAddresses(DefaultKVMBridge, addressesToRemove)
   159  	filtered := filterAddrs(addresses, addressesToRemove)
   160  	logger.Debugf("addresses after filtering: %v", filtered)
   161  	return filtered
   162  }
   163  
   164  // QuoteSpaces takes a slice of space names, and returns a nicely formatted
   165  // form so they show up legible in log messages, etc.
   166  func QuoteSpaces(vals []string) string {
   167  	out := []string{}
   168  	if len(vals) == 0 {
   169  		return "<none>"
   170  	}
   171  	for _, space := range vals {
   172  		out = append(out, fmt.Sprintf("%q", space))
   173  	}
   174  	return strings.Join(out, ", ")
   175  }
   176  
   177  // QuoteSpaceSet is the same as QuoteSpaces, but ensures that a set.Strings
   178  // gets sorted values output.
   179  func QuoteSpaceSet(vals set.Strings) string {
   180  	return QuoteSpaces(vals.SortedValues())
   181  }
   182  
   183  // firstLastAddresses returns the first and last addresses of the subnet.
   184  func firstLastAddresses(subnet *net.IPNet) (net.IP, net.IP) {
   185  	firstIP := subnet.IP
   186  	lastIP := make([]byte, len(firstIP))
   187  	copy(lastIP, firstIP)
   188  
   189  	for i, b := range lastIP {
   190  		lastIP[i] = b ^ (^subnet.Mask[i])
   191  	}
   192  	return firstIP, lastIP
   193  }
   194  
   195  func cidrContains(cidr *net.IPNet, subnet *net.IPNet) bool {
   196  	first, last := firstLastAddresses(subnet)
   197  	return cidr.Contains(first) && cidr.Contains(last)
   198  }
   199  
   200  // SubnetInAnyRange returns true if the subnet's address range is fully
   201  // contained in any of the specified subnet blocks.
   202  func SubnetInAnyRange(cidrs []*net.IPNet, subnet *net.IPNet) bool {
   203  	for _, cidr := range cidrs {
   204  		if cidrContains(cidr, subnet) {
   205  			return true
   206  		}
   207  	}
   208  	return false
   209  }