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 }